no message
This commit is contained in:
4
cocos/platform/android/libcocosxr/AndroidManifest.xml
Normal file
4
cocos/platform/android/libcocosxr/AndroidManifest.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.cocos.lib.xr">
|
||||
|
||||
</manifest>
|
||||
37
cocos/platform/android/libcocosxr/build.gradle
Normal file
37
cocos/platform/android/libcocosxr/build.gradle
Normal file
@@ -0,0 +1,37 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion PROP_COMPILE_SDK_VERSION.toInteger()
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion PROP_MIN_SDK_VERSION
|
||||
targetSdkVersion PROP_TARGET_SDK_VERSION
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
|
||||
sourceSets.main {
|
||||
java.srcDirs "src"
|
||||
res.srcDirs 'res'
|
||||
jniLibs.srcDirs 'libs'
|
||||
manifest.srcFile "AndroidManifest.xml"
|
||||
}
|
||||
|
||||
buildDir = new File(rootProject.buildDir, project.name)
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar','*.aar'])
|
||||
implementation project(':libcocos')
|
||||
}
|
||||
21
cocos/platform/android/libcocosxr/proguard-rules.pro
vendored
Normal file
21
cocos/platform/android/libcocosxr/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -0,0 +1,212 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
|
||||
*
|
||||
* http://www.cocos.com
|
||||
*
|
||||
* 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 com.cocos.lib.xr;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class CocosXRApi {
|
||||
private static final String TAG = "CocosXRApi";
|
||||
private final static String ACTION_ADB_CMD = "com.cocosxr.adb.cmd";
|
||||
private enum ActivityLifecycleType {
|
||||
UnKnown,
|
||||
Created,
|
||||
Started,
|
||||
Resumed,
|
||||
Paused,
|
||||
Stopped,
|
||||
SaveInstanceState,
|
||||
Destroyed
|
||||
}
|
||||
|
||||
private final static CocosXRApi instance = new CocosXRApi();
|
||||
|
||||
/**
|
||||
* adb shell am broadcast -a com.cocosxr.adb.cmd --es CMD_KEY LOG --ei CMD_VALUE 1
|
||||
* adb shell am broadcast -a com.cocosxr.adb.cmd --es CMD_KEY LOG --es CMD_VALUE abc
|
||||
*/
|
||||
private class CocosXRActionReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (ACTION_ADB_CMD.equals(intent.getAction())) {
|
||||
// adb cmd
|
||||
if (intent.getExtras() == null) {
|
||||
Log.w(TAG, "[CocosXRActionReceiver] intent.getExtras() == null");
|
||||
return;
|
||||
}
|
||||
Object cmdKey = intent.getExtras().get("CMD_KEY");
|
||||
String key = cmdKey == null ? "" : cmdKey.toString();
|
||||
Object cmdValue = intent.getExtras().get("CMD_VALUE");
|
||||
String valueStr = null;
|
||||
if (cmdValue instanceof Integer) {
|
||||
valueStr = String.valueOf(intent.getIntExtra("CMD_VALUE", Integer.MIN_VALUE));
|
||||
} else if (cmdValue instanceof String) {
|
||||
valueStr = intent.getStringExtra("CMD_VALUE");
|
||||
}
|
||||
|
||||
try {
|
||||
onAdbCmd(key, valueStr);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CocosXRApi() {
|
||||
}
|
||||
|
||||
public static CocosXRApi getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private Application application;
|
||||
private WeakReference<Activity> activityWeakReference;
|
||||
private Context applicationContext;
|
||||
private Application.ActivityLifecycleCallbacks activityLifecycleCallbacks;
|
||||
private CocosXRActionReceiver actionReceiver;
|
||||
private CocosXRWebViewManager webViewManager;
|
||||
|
||||
public void onCreate(Activity activity) {
|
||||
activityWeakReference = new WeakReference<>(activity);
|
||||
application = activity.getApplication();
|
||||
applicationContext = activity.getApplicationContext();
|
||||
webViewManager = new CocosXRWebViewManager();
|
||||
if (activityLifecycleCallbacks == null) {
|
||||
activityLifecycleCallbacks = new Application.ActivityLifecycleCallbacks() {
|
||||
@Override
|
||||
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
|
||||
try {
|
||||
onActivityLifecycleCallback(ActivityLifecycleType.Created.ordinal(), activity.getLocalClassName());
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStarted(Activity activity) {
|
||||
webViewManager.onCreate(activity);
|
||||
try {
|
||||
onActivityLifecycleCallback(ActivityLifecycleType.Started.ordinal(), activity.getLocalClassName());
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResumed(Activity activity) {
|
||||
try {
|
||||
onActivityLifecycleCallback(ActivityLifecycleType.Resumed.ordinal(), activity.getLocalClassName());
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
webViewManager.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPaused(Activity activity) {
|
||||
try {
|
||||
onActivityLifecycleCallback(ActivityLifecycleType.Paused.ordinal(), activity.getLocalClassName());
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
webViewManager.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStopped(Activity activity) {
|
||||
try {
|
||||
onActivityLifecycleCallback(ActivityLifecycleType.Stopped.ordinal(), activity.getLocalClassName());
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
|
||||
try {
|
||||
onActivityLifecycleCallback(ActivityLifecycleType.SaveInstanceState.ordinal(), activity.getLocalClassName());
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed(Activity activity) {
|
||||
try {
|
||||
onActivityLifecycleCallback(ActivityLifecycleType.Destroyed.ordinal(), activity.getLocalClassName());
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
webViewManager.onDestroy();
|
||||
}
|
||||
};
|
||||
}
|
||||
application.registerActivityLifecycleCallbacks(activityLifecycleCallbacks);
|
||||
|
||||
if(actionReceiver != null) {
|
||||
applicationContext.unregisterReceiver(actionReceiver);
|
||||
actionReceiver =null;
|
||||
}
|
||||
|
||||
actionReceiver = new CocosXRActionReceiver();
|
||||
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(ACTION_ADB_CMD);
|
||||
applicationContext.registerReceiver(actionReceiver, intentFilter);
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
if (application != null && activityLifecycleCallbacks != null) {
|
||||
application.unregisterActivityLifecycleCallbacks(activityLifecycleCallbacks);
|
||||
activityLifecycleCallbacks = null;
|
||||
}
|
||||
|
||||
if(applicationContext != null && actionReceiver != null) {
|
||||
applicationContext.unregisterReceiver(actionReceiver);
|
||||
actionReceiver = null;
|
||||
}
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
public Activity getActivity() {
|
||||
return activityWeakReference.get();
|
||||
}
|
||||
|
||||
// native
|
||||
private native void onActivityLifecycleCallback(int id, String activityClassName);
|
||||
private native void onAdbCmd(String key, String value);
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
|
||||
*
|
||||
* http://www.cocos.com
|
||||
*
|
||||
* 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 com.cocos.lib.xr;
|
||||
|
||||
import android.opengl.GLES11Ext;
|
||||
import android.opengl.GLES30;
|
||||
import android.util.Log;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
public class CocosXRGLHelper {
|
||||
private static final String TAG = "CocosXRGLHelper";
|
||||
|
||||
public static int loadShader(int shaderType, String source) {
|
||||
int shader = GLES30.glCreateShader(shaderType);
|
||||
if (shader != 0) {
|
||||
GLES30.glShaderSource(shader, source);
|
||||
GLES30.glCompileShader(shader);
|
||||
int[] compiled = new int[1];
|
||||
GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compiled, 0);
|
||||
if (compiled[0] == 0) {
|
||||
Log.e(TAG, "Could not compile shader " + shaderType + ":");
|
||||
Log.e(TAG, GLES30.glGetShaderInfoLog(shader));
|
||||
GLES30.glDeleteShader(shader);
|
||||
shader = 0;
|
||||
}
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
public static int createProgram(String vertexSource, String fragmentSource) {
|
||||
int vertexShader = loadShader(GLES30.GL_VERTEX_SHADER, vertexSource);
|
||||
if (vertexShader == 0) {
|
||||
return 0;
|
||||
}
|
||||
checkGLError("vertex shader");
|
||||
|
||||
int pixelShader = loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentSource);
|
||||
if (pixelShader == 0) {
|
||||
return 0;
|
||||
}
|
||||
checkGLError("fragment shader");
|
||||
int program = GLES30.glCreateProgram();
|
||||
if (program != 0) {
|
||||
GLES30.glAttachShader(program, vertexShader);
|
||||
checkGLError("glAttachShader vertexShader");
|
||||
GLES30.glAttachShader(program, pixelShader);
|
||||
checkGLError("glAttachShader pixelShader");
|
||||
GLES30.glLinkProgram(program);
|
||||
GLES30.glDetachShader(program, vertexShader);
|
||||
GLES30.glDetachShader(program, pixelShader);
|
||||
int[] linkStatus = new int[1];
|
||||
GLES30.glGetProgramiv(program, GLES30.GL_LINK_STATUS, linkStatus, 0);
|
||||
if (linkStatus[0] != GLES30.GL_TRUE) {
|
||||
Log.e(TAG, "Could not link program: ");
|
||||
Log.e(TAG, GLES30.glGetProgramInfoLog(program));
|
||||
GLES30.glDeleteProgram(program);
|
||||
program = 0;
|
||||
}
|
||||
}
|
||||
return program;
|
||||
}
|
||||
|
||||
private static final int SIZE_OF_FLOAT = 4;
|
||||
private static final int SIZE_OF_SHORT = 2;
|
||||
public static FloatBuffer createFloatBuffer(float[] array) {
|
||||
ByteBuffer bb = ByteBuffer.allocateDirect(array.length * SIZE_OF_FLOAT);
|
||||
bb.order(ByteOrder.nativeOrder());
|
||||
FloatBuffer fb = bb.asFloatBuffer();
|
||||
fb.put(array);
|
||||
fb.position(0);
|
||||
return fb;
|
||||
}
|
||||
|
||||
public static ShortBuffer createShortBuffer(short[] array) {
|
||||
ByteBuffer bb = ByteBuffer.allocateDirect(array.length * SIZE_OF_SHORT);
|
||||
bb.order(ByteOrder.nativeOrder());
|
||||
ShortBuffer fb = bb.asShortBuffer();
|
||||
fb.put(array);
|
||||
fb.position(0);
|
||||
return fb;
|
||||
}
|
||||
|
||||
public static void checkGLError(String operation) {
|
||||
int errerCode = GLES30.glGetError();
|
||||
if (errerCode != GLES30.GL_NO_ERROR) {
|
||||
String msg = operation + ":error" + errerCode;
|
||||
Log.e(TAG, msg);
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static int createOESTexture() {
|
||||
int[] oesTex = new int[1];
|
||||
GLES30.glGenTextures(1, oesTex, 0);
|
||||
GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, oesTex[0]);
|
||||
GLES30.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR);
|
||||
GLES30.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
|
||||
GLES30.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE);
|
||||
GLES30.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE);
|
||||
GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
|
||||
return oesTex[0];
|
||||
}
|
||||
|
||||
public static class GLQuadScreen {
|
||||
final String quadMeshVertexShader_EXT =
|
||||
" #version 310 es\n in vec4 vertexPosition; \n "
|
||||
+ "in vec2 vertexTexCoord; \n "
|
||||
+ "out vec2 texCoord; \n "
|
||||
+ "uniform mat4 textureMatrix;\n"
|
||||
+ "void main() \n "
|
||||
+ "{"
|
||||
+ " gl_Position = vertexPosition; \n "
|
||||
+ " vec4 temp = vec4(vertexTexCoord.x, vertexTexCoord.y, 0, 1); \n"
|
||||
+ " texCoord = (textureMatrix * temp).xy; \n "
|
||||
+ "}";
|
||||
|
||||
final String quadFragmentShader_EXT =
|
||||
"#version 310 es\n #extension GL_OES_EGL_image_external_essl3 : require \n"
|
||||
+ "precision mediump float; \n"
|
||||
+ "in vec2 texCoord; \n"
|
||||
+ "uniform samplerExternalOES texSampler2D; \n"
|
||||
+ "out vec4 frag_color;\n"
|
||||
+ "void main() \n"
|
||||
+ "{ \n"
|
||||
+ " frag_color = texture(texSampler2D, texCoord); \n"
|
||||
+ "}";
|
||||
float[] orthoQuadVertices = {
|
||||
-1.0f, -1.0f, 0.0f, 1.0f,
|
||||
1.0f, -1.0f, 0.0f, 1.0f,
|
||||
1.0f, 1.0f, 0.0f, 1.0f,
|
||||
-1.0f, 1.0f, 0.0f, 1.0f};
|
||||
|
||||
float[] orthoQuadTexCoords_EXT = {
|
||||
0.0f, 1.0f,
|
||||
1.0f, 1.0f,
|
||||
1.0f, 0.0f,
|
||||
0.0f, 0.0f
|
||||
};
|
||||
|
||||
short[] orthoQuadIndices = {0, 1, 2, 2, 3, 0};
|
||||
|
||||
ShortBuffer indexBuffer;
|
||||
FloatBuffer vetexBuffer;
|
||||
FloatBuffer textureCoordBuffer;
|
||||
|
||||
int program = -1;
|
||||
int vertexHandle = 0;
|
||||
int textureCoordHandle = 0;
|
||||
int textureMatrixHandle = 0;
|
||||
|
||||
public GLQuadScreen() {
|
||||
}
|
||||
|
||||
public void initShader() {
|
||||
if(program == -1) {
|
||||
vetexBuffer = createFloatBuffer(orthoQuadVertices);
|
||||
textureCoordBuffer = createFloatBuffer(orthoQuadTexCoords_EXT);
|
||||
indexBuffer = createShortBuffer(orthoQuadIndices);
|
||||
program = createProgram(quadMeshVertexShader_EXT, quadFragmentShader_EXT);
|
||||
GLES30.glUseProgram(program);
|
||||
vertexHandle = GLES30.glGetAttribLocation(program, "vertexPosition");
|
||||
textureCoordHandle = GLES30.glGetAttribLocation(program, "vertexTexCoord");
|
||||
textureMatrixHandle = GLES30.glGetUniformLocation(program, "textureMatrix");
|
||||
GLES30.glUseProgram(0);
|
||||
}
|
||||
Log.d(TAG, "GLQuadScreen Shader Info:" + program + "," + vertexHandle + "," + textureCoordHandle);
|
||||
}
|
||||
|
||||
public void release() {
|
||||
GLES30.glDeleteProgram(program);
|
||||
program = 0;
|
||||
}
|
||||
|
||||
public void draw(int oesTextureId, float[] videoTransformMatrix) {
|
||||
if(program == -1) {
|
||||
initShader();
|
||||
return;
|
||||
}
|
||||
|
||||
GLES30.glUseProgram(program);
|
||||
GLES30.glVertexAttribPointer(vertexHandle, 4, GLES30.GL_FLOAT, false, 0, vetexBuffer);
|
||||
GLES30.glVertexAttribPointer(textureCoordHandle, 2, GLES30.GL_FLOAT, false, 0, textureCoordBuffer);
|
||||
|
||||
GLES30.glEnableVertexAttribArray(vertexHandle);
|
||||
GLES30.glEnableVertexAttribArray(textureCoordHandle);
|
||||
|
||||
GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
|
||||
GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, oesTextureId);
|
||||
|
||||
GLES30.glUniformMatrix4fv(textureMatrixHandle, 1, false, videoTransformMatrix, 0);
|
||||
|
||||
GLES30.glDrawElements(GLES30.GL_TRIANGLES, indexBuffer.capacity(), GLES30.GL_UNSIGNED_SHORT, indexBuffer);
|
||||
|
||||
GLES30.glDisableVertexAttribArray(vertexHandle);
|
||||
GLES30.glDisableVertexAttribArray(textureCoordHandle);
|
||||
GLES30.glUseProgram(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,484 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
|
||||
*
|
||||
* http://www.cocos.com
|
||||
*
|
||||
* 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 com.cocos.lib.xr;
|
||||
|
||||
import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.opengl.EGL14;
|
||||
import android.opengl.EGLConfig;
|
||||
import android.opengl.EGLContext;
|
||||
import android.opengl.EGLDisplay;
|
||||
import android.opengl.EGLSurface;
|
||||
import android.opengl.GLES30;
|
||||
import android.util.Log;
|
||||
import com.cocos.lib.JsbBridgeWrapper;
|
||||
import com.cocos.lib.xr.permission.CocosXRPermissionHelper;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class CocosXRVideoManager {
|
||||
private static final String TAG = "CocosXRVideoManager";
|
||||
private final static CocosXRVideoManager instance = new CocosXRVideoManager();
|
||||
|
||||
class VideoEventData {
|
||||
public String headTag;
|
||||
public int eventId;
|
||||
public String videoPlayerHandleKey;
|
||||
public int videoSourceType;
|
||||
public String videoSourceUrl;
|
||||
public int videoWidth;
|
||||
public int videoHeight;
|
||||
public int videoTextureId;
|
||||
public boolean isLoop;
|
||||
public int seekToMsec;
|
||||
public String eventName;
|
||||
public float volume;
|
||||
public float playbackSpeed = 1.0f;
|
||||
|
||||
public VideoEventData(String data) {
|
||||
String[] dataArray = data.split("&");
|
||||
if (dataArray[0].equals(XR_VIDEO_EVENT_TAG)) {
|
||||
this.eventId = Integer.parseInt(dataArray[1]);
|
||||
this.eventName = dataArray[2];
|
||||
this.videoPlayerHandleKey = dataArray[3];
|
||||
switch (this.eventId) {
|
||||
case VIDEO_EVENT_PREPARE: {
|
||||
this.videoSourceType = Integer.parseInt(dataArray[4]);
|
||||
this.videoSourceUrl = dataArray[5];
|
||||
this.isLoop = Integer.parseInt(dataArray[6]) == 1;
|
||||
this.volume = Float.parseFloat(dataArray[7]);
|
||||
this.playbackSpeed = Float.parseFloat(dataArray[8]);
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDEO_EVENT_SEEK_TO: {
|
||||
this.seekToMsec = Integer.parseInt(dataArray[4]);
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDEO_EVENT_SET_LOOP: {
|
||||
this.isLoop = Integer.parseInt(dataArray[4]) == 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDEO_EVENT_SET_VOLUME: {
|
||||
this.volume = Float.parseFloat(dataArray[4]);
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDEO_EVENT_SET_TEXTURE_INFO: {
|
||||
this.videoWidth = Integer.parseInt(dataArray[4]);
|
||||
this.videoHeight = Integer.parseInt(dataArray[5]);
|
||||
this.videoTextureId = Integer.parseInt(dataArray[6]);
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDEO_EVENT_SET_SPEED: {
|
||||
this.playbackSpeed = Float.parseFloat(dataArray[4]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CocosXRVideoManager() {
|
||||
}
|
||||
|
||||
public static CocosXRVideoManager getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
final int MAX_COUNT = 3;
|
||||
public static final int VIDEO_SOURCE_TYPE_LOCAL = 1;
|
||||
public static final int VIDEO_SOURCE_TYPE_REMOTE = 2;
|
||||
|
||||
public static final int VIDEO_EVENT_INVALID = 1;
|
||||
//xr-event&id&handle&
|
||||
public static final int VIDEO_EVENT_PREPARE = 2;
|
||||
public static final int VIDEO_EVENT_PLAY = 3;
|
||||
public static final int VIDEO_EVENT_PAUSE = 4;
|
||||
public static final int VIDEO_EVENT_STOP = 5;
|
||||
public static final int VIDEO_EVENT_RESET = 6;
|
||||
public static final int VIDEO_EVENT_DESTROY = 7;
|
||||
|
||||
public static final int VIDEO_EVENT_GET_POSITION = 30;
|
||||
public static final int VIDEO_EVENT_GET_DURATION = 31;
|
||||
public static final int VIDEO_EVENT_GET_IS_PALYING = 32;
|
||||
public static final int VIDEO_EVENT_GET_IS_LOOPING = 33;
|
||||
|
||||
public static final int VIDEO_EVENT_SET_LOOP = 50;
|
||||
public static final int VIDEO_EVENT_SEEK_TO = 51;
|
||||
public static final int VIDEO_EVENT_SET_VOLUME = 52;
|
||||
public static final int VIDEO_EVENT_SET_TEXTURE_INFO = 53;
|
||||
public static final int VIDEO_EVENT_SET_SPEED = 54;
|
||||
|
||||
public static final int VIDEO_EVENT_MEDIA_PLAYER_PREPARED = 100;
|
||||
public static final int VIDEO_EVENT_MEDIA_PLAYER_PLAY_COMPLETE = 101;
|
||||
public static final int VIDEO_EVENT_MEDIA_PLAYER_SEEK_COMPLETE = 102;
|
||||
public static final int VIDEO_EVENT_MEDIA_PLAYER_ERROR = 103;
|
||||
public static final int VIDEO_EVENT_MEDIA_PLAYER_VIDEO_SIZE = 104;
|
||||
public static final int VIDEO_EVENT_MEDIA_PLAYER_ON_INFO = 105;
|
||||
|
||||
private WeakReference<Activity> activityWeakReference;
|
||||
final String XR_VIDEO_PLAYER_EVENT_NAME = "xr-video-player:";
|
||||
//xr-event&handle&id&url&512&512&1
|
||||
final String XR_VIDEO_EVENT_TAG = "xr-event";
|
||||
|
||||
HashMap<String, CocosXRVideoPlayer> xrVideoPlayerHashMap = new HashMap<>();
|
||||
HashMap<String, ArrayList<String>> cachedScriptEventHashMap = new HashMap<>();
|
||||
CocosXRVideoGLThread videoGLThread = null;
|
||||
boolean isPaused = false;
|
||||
public void onCreate(Activity activity) {
|
||||
activityWeakReference = new WeakReference<>(activity);
|
||||
for (int i = 0; i < MAX_COUNT; i++) {
|
||||
JsbBridgeWrapper.getInstance().addScriptEventListener(XR_VIDEO_PLAYER_EVENT_NAME + i, eventData -> {
|
||||
if(isPaused) {
|
||||
return;
|
||||
}
|
||||
processVideoEvent(eventData);
|
||||
});
|
||||
}
|
||||
JsbBridgeWrapper.getInstance().addScriptEventListener(CocosXRPermissionHelper.XR_PERMISSION_EVENT_NAME, CocosXRPermissionHelper::onScriptEvent);
|
||||
|
||||
CocosXRApi.getInstance().onCreate(activity);
|
||||
}
|
||||
|
||||
public void onResume() {
|
||||
Log.d(TAG, "onResume");
|
||||
isPaused = false;
|
||||
if(videoGLThread != null) {
|
||||
videoGLThread.onResume();
|
||||
}
|
||||
Set<Map.Entry<String, ArrayList<String>>> entrySets = cachedScriptEventHashMap.entrySet();
|
||||
for (Map.Entry<String, ArrayList<String>> entrySet : entrySets) {
|
||||
if (entrySet.getKey() != null && entrySet.getValue() != null) {
|
||||
for (String data : entrySet.getValue()) {
|
||||
Log.d(TAG, "onResume.dispatchEventToScript:" + entrySet.getKey() + ":" + data);
|
||||
JsbBridgeWrapper.getInstance().dispatchEventToScript(entrySet.getKey(), data);
|
||||
}
|
||||
}
|
||||
}
|
||||
cachedScriptEventHashMap.clear();
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
Log.d(TAG, "onPause");
|
||||
isPaused = true;
|
||||
if(videoGLThread != null) {
|
||||
videoGLThread.onPause();
|
||||
}
|
||||
|
||||
Set<Map.Entry<String, CocosXRVideoPlayer>> entrySets = xrVideoPlayerHashMap.entrySet();
|
||||
for (Map.Entry<String, CocosXRVideoPlayer> entrySet : entrySets) {
|
||||
if(entrySet.getValue() != null) {
|
||||
entrySet.getValue().pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void sendVideoEvent(VideoEventData videoEventData, String... eventData) {
|
||||
sendVideoEvent(videoEventData.eventId, videoEventData.eventName, videoEventData.videoPlayerHandleKey, eventData);
|
||||
}
|
||||
|
||||
public void sendVideoEvent(int eventId, String eventName, String videoPlayerHandleKey, String... eventData) {
|
||||
StringBuilder data = new StringBuilder("xr-event&" + eventId + "&" + eventName + '&' + videoPlayerHandleKey);
|
||||
if (eventData.length > 0) {
|
||||
for (String evtData : eventData) {
|
||||
data.append("&").append(evtData);
|
||||
}
|
||||
}
|
||||
|
||||
if(videoGLThread == null) return;
|
||||
if(isPaused) {
|
||||
Log.e(TAG, "sendVideoEvent failed, because is paused !!! [" + data + "]");
|
||||
if (!cachedScriptEventHashMap.containsKey(eventName)) {
|
||||
cachedScriptEventHashMap.put(eventName, new ArrayList<>());
|
||||
}
|
||||
Objects.requireNonNull(cachedScriptEventHashMap.get(eventName)).add(data.toString());
|
||||
return;
|
||||
}
|
||||
JsbBridgeWrapper.getInstance().dispatchEventToScript(eventName, data.toString());
|
||||
}
|
||||
|
||||
private void processVideoEvent(String eventData) {
|
||||
VideoEventData videoEventData = new VideoEventData(eventData);
|
||||
if (videoEventData.eventId == VIDEO_EVENT_PREPARE) {
|
||||
CocosXRVideoPlayer videoPlayer = xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey);
|
||||
if (videoPlayer == null) {
|
||||
videoPlayer = new CocosXRVideoPlayer(activityWeakReference, videoEventData.videoPlayerHandleKey, videoEventData.eventName);
|
||||
videoPlayer.prepare(videoEventData);
|
||||
xrVideoPlayerHashMap.put(videoEventData.videoPlayerHandleKey, videoPlayer);
|
||||
} else {
|
||||
videoPlayer.prepare(videoEventData);
|
||||
}
|
||||
|
||||
if (videoGLThread == null) {
|
||||
videoGLThread = new CocosXRVideoGLThread();
|
||||
videoGLThread.start();
|
||||
}
|
||||
} else if (videoEventData.eventId == VIDEO_EVENT_PLAY) {
|
||||
Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).play();
|
||||
} else if (videoEventData.eventId == VIDEO_EVENT_PAUSE) {
|
||||
Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).pause();
|
||||
} else if (videoEventData.eventId == VIDEO_EVENT_STOP) {
|
||||
Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).stop();
|
||||
} else if (videoEventData.eventId == VIDEO_EVENT_RESET) {
|
||||
Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).reset();
|
||||
} else if (videoEventData.eventId == VIDEO_EVENT_DESTROY) {
|
||||
Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).release();
|
||||
videoGLThread.queueEvent(() -> {
|
||||
Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).onGLDestroy();
|
||||
xrVideoPlayerHashMap.remove(videoEventData.videoPlayerHandleKey);
|
||||
});
|
||||
} else if (videoEventData.eventId == VIDEO_EVENT_GET_POSITION) {
|
||||
int position = Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).getCurrentPosition();
|
||||
sendVideoEvent(videoEventData, String.valueOf(position));
|
||||
} else if (videoEventData.eventId == VIDEO_EVENT_GET_DURATION) {
|
||||
int duration = Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).getDuration();
|
||||
sendVideoEvent(videoEventData, String.valueOf(duration));
|
||||
} else if (videoEventData.eventId == VIDEO_EVENT_GET_IS_PALYING) {
|
||||
boolean isPlaying = Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).isPlaying();
|
||||
sendVideoEvent(videoEventData, String.valueOf(isPlaying));
|
||||
} else if (videoEventData.eventId == VIDEO_EVENT_GET_IS_LOOPING) {
|
||||
boolean isLooping = Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).isLooping();
|
||||
sendVideoEvent(videoEventData, String.valueOf(isLooping));
|
||||
} else if (videoEventData.eventId == VIDEO_EVENT_SET_LOOP) {
|
||||
Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).setLooping(videoEventData.isLoop);
|
||||
} else if (videoEventData.eventId == VIDEO_EVENT_SEEK_TO) {
|
||||
Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).seekTo(videoEventData.seekToMsec);
|
||||
} else if(videoEventData.eventId == VIDEO_EVENT_SET_VOLUME) {
|
||||
Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).setVolume(videoEventData.volume);
|
||||
} else if(videoEventData.eventId == VIDEO_EVENT_SET_TEXTURE_INFO) {
|
||||
Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).setTextureInfo(videoEventData.videoWidth, videoEventData.videoHeight, videoEventData.videoTextureId);
|
||||
} else if(videoEventData.eventId == VIDEO_EVENT_SET_SPEED) {
|
||||
Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).setPlaybackSpeed(videoEventData.playbackSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
Log.d(TAG, "onDestroy:" + xrVideoPlayerHashMap.size());
|
||||
CocosXRApi.getInstance().onDestroy();
|
||||
if(videoGLThread != null) {
|
||||
videoGLThread.onDestroy();
|
||||
videoGLThread = null;
|
||||
}
|
||||
for (int i = 0; i < MAX_COUNT; i++) {
|
||||
JsbBridgeWrapper.getInstance().removeAllListenersForEvent(XR_VIDEO_PLAYER_EVENT_NAME + i);
|
||||
}
|
||||
JsbBridgeWrapper.getInstance().removeAllListenersForEvent(CocosXRPermissionHelper.XR_PERMISSION_EVENT_NAME);
|
||||
Set<Map.Entry<String, CocosXRVideoPlayer>> entrySets = xrVideoPlayerHashMap.entrySet();
|
||||
for (Map.Entry<String, CocosXRVideoPlayer> entrySet : entrySets) {
|
||||
entrySet.getValue().release();
|
||||
}
|
||||
xrVideoPlayerHashMap.clear();
|
||||
cachedScriptEventHashMap.clear();
|
||||
}
|
||||
|
||||
class CocosXRVideoGLThread extends Thread {
|
||||
private final ReentrantLock lockObj = new ReentrantLock(true);
|
||||
private final Condition pauseCondition = lockObj.newCondition();
|
||||
private boolean requestPaused = false;
|
||||
private boolean requestExited = false;
|
||||
long lastTickTime = System.nanoTime();
|
||||
private final ArrayList<Runnable> mEventQueue = new ArrayList<>();
|
||||
|
||||
EGLContext eglContext;
|
||||
EGLDisplay eglDisplay;
|
||||
EGLSurface pBufferSurface;
|
||||
EGLContext parentContext;
|
||||
int renderTargetFboId;
|
||||
|
||||
CocosXRVideoGLThread() {
|
||||
parentContext = EGL14.eglGetCurrentContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
init();
|
||||
while (true) {
|
||||
lockObj.lock();
|
||||
try {
|
||||
if (requestExited) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (requestPaused) {
|
||||
pauseCondition.await();
|
||||
}
|
||||
|
||||
if (requestExited) {
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
lockObj.unlock();
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
if (!mEventQueue.isEmpty()) {
|
||||
Runnable event = mEventQueue.remove(0);
|
||||
if (event != null) {
|
||||
event.run();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tick();
|
||||
}
|
||||
|
||||
exit();
|
||||
}
|
||||
|
||||
void init() {
|
||||
eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
|
||||
int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL14.EGL_NONE};
|
||||
int[] attrList = new int[] {EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT,
|
||||
EGL14.EGL_RENDERABLE_TYPE, 0x00000040,
|
||||
EGL14.EGL_RED_SIZE, 8,
|
||||
EGL14.EGL_GREEN_SIZE, 8,
|
||||
EGL14.EGL_BLUE_SIZE, 8,
|
||||
EGL14.EGL_DEPTH_SIZE, 8,
|
||||
EGL14.EGL_SAMPLE_BUFFERS, 1,
|
||||
EGL14.EGL_SAMPLES, 1,
|
||||
EGL14.EGL_STENCIL_SIZE, 0,
|
||||
EGL14.EGL_NONE};
|
||||
EGLConfig[] configOut = new EGLConfig[1];
|
||||
int[] configNumOut = new int[1];
|
||||
EGL14.eglChooseConfig(eglDisplay, attrList, 0, configOut, 0, 1,
|
||||
configNumOut, 0);
|
||||
eglContext = EGL14.eglCreateContext(eglDisplay, configOut[0], parentContext, attrib_list, 0);
|
||||
|
||||
int[] sur_attrib_list = {EGL14.EGL_WIDTH, 1, EGL14.EGL_HEIGHT, 1, EGL14.EGL_NONE};
|
||||
pBufferSurface = EGL14.eglCreatePbufferSurface(eglDisplay, configOut[0], sur_attrib_list, 0);
|
||||
|
||||
EGL14.eglMakeCurrent(eglDisplay, pBufferSurface, pBufferSurface, eglContext);
|
||||
GLES30.glDisable(GLES30.GL_DEPTH_TEST);
|
||||
GLES30.glDisable(GLES30.GL_BLEND);
|
||||
GLES30.glDisable(GLES30.GL_CULL_FACE);
|
||||
|
||||
int[] tmpFboId = new int[1];
|
||||
GLES30.glGenFramebuffers(1, tmpFboId, 0);
|
||||
renderTargetFboId = tmpFboId[0];
|
||||
CocosXRGLHelper.checkGLError("fbo");
|
||||
|
||||
lastTickTime = System.nanoTime();
|
||||
|
||||
Set<Map.Entry<String, CocosXRVideoPlayer>> entrySets = xrVideoPlayerHashMap.entrySet();
|
||||
for (Map.Entry<String, CocosXRVideoPlayer> entrySet : entrySets) {
|
||||
entrySet.getValue().onGLReady();
|
||||
}
|
||||
Log.d(TAG, "CocosXRVideoGLThread init");
|
||||
}
|
||||
|
||||
void tick() {
|
||||
// draw
|
||||
lastTickTime = System.nanoTime();
|
||||
Set<Map.Entry<String, CocosXRVideoPlayer>> entrySets = xrVideoPlayerHashMap.entrySet();
|
||||
for (Map.Entry<String, CocosXRVideoPlayer> entrySet : entrySets) {
|
||||
entrySet.getValue().onBeforeGLDrawFrame();
|
||||
if(entrySet.getValue().isStopped() || entrySet.getValue().getVideoTextureWidth() == 0 || entrySet.getValue().getVideoTextureHeight() == 0) {
|
||||
continue;
|
||||
}
|
||||
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, renderTargetFboId);
|
||||
GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, entrySet.getValue().getTargetTextureId(), 0);
|
||||
int offsetX = (entrySet.getValue().getVideoTextureWidth() - entrySet.getValue().getVideoSourceWidth()) / 2;
|
||||
int offsetY = (entrySet.getValue().getVideoTextureHeight() - entrySet.getValue().getVideoSourceHeight()) / 2;
|
||||
GLES30.glViewport(offsetX, offsetY, entrySet.getValue().getVideoSourceWidth(), entrySet.getValue().getVideoSourceHeight());
|
||||
GLES30.glScissor(0, 0, entrySet.getValue().getVideoTextureWidth(), entrySet.getValue().getVideoTextureHeight());
|
||||
entrySet.getValue().onGLDrawFrame();
|
||||
}
|
||||
EGL14.eglSwapBuffers(eglDisplay, pBufferSurface);
|
||||
|
||||
if (System.nanoTime() - lastTickTime < 16666666) {
|
||||
try {
|
||||
long sleepTimeNS = 16666666 - (System.nanoTime() - lastTickTime);
|
||||
Thread.sleep(sleepTimeNS / 1000000);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void exit() {
|
||||
Set<Map.Entry<String, CocosXRVideoPlayer>> entrySets = xrVideoPlayerHashMap.entrySet();
|
||||
for (Map.Entry<String, CocosXRVideoPlayer> entrySet : entrySets) {
|
||||
entrySet.getValue().onGLDestroy();
|
||||
}
|
||||
GLES30.glDeleteFramebuffers(1, new int[] {renderTargetFboId}, 0);
|
||||
EGL14.eglMakeCurrent(eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
|
||||
EGL14.eglDestroySurface(eglDisplay, pBufferSurface);
|
||||
EGL14.eglDestroyContext(eglDisplay, eglContext);
|
||||
Log.d(TAG, "CocosXRVideoGLThread exit");
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
lockObj.lock();
|
||||
requestPaused = true;
|
||||
lockObj.unlock();
|
||||
Log.d(TAG, "CocosXRVideoGLThread onPause");
|
||||
}
|
||||
|
||||
public void onResume() {
|
||||
lockObj.lock();
|
||||
requestPaused = false;
|
||||
pauseCondition.signalAll();
|
||||
lockObj.unlock();
|
||||
Log.d(TAG, "CocosXRVideoGLThread onResume");
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
lockObj.lock();
|
||||
requestExited = true;
|
||||
pauseCondition.signalAll();
|
||||
lockObj.unlock();
|
||||
|
||||
try {
|
||||
videoGLThread.join();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, e.getLocalizedMessage());
|
||||
}
|
||||
Log.d(TAG, "CocosXRVideoGLThread onDestroy");
|
||||
}
|
||||
|
||||
public void queueEvent(Runnable r) {
|
||||
synchronized (this) {
|
||||
mEventQueue.add(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
|
||||
*
|
||||
* http://www.cocos.com
|
||||
*
|
||||
* 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 com.cocos.lib.xr;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.Surface;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class CocosXRVideoPlayer {
|
||||
enum MediaPlayerState {
|
||||
IDLE,
|
||||
INITIALIZED,
|
||||
READY_PREPARE,
|
||||
PREPARING,
|
||||
PREPARED,
|
||||
STARTED,
|
||||
STOPPED,
|
||||
PAUSED,
|
||||
END,
|
||||
ERROR,
|
||||
COMPLETED
|
||||
}
|
||||
|
||||
private static final String TAG = "CocosXRVideoPlayer";
|
||||
private String uniqueKey;
|
||||
private String eventName;
|
||||
private CocosXRVideoTexture videoTexture;
|
||||
private String videoSourceUrl;
|
||||
private int videoSourceType;
|
||||
private int videoTextureId = 0;
|
||||
private boolean isGLInitialized = false;
|
||||
private int videoSourceSizeWidth = 0;
|
||||
private int videoSourceSizeHeight = 0;
|
||||
private int videoTextureWidth = 0;
|
||||
private int videoTextureHeight = 0;
|
||||
private MediaPlayerState mediaPlayerState = MediaPlayerState.IDLE;
|
||||
|
||||
CocosXRGLHelper.GLQuadScreen quadScreen;
|
||||
MediaPlayer mediaPlayer;
|
||||
WeakReference<Activity> atyWeakReference;
|
||||
|
||||
public CocosXRVideoPlayer(WeakReference<Activity> activityWeakReference, String key, String eventName) {
|
||||
this.atyWeakReference = activityWeakReference;
|
||||
this.uniqueKey = key;
|
||||
this.eventName = eventName;
|
||||
this.quadScreen = new CocosXRGLHelper.GLQuadScreen();
|
||||
this.mediaPlayer = new MediaPlayer();
|
||||
this.mediaPlayer.setOnErrorListener((mp, what, extra) -> {
|
||||
mediaPlayerState = MediaPlayerState.ERROR;
|
||||
Log.e(TAG, "onError " + what + "," + extra + "." + mp.toString());
|
||||
CocosXRVideoManager.getInstance().sendVideoEvent(CocosXRVideoManager.VIDEO_EVENT_MEDIA_PLAYER_ERROR, eventName, uniqueKey);
|
||||
return false;
|
||||
});
|
||||
this.mediaPlayer.setOnInfoListener((mp, what, extra) -> {
|
||||
// Log.d(TAG, "onInfo " + what + "," + extra + "." + mp.toString());
|
||||
CocosXRVideoManager.getInstance().sendVideoEvent(CocosXRVideoManager.VIDEO_EVENT_MEDIA_PLAYER_ON_INFO, eventName, uniqueKey, String.valueOf(what));
|
||||
return false;
|
||||
});
|
||||
this.mediaPlayer.setOnPreparedListener(mp -> {
|
||||
mediaPlayerState = MediaPlayerState.PREPARED;
|
||||
CocosXRVideoManager.getInstance().sendVideoEvent(CocosXRVideoManager.VIDEO_EVENT_MEDIA_PLAYER_PREPARED, eventName, uniqueKey);
|
||||
CocosXRVideoManager.getInstance().sendVideoEvent(CocosXRVideoManager.VIDEO_EVENT_GET_DURATION, eventName, uniqueKey, String.valueOf(mp.getDuration()));
|
||||
Log.d(TAG, "onPrepared." + mp+ ", getDuration." + mp.getDuration() + "," + mp.getVideoWidth() + "X" + mp.getVideoHeight() + "," + mp.isPlaying());
|
||||
});
|
||||
this.mediaPlayer.setOnCompletionListener(mp -> {
|
||||
Log.d(TAG, "onCompletion." + mp.toString());
|
||||
mediaPlayerState = MediaPlayerState.COMPLETED;
|
||||
CocosXRVideoManager.getInstance().sendVideoEvent(CocosXRVideoManager.VIDEO_EVENT_MEDIA_PLAYER_PLAY_COMPLETE, eventName, uniqueKey);
|
||||
});
|
||||
this.mediaPlayer.setOnSeekCompleteListener(mp -> {
|
||||
// Log.d(TAG, "onSeekComplete." + mp.toString());
|
||||
CocosXRVideoManager.getInstance().sendVideoEvent(CocosXRVideoManager.VIDEO_EVENT_MEDIA_PLAYER_SEEK_COMPLETE, eventName, uniqueKey);
|
||||
});
|
||||
this.mediaPlayer.setOnVideoSizeChangedListener((mp, width, height) -> {
|
||||
Log.d(TAG, "onVideoSizeChanged " + width + "x" + height + "." + mp.toString() + ", isPlaying." + mp.isPlaying());
|
||||
if(videoSourceSizeWidth != width || videoSourceSizeHeight != height) {
|
||||
videoSourceSizeWidth = width;
|
||||
videoSourceSizeHeight = height;
|
||||
CocosXRVideoManager.getInstance().sendVideoEvent(CocosXRVideoManager.VIDEO_EVENT_GET_IS_PALYING, eventName, uniqueKey, String.valueOf(mp.isPlaying() ? 1 : 0));
|
||||
CocosXRVideoManager.getInstance().sendVideoEvent(CocosXRVideoManager.VIDEO_EVENT_MEDIA_PLAYER_VIDEO_SIZE, eventName, uniqueKey, width + "&" + height);
|
||||
}
|
||||
});
|
||||
this.mediaPlayer.setAudioAttributes(new AudioAttributes.Builder().setLegacyStreamType(AudioManager.STREAM_MUSIC).build());
|
||||
mediaPlayerState = MediaPlayerState.INITIALIZED;
|
||||
Log.d(TAG, "constructor() " + key + "|" + eventName + "|" + quadScreen.toString());
|
||||
}
|
||||
|
||||
public void runOnUIThread(Runnable runnable) {
|
||||
if (atyWeakReference != null && atyWeakReference.get() != null) {
|
||||
atyWeakReference.get().runOnUiThread(runnable);
|
||||
} else {
|
||||
Log.e(TAG, "runOnUIThread failed, activity not exist !!!");
|
||||
}
|
||||
}
|
||||
|
||||
public void setTextureInfo(int textureWidth, int textureHeight, int videoTextureId) {
|
||||
this.videoTextureId = videoTextureId;
|
||||
this.videoTextureWidth = textureWidth;
|
||||
this.videoTextureHeight = textureHeight;
|
||||
Log.d(TAG, "setTextureInfo." + textureWidth + "x" + textureHeight + ":" + videoTextureId);
|
||||
}
|
||||
|
||||
public void prepare(CocosXRVideoManager.VideoEventData data) {
|
||||
if (this.videoTexture == null) {
|
||||
this.videoTexture = new CocosXRVideoTexture();
|
||||
}
|
||||
this.videoSourceType = data.videoSourceType;
|
||||
this.videoSourceUrl = data.videoSourceUrl;
|
||||
if(TextUtils.isEmpty(this.videoSourceUrl)) {
|
||||
Log.w(TAG, "prepare failed, because video source is empty !!!");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (data.videoSourceType == CocosXRVideoManager.VIDEO_SOURCE_TYPE_LOCAL) {
|
||||
AssetFileDescriptor afd = atyWeakReference.get().getResources().getAssets().openFd(data.videoSourceUrl);
|
||||
mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
|
||||
} else {
|
||||
mediaPlayer.setDataSource(data.videoSourceUrl);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, e.getLocalizedMessage());
|
||||
}
|
||||
|
||||
mediaPlayer.setLooping(data.isLoop);
|
||||
mediaPlayer.setVolume(data.volume, data.volume);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
try {
|
||||
mediaPlayer.setPlaybackParams(mediaPlayer.getPlaybackParams().setSpeed(data.playbackSpeed));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "prepare:" + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
mediaPlayerState = MediaPlayerState.READY_PREPARE;
|
||||
if (isGLInitialized) {
|
||||
runOnUIThread(() -> {
|
||||
if(mediaPlayerState == MediaPlayerState.READY_PREPARE) {
|
||||
mediaPlayerState = MediaPlayerState.PREPARING;
|
||||
mediaPlayer.prepareAsync();
|
||||
}
|
||||
});
|
||||
}
|
||||
Log.d(TAG, "prepare");
|
||||
}
|
||||
|
||||
public int getTargetTextureId() {
|
||||
return videoTextureId;
|
||||
}
|
||||
|
||||
public int getVideoTextureWidth() {
|
||||
return videoTextureWidth;
|
||||
}
|
||||
|
||||
public int getVideoTextureHeight() {
|
||||
return videoTextureHeight;
|
||||
}
|
||||
|
||||
public int getVideoSourceWidth() {
|
||||
return videoSourceSizeWidth;
|
||||
}
|
||||
|
||||
public int getVideoSourceHeight() {
|
||||
return videoSourceSizeHeight;
|
||||
}
|
||||
|
||||
public String getVideoSourceUrl() {
|
||||
return videoSourceUrl;
|
||||
}
|
||||
|
||||
public int getVideoSourceType() {
|
||||
return videoSourceType;
|
||||
}
|
||||
|
||||
public void onGLReady() {
|
||||
Log.d(TAG, "onGLReady." + this.hashCode());
|
||||
videoTexture.createSurfaceTexture();
|
||||
quadScreen.initShader();
|
||||
Surface surface = new Surface(videoTexture.getSurfaceTexture());
|
||||
mediaPlayer.setSurface(surface);
|
||||
surface.release();
|
||||
isGLInitialized = true;
|
||||
if(mediaPlayerState == MediaPlayerState.READY_PREPARE) {
|
||||
runOnUIThread(() -> {
|
||||
if(mediaPlayerState == MediaPlayerState.READY_PREPARE) {
|
||||
mediaPlayerState = MediaPlayerState.PREPARING;
|
||||
mediaPlayer.prepareAsync();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void onBeforeGLDrawFrame() {
|
||||
if (!isGLInitialized) {
|
||||
onGLReady();
|
||||
}
|
||||
}
|
||||
|
||||
public void onGLDrawFrame() {
|
||||
if (videoTextureId == 0 || videoTextureWidth == 0 || videoTextureHeight == 0) {
|
||||
return;
|
||||
}
|
||||
videoTexture.updateTexture();
|
||||
quadScreen.draw(videoTexture.getOESTextureId(), videoTexture.getVideoMatrix());
|
||||
}
|
||||
|
||||
public void onGLDestroy() {
|
||||
if (videoTexture != null) {
|
||||
videoTexture.release();
|
||||
videoTexture = null;
|
||||
}
|
||||
|
||||
if (quadScreen != null) {
|
||||
quadScreen.release();
|
||||
quadScreen = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void play() {
|
||||
runOnUIThread(() -> {
|
||||
Log.d(TAG, "- start");
|
||||
mediaPlayer.start();
|
||||
mediaPlayerState = MediaPlayerState.STARTED;
|
||||
CocosXRVideoManager.getInstance().sendVideoEvent(CocosXRVideoManager.VIDEO_EVENT_GET_IS_PALYING, eventName, uniqueKey, String.valueOf(mediaPlayer.isPlaying() ? 1 : 0));
|
||||
});
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
if(!mediaPlayer.isPlaying()) return;
|
||||
runOnUIThread(() -> {
|
||||
Log.d(TAG, "- pause");
|
||||
mediaPlayer.pause();
|
||||
mediaPlayerState = MediaPlayerState.PAUSED;
|
||||
CocosXRVideoManager.getInstance().sendVideoEvent(CocosXRVideoManager.VIDEO_EVENT_GET_IS_PALYING, eventName, uniqueKey, String.valueOf(mediaPlayer.isPlaying() ? 1 : 0));
|
||||
});
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
runOnUIThread(() -> {
|
||||
Log.d(TAG, "- stop");
|
||||
mediaPlayer.stop();
|
||||
mediaPlayerState = MediaPlayerState.STOPPED;
|
||||
});
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
runOnUIThread(() -> {
|
||||
Log.d(TAG, "- reset");
|
||||
mediaPlayer.reset();
|
||||
mediaPlayerState = MediaPlayerState.IDLE;
|
||||
});
|
||||
}
|
||||
|
||||
public void release() {
|
||||
runOnUIThread(() -> {
|
||||
if (mediaPlayer != null) {
|
||||
try {
|
||||
if (mediaPlayer.isPlaying()) {
|
||||
mediaPlayer.pause();
|
||||
}
|
||||
mediaPlayer.stop();
|
||||
mediaPlayer.release();
|
||||
mediaPlayer = null;
|
||||
mediaPlayerState = MediaPlayerState.END;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, e.getLocalizedMessage());
|
||||
}
|
||||
Log.d(TAG, "- release");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean isPlaying() {
|
||||
return mediaPlayer != null && mediaPlayer.isPlaying();
|
||||
}
|
||||
|
||||
public boolean isStopped() {
|
||||
return mediaPlayerState == MediaPlayerState.STOPPED;
|
||||
}
|
||||
|
||||
public boolean isLooping() {
|
||||
return mediaPlayer.isLooping();
|
||||
}
|
||||
|
||||
public void setLooping(boolean looping) {
|
||||
runOnUIThread(() -> {
|
||||
Log.d(TAG, "- setLooping." + looping);
|
||||
mediaPlayer.setLooping(looping);
|
||||
});
|
||||
}
|
||||
|
||||
public void setVolume(float volume) {
|
||||
runOnUIThread(() -> mediaPlayer.setVolume(volume, volume));
|
||||
}
|
||||
|
||||
public int getDuration() {
|
||||
return mediaPlayer.getDuration();
|
||||
}
|
||||
|
||||
public int getCurrentPosition() {
|
||||
return mediaPlayer.getCurrentPosition();
|
||||
}
|
||||
|
||||
public void seekTo(int mSec) {
|
||||
runOnUIThread(() -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
mediaPlayer.seekTo(mSec, MediaPlayer.SEEK_CLOSEST);
|
||||
} else {
|
||||
mediaPlayer.seekTo(mSec);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public float getPlaybackSpeed() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
return mediaPlayer.getPlaybackParams().getSpeed();
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
public void setPlaybackSpeed(float speed) {
|
||||
runOnUIThread(() -> {
|
||||
Log.d(TAG, "- setPlaybackSpeed." + speed);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
try {
|
||||
mediaPlayer.setPlaybackParams(mediaPlayer.getPlaybackParams().setSpeed(Math.max(speed, 0.1f)));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
|
||||
*
|
||||
* http://www.cocos.com
|
||||
*
|
||||
* 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 com.cocos.lib.xr;
|
||||
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.opengl.GLES30;
|
||||
import android.opengl.Matrix;
|
||||
|
||||
public class CocosXRVideoTexture implements SurfaceTexture.OnFrameAvailableListener {
|
||||
SurfaceTexture surfaceTexture;
|
||||
private boolean surfaceNeedsUpdate = false;
|
||||
private long videoTimestampNs = -1;
|
||||
private final float[] videoSTMatrix = new float[16];
|
||||
private long lastFrameAvailableTime = 0;
|
||||
|
||||
@Override
|
||||
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
|
||||
surfaceNeedsUpdate = true;
|
||||
lastFrameAvailableTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private int videoOESTextureId;
|
||||
|
||||
public CocosXRVideoTexture() {
|
||||
Matrix.setIdentityM(videoSTMatrix, 0);
|
||||
}
|
||||
|
||||
public SurfaceTexture createSurfaceTexture() {
|
||||
videoOESTextureId = CocosXRGLHelper.createOESTexture();
|
||||
surfaceTexture = new SurfaceTexture(videoOESTextureId);
|
||||
surfaceTexture.setOnFrameAvailableListener(this);
|
||||
return surfaceTexture;
|
||||
}
|
||||
|
||||
public SurfaceTexture getSurfaceTexture() {
|
||||
return surfaceTexture;
|
||||
}
|
||||
|
||||
public int getOESTextureId() {
|
||||
return videoOESTextureId;
|
||||
}
|
||||
|
||||
public float[] getVideoMatrix() {
|
||||
return videoSTMatrix;
|
||||
}
|
||||
|
||||
public long getVideoTimestampNs() {
|
||||
return videoTimestampNs;
|
||||
}
|
||||
|
||||
public synchronized boolean updateTexture() {
|
||||
if (!surfaceNeedsUpdate && System.currentTimeMillis() - lastFrameAvailableTime > 30) {
|
||||
surfaceNeedsUpdate = true;
|
||||
lastFrameAvailableTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
if (surfaceNeedsUpdate) {
|
||||
surfaceTexture.updateTexImage();
|
||||
surfaceTexture.getTransformMatrix(videoSTMatrix);
|
||||
videoTimestampNs = surfaceTexture.getTimestamp();
|
||||
surfaceNeedsUpdate = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isFrameAvailable() {
|
||||
return surfaceNeedsUpdate;
|
||||
}
|
||||
|
||||
public void release() {
|
||||
if (surfaceTexture != null) {
|
||||
surfaceTexture.release();
|
||||
}
|
||||
if (videoOESTextureId != 0) {
|
||||
GLES30.glDeleteTextures(1, new int[] {videoOESTextureId}, 0);
|
||||
videoOESTextureId = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,476 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
|
||||
*
|
||||
* http://www.cocos.com
|
||||
*
|
||||
* 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 com.cocos.lib.xr;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.net.http.SslError;
|
||||
import android.opengl.GLES30;
|
||||
import android.os.Build;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.InputDevice;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
import android.webkit.ClientCertRequest;
|
||||
import android.webkit.CookieManager;
|
||||
import android.webkit.HttpAuthHandler;
|
||||
import android.webkit.PermissionRequest;
|
||||
import android.webkit.SafeBrowsingResponse;
|
||||
import android.webkit.SslErrorHandler;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebResourceError;
|
||||
import android.webkit.WebResourceRequest;
|
||||
import android.webkit.WebResourceResponse;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class CocosXRWebViewContainer extends FrameLayout {
|
||||
private static final String TAG = "CocosXRWebViewContainer";
|
||||
WebView webView;
|
||||
private long lastDrawTime = 0;
|
||||
private CocosXRVideoTexture videoTexture;
|
||||
private Surface webViewSurface;
|
||||
private CocosXRGLHelper.GLQuadScreen quadScreen;
|
||||
private boolean isGLInitialized = false;
|
||||
private int renderTargetFboId;
|
||||
private int videoTextureId = 0;
|
||||
private int videoTextureWidth = 0;
|
||||
private int videoTextureHeight = 0;
|
||||
|
||||
private boolean isKeyDown = false;
|
||||
private long keyDownTime = 0;
|
||||
|
||||
public CocosXRWebViewContainer(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public CocosXRWebViewContainer(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public CocosXRWebViewContainer(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
init();
|
||||
}
|
||||
|
||||
void init() {
|
||||
webView = new WebView(getContext());
|
||||
webView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT, Gravity.NO_GRAVITY));
|
||||
webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
|
||||
|
||||
webView.getSettings().setSupportZoom(false);
|
||||
webView.getSettings().setBuiltInZoomControls(true);
|
||||
webView.getSettings().setTextZoom(100);
|
||||
|
||||
webView.getSettings().setDisplayZoomControls(false);
|
||||
webView.getSettings().setLoadWithOverviewMode(true);
|
||||
webView.getSettings().setUseWideViewPort(true);
|
||||
webView.getSettings().setMediaPlaybackRequiresUserGesture(false);
|
||||
|
||||
webView.getSettings().setJavaScriptEnabled(true);
|
||||
webView.getSettings().setDomStorageEnabled(true);
|
||||
webView.getSettings().setDefaultTextEncodingName("UTF-8");
|
||||
webView.getSettings().setAllowFileAccess(true);
|
||||
webView.getSettings().setAllowContentAccess(true);
|
||||
webView.getSettings().setAllowFileAccessFromFileURLs(true);
|
||||
webView.getSettings().setDatabaseEnabled(true);
|
||||
webView.getSettings().setAppCacheEnabled(true);
|
||||
webView.getSettings().setLoadsImagesAutomatically(true);
|
||||
webView.getSettings().setSupportMultipleWindows(false);
|
||||
webView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);
|
||||
webView.getSettings().setBlockNetworkImage(false);
|
||||
webView.getSettings().setGeolocationEnabled(true);
|
||||
webView.getSettings().setPluginState(WebSettings.PluginState.ON);
|
||||
webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
webView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
|
||||
} else {
|
||||
webView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
webView.getSettings().setSafeBrowsingEnabled(false);
|
||||
}
|
||||
CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);
|
||||
webView.setWebViewClient(new WebViewClient() {
|
||||
|
||||
@Override
|
||||
public void onSafeBrowsingHit(WebView view, WebResourceRequest request, int threatType, SafeBrowsingResponse callback) {
|
||||
Log.d(TAG, "onSafeBrowsingHit:" + threatType + "," + request.getUrl().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
|
||||
return super.shouldInterceptRequest(view, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
|
||||
Log.d(TAG, "onReceivedSslError:" + error.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) {
|
||||
Log.d(TAG, "onReceivedClientCertRequest:" + request.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
|
||||
Log.d(TAG, "onReceivedHttpAuthRequest:" + host + ":" + realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedLoginRequest(WebView view, String realm, String account, String args) {
|
||||
Log.d(TAG, "onReceivedLoginRequest:" + account + ":" + realm + ":" + args);
|
||||
}
|
||||
|
||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
||||
Log.e(TAG, "shouldOverrideUrlLoading failed:" + url);
|
||||
view.reload();
|
||||
return false;
|
||||
}
|
||||
|
||||
view.loadUrl(url);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
Log.e(TAG, "onReceivedError:" + error.getDescription() + "," + error.getErrorCode() + "," + view.getTitle());
|
||||
}
|
||||
super.onReceivedError(view, request, error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
|
||||
Log.e(TAG, "onReceivedHttpError:" + errorResponse.toString());
|
||||
super.onReceivedHttpError(view, request, errorResponse);
|
||||
}
|
||||
});
|
||||
webView.setWebChromeClient(new WebChromeClient() {
|
||||
|
||||
@Override
|
||||
public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
|
||||
Log.d(TAG, "onCreateWindow:" + resultMsg.toString());
|
||||
return super.onCreateWindow(view, isDialog, isUserGesture, resultMsg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCloseWindow(WebView window) {
|
||||
Log.d(TAG, "onCreateWindowonCloseWindow");
|
||||
super.onCloseWindow(window);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedTitle(WebView view, String title) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(WebView view, int newProgress) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShowCustomView(View view, CustomViewCallback callback) {
|
||||
Log.d(TAG, "onShowCustomView");
|
||||
if(callback != null) {
|
||||
view.setVisibility(View.GONE);
|
||||
callback.onCustomViewHidden();
|
||||
}
|
||||
/*ViewParent viewParent = webView.getParent();
|
||||
if (viewParent instanceof ViewGroup) {
|
||||
ViewGroup viewGroup = (ViewGroup)viewParent;
|
||||
if (viewGroup.getChildCount() > 1) {
|
||||
callback.onCustomViewHidden();
|
||||
return;
|
||||
}
|
||||
viewGroup.getChildAt(0).setVisibility(View.GONE);
|
||||
view.setBackgroundColor(0);
|
||||
viewGroup.addView(view);
|
||||
view.setVisibility(View.VISIBLE);
|
||||
}*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHideCustomView() {
|
||||
Log.d(TAG, "onHideCustomView");
|
||||
/*ViewParent viewParent = webView.getParent();
|
||||
if (viewParent instanceof ViewGroup) {
|
||||
ViewGroup viewGroup = (ViewGroup)viewParent;
|
||||
while (viewGroup.getChildCount() > 1)
|
||||
viewGroup.removeViewAt(1);
|
||||
viewGroup.getChildAt(0).setVisibility(View.VISIBLE);
|
||||
}*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPermissionRequest(PermissionRequest request) {
|
||||
String[] resources = request.getResources();
|
||||
ArrayList<String> permissions = new ArrayList<>();
|
||||
for (String resource : resources) {
|
||||
Log.d(TAG, "onPermissionRequest.resource:" + resource);
|
||||
if (resource.equals("android.webkit.resource.AUDIO_CAPTURE")) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (getContext().checkSelfPermission("android.permission.RECORD_AUDIO") != PackageManager.PERMISSION_GRANTED) {
|
||||
permissions.add(resource);
|
||||
}
|
||||
}
|
||||
} else if (resource.equals("android.webkit.resource.PROTECTED_MEDIA_ID")) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (getContext().checkSelfPermission("android.permission.PROTECTED_MEDIA_ID") != PackageManager.PERMISSION_GRANTED) {
|
||||
permissions.add(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(permissions.size() > 0) {
|
||||
String[] names = new String[permissions.size()];
|
||||
permissions.toArray(names);
|
||||
Log.d(TAG, "acquirePermissions:" + Arrays.toString(names));
|
||||
request.grant(names);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
String userAgentString = webView.getSettings().getUserAgentString();
|
||||
Log.d(TAG, "ua:" + userAgentString);
|
||||
|
||||
|
||||
this.quadScreen = new CocosXRGLHelper.GLQuadScreen();
|
||||
videoTexture = new CocosXRVideoTexture();
|
||||
webView.clearFocus();
|
||||
webView.setFocusableInTouchMode(false);
|
||||
addView(webView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
lastDrawTime = System.currentTimeMillis();
|
||||
if (webViewSurface == null && canvas != null) {
|
||||
super.draw(canvas);
|
||||
return;
|
||||
} else if(webViewSurface == null) {
|
||||
return;
|
||||
}
|
||||
//returns canvas attached to gl texture to draw on
|
||||
Canvas glAttachedCanvas;
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
||||
glAttachedCanvas = webViewSurface.lockHardwareCanvas();
|
||||
} else {
|
||||
glAttachedCanvas = webViewSurface.lockCanvas(null);
|
||||
}
|
||||
if (glAttachedCanvas != null) {
|
||||
glAttachedCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
|
||||
glAttachedCanvas.scale(1.0F, 1.0F);
|
||||
glAttachedCanvas.translate(-getScrollX(), -getScrollY());
|
||||
//draw the view to provided canvas
|
||||
super.draw(glAttachedCanvas);
|
||||
}
|
||||
webViewSurface.unlockCanvasAndPost(glAttachedCanvas);
|
||||
if (videoTexture != null) {
|
||||
videoTexture.onFrameAvailable(null);
|
||||
}
|
||||
}
|
||||
|
||||
public void onDrawCheck() {
|
||||
if (webViewSurface != null && System.currentTimeMillis() - lastDrawTime > 16) {
|
||||
post(() -> draw(null));
|
||||
}
|
||||
}
|
||||
|
||||
CocosXRVideoTexture getVideoTexture() {
|
||||
return videoTexture;
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
Log.d(TAG, "destroy." + hashCode());
|
||||
if(webView != null) {
|
||||
webView.removeAllViews();
|
||||
webView.destroy();
|
||||
webView = null;
|
||||
}
|
||||
|
||||
if (webViewSurface != null) {
|
||||
webViewSurface.release();
|
||||
webViewSurface = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setTextureInfo(int textureWidth, int textureHeight, int videoTextureId) {
|
||||
this.videoTextureId = videoTextureId;
|
||||
this.videoTextureWidth = textureWidth;
|
||||
this.videoTextureHeight = textureHeight;
|
||||
}
|
||||
|
||||
public int getTargetTextureId() {
|
||||
return videoTextureId;
|
||||
}
|
||||
|
||||
public int getVideoTextureWidth() {
|
||||
return videoTextureWidth;
|
||||
}
|
||||
|
||||
public int getVideoTextureHeight() {
|
||||
return videoTextureHeight;
|
||||
}
|
||||
|
||||
public void onGLReady() {
|
||||
videoTexture.createSurfaceTexture();
|
||||
videoTexture.getSurfaceTexture().setDefaultBufferSize(this.videoTextureWidth, this.videoTextureHeight);
|
||||
quadScreen.initShader();
|
||||
webViewSurface = new Surface(videoTexture.getSurfaceTexture());
|
||||
isGLInitialized = true;
|
||||
|
||||
int[] tmpFboId = new int[1];
|
||||
GLES30.glGenFramebuffers(1, tmpFboId, 0);
|
||||
renderTargetFboId = tmpFboId[0];
|
||||
CocosXRGLHelper.checkGLError("fbo");
|
||||
Log.d(TAG, "onGLReady." + this.hashCode() + ",oes." + videoTexture.getOESTextureId() + ",fbo." + tmpFboId[0] +
|
||||
"," + this.videoTextureWidth + "x" + this.videoTextureHeight);
|
||||
}
|
||||
|
||||
public void onBeforeGLDrawFrame() {
|
||||
if (videoTextureId == 0 || videoTextureWidth == 0 || videoTextureHeight == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isGLInitialized) {
|
||||
onGLReady();
|
||||
}
|
||||
}
|
||||
|
||||
public void onGLDrawFrame() {
|
||||
if (videoTextureId == 0 || videoTextureWidth == 0 || videoTextureHeight == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, renderTargetFboId);
|
||||
GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, videoTextureId, 0);
|
||||
GLES30.glViewport(0, 0, videoTextureWidth, videoTextureHeight);
|
||||
GLES30.glScissor(0, 0, videoTextureWidth, videoTextureHeight);
|
||||
onDrawCheck();
|
||||
videoTexture.updateTexture();
|
||||
quadScreen.draw(videoTexture.getOESTextureId(), videoTexture.getVideoMatrix());
|
||||
GLES30.glFlush();
|
||||
}
|
||||
|
||||
public void onGLDestroy() {
|
||||
Log.d(TAG, "onGLDestroy." + this.hashCode());
|
||||
if(renderTargetFboId > 0) {
|
||||
GLES30.glDeleteFramebuffers(1, new int[]{renderTargetFboId}, 0);
|
||||
renderTargetFboId = 0;
|
||||
}
|
||||
|
||||
if (videoTexture != null) {
|
||||
videoTexture.release();
|
||||
videoTexture = null;
|
||||
}
|
||||
|
||||
if (quadScreen != null) {
|
||||
quadScreen.release();
|
||||
quadScreen = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void simulateTouchDown(float ux, float uy) {
|
||||
isKeyDown = true;
|
||||
keyDownTime = SystemClock.uptimeMillis();
|
||||
float touchX = ux * getWidth();
|
||||
float touchY = getHeight() - uy * getHeight();
|
||||
post(() -> {
|
||||
MotionEvent motionEvent = MotionEvent.obtain(keyDownTime, SystemClock.uptimeMillis() + 100, MotionEvent.ACTION_DOWN, touchX, touchY, 0);
|
||||
motionEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
|
||||
dispatchTouchEvent(motionEvent);
|
||||
});
|
||||
}
|
||||
|
||||
public void simulateTouchMove(float ux, float uy) {
|
||||
if (isKeyDown) {
|
||||
post(() -> {
|
||||
float touchX = ux * getWidth();
|
||||
float touchY = getHeight() - uy * getHeight();
|
||||
MotionEvent motionEvent = MotionEvent.obtain(keyDownTime, SystemClock.uptimeMillis() + 100, MotionEvent.ACTION_MOVE, touchX, touchY, 0);
|
||||
motionEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
|
||||
dispatchTouchEvent(motionEvent);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void simulateTouchUp(float ux, float uy) {
|
||||
isKeyDown = false;
|
||||
float touchX = ux * getWidth();
|
||||
float touchY = getHeight() - uy * getHeight();
|
||||
post(() -> {
|
||||
MotionEvent motionEvent = MotionEvent.obtain(keyDownTime, SystemClock.uptimeMillis() + 100, MotionEvent.ACTION_UP, touchX, touchY, 0);
|
||||
motionEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
|
||||
dispatchTouchEvent(motionEvent);
|
||||
});
|
||||
}
|
||||
|
||||
public void loadUrl(String url) {
|
||||
if(webView != null) {
|
||||
HashMap<String, String> hashMap = new HashMap<>();
|
||||
hashMap.put("Referer", "https://www.cocos.com/");
|
||||
webView.loadUrl(url, hashMap);
|
||||
}
|
||||
}
|
||||
|
||||
public WebSettings getSettings() {
|
||||
return webView.getSettings();
|
||||
}
|
||||
|
||||
public void goForward() {
|
||||
webView.goForward();
|
||||
}
|
||||
|
||||
public void goBack() {
|
||||
webView.goBack();
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
webView.reload();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,450 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
|
||||
*
|
||||
* http://www.cocos.com
|
||||
*
|
||||
* 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 com.cocos.lib.xr;
|
||||
|
||||
import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.opengl.EGL14;
|
||||
import android.opengl.EGLConfig;
|
||||
import android.opengl.EGLContext;
|
||||
import android.opengl.EGLDisplay;
|
||||
import android.opengl.EGLSurface;
|
||||
import android.opengl.GLES30;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.cocos.lib.JsbBridgeWrapper;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class CocosXRWebViewManager {
|
||||
private static final String TAG = "CocosXRWebViewManager";
|
||||
public static final String XR_WEBVIEW_EVENT_NAME = "xr-webview-";
|
||||
public static final String XR_WEBVIEW_EVENT_TAG_TO_ADD = "to-add";
|
||||
public static final String XR_WEBVIEW_EVENT_TAG_TO_REMOVE = "to-remove";
|
||||
public static final String XR_WEBVIEW_EVENT_TAG_ADDED = "added";
|
||||
public static final String XR_WEBVIEW_EVENT_TAG_REMOVED = "removed";
|
||||
public static final String XR_WEBVIEW_EVENT_TAG_TEXTUREINFO = "textureinfo";
|
||||
public static final String XR_WEBVIEW_EVENT_TAG_HOVER = "hover";
|
||||
public static final String XR_WEBVIEW_EVENT_TAG_CLICK_DOWN = "click-down";
|
||||
public static final String XR_WEBVIEW_EVENT_TAG_CLICK_UP = "click-up";
|
||||
public static final String XR_WEBVIEW_EVENT_TAG_GOFORWARD = "go-forward";
|
||||
public static final String XR_WEBVIEW_EVENT_TAG_GOBACK = "go-back";
|
||||
public static final String XR_WEBVIEW_EVENT_TAG_LOADURL = "load-url";
|
||||
public static final String XR_WEBVIEW_EVENT_TAG_RELOAD = "reload";
|
||||
final int MAX_COUNT = 3;
|
||||
|
||||
ConcurrentHashMap<String, CocosXRWebViewContainer> xrWebViewHashMap = new ConcurrentHashMap<>();
|
||||
private WeakReference<Activity> activityWeakReference;
|
||||
CocosXRWebViewGLThread webViewGLThread;
|
||||
|
||||
private final boolean isMobileUA = false;
|
||||
|
||||
public void createWebView(int webviewId, int textureWidth, int textureHeight, String url) {
|
||||
Log.d(TAG, "createWebView:" + webviewId + "," + url);
|
||||
if (activityWeakReference.get() != null) {
|
||||
activityWeakReference.get().runOnUiThread(() -> {
|
||||
CocosXRWebViewContainer xrWebViewContainer = new CocosXRWebViewContainer(activityWeakReference.get());
|
||||
View decorView = activityWeakReference.get().getWindow().getDecorView();
|
||||
if(decorView instanceof FrameLayout) {
|
||||
FrameLayout parentLayout = (FrameLayout) decorView;
|
||||
FrameLayout.LayoutParams frameLayoutParams = new FrameLayout.LayoutParams(textureWidth, textureHeight);
|
||||
parentLayout.addView(xrWebViewContainer, frameLayoutParams);
|
||||
xrWebViewContainer.setZ(-100 + webviewId);
|
||||
}
|
||||
if (url != null) {
|
||||
if (isMobileUA) {
|
||||
xrWebViewContainer.getSettings().setUserAgentString("Mozilla/5.0 (Linux; Android 11) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.82 Mobile Safari/537.36");
|
||||
} else {
|
||||
xrWebViewContainer.getSettings().setUserAgentString("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36");
|
||||
}
|
||||
xrWebViewContainer.loadUrl(url);
|
||||
}
|
||||
|
||||
xrWebViewHashMap.put(String.valueOf(webviewId), xrWebViewContainer);
|
||||
// notify
|
||||
JsbBridgeWrapper.getInstance().dispatchEventToScript(XR_WEBVIEW_EVENT_NAME.concat(String.valueOf(webviewId)), XR_WEBVIEW_EVENT_TAG_ADDED);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void removeWebView(int webviewId) {
|
||||
if (!xrWebViewHashMap.containsKey(String.valueOf(webviewId))) {
|
||||
return;
|
||||
}
|
||||
Log.d(TAG, "removeWebView:" + webviewId);
|
||||
if (activityWeakReference.get() != null) {
|
||||
activityWeakReference.get().runOnUiThread(() -> {
|
||||
CocosXRWebViewContainer xrWebViewContainer = xrWebViewHashMap.get(String.valueOf(webviewId));
|
||||
if (xrWebViewContainer != null) {
|
||||
View decorView = activityWeakReference.get().getWindow().getDecorView();
|
||||
if(decorView instanceof FrameLayout) {
|
||||
FrameLayout parentLayout = (FrameLayout) decorView;
|
||||
parentLayout.removeView(xrWebViewContainer);
|
||||
}
|
||||
|
||||
xrWebViewContainer.onDestroy();
|
||||
webViewGLThread.queueEvent(() -> {
|
||||
xrWebViewContainer.onGLDestroy();
|
||||
xrWebViewHashMap.remove(String.valueOf(webviewId));
|
||||
});
|
||||
// notify
|
||||
JsbBridgeWrapper.getInstance().dispatchEventToScript(XR_WEBVIEW_EVENT_TAG_REMOVED.concat(String.valueOf(webviewId)));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void goForward(int webviewId) {
|
||||
if (activityWeakReference.get() != null) {
|
||||
activityWeakReference.get().runOnUiThread(() -> {
|
||||
CocosXRWebViewContainer xrWebViewContainer = xrWebViewHashMap.get(String.valueOf(webviewId));
|
||||
if (xrWebViewContainer != null) {
|
||||
xrWebViewContainer.goForward();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void goBack(int webviewId) {
|
||||
if (activityWeakReference.get() != null) {
|
||||
activityWeakReference.get().runOnUiThread(() -> {
|
||||
CocosXRWebViewContainer xrWebViewContainer = xrWebViewHashMap.get(String.valueOf(webviewId));
|
||||
if (xrWebViewContainer != null) {
|
||||
xrWebViewContainer.goBack();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void reload(int webviewId) {
|
||||
if (activityWeakReference.get() != null) {
|
||||
activityWeakReference.get().runOnUiThread(() -> {
|
||||
CocosXRWebViewContainer xrWebViewContainer = xrWebViewHashMap.get(String.valueOf(webviewId));
|
||||
if (xrWebViewContainer != null) {
|
||||
xrWebViewContainer.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void loadUrl(int webviewId, String url) {
|
||||
if (activityWeakReference.get() != null) {
|
||||
activityWeakReference.get().runOnUiThread(() -> {
|
||||
CocosXRWebViewContainer xrWebViewContainer = xrWebViewHashMap.get(String.valueOf(webviewId));
|
||||
if (xrWebViewContainer != null) {
|
||||
if (isMobileUA) {
|
||||
xrWebViewContainer.getSettings().setUserAgentString("Mozilla/5.0 (Linux; Android 11) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.82 Mobile Safari/537.36");
|
||||
} else {
|
||||
xrWebViewContainer.getSettings().setUserAgentString("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36");
|
||||
}
|
||||
xrWebViewContainer.loadUrl(url);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void setTextureInfo(int webViewId, int textureId, int textureWidth, int textureHeight) {
|
||||
Log.d(TAG, "setTextureInfo:" + webViewId + "," + textureWidth + "x" + textureHeight + ":" + textureId);
|
||||
CocosXRWebViewContainer xrWebViewContainer = xrWebViewHashMap.get(String.valueOf(webViewId));
|
||||
if (xrWebViewContainer != null) {
|
||||
xrWebViewContainer.setTextureInfo(textureWidth, textureHeight, textureId);
|
||||
}
|
||||
|
||||
if (webViewGLThread == null) {
|
||||
webViewGLThread = new CocosXRWebViewGLThread();
|
||||
webViewGLThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
public void onCreate(Activity activity) {
|
||||
Log.d(TAG, "onCreate");
|
||||
activityWeakReference = new WeakReference<>(activity);
|
||||
for (int i = 0; i < MAX_COUNT; i++) {
|
||||
String eventName = XR_WEBVIEW_EVENT_NAME.concat(String.valueOf(i));
|
||||
int webviewId = i;
|
||||
JsbBridgeWrapper.getInstance().addScriptEventListener(eventName, arg -> {
|
||||
if (arg == null) {
|
||||
Log.e(TAG, "Invalid arg is null !!!");
|
||||
return;
|
||||
}
|
||||
String[] dataArray = arg.split("&");
|
||||
if (TextUtils.equals(dataArray[0], XR_WEBVIEW_EVENT_TAG_TEXTUREINFO)) {
|
||||
// id&w&h
|
||||
setTextureInfo(webviewId, Integer.parseInt(dataArray[1]), Integer.parseInt(dataArray[2]), Integer.parseInt(dataArray[3]));
|
||||
} else if (TextUtils.equals(dataArray[0], XR_WEBVIEW_EVENT_TAG_TO_ADD)) {
|
||||
int textureWidth = Integer.parseInt(dataArray[1]);
|
||||
int textureHeight = Integer.parseInt(dataArray[2]);
|
||||
String url = dataArray.length > 3 ? dataArray[3] : null;
|
||||
createWebView(webviewId, textureWidth, textureHeight, url);
|
||||
} else if (TextUtils.equals(dataArray[0], XR_WEBVIEW_EVENT_TAG_TO_REMOVE)) {
|
||||
removeWebView(webviewId);
|
||||
} else if (TextUtils.equals(dataArray[0], XR_WEBVIEW_EVENT_TAG_HOVER)) {
|
||||
CocosXRWebViewContainer xrWebViewContainer = xrWebViewHashMap.get(String.valueOf(webviewId));
|
||||
if (xrWebViewContainer != null) {
|
||||
xrWebViewContainer.simulateTouchMove(Float.parseFloat(dataArray[1]), Float.parseFloat(dataArray[2]));
|
||||
}
|
||||
} else if (TextUtils.equals(dataArray[0], XR_WEBVIEW_EVENT_TAG_CLICK_DOWN)) {
|
||||
CocosXRWebViewContainer xrWebViewContainer = xrWebViewHashMap.get(String.valueOf(webviewId));
|
||||
if (xrWebViewContainer != null) {
|
||||
xrWebViewContainer.simulateTouchDown(Float.parseFloat(dataArray[1]), Float.parseFloat(dataArray[2]));
|
||||
}
|
||||
} else if (TextUtils.equals(dataArray[0], XR_WEBVIEW_EVENT_TAG_CLICK_UP)) {
|
||||
CocosXRWebViewContainer xrWebViewContainer = xrWebViewHashMap.get(String.valueOf(webviewId));
|
||||
if (xrWebViewContainer != null) {
|
||||
xrWebViewContainer.simulateTouchUp(Float.parseFloat(dataArray[1]), Float.parseFloat(dataArray[2]));
|
||||
}
|
||||
} else if (TextUtils.equals(dataArray[0], XR_WEBVIEW_EVENT_TAG_LOADURL)) {
|
||||
loadUrl(webviewId, dataArray[1]);
|
||||
} else if (TextUtils.equals(dataArray[0], XR_WEBVIEW_EVENT_TAG_RELOAD)) {
|
||||
reload(webviewId);
|
||||
} else if (TextUtils.equals(dataArray[0], XR_WEBVIEW_EVENT_TAG_GOBACK)) {
|
||||
goBack(webviewId);
|
||||
} else if (TextUtils.equals(dataArray[0], XR_WEBVIEW_EVENT_TAG_GOFORWARD)) {
|
||||
goForward(webviewId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void onResume() {
|
||||
Log.d(TAG, "onResume");
|
||||
if (webViewGLThread != null) {
|
||||
webViewGLThread.onResume();
|
||||
}
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
Log.d(TAG, "onPause");
|
||||
if (webViewGLThread != null) {
|
||||
webViewGLThread.onPause();
|
||||
}
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
Log.d(TAG, "onDestroy");
|
||||
if (webViewGLThread != null) {
|
||||
webViewGLThread.onDestroy();
|
||||
webViewGLThread = null;
|
||||
}
|
||||
for (int i = 0; i < MAX_COUNT; i++) {
|
||||
JsbBridgeWrapper.getInstance().removeAllListenersForEvent(XR_WEBVIEW_EVENT_NAME.concat(String.valueOf(i)));
|
||||
}
|
||||
Set<Map.Entry<String, CocosXRWebViewContainer>> entrySets = xrWebViewHashMap.entrySet();
|
||||
for (Map.Entry<String, CocosXRWebViewContainer> entrySet : entrySets) {
|
||||
CocosXRWebViewContainer xrWebViewContainer = entrySet.getValue();
|
||||
if (activityWeakReference.get() != null) {
|
||||
View decorView = activityWeakReference.get().getWindow().getDecorView();
|
||||
if(decorView instanceof FrameLayout) {
|
||||
FrameLayout parentLayout = (FrameLayout) decorView;
|
||||
parentLayout.removeView(xrWebViewContainer);
|
||||
}
|
||||
}
|
||||
xrWebViewContainer.onDestroy();
|
||||
}
|
||||
xrWebViewHashMap.clear();
|
||||
}
|
||||
|
||||
class CocosXRWebViewGLThread extends Thread {
|
||||
private final ReentrantLock lockObj = new ReentrantLock(true);
|
||||
private final Condition pauseCondition = lockObj.newCondition();
|
||||
private boolean running = false;
|
||||
private boolean requestPaused = false;
|
||||
private boolean requestExited = false;
|
||||
long lastTickTime = System.nanoTime();
|
||||
private final ArrayList<Runnable> mEventQueue = new ArrayList<>();
|
||||
|
||||
EGLContext eglContext;
|
||||
EGLDisplay eglDisplay;
|
||||
EGLSurface pBufferSurface;
|
||||
EGLContext parentContext;
|
||||
|
||||
CocosXRWebViewGLThread() {
|
||||
parentContext = EGL14.eglGetCurrentContext();
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
init();
|
||||
while (true) {
|
||||
lockObj.lock();
|
||||
try {
|
||||
if (requestExited) {
|
||||
running = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (requestPaused) {
|
||||
running = false;
|
||||
pauseCondition.await();
|
||||
running = true;
|
||||
}
|
||||
|
||||
if (requestExited) {
|
||||
running = false;
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
lockObj.unlock();
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
if (!mEventQueue.isEmpty()) {
|
||||
Runnable event = mEventQueue.remove(0);
|
||||
if (event != null) {
|
||||
event.run();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tick();
|
||||
}
|
||||
|
||||
exit();
|
||||
}
|
||||
|
||||
void init() {
|
||||
running = true;
|
||||
eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
|
||||
int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL14.EGL_NONE};
|
||||
int[] attrList = new int[]{EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT,
|
||||
EGL14.EGL_RENDERABLE_TYPE, 0x00000040,
|
||||
EGL14.EGL_RED_SIZE, 8,
|
||||
EGL14.EGL_GREEN_SIZE, 8,
|
||||
EGL14.EGL_BLUE_SIZE, 8,
|
||||
EGL14.EGL_DEPTH_SIZE, 0,
|
||||
EGL14.EGL_SAMPLE_BUFFERS, 1,
|
||||
EGL14.EGL_SAMPLES, 1,
|
||||
EGL14.EGL_STENCIL_SIZE, 0,
|
||||
EGL14.EGL_NONE};
|
||||
EGLConfig[] configOut = new EGLConfig[1];
|
||||
int[] configNumOut = new int[1];
|
||||
EGL14.eglChooseConfig(eglDisplay, attrList, 0, configOut, 0, 1,
|
||||
configNumOut, 0);
|
||||
eglContext = EGL14.eglCreateContext(eglDisplay, configOut[0], parentContext, attrib_list, 0);
|
||||
|
||||
int[] sur_attrib_list = {EGL14.EGL_WIDTH, 1, EGL14.EGL_HEIGHT, 1, EGL14.EGL_NONE};
|
||||
pBufferSurface = EGL14.eglCreatePbufferSurface(eglDisplay, configOut[0], sur_attrib_list, 0);
|
||||
|
||||
EGL14.eglMakeCurrent(eglDisplay, pBufferSurface, pBufferSurface, eglContext);
|
||||
GLES30.glDisable(GLES30.GL_DEPTH_TEST);
|
||||
GLES30.glDisable(GLES30.GL_BLEND);
|
||||
GLES30.glDisable(GLES30.GL_CULL_FACE);
|
||||
|
||||
lastTickTime = System.nanoTime();
|
||||
Log.d(TAG, "CocosXRWebViewGLThread init");
|
||||
}
|
||||
|
||||
void tick() {
|
||||
// draw
|
||||
lastTickTime = System.nanoTime();
|
||||
|
||||
Set<Map.Entry<String, CocosXRWebViewContainer>> entrySets = xrWebViewHashMap.entrySet();
|
||||
for (Map.Entry<String, CocosXRWebViewContainer> entrySet : entrySets) {
|
||||
entrySet.getValue().onBeforeGLDrawFrame();
|
||||
if(entrySet.getValue().getVideoTextureWidth() == 0 || entrySet.getValue().getVideoTextureHeight() == 0) {
|
||||
continue;
|
||||
}
|
||||
entrySet.getValue().onGLDrawFrame();
|
||||
}
|
||||
|
||||
EGL14.eglSwapBuffers(eglDisplay, pBufferSurface);
|
||||
|
||||
if (System.nanoTime() - lastTickTime < 16666666) {
|
||||
// lock 60fps
|
||||
try {
|
||||
long sleepTimeNS = 16666666 - (System.nanoTime() - lastTickTime);
|
||||
Thread.sleep(sleepTimeNS / 1000000);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void exit() {
|
||||
running = false;
|
||||
Set<Map.Entry<String, CocosXRWebViewContainer>> entrySets = xrWebViewHashMap.entrySet();
|
||||
for (Map.Entry<String, CocosXRWebViewContainer> entrySet : entrySets) {
|
||||
entrySet.getValue().onGLDestroy();
|
||||
}
|
||||
EGL14.eglMakeCurrent(eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
|
||||
EGL14.eglDestroySurface(eglDisplay, pBufferSurface);
|
||||
EGL14.eglDestroyContext(eglDisplay, eglContext);
|
||||
Log.d(TAG, "CocosXRWebViewGLThread exit");
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
lockObj.lock();
|
||||
requestPaused = true;
|
||||
lockObj.unlock();
|
||||
Log.d(TAG, "CocosXRWebViewGLThread onPause");
|
||||
}
|
||||
|
||||
public void onResume() {
|
||||
lockObj.lock();
|
||||
requestPaused = false;
|
||||
pauseCondition.signalAll();
|
||||
lockObj.unlock();
|
||||
Log.d(TAG, "CocosXRWebViewGLThread onResume");
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
lockObj.lock();
|
||||
requestExited = true;
|
||||
pauseCondition.signalAll();
|
||||
lockObj.unlock();
|
||||
|
||||
try {
|
||||
join();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, e.getLocalizedMessage());
|
||||
}
|
||||
Log.d(TAG, "CocosXRWebViewGLThread onDestroy");
|
||||
}
|
||||
|
||||
public void queueEvent(Runnable r) {
|
||||
synchronized (this) {
|
||||
mEventQueue.add(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
|
||||
*
|
||||
* http://www.cocos.com
|
||||
*
|
||||
* 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 com.cocos.lib.xr.permission;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.app.FragmentTransaction;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class CocosXRPermissionFragment extends Fragment {
|
||||
private static final int PERMISSION_REQUEST_CODE = 1101;
|
||||
private static final String TAG = "CocosPermissionFragment";
|
||||
private static final String PERMISSION_TAG = "TAG_PermissionFragment";
|
||||
|
||||
public static CocosXRPermissionFragment getInstance(Activity activity) {
|
||||
FragmentManager fm = activity.getFragmentManager();
|
||||
CocosXRPermissionFragment fragment = (CocosXRPermissionFragment) fm.findFragmentByTag(PERMISSION_TAG);
|
||||
if (fragment == null) {
|
||||
try {
|
||||
Log.d(TAG, "Creating CocosXRPermissionFragment");
|
||||
fragment = new CocosXRPermissionFragment();
|
||||
FragmentTransaction trans = fm.beginTransaction();
|
||||
trans.add(fragment, PERMISSION_TAG);
|
||||
trans.commit();
|
||||
fm.executePendingTransactions();
|
||||
} catch (Throwable th) {
|
||||
Log.e(TAG, "Cannot launch PermissionFragment:" + th.getMessage(), th);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setRetainInstance(true);
|
||||
Log.d(TAG, "onCreate");
|
||||
}
|
||||
|
||||
public void acquirePermissions(String[] permissions) {
|
||||
Handler handler = new Handler(Looper.getMainLooper());
|
||||
handler.post(() -> {
|
||||
final int[] grantResults = new int[permissions.length];
|
||||
Activity context = getActivity();
|
||||
if (context != null) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
String packageName = context.getPackageName();
|
||||
for (String permission : permissions) {
|
||||
if (PackageManager.PERMISSION_DENIED == packageManager.checkPermission(permission, packageName)) {
|
||||
CocosXRPermissionFragment.this.requestPermissions(permissions, PERMISSION_REQUEST_CODE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Arrays.fill(grantResults, PackageManager.PERMISSION_GRANTED);
|
||||
onRequestPermissionsResult(PERMISSION_REQUEST_CODE, permissions, grantResults);
|
||||
} else {
|
||||
Log.e(TAG, "acquirePermissions failed !");
|
||||
Arrays.fill(grantResults, PackageManager.PERMISSION_DENIED);
|
||||
onRequestPermissionsResult(PERMISSION_REQUEST_CODE, permissions, grantResults);
|
||||
}
|
||||
} else {
|
||||
Arrays.fill(grantResults, PackageManager.PERMISSION_DENIED);
|
||||
onRequestPermissionsResult(PERMISSION_REQUEST_CODE, permissions, grantResults);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||
if (requestCode == PERMISSION_REQUEST_CODE && permissions.length > 0) {
|
||||
CocosXRPermissionHelper.onAcquirePermissions(permissions, grantResults);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
|
||||
*
|
||||
* http://www.cocos.com
|
||||
*
|
||||
* 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 com.cocos.lib.xr.permission;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Process;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import com.cocos.lib.CocosHelper;
|
||||
import com.cocos.lib.JsbBridgeWrapper;
|
||||
import com.cocos.lib.xr.CocosXRApi;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class CocosXRPermissionHelper {
|
||||
private static final String LOG_TAG = "CocosXRPermissionHelper";
|
||||
public static final String XR_PERMISSION_EVENT_NAME = "xr-permission";
|
||||
public static final String XR_PERMISSION_TAG_CHECK = "check";
|
||||
public static final String XR_PERMISSION_TAG_REQUEST = "request";
|
||||
public interface PermissionCallback {
|
||||
void onRequestPermissionsResult(String[] permissions, int[] grantResults);
|
||||
}
|
||||
|
||||
private static PermissionCallback permissionCallback = null;
|
||||
public static void onScriptEvent(String arg) {
|
||||
String[] array = arg.split(":");
|
||||
if (TextUtils.equals(array[0], XR_PERMISSION_TAG_CHECK)) {
|
||||
int result = checkPermission(array[1]) ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED;
|
||||
CocosHelper.runOnGameThread(() -> JsbBridgeWrapper.getInstance().dispatchEventToScript(XR_PERMISSION_EVENT_NAME, XR_PERMISSION_TAG_CHECK + ":" + array[1] + ":" + result));
|
||||
} else if (TextUtils.equals(array[0], XR_PERMISSION_TAG_REQUEST)) {
|
||||
String[] permissionNames = array[1].split("&");
|
||||
acquirePermissions(permissionNames, (PermissionCallback) null);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean checkPermission(String permission) {
|
||||
Context context = CocosXRApi.getInstance().getContext();
|
||||
if (context == null)
|
||||
return false;
|
||||
if (context.checkPermission(permission, android.os.Process.myPid(), Process.myUid()) == PackageManager.PERMISSION_GRANTED) {
|
||||
Log.d(LOG_TAG, "checkPermission: " + permission + " has granted");
|
||||
return true;
|
||||
} else {
|
||||
Log.d(LOG_TAG, "checkPermission: " + permission + " has not granted");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void acquirePermissions(String[] permissions, PermissionCallback callback) {
|
||||
permissionCallback = callback;
|
||||
CocosXRPermissionHelper.acquirePermissions(permissions, CocosXRApi.getInstance().getActivity());
|
||||
}
|
||||
|
||||
public static void acquirePermissions(String[] permissions, Activity InActivity) {
|
||||
if (InActivity == null)
|
||||
return;
|
||||
final Activity activity = InActivity;
|
||||
activity.runOnUiThread(() -> {
|
||||
CocosXRPermissionFragment fragment = CocosXRPermissionFragment.getInstance(activity);
|
||||
if (fragment != null) {
|
||||
fragment.acquirePermissions(permissions);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void onAcquirePermissions(String[] permissions, int[] grantResults) {
|
||||
Log.d(LOG_TAG, "onAcquirePermissions:" + Arrays.toString(permissions) + "|" + Arrays.toString(grantResults));
|
||||
//
|
||||
CocosHelper.runOnGameThread(() -> {
|
||||
StringBuilder stringBuilder = new StringBuilder(XR_PERMISSION_TAG_REQUEST);
|
||||
stringBuilder.append(":");
|
||||
for (int i = 0; i < permissions.length; i++) {
|
||||
stringBuilder.append(permissions[i]).append("#").append(grantResults[i]);
|
||||
if (i != permissions.length - 1) {
|
||||
stringBuilder.append("&");
|
||||
}
|
||||
}
|
||||
JsbBridgeWrapper.getInstance().dispatchEventToScript(XR_PERMISSION_EVENT_NAME, stringBuilder.toString());
|
||||
});
|
||||
if (permissionCallback != null) {
|
||||
permissionCallback.onRequestPermissionsResult(permissions, grantResults);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user