no message

This commit is contained in:
gem
2025-02-18 15:21:31 +08:00
commit 2d133e56d7
1980 changed files with 465595 additions and 0 deletions

View 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>

View 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')
}

View 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

View File

@@ -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);
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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());
}
}
});
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}