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,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>libcocos2dx</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cocos.lib"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="9"/>
</manifest>

View File

@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="cocos2dxandroid" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
It contains the path to the SDK. It should *NOT* be checked into
Version Control Systems. -->
<property file="local.properties" />
<!-- The ant.properties file can be created by you. It is only edited by the
'android' tool to add properties to it.
This is the place to change some Ant specific build properties.
Here are some properties you may want to change/update:
source.dir
The name of the source directory. Default is 'src'.
out.dir
The name of the output directory. Default is 'bin'.
For other overridable properties, look at the beginning of the rules
files in the SDK, at tools/ant/build.xml
Properties related to the SDK location or the project target should
be updated using the 'android' tool with the 'update' action.
This file is an integral part of the build system for your
application and should be checked into Version Control Systems.
-->
<property file="ant.properties" />
<!-- The project.properties file is created and updated by the 'android'
tool, as well as ADT.
This contains project specific properties such as project target, and library
dependencies. Lower level build properties are stored in ant.properties
(or in .classpath for Eclipse projects).
This file is an integral part of the build system for your
application and should be checked into Version Control Systems. -->
<loadproperties srcFile="project.properties" />
<!-- quick check on sdk.dir -->
<fail
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through an env var"
unless="sdk.dir"
/>
<!--
Import per project custom build rules if present at the root of the project.
This is the place to put custom intermediary targets such as:
-pre-build
-pre-compile
-post-compile (This is typically used for code obfuscation.
Compiled code location: ${out.classes.absolute.dir}
If this is not done in place, override ${out.dex.input.absolute.dir})
-post-package
-post-build
-pre-clean
-->
<import file="custom_rules.xml" optional="true" />
<!-- Import the actual build file.
To customize existing targets, there are two options:
- Customize only one target:
- copy/paste the target into this file, *before* the
<import> task.
- customize it to your needs.
- Customize the whole content of build.xml
- copy/paste the content of the rules files (minus the top node)
into this file, replacing the <import> task.
- customize to your needs.
***********************
****** IMPORTANT ******
***********************
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
in order to avoid having your file be overridden by tools such as "android update project"
-->
<!-- version-tag: 1 -->
<import file="${sdk.dir}/tools/ant/build.xml" />
</project>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,13 @@
# To enable ProGuard in your project, edit project.properties
# to define the proguard.config property as described in that file.
#
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in ${sdk.dir}/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in project.properties.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:

View File

@@ -0,0 +1,15 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system edit
# "ant.properties", and override values to adapt the script to your
# project structure.
#
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
android.library=true
# Project target.
target=android-10

View File

@@ -0,0 +1,529 @@
/****************************************************************************
* Copyright (c) 2018 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;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Typeface;
import android.os.Build;
import android.text.TextPaint;
import android.util.Log;
import java.lang.ref.WeakReference;
import java.util.HashMap;
public class CanvasRenderingContext2DImpl {
private static final String TAG = "CanvasContext2D";
private static final int TEXT_ALIGN_LEFT = 0;
private static final int TEXT_ALIGN_CENTER = 1;
private static final int TEXT_ALIGN_RIGHT = 2;
private static final int TEXT_BASELINE_TOP = 0;
private static final int TEXT_BASELINE_MIDDLE = 1;
private static final int TEXT_BASELINE_BOTTOM = 2;
private static final int TEXT_BASELINE_ALPHABETIC = 3;
private static WeakReference<Context> sContext;
private TextPaint mTextPaint;
private Paint mLinePaint;
private Path mLinePath;
private Canvas mCanvas = new Canvas();
private Bitmap mBitmap;
private int mTextAlign = TEXT_ALIGN_LEFT;
private int mTextBaseline = TEXT_BASELINE_BOTTOM;
private int mFillStyleR = 0;
private int mFillStyleG = 0;
private int mFillStyleB = 0;
private int mFillStyleA = 255;
private int mStrokeStyleR = 0;
private int mStrokeStyleG = 0;
private int mStrokeStyleB = 0;
private int mStrokeStyleA = 255;
private String mFontName = "Arial";
private float mFontSize = 40.0f;
private float mLineWidth = 0.0f;
private float mShadowBlur = 0.0f;
private float mShadowOffsetX = 0.0f;
private float mShadowOffsetY = 0.0f;
private int mShadowColorA = 0;
private int mShadowColorB = 0;
private int mShadowColorG = 0;
private int mShadowColorR = 0;
private static float _sApproximatingOblique = -0.25f;//please check paint api documentation
private boolean mIsBoldFont = false;
private boolean mIsItalicFont = false;
private boolean mIsObliqueFont = false;
private boolean mIsSmallCapsFontVariant = false;
private String mLineCap = "butt";
private String mLineJoin = "miter";
private class Size {
Size(float w, float h) {
this.width = w;
this.height = h;
}
Size() {
this.width = 0;
this.height = 0;
}
public float width;
public float height;
}
private class Point {
Point(float x, float y) {
this.x = x;
this.y = y;
}
Point() {
this.x = this.y = 0.0f;
}
Point(Point pt) {
this.x = pt.x;
this.y = pt.y;
}
void set(float x, float y) {
this.x = x;
this.y = y;
}
public float x;
public float y;
}
static void init(Context context) {
sContext = new WeakReference<>(context);
}
static void destroy() {
sContext = null;
}
private static HashMap<String, Typeface> sTypefaceCache = new HashMap<>();
// url is a full path started with '@assets/'
private static void loadTypeface(String familyName, String url) {
if (!sTypefaceCache.containsKey(familyName)) {
try {
Typeface typeface = null;
if (url.startsWith("/")) {
typeface = Typeface.createFromFile(url);
} else if (sContext.get() != null) {
final String prefix = "@assets/";
if (url.startsWith(prefix)) {
url = url.substring(prefix.length());
}
typeface = Typeface.createFromAsset(sContext.get().getAssets(), url);
}
if (typeface != null) {
sTypefaceCache.put(familyName, typeface);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
// REFINE:: native should clear font cache before exiting game.
private static void clearTypefaceCache() {
sTypefaceCache.clear();
}
private static TextPaint newPaint(String fontName, int fontSize, boolean enableBold, boolean enableItalic, boolean obliqueFont, boolean smallCapsFontVariant) {
TextPaint paint = new TextPaint();
paint.setTextSize(fontSize);
paint.setAntiAlias(true);
paint.setSubpixelText(true);
int style = Typeface.NORMAL;
if (enableBold && enableItalic) {
paint.setFakeBoldText(true);
style = Typeface.BOLD_ITALIC;
} else if (enableBold) {
paint.setFakeBoldText(true);
style = Typeface.BOLD;
} else if (enableItalic) {
style = Typeface.ITALIC;
}
Typeface typeFace = null;
if (sTypefaceCache.containsKey(fontName)) {
typeFace = sTypefaceCache.get(fontName);
typeFace = Typeface.create(typeFace, style);
} else {
typeFace = Typeface.create(fontName, style);
}
paint.setTypeface(typeFace);
if(obliqueFont) {
paint.setTextSkewX(_sApproximatingOblique);
}
if(smallCapsFontVariant && Build.VERSION.SDK_INT >= 21) {
CocosReflectionHelper.<Void>invokeInstanceMethod(paint,
"setFontFeatureSettings",
new Class[]{String.class},
new Object[]{"smcp"});
}
return paint;
}
private CanvasRenderingContext2DImpl() {
// Log.d(TAG, "constructor");
}
private void recreateBuffer(float w, float h) {
// Log.d(TAG, "recreateBuffer:" + w + ", " + h);
if (mBitmap != null) {
mBitmap.recycle();
}
mBitmap = Bitmap.createBitmap((int)Math.ceil(w), (int)Math.ceil(h), Bitmap.Config.ARGB_8888);
// FIXME: in MIX 2S, its API level is 28, but can not find invokeInstanceMethod. It seems
// devices may not obey the specification, so comment the codes.
// if (Build.VERSION.SDK_INT >= 19) {
// CocosReflectionHelper.<Void>invokeInstanceMethod(mBitmap,
// "setPremultiplied",
// new Class[]{Boolean.class},
// new Object[]{Boolean.FALSE});
// }
mCanvas.setBitmap(mBitmap);
}
private void beginPath() {
if (mLinePath == null) {
mLinePath = new Path();
}
mLinePath.reset();
}
private void closePath() {
mLinePath.close();
}
private void moveTo(float x, float y) {
mLinePath.moveTo(x, y);
}
private void lineTo(float x, float y) {
mLinePath.lineTo(x, y);
}
private void stroke() {
if (mLinePaint == null) {
mLinePaint = new Paint();
mLinePaint.setAntiAlias(true);
}
if(mLinePath == null) {
mLinePath = new Path();
}
mLinePaint.setARGB(mStrokeStyleA, mStrokeStyleR, mStrokeStyleG, mStrokeStyleB);
mLinePaint.setStyle(Paint.Style.STROKE);
mLinePaint.setStrokeWidth(mLineWidth);
this.setStrokeCap(mLinePaint);
this.setStrokeJoin(mLinePaint);
mCanvas.drawPath(mLinePath, mLinePaint);
}
private void setStrokeCap(Paint paint) {
switch (mLineCap) {
case "butt":
paint.setStrokeCap(Paint.Cap.BUTT);
break;
case "round":
paint.setStrokeCap(Paint.Cap.ROUND);
break;
case "square":
paint.setStrokeCap(Paint.Cap.SQUARE);
break;
}
}
private void setStrokeJoin(Paint paint) {
switch (mLineJoin) {
case "bevel":
paint.setStrokeJoin(Paint.Join.BEVEL);
break;
case "round":
paint.setStrokeJoin(Paint.Join.ROUND);
break;
case "miter":
paint.setStrokeJoin(Paint.Join.MITER);
break;
}
}
private void fill() {
if (mLinePaint == null) {
mLinePaint = new Paint();
}
if(mLinePath == null) {
mLinePath = new Path();
}
mLinePaint.setARGB(mFillStyleA, mFillStyleR, mFillStyleG, mFillStyleB);
mLinePaint.setStyle(Paint.Style.FILL);
mCanvas.drawPath(mLinePath, mLinePaint);
// workaround: draw a hairline to cover the border
mLinePaint.setStrokeWidth(0);
this.setStrokeCap(mLinePaint);
this.setStrokeJoin(mLinePaint);
mLinePaint.setStyle(Paint.Style.STROKE);
mCanvas.drawPath(mLinePath, mLinePaint);
mLinePaint.setStrokeWidth(mLineWidth);
}
private void setLineCap(String lineCap) {
mLineCap = lineCap;
}
private void setLineJoin(String lineJoin) {
mLineJoin = lineJoin;
}
private void setShadowBlur(float blur) {
mShadowBlur = blur * 0.5f;
}
private void setShadowColor(int r, int g, int b, int a) {
mShadowColorR = r;
mShadowColorG = g;
mShadowColorB = b;
mShadowColorA = a;
}
private void setShadowOffsetX(float offsetX) {
mShadowOffsetX = offsetX;
}
private void setShadowOffsetY(float offsetY) {
mShadowOffsetY = offsetY;
}
private void saveContext() {
mCanvas.save();
}
private void restoreContext() {
// If there is no saved state, this method should do nothing.
if (mCanvas.getSaveCount() > 1){
mCanvas.restore();
}
}
private void rect(float x, float y, float w, float h) {
// Log.d(TAG, "this: " + this + ", rect: " + x + ", " + y + ", " + w + ", " + h);
beginPath();
moveTo(x, y);
lineTo(x, y + h);
lineTo(x + w, y + h);
lineTo(x + w, y);
closePath();
}
private void clearRect(float x, float y, float w, float h) {
// Log.d(TAG, "this: " + this + ", clearRect: " + x + ", " + y + ", " + w + ", " + h);
int clearSize = (int)(w * h);
int[] clearColor = new int[clearSize];
for (int i = 0; i < clearSize; ++i) {
clearColor[i] = Color.TRANSPARENT;
}
mBitmap.setPixels(clearColor, 0, (int) w, (int) x, (int) y, (int) w, (int) h);
}
private void createTextPaintIfNeeded() {
if (mTextPaint == null) {
mTextPaint = newPaint(mFontName, (int) mFontSize, mIsBoldFont, mIsItalicFont, mIsObliqueFont, mIsSmallCapsFontVariant);
}
}
private void fillRect(float x, float y, float w, float h) {
// Log.d(TAG, "fillRect: " + x + ", " + y + ", " + ", " + w + ", " + h);
int pixelValue = (mFillStyleA & 0xff) << 24 | (mFillStyleR & 0xff) << 16 | (mFillStyleG & 0xff) << 8 | (mFillStyleB & 0xff);
int fillSize = (int)(w * h);
int[] fillColors = new int[fillSize];
for (int i = 0; i < fillSize; ++i) {
fillColors[i] = pixelValue;
}
mBitmap.setPixels(fillColors, 0, (int) w, (int)x, (int)y, (int)w, (int)h);
}
private void scaleX(TextPaint textPaint, String text, float maxWidth) {
if(maxWidth < Float.MIN_VALUE) return;
float measureWidth = this.measureText(text);
if((measureWidth - maxWidth) < Float.MIN_VALUE) return;
float scaleX = maxWidth/measureWidth;
textPaint.setTextScaleX(scaleX);
}
private void fillText(String text, float x, float y, float maxWidth) {
// Log.d(TAG, "this: " + this + ", fillText: " + text + ", " + x + ", " + y + ", " + ", " + maxWidth);
createTextPaintIfNeeded();
configShadow(mTextPaint);
mTextPaint.setARGB(mFillStyleA, mFillStyleR, mFillStyleG, mFillStyleB);
mTextPaint.setStyle(Paint.Style.FILL);
scaleX(mTextPaint, text, maxWidth);
Point pt = convertDrawPoint(new Point(x, y), text);
mCanvas.drawText(text, pt.x, pt.y, mTextPaint);
}
private void strokeText(String text, float x, float y, float maxWidth) {
// Log.d(TAG, "strokeText: " + text + ", " + x + ", " + y + ", " + ", " + maxWidth);
createTextPaintIfNeeded();
configShadow(mTextPaint);
mTextPaint.setARGB(mStrokeStyleA, mStrokeStyleR, mStrokeStyleG, mStrokeStyleB);
mTextPaint.setStyle(Paint.Style.STROKE);
mTextPaint.setStrokeWidth(mLineWidth);
scaleX(mTextPaint, text, maxWidth);
Point pt = convertDrawPoint(new Point(x, y), text);
mCanvas.drawText(text, pt.x, pt.y, mTextPaint);
}
private void configShadow(Paint paint) {
if ((Math.abs(mShadowOffsetX) > Float.MIN_VALUE || Math.abs(mShadowOffsetY) > Float.MIN_VALUE)) {
if (mShadowBlur < 0) {
return;
}
if (mShadowBlur < Float.MIN_VALUE) {
mShadowBlur = 0.001f;//If shadowBlur is 0, the shadow effect is not consistent with the web.
}
paint.setShadowLayer(mShadowBlur, mShadowOffsetX, mShadowOffsetY,
Color.argb(mShadowColorA, mShadowColorR, mShadowColorG, mShadowColorB));
}
}
private float measureText(String text) {
createTextPaintIfNeeded();
float ret = mTextPaint.measureText(text);
// Log.d(TAG, "measureText: " + text + ", return: " + ret);
return ret;
}
private void updateFont(String fontName, float fontSize, boolean bold, boolean italic, boolean oblique, boolean smallCaps) {
// Log.d(TAG, "updateFont: " + fontName + ", " + fontSize);
mFontName = fontName;
mFontSize = fontSize;
mIsBoldFont = bold;
mIsItalicFont = italic;
mIsObliqueFont = oblique;
mIsSmallCapsFontVariant = smallCaps;
mTextPaint = null; // Reset paint to re-create paint object in createTextPaintIfNeeded
}
private void setTextAlign(int align) {
// Log.d(TAG, "setTextAlign: " + align);
mTextAlign = align;
}
private void setTextBaseline(int baseline) {
// Log.d(TAG, "setTextBaseline: " + baseline);
mTextBaseline = baseline;
}
private void setFillStyle(int r, int g, int b, int a) {
// Log.d(TAG, "setFillStyle: " + r + ", " + g + ", " + b + ", " + a);
mFillStyleR = r;
mFillStyleG = g;
mFillStyleB = b;
mFillStyleA = a;
}
private void setStrokeStyle(int r, int g, int b, int a) {
// Log.d(TAG, "setStrokeStyle: " + r + ", " + g + ", " + b + ", " + a);
mStrokeStyleR = r;
mStrokeStyleG = g;
mStrokeStyleB = b;
mStrokeStyleA = a;
}
private void setLineWidth(float lineWidth) {
mLineWidth = lineWidth;
}
private void _fillImageData(int[] imageData, float imageWidth, float imageHeight, float offsetX, float offsetY) {
Log.d(TAG, "_fillImageData: ");
int fillSize = (int) (imageWidth * imageHeight);
int[] fillColors = new int[fillSize];
int r, g, b, a;
for (int i = 0; i < fillSize; ++i) {
// imageData Pixel (RGBA) -> fillColors int (ARGB)
fillColors[i] = Integer.rotateRight(imageData[i], 8);
}
mBitmap.setPixels(fillColors, 0, (int) imageWidth, (int) offsetX, (int) offsetY, (int) imageWidth, (int) imageHeight);
}
private Point convertDrawPoint(final Point point, String text) {
// The parameter 'point' is located at left-bottom position.
// Need to adjust 'point' according 'text align' & 'text base line'.
Point ret = new Point(point);
createTextPaintIfNeeded();
Paint.FontMetrics fm = mTextPaint.getFontMetrics();
float width = measureText(text);
if (mTextAlign == TEXT_ALIGN_CENTER)
{
ret.x -= width / 2;
}
else if (mTextAlign == TEXT_ALIGN_RIGHT)
{
ret.x -= width;
}
// Canvas.drawText accepts the y parameter as the baseline position, not the most bottom
if (mTextBaseline == TEXT_BASELINE_TOP)
{
ret.y += -fm.ascent;
}
else if (mTextBaseline == TEXT_BASELINE_MIDDLE)
{
ret.y += (fm.descent - fm.ascent) / 2 - fm.descent;
}
else if (mTextBaseline == TEXT_BASELINE_BOTTOM)
{
ret.y += -fm.descent;
}
return ret;
}
@SuppressWarnings("unused")
private Bitmap getBitmap() {
return mBitmap;
}
}

View File

@@ -0,0 +1,222 @@
/****************************************************************************
* Copyright (c) 2018-2022 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;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.media.AudioManager;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;
import com.google.androidgamesdk.GameActivity;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
public class CocosActivity extends GameActivity {
private static final String TAG = "CocosActivity";
private CocosWebViewHelper mWebViewHelper = null;
private CocosVideoHelper mVideoHelper = null;
private CocosSensorHandler mSensorHandler;
private List<CocosSurfaceView> mSurfaceViewArray;
private FrameLayout mRootLayout;
private native void onCreateNative();
@Override
protected void onCreate(Bundle savedInstanceState) {
onLoadNativeLibraries();
onCreateNative();
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
// GlobalObject.init should be initialized at first.
GlobalObject.init(this, this);
CocosHelper.registerBatteryLevelReceiver(this);
CocosHelper.init();
CocosAudioFocusManager.registerAudioFocusListener(this);
CanvasRenderingContext2DImpl.init(this);
this.setVolumeControlStream(AudioManager.STREAM_MUSIC);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
initView();
mSensorHandler = new CocosSensorHandler(this);
setImmersiveMode();
Utils.hideVirtualButton();
mSurfaceView.setOnTouchListener((v, event) -> processMotionEvent(event));
}
private void setImmersiveMode() {
WindowManager.LayoutParams lp = getWindow().getAttributes();
try {
Field field = lp.getClass().getField("layoutInDisplayCutoutMode");
//Field constValue = lp.getClass().getDeclaredField("LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER");
Field constValue = lp.getClass().getDeclaredField("LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES");
field.setInt(lp, constValue.getInt(null));
// https://developer.android.com/training/system-ui/immersive
int flag = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
flag |= View.class.getDeclaredField("SYSTEM_UI_FLAG_IMMERSIVE_STICKY").getInt(null);
View view = getWindow().getDecorView();
view.setSystemUiVisibility(flag);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
protected void initView() {
mRootLayout = findViewById(contentViewId);
if (mWebViewHelper == null) {
mWebViewHelper = new CocosWebViewHelper(mRootLayout);
}
if (mVideoHelper == null) {
mVideoHelper = new CocosVideoHelper(this, mRootLayout);
}
}
public SurfaceView getSurfaceView() {
return this.mSurfaceView;
}
@Override
protected void onDestroy() {
super.onDestroy();
CocosHelper.unregisterBatteryLevelReceiver(this);
CocosAudioFocusManager.unregisterAudioFocusListener(this);
CanvasRenderingContext2DImpl.destroy();
GlobalObject.destroy();
}
@Override
protected void onPause() {
super.onPause();
mSensorHandler.onPause();
}
@Override
protected void onResume() {
super.onResume();
mSensorHandler.onResume();
Utils.hideVirtualButton();
if (CocosAudioFocusManager.isAudioFocusLoss()) {
CocosAudioFocusManager.registerAudioFocusListener(this);
}
}
@Override
protected void onStop() {
super.onStop();
mSurfaceView.setVisibility(View.INVISIBLE);
if (null != mSurfaceViewArray) {
for (CocosSurfaceView surfaceView : mSurfaceViewArray) {
surfaceView.setVisibility(View.INVISIBLE);
}
}
}
@Override
protected void onStart() {
super.onStart();
mSurfaceView.setVisibility(View.VISIBLE);
if (null != mSurfaceViewArray) {
for (CocosSurfaceView surfaceView : mSurfaceViewArray) {
surfaceView.setVisibility(View.VISIBLE);
}
}
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus && CocosAudioFocusManager.isAudioFocusLoss()) {
CocosAudioFocusManager.registerAudioFocusListener(this);
}
}
// invoke from native code
@SuppressWarnings({"UnusedDeclaration"})
private void createSurface(int x, int y, int width, int height, int windowId) {
runOnUiThread(new Runnable() {
@Override
public void run() {
CocosSurfaceView view = new CocosSurfaceView(CocosActivity.this, windowId);
view.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(width, height);
params.leftMargin = x;
params.topMargin = y;
//mSubsurfaceView.setBackgroundColor(Color.BLUE);
mRootLayout.addView(view, params);
if (null == mSurfaceViewArray) {
mSurfaceViewArray = new ArrayList<>();
}
mSurfaceViewArray.add(view);
}
});
}
private void onLoadNativeLibraries() {
try {
ApplicationInfo ai = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
Bundle bundle = ai.metaData;
String libName = bundle.getString("android.app.lib_name");
if (TextUtils.isEmpty(libName)) {
Log.e(TAG, "can not find library, please config android.app.lib_name at AndroidManifest.xml");
}
assert libName != null;
System.loadLibrary(libName);
getIntent().putExtra(GameActivity.META_DATA_LIB_NAME, libName);
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,121 @@
/****************************************************************************
Copyright (c) 2018-2022 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 engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
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;
import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioFocusRequest;
import android.media.AudioManager;
import android.os.Build;
import android.util.Log;
class CocosAudioFocusManager {
private static final String _TAG = "CocosAudioFocusManager";
private static boolean isAudioFocusLost = true;
private final static AudioManager.OnAudioFocusChangeListener sAfChangeListener = focusChange -> {
Log.d(_TAG, "onAudioFocusChange: " + focusChange + ", thread: " + Thread.currentThread().getName());
if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
// Permanent loss of audio focus
// Pause playback immediately
Log.d(_TAG, "Pause music by AUDIOFOCUS_LOSS");
isAudioFocusLost = true;
CocosHelper.runOnGameThreadAtForeground(() -> nativeSetAudioVolumeFactor(0));
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
// Pause playback
Log.d(_TAG, "Pause music by AUDIOFOCUS_LOSS_TRANSILENT");
isAudioFocusLost = true;
CocosHelper.runOnGameThreadAtForeground(() -> nativeSetAudioVolumeFactor(0));
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
// Lower the volume, keep playing
Log.d(_TAG, "Lower the volume, keep playing by AUDIOFOCUS_LOSS_TRANSILENT_CAN_DUCK");
isAudioFocusLost = false;
CocosHelper.runOnGameThreadAtForeground(() -> nativeSetAudioVolumeFactor(0.1f));
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
// Your app has been granted audio focus again
// Raise volume to normal, restart playback if necessary
Log.d(_TAG, "Resume music by AUDIOFOCUS_GAIN");
isAudioFocusLost = false;
CocosHelper.runOnGameThreadAtForeground(() -> nativeSetAudioVolumeFactor(1.0f));
}
};
static void registerAudioFocusListener(Context context) {
AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
assert am != null;
int result;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
AudioAttributes playbackAttributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_GAME)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build();
// set the playback attributes for the focus requester
AudioFocusRequest focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK)
.setAudioAttributes(playbackAttributes)
.setWillPauseWhenDucked(true)
.setOnAudioFocusChangeListener(sAfChangeListener)
.build();
result = am.requestAudioFocus(focusRequest);
} else {
// Request audio focus for playback
result = am.requestAudioFocus(sAfChangeListener,
// Use the music stream.
AudioManager.STREAM_MUSIC,
// Request permanent focus.
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
}
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.d(_TAG, "requestAudioFocus succeed");
isAudioFocusLost = false;
CocosHelper.runOnGameThreadAtForeground(() -> nativeSetAudioVolumeFactor(1.0f));
return;
}
Log.e(_TAG, "requestAudioFocus failed!");
}
static void unregisterAudioFocusListener(Context context) {
AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
assert am != null;
int result = am.abandonAudioFocus(sAfChangeListener);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.d(_TAG, "abandonAudioFocus succeed!");
} else {
Log.e(_TAG, "abandonAudioFocus failed!");
}
}
static boolean isAudioFocusLoss() {
return isAudioFocusLost;
}
private static native void nativeSetAudioVolumeFactor(float focus);
}

View File

@@ -0,0 +1,406 @@
/****************************************************************************
Copyright (c) 2017-2018 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 engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
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;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import org.cocos2dx.okhttp3.Call;
import org.cocos2dx.okhttp3.Callback;
import org.cocos2dx.okhttp3.Dispatcher;
import org.cocos2dx.okhttp3.OkHttpClient;
import org.cocos2dx.okhttp3.Protocol;
import org.cocos2dx.okhttp3.Request;
import org.cocos2dx.okhttp3.Response;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
// Rename package okhttp3 to org.cocos2dx.okhttp3
// Github repo: https://github.com/PatriceJiang/okhttp/tree/cocos2dx-rename-3.12.x
// and https://github.com/PatriceJiang/okio/tree/cocos2dx-rename-1.15.0
public class CocosDownloader {
private int _id;
private OkHttpClient _httpClient = null;
private static Dispatcher dispatcher = null;
private String _tempFileNameSuffix;
private int _countOfMaxProcessingTasks;
private ConcurrentHashMap<Integer,Call> _taskMap = new ConcurrentHashMap<>();
private Queue<Runnable> _taskQueue = new LinkedList<>();
private int _runningTaskCount = 0;
private void onProgress(final int id, final long downloadBytes, final long downloadNow, final long downloadTotal) {
CocosHelper.runOnGameThread(new Runnable() {
@Override
public void run() {
nativeOnProgress(_id, id, downloadBytes, downloadNow, downloadTotal);
}
});
}
private void onFinish(final int id, final int errCode, final String errStr, final byte[] data) {
Call task =_taskMap.get(id);
if (null == task) return;
_taskMap.remove(id);
_runningTaskCount -= 1;
CocosHelper.runOnGameThread(new Runnable() {
@Override
public void run() {
nativeOnFinish(_id, id, errCode, errStr, data);
}
});
runNextTaskIfExists();
}
public static CocosDownloader createDownloader(int id, int timeoutInSeconds, String tempFileSuffix, int maxProcessingTasks) {
CocosDownloader downloader = new CocosDownloader();
downloader._id = id;
if(dispatcher == null) {
dispatcher = new Dispatcher();
}
if (timeoutInSeconds > 0) {
downloader._httpClient = new OkHttpClient().newBuilder()
.dispatcher(dispatcher)
.followRedirects(true)
.followSslRedirects(true)
.connectTimeout(timeoutInSeconds, TimeUnit.SECONDS)
.protocols(Collections.singletonList(Protocol.HTTP_1_1))
.build();
} else {
downloader._httpClient = new OkHttpClient().newBuilder()
.dispatcher(dispatcher)
.followRedirects(true)
.followSslRedirects(true)
.protocols(Collections.singletonList(Protocol.HTTP_1_1))
.build();
}
downloader._tempFileNameSuffix = tempFileSuffix;
downloader._countOfMaxProcessingTasks = maxProcessingTasks;
return downloader;
}
public static void createTask(final CocosDownloader downloader, int id_, String url_, String path_, String []header_) {
final int id = id_;
final String url = url_;
final String path = path_;
final String pathWithUrlHash = path + url.hashCode();
final String tempFilePath = pathWithUrlHash + downloader._tempFileNameSuffix;
final String[] header = header_;
Runnable taskRunnable = new Runnable() {
String domain = null;
String host = null;
File tempFile = null;
File finalFile = null;
long downloadStart = 0;
@Override
public void run() {
Call task = null;
do {
if (path.length() > 0) {
try {
URI uri = new URI(url);
domain = uri.getHost();
} catch (URISyntaxException e) {
e.printStackTrace();
break;
} catch (NullPointerException e) {
e.printStackTrace();
break;
}
// file task
tempFile = new File(tempFilePath);
if (tempFile.isDirectory()) break;
File parent = tempFile.getParentFile();
if (parent == null) {
String errStr = "Invalid path " + path + " : The current path is inaccessible.";
Log.e("CocosDownloader", errStr);
CocosHelper.runOnGameThread(new Runnable() {
@Override
public void run() {
downloader.nativeOnFinish(downloader._id, id, 0, errStr, null);
}
});
break;
}
if (!parent.isDirectory() && !parent.mkdirs()) break;
finalFile = new File(path);
if (finalFile.isDirectory()) break;
long fileLen = tempFile.length();
host = domain.startsWith("www.") ? domain.substring(4) : domain;
if (fileLen > 0) {
SharedPreferences sharedPreferences = GlobalObject.getContext().getSharedPreferences("breakpointDownloadSupport", Context.MODE_PRIVATE);
if (sharedPreferences.contains(host) && sharedPreferences.getBoolean(host, false)) {
downloadStart = fileLen;
} else {
// Remove previous downloaded context
try {
PrintWriter writer = new PrintWriter(tempFile);
writer.print("");
writer.close();
}
// Not found then nothing to do
catch (FileNotFoundException e) {
}
}
}
}
final Request.Builder builder = new Request.Builder().url(url);
for (int i = 0; i < header.length / 2; i++) {
builder.addHeader(header[i * 2], header[(i * 2) + 1]);
}
if (downloadStart > 0) {
builder.addHeader("RANGE", "bytes=" + downloadStart + "-");
}
final Request request = builder.build();
task = downloader._httpClient.newCall(request);
if (null == task) {
final String errStr = "Can't create DownloadTask for " + url;
CocosHelper.runOnGameThread(new Runnable() {
@Override
public void run() {
downloader.nativeOnFinish(downloader._id, id, 0, errStr, null);
}
});
} else {
downloader._taskMap.put(id, task);
}
task.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
downloader.onFinish(id, 0, e.toString(), null);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
InputStream is = null;
byte[] buf = new byte[4096];
FileOutputStream fos = null;
try {
if(!(response.code() >= 200 && response.code() <= 206)) {
// it is encourage to delete the tmp file when requested range not satisfiable.
if (response.code() == 416) {
File file = new File(tempFilePath);
if (file.exists() && file.isFile()) {
file.delete();
}
}
downloader.onFinish(id, -2, response.message(), null);
return;
}
// save breakpointDownloadSupport Data to SharedPreferences storage
Context context = GlobalObject.getContext();
SharedPreferences sharedPreferences = context.getSharedPreferences("breakpointDownloadSupport", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
long total = response.body().contentLength() + downloadStart;
if (path.length() > 0 && !sharedPreferences.contains(host)) {
if (total > 0) {
editor.putBoolean(host, true);
} else {
editor.putBoolean(host, false);
}
editor.commit();
}
long current = downloadStart;
is = response.body().byteStream();
if (path.length() > 0) {
if (downloadStart > 0) {
fos = new FileOutputStream(tempFile, true);
} else {
fos = new FileOutputStream(tempFile, false);
}
int len;
while ((len = is.read(buf)) != -1) {
current += len;
fos.write(buf, 0, len);
downloader.onProgress(id, len, current, total);
}
fos.flush();
String errStr = null;
do {
// rename temp file to final file, if final file exist, remove it
if (finalFile.exists()) {
if (finalFile.isDirectory()) {
break;
}
if (!finalFile.delete()) {
errStr = "Can't remove old file:" + finalFile.getAbsolutePath();
break;
}
}
tempFile.renameTo(finalFile);
} while (false);
if (errStr == null) {
downloader.onFinish(id, 0, null, null);
downloader.runNextTaskIfExists();
} else {
downloader.onFinish(id, 0, errStr, null);
}
if (sharedPreferences.contains(host)) {
editor.remove(host);
editor.commit();
}
} else {
// non-file
ByteArrayOutputStream buffer;
if(total > 0) {
buffer = new ByteArrayOutputStream((int) total);
} else {
buffer = new ByteArrayOutputStream(4096);
}
int len;
while ((len = is.read(buf)) != -1) {
current += len;
buffer.write(buf, 0, len);
downloader.onProgress(id, len, current, total);
}
downloader.onFinish(id, 0, null, buffer.toByteArray());
downloader.runNextTaskIfExists();
}
} catch (IOException e) {
e.printStackTrace();
downloader.onFinish(id, 0, e.toString(), null);
} finally {
try {
if (is != null) {
is.close();
}
if (fos != null) {
fos.close();
}
} catch (IOException e) {
Log.e("CocosDownloader", e.toString());
}
}
}
});
} while (false);
}
};
downloader.enqueueTask(taskRunnable);
}
public static void abort(final CocosDownloader downloader, final int id) {
GlobalObject.runOnUiThread(new Runnable() {
@Override
public void run() {
Iterator iter = downloader._taskMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
Object key = entry.getKey();
Call task = (Call) entry.getValue();
if (null != task && Integer.parseInt(key.toString()) == id) {
task.cancel();
downloader._taskMap.remove(id);
downloader.runNextTaskIfExists();
break;
}
}
}
});
}
public static void cancelAllRequests(final CocosDownloader downloader) {
GlobalObject.runOnUiThread(new Runnable() {
@Override
public void run() {
for (Object o : downloader._taskMap.entrySet()) {
Map.Entry entry = (Map.Entry) o;
Call task = (Call) entry.getValue();
if (null != task) {
task.cancel();
}
}
}
});
}
private void enqueueTask(Runnable taskRunnable) {
synchronized (_taskQueue) {
if (_runningTaskCount < _countOfMaxProcessingTasks) {
GlobalObject.runOnUiThread(taskRunnable);
_runningTaskCount++;
} else {
_taskQueue.add(taskRunnable);
}
}
}
private void runNextTaskIfExists() {
synchronized (_taskQueue) {
while (_runningTaskCount < _countOfMaxProcessingTasks &&
CocosDownloader.this._taskQueue.size() > 0) {
Runnable taskRunnable = CocosDownloader.this._taskQueue.poll();
GlobalObject.runOnUiThread(taskRunnable);
_runningTaskCount += 1;
}
}
}
native void nativeOnProgress(int id, int taskId, long dl, long dlnow, long dltotal);
native void nativeOnFinish(int id, int taskId, int errCode, String errStr, final byte[] data);
}

View File

@@ -0,0 +1,486 @@
/****************************************************************************
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
package com.cocos.lib;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.StateListDrawable;
import android.graphics.drawable.shapes.RoundRectShape;
import android.os.Build;
import android.os.Bundle;
import android.text.Editable;
import android.text.InputFilter;
import android.text.InputType;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
public class CocosEditBoxActivity extends Activity {
// a color of dark green, was used for confirm button background
private static final int DARK_GREEN = Color.parseColor("#1fa014");
private static final int DARK_GREEN_PRESS = Color.parseColor("#008e26");
private static CocosEditBoxActivity sThis = null;
private Cocos2dxEditText mEditText = null;
private Button mButton = null;
private String mButtonTitle = null;
private boolean mConfirmHold = true;
private int mEditTextID = 1;
private int mButtonLayoutID = 2;
/***************************************************************************************
Inner class.
**************************************************************************************/
class Cocos2dxEditText extends EditText {
private final String TAG = "Cocos2dxEditBox";
private boolean mIsMultiLine = false;
private TextWatcher mTextWatcher = null;
private boolean keyboardVisible = false;
private int mScreenHeight;
private boolean mCheckKeyboardShowNormally = false;
public Cocos2dxEditText(Activity context){
super(context);
this.setTextColor(Color.BLACK);
mScreenHeight = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).
getDefaultDisplay().getHeight();
mTextWatcher = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
// Pass text to c++.
CocosEditBoxActivity.this.onKeyboardInput(s.toString());
}
};
registKeyboardVisible();
}
/***************************************************************************************
Public functions.
**************************************************************************************/
public void show(String defaultValue, int maxLength, boolean isMultiline, boolean confirmHold, String confirmType, String inputType) {
mIsMultiLine = isMultiline;
this.setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength) });
this.setText(defaultValue);
if (this.getText().length() >= defaultValue.length()) {
this.setSelection(defaultValue.length());
} else {
this.setSelection(this.getText().length());
}
this.setConfirmType(confirmType);
this.setInputType(inputType, mIsMultiLine);
this.setVisibility(View.VISIBLE);
// Open soft keyboard manually. Should request focus to open soft keyboard.
this.requestFocus();
this.addListeners();
}
public void hide() {
mEditText.setVisibility(View.INVISIBLE);
this.removeListeners();
}
/***************************************************************************************
Private functions.
**************************************************************************************/
private void setConfirmType(final String confirmType) {
if (confirmType.contentEquals("done")) {
this.setImeOptions(EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
mButtonTitle = getResources().getString(R.string.done);
} else if (confirmType.contentEquals("next")) {
this.setImeOptions(EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
mButtonTitle = getResources().getString(R.string.next);
} else if (confirmType.contentEquals("search")) {
this.setImeOptions(EditorInfo.IME_ACTION_SEARCH | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
mButtonTitle = getResources().getString(R.string.search);
} else if (confirmType.contentEquals("go")) {
this.setImeOptions(EditorInfo.IME_ACTION_GO | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
mButtonTitle = getResources().getString(R.string.go);
} else if (confirmType.contentEquals("send")) {
this.setImeOptions(EditorInfo.IME_ACTION_SEND | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
mButtonTitle = getResources().getString(R.string.send);
} else{
mButtonTitle = null;
Log.e(TAG, "unknown confirm type " + confirmType);
}
}
private void setInputType(final String inputType, boolean isMultiLine){
mCheckKeyboardShowNormally = false;
if (inputType.contentEquals("text")) {
if (isMultiLine)
this.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
else
this.setInputType(InputType.TYPE_CLASS_TEXT);
}
else if (inputType.contentEquals("email"))
this.setInputType(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
else if (inputType.contentEquals("number"))
this.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL | InputType.TYPE_NUMBER_FLAG_SIGNED);
else if (inputType.contentEquals("phone"))
this.setInputType(InputType.TYPE_CLASS_PHONE);
else if (inputType.contentEquals("password")) {
if (Build.BRAND.equalsIgnoreCase("oppo")) {
mCheckKeyboardShowNormally = true;
}
this.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
}
else
Log.e(TAG, "unknown input type " + inputType);
}
private void addListeners() {
this.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (! mIsMultiLine) {
CocosEditBoxActivity.this.hide();
}
return false; // pass on to other listeners.
}
});
this.addTextChangedListener(mTextWatcher);
}
private void removeListeners() {
this.setOnEditorActionListener(null);
this.removeTextChangedListener(mTextWatcher);
}
private boolean isSystemAdjustUIWhenPopKeyboard(int bottom) {
int bottomOffset = 0;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
bottomOffset = getWindow().getDecorView().getRootWindowInsets().getSystemWindowInsetBottom();
}
// view will be scrolled to the target position by system,
if (Math.abs(bottom - bottomOffset) < 10) {
return true;
}
return false;
}
private void registKeyboardVisible() {
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
getWindowVisibleDisplayFrame(r);
int heightDiff = getRootView().getHeight() - (r.bottom - r.top);
// if more than a quarter of the screen, its probably a keyboard
if (heightDiff > mScreenHeight/4) {
if (!keyboardVisible) {
keyboardVisible = true;
}
if (!isSystemAdjustUIWhenPopKeyboard(heightDiff)) {
getRootView().scrollTo(0, heightDiff);
}
} else {
getRootView().scrollTo(0, 0);
if (mCheckKeyboardShowNormally && !keyboardVisible) {
Toast.makeText(CocosEditBoxActivity.this, R.string.tip_disable_safe_input_type, Toast.LENGTH_SHORT).show();
}
if (keyboardVisible) {
keyboardVisible = false;
CocosEditBoxActivity.this.hide();
}
}
}
});
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
CocosEditBoxActivity.sThis = this;
ViewGroup.LayoutParams frameLayoutParams =
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
RelativeLayout frameLayout = new RelativeLayout(this);
frameLayout.setLayoutParams(frameLayoutParams);
frameLayout.setClickable(true);
frameLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
CocosEditBoxActivity.this.hide();
}
});
setContentView(frameLayout);
this.addItems(frameLayout);
Intent intent = getIntent();
Bundle extras = null;
if (null != intent) {
extras = intent.getExtras();
}
if(extras == null){
show("",
10,
false,
false,
"done",
"text"
);
} else {
show(extras.getString("defaultValue"),
extras.getInt("maxLength"),
extras.getBoolean("isMultiline"),
extras.getBoolean("confirmHold"),
extras.getString("confirmType"),
extras.getString("inputType"));
}
}
/***************************************************************************************
Public functions.
**************************************************************************************/
/***************************************************************************************
Private functions.
**************************************************************************************/
private void addItems(RelativeLayout layout) {
RelativeLayout myLayout = new RelativeLayout(this);
myLayout.setBackgroundColor(Color.argb(255, 244, 244, 244));
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
layout.addView(myLayout, layoutParams);
this.addEditText(myLayout);
this.addButton(myLayout);
}
private int dpToPixel(int dp) {
final float scale = getResources().getDisplayMetrics().density;
int px = (int) (dp * scale + 0.5f);
return px;
}
private void addEditText(RelativeLayout layout) {
mEditText = new Cocos2dxEditText(this);
mEditText.setVisibility(View.INVISIBLE);
mEditText.setGravity(Gravity.CENTER_VERTICAL);
mEditText.setBackground(getRoundRectShape(18, Color.WHITE, Color.WHITE));
mEditText.setId(mEditTextID);
int bottomPadding = dpToPixel(4);
int leftPadding = dpToPixel(3);
mEditText.setPadding(leftPadding,bottomPadding,leftPadding,bottomPadding);
RelativeLayout.LayoutParams editParams = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
editParams.addRule(RelativeLayout.CENTER_VERTICAL);
editParams.addRule(RelativeLayout.LEFT_OF, mButtonLayoutID);
int bottomMargin = dpToPixel(5);
int leftMargin = dpToPixel(4);
editParams.setMargins(leftMargin, bottomMargin, bottomMargin, bottomMargin);
layout.addView(mEditText, editParams);
}
private void addButton(RelativeLayout layout) {
mButton = new Button(this);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
mButton.setTextColor(Color.WHITE);
mButton.setTextSize(16);
mButton.setBackground(getRoundRectShape(18, DARK_GREEN, DARK_GREEN_PRESS));
int paddingLeft = dpToPixel(5);
mButton.setPadding(paddingLeft,0,paddingLeft,0);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
layoutParams.addRule(RelativeLayout.ALIGN_TOP, mEditTextID);
layoutParams.addRule(RelativeLayout.ALIGN_BOTTOM, mEditTextID);
layoutParams.rightMargin = dpToPixel(4);
layout.addView(mButton, layoutParams);
mButton.setGravity(Gravity.CENTER);
mButton.setId(mButtonLayoutID);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
CocosEditBoxActivity.this.onKeyboardConfirm(mEditText.getText().toString());
if (!CocosEditBoxActivity.this.mConfirmHold)
CocosEditBoxActivity.this.hide();
}
});
}
private Drawable getRoundRectShape(int radius, int normalColor, int pressColor) {
float[] outerRadii = new float[]{radius, radius, radius, radius, radius, radius, radius, radius};
RoundRectShape roundRectShape = new RoundRectShape(outerRadii, null, null);
ShapeDrawable shapeDrawableNormal = new ShapeDrawable();
shapeDrawableNormal.setShape(roundRectShape);
shapeDrawableNormal.getPaint().setStyle(Paint.Style.FILL);
shapeDrawableNormal.getPaint().setColor(normalColor);
ShapeDrawable shapeDrawablePress = new ShapeDrawable();
shapeDrawablePress.setShape(roundRectShape);
shapeDrawablePress.getPaint().setStyle(Paint.Style.FILL);
shapeDrawablePress.getPaint().setColor(pressColor);
StateListDrawable drawable = new StateListDrawable();
drawable.addState(new int[]{android.R.attr.state_pressed}, shapeDrawablePress);
drawable.addState(new int[]{}, shapeDrawableNormal);
return drawable;
}
private void hide() {
Utils.hideVirtualButton();
this.closeKeyboard();
finish();
}
public void show(String defaultValue, int maxLength, boolean isMultiline, boolean confirmHold, String confirmType, String inputType) {
mConfirmHold = confirmHold;
mEditText.show(defaultValue, maxLength, isMultiline, confirmHold, confirmType, inputType);
mButton.setText(mButtonTitle);
if (TextUtils.isEmpty(mButtonTitle)) {
mButton.setVisibility(View.INVISIBLE);
} else {
mButton.setVisibility(View.VISIBLE);
}
this.openKeyboard();
}
private void closeKeyboard() {
InputMethodManager imm = (InputMethodManager) getSystemService(this.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
this.onKeyboardComplete(mEditText.getText().toString());
}
private void openKeyboard() {
InputMethodManager imm = (InputMethodManager) getSystemService(this.INPUT_METHOD_SERVICE);
imm.showSoftInput(mEditText, InputMethodManager.SHOW_IMPLICIT);
}
/***************************************************************************************
Functions invoked by CPP.
**************************************************************************************/
private static void showNative(String defaultValue, int maxLength, boolean isMultiline, boolean confirmHold, String confirmType, String inputType) {
GlobalObject.runOnUiThread(new Runnable() {
@Override
public void run() {
Intent i = new Intent(GlobalObject.getActivity(), CocosEditBoxActivity.class);
i.putExtra("defaultValue", defaultValue);
i.putExtra("maxLength", maxLength);
i.putExtra("isMultiline", isMultiline);
i.putExtra("confirmHold", confirmHold);
i.putExtra("confirmType", confirmType);
i.putExtra("inputType", inputType);
GlobalObject.getActivity().startActivity(i);
}
});
}
private static void hideNative() {
if (null != CocosEditBoxActivity.sThis) {
GlobalObject.runOnUiThread(new Runnable() {
@Override
public void run() {
CocosEditBoxActivity.sThis.hide();
}
});
}
}
/***************************************************************************************
Native functions invoked by UI.
**************************************************************************************/
private void onKeyboardInput(String text) {
CocosHelper.runOnGameThread(new Runnable() {
@Override
public void run() {
CocosEditBoxActivity.onKeyboardInputNative(text);
}
});
}
private void onKeyboardComplete(String text) {
CocosHelper.runOnGameThread(new Runnable() {
@Override
public void run() {
CocosEditBoxActivity.onKeyboardCompleteNative(text);
}
});
}
private void onKeyboardConfirm(String text) {
CocosHelper.runOnGameThread(new Runnable() {
@Override
public void run() {
CocosEditBoxActivity.onKeyboardConfirmNative(text);
}
});
}
private static native void onKeyboardInputNative(String text);
private static native void onKeyboardCompleteNative(String text);
private static native void onKeyboardConfirmNative(String text);
}

View File

@@ -0,0 +1,105 @@
/****************************************************************************
Copyright (c) 2010-2013 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
package com.cocos.lib;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import java.lang.ref.WeakReference;
public class CocosHandler extends Handler {
// ===========================================================
// Constants
// ===========================================================
public final static int HANDLER_SHOW_DIALOG = 1;
// ===========================================================
// Fields
// ===========================================================
private WeakReference<Activity> mActivity;
// ===========================================================
// Constructors
// ===========================================================
public CocosHandler(Activity activity) {
this.mActivity = new WeakReference<Activity>(activity);
}
// ===========================================================
// Getter & Setter
// ===========================================================
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
// ===========================================================
// Methods
// ===========================================================
public void handleMessage(Message msg) {
switch (msg.what) {
case CocosHandler.HANDLER_SHOW_DIALOG:
showDialog(msg);
break;
}
}
private void showDialog(Message msg) {
Activity theActivity = this.mActivity.get();
DialogMessage dialogMessage = (DialogMessage)msg.obj;
new AlertDialog.Builder(theActivity)
.setTitle(dialogMessage.title)
.setMessage(dialogMessage.message)
.setPositiveButton("Ok",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// REFINE: Auto-generated method stub
}
}).create().show();
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
public static class DialogMessage {
public String title;
public String message;
public DialogMessage(String title, String message) {
this.title = title;
this.message = message;
}
}
}

View File

@@ -0,0 +1,435 @@
/****************************************************************************
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
package com.cocos.lib;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.AssetFileDescriptor;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.Build;
import android.os.Environment;
import android.os.LocaleList;
import android.os.ParcelFileDescriptor;
import android.os.Vibrator;
import android.util.Log;
import android.view.Display;
import android.view.Surface;
import android.view.WindowInsets;
import android.view.WindowManager;
import com.android.vending.expansion.zipfile.APKExpansionSupport;
import com.android.vending.expansion.zipfile.ZipResourceFile;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Queue;
import java.util.concurrent.locks.ReentrantLock;
public class CocosHelper {
// ===========================================================
// Constants
// ===========================================================
private static final String TAG = CocosHelper.class.getSimpleName();
// ===========================================================
// Fields
// ===========================================================
private static Vibrator sVibrateService;
private static BatteryReceiver sBatteryReceiver = new BatteryReceiver();
public static final int NETWORK_TYPE_NONE = 0;
public static final int NETWORK_TYPE_LAN = 1;
public static final int NETWORK_TYPE_WWAN = 2;
// The absolute path to the OBB if it exists.
private static String sObbFilePath = "";
// The OBB file
private static ZipResourceFile sOBBFile = null;
static class LockedTaskQ {
private final Object readMtx = new Object();
private Queue<Runnable> sTaskQ = new LinkedList<>();
public void addTask(Runnable runnable) {
synchronized (readMtx) {
sTaskQ.add(runnable);
}
}
public void runTasks(){
Queue<Runnable> tmp;
synchronized (readMtx) {
tmp = sTaskQ;
sTaskQ = new LinkedList<>();
}
for(Runnable runnable : tmp){
runnable.run();
}
}
}
private static LockedTaskQ sTaskQOnGameThread = new LockedTaskQ();
private static LockedTaskQ sForegroundTaskQOnGameThread = new LockedTaskQ();
/**
* Battery receiver to getting battery level.
*/
static class BatteryReceiver extends BroadcastReceiver {
public float sBatteryLevel = 0.0f;
@Override
public void onReceive(Context context, Intent intent) {
setBatteryLevelByIntent(intent);
}
public void setBatteryLevelByIntent(Intent intent) {
if (null != intent) {
int current = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
int total = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 1);
float level = current * 1.0f / total;
// clamp to 0~1
sBatteryLevel = Math.min(Math.max(level, 0.0f), 1.0f);
}
}
}
static void registerBatteryLevelReceiver(Context context) {
Intent intent = context.registerReceiver(sBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
sBatteryReceiver.setBatteryLevelByIntent(intent);
}
static void unregisterBatteryLevelReceiver(Context context) {
context.unregisterReceiver(sBatteryReceiver);
}
//Run on game thread forever, no matter foreground or background
public static void runOnGameThread(final Runnable runnable) {
sTaskQOnGameThread.addTask(runnable);
}
static void flushTasksOnGameThread() {
sTaskQOnGameThread.runTasks();
}
public static void runOnGameThreadAtForeground(final Runnable runnable) {
sForegroundTaskQOnGameThread.addTask(runnable);
}
static void flushTasksOnGameThreadAtForeground() {
sForegroundTaskQOnGameThread.runTasks();
}
public static int getNetworkType() {
int status = NETWORK_TYPE_NONE;
NetworkInfo networkInfo;
try {
ConnectivityManager connMgr = (ConnectivityManager) GlobalObject.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
networkInfo = connMgr.getActiveNetworkInfo();
} catch (Exception e) {
e.printStackTrace();
return status;
}
if (networkInfo == null) {
return status;
}
int nType = networkInfo.getType();
if (nType == ConnectivityManager.TYPE_MOBILE) {
status = NETWORK_TYPE_WWAN;
} else if (nType == ConnectivityManager.TYPE_WIFI) {
status = NETWORK_TYPE_LAN;
}
return status;
}
// ===========================================================
// Constructors
// ===========================================================
private static boolean sInited = false;
public static void init() {
if (!sInited) {
CocosHelper.sVibrateService = (Vibrator) GlobalObject.getContext().getSystemService(Context.VIBRATOR_SERVICE);
CocosHelper.initObbFilePath();
CocosHelper.initializeOBBFile();
sInited = true;
}
}
public static float getBatteryLevel() {
return sBatteryReceiver.sBatteryLevel;
}
public static String getObbFilePath() {
return CocosHelper.sObbFilePath;
}
public static String getWritablePath() {
return GlobalObject.getContext().getFilesDir().getAbsolutePath();
}
public static String getCurrentLanguage() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return LocaleList.getDefault().get(0).getLanguage();
} else {
return Locale.getDefault().getLanguage();
}
}
public static String getCurrentLanguageCode() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return LocaleList.getDefault().get(0).toString();
} else {
return Locale.getDefault().toString();
}
}
public static String getDeviceModel() {
return Build.MODEL;
}
public static String getSystemVersion() {
return Build.VERSION.RELEASE;
}
public static void vibrate(float duration) {
try {
if (sVibrateService != null && sVibrateService.hasVibrator()) {
if (android.os.Build.VERSION.SDK_INT >= 26) {
Class<?> vibrationEffectClass = Class.forName("android.os.VibrationEffect");
if (vibrationEffectClass != null) {
final int DEFAULT_AMPLITUDE = CocosReflectionHelper.<Integer>getConstantValue(vibrationEffectClass,
"DEFAULT_AMPLITUDE");
//VibrationEffect.createOneShot(long milliseconds, int amplitude)
final Method method = vibrationEffectClass.getMethod("createOneShot",
new Class[]{Long.TYPE, Integer.TYPE});
Class<?> type = method.getReturnType();
Object effect = method.invoke(vibrationEffectClass,
new Object[]{(long) (duration * 1000), DEFAULT_AMPLITUDE});
//sVibrateService.vibrate(VibrationEffect effect);
CocosReflectionHelper.invokeInstanceMethod(sVibrateService, "vibrate",
new Class[]{type}, new Object[]{(effect)});
}
} else {
sVibrateService.vibrate((long) (duration * 1000));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static boolean openURL(String url) {
if (GlobalObject.getActivity() == null) {
Log.e(TAG, "activity is null");
return false;
}
boolean ret = false;
try {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
GlobalObject.getActivity().startActivity(i);
ret = true;
} catch (Exception e) {
}
return ret;
}
public static void copyTextToClipboard(final String text) {
GlobalObject.runOnUiThread(new Runnable() {
@Override
public void run() {
ClipboardManager myClipboard = (ClipboardManager) GlobalObject.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
ClipData myClip = ClipData.newPlainText("text", text);
myClipboard.setPrimaryClip(myClip);
}
});
}
public static long[] getObbAssetFileDescriptor(final String path) {
long[] array = new long[3];
if (CocosHelper.sOBBFile != null) {
AssetFileDescriptor descriptor = CocosHelper.sOBBFile.getAssetFileDescriptor(path);
if (descriptor != null) {
try {
ParcelFileDescriptor parcel = descriptor.getParcelFileDescriptor();
Method method = parcel.getClass().getMethod("getFd", new Class[]{});
array[0] = (Integer) method.invoke(parcel);
array[1] = descriptor.getStartOffset();
array[2] = descriptor.getLength();
} catch (NoSuchMethodException e) {
Log.e(CocosHelper.TAG, "Accessing file descriptor directly from the OBB is only supported from Android 3.1 (API level 12) and above.");
} catch (IllegalAccessException e) {
Log.e(CocosHelper.TAG, e.toString());
} catch (InvocationTargetException e) {
Log.e(CocosHelper.TAG, e.toString());
}
}
}
return array;
}
public static int getDeviceRotation() {
try {
Display display = ((WindowManager) GlobalObject.getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
return display.getRotation();
} catch (NullPointerException e) {
e.printStackTrace();
}
return Surface.ROTATION_0;
}
// ===========================================================
// Private functions.
// ===========================================================
// Initialize asset path:
// - absolute path to the OBB if it exists,
// - else empty string.
private static void initObbFilePath() {
int versionCode = 1;
final ApplicationInfo applicationInfo = GlobalObject.getContext().getApplicationInfo();
try {
versionCode = GlobalObject.getContext().getPackageManager().getPackageInfo(applicationInfo.packageName, 0).versionCode;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
String pathToOBB = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/obb/" + applicationInfo.packageName + "/main." + versionCode + "." + applicationInfo.packageName + ".obb";
File obbFile = new File(pathToOBB);
if (obbFile.exists())
CocosHelper.sObbFilePath = pathToOBB;
}
private static void initializeOBBFile() {
int versionCode = 1;
final ApplicationInfo applicationInfo = GlobalObject.getContext().getApplicationInfo();
try {
versionCode = GlobalObject.getContext().getPackageManager().getPackageInfo(applicationInfo.packageName, 0).versionCode;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
try {
CocosHelper.sOBBFile = APKExpansionSupport.getAPKExpansionZipFile(GlobalObject.getContext(), versionCode, 0);
} catch (IOException e) {
e.printStackTrace();
}
}
public static float[] getSafeArea() {
if (GlobalObject.getActivity() == null) {
Log.e(TAG, "activity is null");
return new float[]{0, 0, 0, 0};
}
if (android.os.Build.VERSION.SDK_INT >= 28) {
do {
Object windowInsectObj = GlobalObject.getActivity().getWindow().getDecorView().getRootWindowInsets();
if (windowInsectObj == null) break;
Class<?> windowInsets = WindowInsets.class;
try {
Method wiGetDisplayCutout = windowInsets.getMethod("getDisplayCutout");
Object cutout = wiGetDisplayCutout.invoke(windowInsectObj);
if (cutout == null) break;
Class<?> displayCutout = cutout.getClass();
Method dcGetLeft = displayCutout.getMethod("getSafeInsetLeft");
Method dcGetRight = displayCutout.getMethod("getSafeInsetRight");
Method dcGetBottom = displayCutout.getMethod("getSafeInsetBottom");
Method dcGetTop = displayCutout.getMethod("getSafeInsetTop");
if (dcGetLeft != null && dcGetRight != null && dcGetBottom != null && dcGetTop != null) {
int left = (Integer) dcGetLeft.invoke(cutout);
int right = (Integer) dcGetRight.invoke(cutout);
int top = (Integer) dcGetTop.invoke(cutout);
int bottom = (Integer) dcGetBottom.invoke(cutout);
return new float[]{top, left, bottom, right};
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} while (false);
}
return new float[]{0, 0, 0, 0};
}
public static void finishActivity() {
if (GlobalObject.getActivity() == null) {
Log.e(TAG, "activity is null");
return;
}
GlobalObject.runOnUiThread(new Runnable() {
@Override
public void run() {
GlobalObject.getActivity().finish();
}
});
}
public static void setKeepScreenOn(boolean keepScreenOn) {
if (GlobalObject.getActivity() == null) {
Log.e(TAG, "activity is null");
return;
}
GlobalObject.runOnUiThread(new Runnable() {
@Override
public void run() {
if (keepScreenOn) {
GlobalObject.getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
} else {
GlobalObject.getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
}
});
}
public static boolean supportHPE() {
PackageManager pm = GlobalObject.getContext().getPackageManager();
return pm.hasSystemFeature("com.google.android.play.feature.HPE_EXPERIENCE");
}
}

View File

@@ -0,0 +1,414 @@
/****************************************************************************
Copyright (c) 2010-2014 cocos2d-x.org
Copyright (c) 2014-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
package com.cocos.lib;
import android.util.Log;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.zip.GZIPInputStream;
import java.util.zip.InflaterInputStream;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
public class CocosHttpURLConnection
{
private static String TAG = "CocosHttpURLConnection";
private static final String POST_METHOD = "POST" ;
private static final String PUT_METHOD = "PUT" ;
private static final String PATCH_METHOD = "PATCH" ;
static HttpURLConnection createHttpURLConnection(String linkURL) {
URL url;
HttpURLConnection urlConnection;
try {
url = new URL(linkURL);
urlConnection = (HttpURLConnection) url.openConnection();
//Accept-Encoding
urlConnection.setRequestProperty("Accept-Encoding", "identity");
urlConnection.setDoInput(true);
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "createHttpURLConnection:" + e.toString());
return null;
}
return urlConnection;
}
static void setReadAndConnectTimeout(HttpURLConnection urlConnection, int readMiliseconds, int connectMiliseconds) {
urlConnection.setReadTimeout(readMiliseconds);
urlConnection.setConnectTimeout(connectMiliseconds);
}
static void setRequestMethod(HttpURLConnection urlConnection, String method){
try {
urlConnection.setRequestMethod(method);
if(method.equalsIgnoreCase(POST_METHOD) || method.equalsIgnoreCase(PUT_METHOD) || method.equalsIgnoreCase(PATCH_METHOD)) {
urlConnection.setDoOutput(true);
}
} catch (ProtocolException e) {
Log.e(TAG, "setRequestMethod:" + e.toString());
}
}
static void setVerifySSL(HttpURLConnection urlConnection, String sslFilename) {
if(!(urlConnection instanceof HttpsURLConnection))
return;
HttpsURLConnection httpsURLConnection = (HttpsURLConnection)urlConnection;
try {
InputStream caInput = null;
if (sslFilename.startsWith("/")) {
caInput = new BufferedInputStream(new FileInputStream(sslFilename));
}else {
String assetString = "assets/";
String assetsfilenameString = sslFilename.substring(assetString.length());
caInput = new BufferedInputStream(GlobalObject.getContext().getAssets().open(assetsfilenameString));
}
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate ca;
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
caInput.close();
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
httpsURLConnection.setSSLSocketFactory(context.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "setVerifySSL:" + e.toString());
}
}
//Add header
static void addRequestHeader(HttpURLConnection urlConnection, String key, String value) {
urlConnection.setRequestProperty(key, value);
}
static int connect(HttpURLConnection http) {
int suc = 0;
try {
http.connect();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "connect" + e.toString());
suc = 1;
}
return suc;
}
static void disconnect(HttpURLConnection http) {
http.disconnect();
}
static void sendRequest(HttpURLConnection http, byte[] byteArray) {
try {
OutputStream out = http.getOutputStream();
if(null != byteArray) {
out.write(byteArray);
out.flush();
}
out.close();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "sendRequest:" + e.toString());
}
}
static String getResponseHeaders(HttpURLConnection http) {
Map<String, List<String>> headers = http.getHeaderFields();
if (null == headers) {
return null;
}
String header = "";
for (Entry<String, List<String>> entry: headers.entrySet()) {
String key = entry.getKey();
if (null == key) {
header += listToString(entry.getValue(), ",") + "\n";
} else {
header += key + ":" + listToString(entry.getValue(), ",") + "\n";
}
}
return header;
}
static String getResponseHeaderByIdx(HttpURLConnection http, int idx) {
Map<String, List<String>> headers = http.getHeaderFields();
if (null == headers) {
return null;
}
String header = null;
int counter = 0;
for (Entry<String, List<String>> entry: headers.entrySet()) {
if (counter == idx) {
String key = entry.getKey();
if (null == key) {
header = listToString(entry.getValue(), ",") + "\n";
} else {
header = key + ":" + listToString(entry.getValue(), ",") + "\n";
}
break;
}
counter++;
}
return header;
}
static String getResponseHeaderByKey(HttpURLConnection http, String key) {
if (null == key) {
return null;
}
Map<String, List<String>> headers = http.getHeaderFields();
if (null == headers) {
return null;
}
String header = null;
for (Entry<String, List<String>> entry: headers.entrySet()) {
if (key.equalsIgnoreCase(entry.getKey())) {
if ("set-cookie".equalsIgnoreCase(key)) {
header = combinCookies(entry.getValue(), http.getURL().getHost());
} else {
header = listToString(entry.getValue(), ",");
}
break;
}
}
return header;
}
static int getResponseHeaderByKeyInt(HttpURLConnection http, String key) {
String value = http.getHeaderField(key);
if (null == value) {
return 0;
} else {
return Integer.parseInt(value);
}
}
static byte[] getResponseContent(HttpURLConnection http) {
InputStream in;
try {
in = http.getInputStream();
String contentEncoding = http.getContentEncoding();
if (contentEncoding != null) {
if(contentEncoding.equalsIgnoreCase("gzip")){
in = new GZIPInputStream(http.getInputStream()); //reads 2 bytes to determine GZIP stream!
}
else if(contentEncoding.equalsIgnoreCase("deflate")){
in = new InflaterInputStream(http.getInputStream());
}
}
} catch (IOException e) {
in = http.getErrorStream();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "1 getResponseContent: " + e.toString());
return null;
}
try {
byte[] buffer = new byte[1024];
int size = 0;
ByteArrayOutputStream bytestream = new ByteArrayOutputStream();
while((size = in.read(buffer, 0 , 1024)) != -1)
{
bytestream.write(buffer, 0, size);
}
byte retbuffer[] = bytestream.toByteArray();
bytestream.close();
return retbuffer;
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "2 getResponseContent:" + e.toString());
}
return null;
}
static int getResponseCode(HttpURLConnection http) {
int code = 0;
try {
code = http.getResponseCode();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "getResponseCode:" + e.toString());
}
return code;
}
static String getResponseMessage(HttpURLConnection http) {
String msg;
try {
msg = http.getResponseMessage();
} catch (Exception e) {
e.printStackTrace();
msg = e.toString();
Log.e(TAG, "getResponseMessage: " + msg);
}
return msg;
}
public static String listToString(List<String> list, String strInterVal) {
if (list == null) {
return null;
}
StringBuilder result = new StringBuilder();
boolean flag = false;
for (String str : list) {
if (flag) {
result.append(strInterVal);
}
if (null == str) {
str = "";
}
result.append(str);
flag = true;
}
return result.toString();
}
public static String combinCookies(List<String> list, String hostDomain) {
StringBuilder sbCookies = new StringBuilder();
String domain = hostDomain;
String tailmatch = "FALSE";
String path = "/";
String secure = "FALSE";
String key = null;
String value = null;
String expires = null;
for (String str : list) {
String[] parts = str.split(";");
for (String part : parts) {
int firstIndex = part.indexOf("=");
if (-1 == firstIndex)
continue;
String[] item = {part.substring(0, firstIndex), part.substring(firstIndex + 1)};
if ("expires".equalsIgnoreCase(item[0].trim())) {
expires = str2Seconds(item[1].trim());
} else if("path".equalsIgnoreCase(item[0].trim())) {
path = item[1];
} else if("secure".equalsIgnoreCase(item[0].trim())) {
secure = item[1];
} else if("domain".equalsIgnoreCase(item[0].trim())) {
domain = item[1];
} else if("version".equalsIgnoreCase(item[0].trim()) || "max-age".equalsIgnoreCase(item[0].trim())) {
//do nothing
} else {
key = item[0];
value = item[1];
}
}
if (null == domain) {
domain = "none";
}
sbCookies.append(domain);
sbCookies.append('\t');
sbCookies.append(tailmatch); //access
sbCookies.append('\t');
sbCookies.append(path); //path
sbCookies.append('\t');
sbCookies.append(secure); //secure
sbCookies.append('\t');
sbCookies.append(expires); //expires
sbCookies.append("\t");
sbCookies.append(key); //key
sbCookies.append("\t");
sbCookies.append(value); //value
sbCookies.append('\n');
}
return sbCookies.toString();
}
private static String str2Seconds(String strTime) {
Calendar c = Calendar.getInstance();
long milliseconds = 0;
try {
c.setTime(new SimpleDateFormat("EEE, dd-MMM-yy hh:mm:ss zzz", Locale.US).parse(strTime));
milliseconds = c.getTimeInMillis() / 1000;
} catch (ParseException e) {
Log.e(TAG, "str2Seconds: " + e.toString());
}
return Long.toString(milliseconds);
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2013-2016 Chukong Technologies Inc.
* Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
*
* 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;
public class CocosJavascriptJavaBridge {
public static native int evalString(String value);
}

View File

@@ -0,0 +1,87 @@
/****************************************************************************
Copyright (c) 2010-2013 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
package com.cocos.lib;
import android.view.KeyEvent;
public class CocosKeyCodeHandler {
private CocosActivity mAct;
public native void handleKeyDown(final int keyCode);
public native void handleKeyUp(final int keyCode);
public CocosKeyCodeHandler(CocosActivity act) {
mAct = act;
}
public boolean onKeyDown(final int keyCode, final KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
// CocosVideoHelper.mVideoHandler.sendEmptyMessage(CocosVideoHelper.KeyEventBack);
case KeyEvent.KEYCODE_MENU:
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_DPAD_RIGHT:
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN:
case KeyEvent.KEYCODE_ENTER:
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
case KeyEvent.KEYCODE_DPAD_CENTER:
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
@Override
public void run() {
handleKeyDown(keyCode);
}
});
return true;
default:
return false;
}
}
public boolean onKeyUp(final int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
case KeyEvent.KEYCODE_MENU:
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_DPAD_RIGHT:
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN:
case KeyEvent.KEYCODE_ENTER:
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
case KeyEvent.KEYCODE_DPAD_CENTER:
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
@Override
public void run() {
handleKeyUp(keyCode);
}
});
return true;
default:
return false;
}
}
}

View File

@@ -0,0 +1,170 @@
/****************************************************************************
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
package com.cocos.lib;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class CocosLocalStorage {
private static final String TAG = "CocosLocalStorage";
private static String DATABASE_NAME = "jsb.sqlite";
private static String TABLE_NAME = "data";
private static final int DATABASE_VERSION = 1;
private static DBOpenHelper mDatabaseOpenHelper = null;
private static SQLiteDatabase mDatabase = null;
public static boolean init(String dbName, String tableName) {
if (GlobalObject.getContext() != null) {
DATABASE_NAME = dbName;
TABLE_NAME = tableName;
mDatabaseOpenHelper = new DBOpenHelper(GlobalObject.getContext());
mDatabase = mDatabaseOpenHelper.getWritableDatabase();
return true;
}
return false;
}
public static void destroy() {
if (mDatabase != null) {
mDatabase.close();
}
}
public static void setItem(String key, String value) {
try {
String sql = "replace into "+TABLE_NAME+"(key,value)values(?,?)";
mDatabase.execSQL(sql, new Object[] { key, value });
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getItem(String key) {
String ret = null;
try {
String sql = "select value from "+TABLE_NAME+" where key=?";
Cursor c = mDatabase.rawQuery(sql, new String[]{key});
while (c.moveToNext()) {
// only return the first value
if (ret != null)
{
Log.e(TAG, "The key contains more than one value.");
break;
}
ret = c.getString(c.getColumnIndex("value"));
}
c.close();
} catch (Exception e) {
e.printStackTrace();
}
return ret;
}
public static void removeItem(String key) {
try {
String sql = "delete from "+TABLE_NAME+" where key=?";
mDatabase.execSQL(sql, new Object[] {key});
} catch (Exception e) {
e.printStackTrace();
}
}
public static void clear() {
try {
String sql = "delete from "+TABLE_NAME;
mDatabase.execSQL(sql);
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getKey(int nIndex) {
String ret = null;
try {
int nCount = 0;
String sql = "select key from "+TABLE_NAME + " order by rowid asc";
Cursor c = mDatabase.rawQuery(sql, null);
if(nIndex < 0 || nIndex >= c.getCount()) {
return null;
}
while (c.moveToNext()) {
if(nCount == nIndex) {
ret = c.getString(c.getColumnIndex("key"));
break;
}
nCount++;
}
c.close();
} catch (Exception e) {
e.printStackTrace();
}
return ret;
}
public static int getLength() {
int res = 0;
try {
String sql = "select count(*) as nums from "+TABLE_NAME;
Cursor c = mDatabase.rawQuery(sql, null);
if (c.moveToNext()){
res = c.getInt(c.getColumnIndex("nums"));
}
c.close();
} catch (Exception e) {
e.printStackTrace();
}
return res;
}
/**
* This creates/opens the database.
*/
private static class DBOpenHelper extends SQLiteOpenHelper {
DBOpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS "+TABLE_NAME+"(key TEXT PRIMARY KEY,value TEXT);");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
//db.execSQL("DROP TABLE IF EXISTS " + VIRTUAL_TABLE);
//onCreate(db);
}
}
}

View File

@@ -0,0 +1,62 @@
/****************************************************************************
* Copyright (c) 2020 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;
import android.content.Context;
import android.view.OrientationEventListener;
public class CocosOrientationHelper extends OrientationEventListener {
private int mCurrentOrientation;
public CocosOrientationHelper(Context context) {
super(context);
mCurrentOrientation = CocosHelper.getDeviceRotation();
}
public void onPause() {
this.disable();
}
public void onResume() {
this.enable();
}
@Override
public void onOrientationChanged(int orientation) {
int curOrientation = CocosHelper.getDeviceRotation();
if (curOrientation != mCurrentOrientation) {
mCurrentOrientation = CocosHelper.getDeviceRotation();
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
@Override
public void run() {
nativeOnOrientationChanged(mCurrentOrientation);
}
});
}
}
private static native void nativeOnOrientationChanged(int rotation);
}

View File

@@ -0,0 +1,75 @@
/****************************************************************************
Copyright (c) 2016 cocos2d-x.org
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
package com.cocos.lib;
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class CocosReflectionHelper {
public static <T> T getConstantValue(final Class aClass, final String constantName) {
try {
return (T)aClass.getDeclaredField(constantName).get(null);
} catch (NoSuchFieldException e) {
Log.e("error", "can not find " + constantName + " in " + aClass.getName());
}
catch (IllegalAccessException e) {
Log.e("error", constantName + " is not accessable");
}
catch (IllegalArgumentException e) {
Log.e("error", "arguments error when get " + constantName);
}
catch (Exception e) {
Log.e("error", "can not get constant" + constantName);
}
return null;
}
public static <T> T invokeInstanceMethod(final Object instance, final String methodName,
final Class[] parameterTypes, final Object[] parameters) {
final Class aClass = instance.getClass();
try {
final Method method = aClass.getMethod(methodName, parameterTypes);
return (T)method.invoke(instance, parameters);
} catch (NoSuchMethodException e) {
Log.e("error", "can not find " + methodName + " in " + aClass.getName());
}
catch (IllegalAccessException e) {
Log.e("error", methodName + " is not accessible");
}
catch (IllegalArgumentException e) {
Log.e("error", "arguments are error when invoking " + methodName);
}
catch (InvocationTargetException e) {
Log.e("error", "an exception was thrown by the invoked method when invoking " + methodName);
}
return null;
}
}

View File

@@ -0,0 +1,145 @@
/****************************************************************************
Copyright (c) 2010-2013 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
package com.cocos.lib;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
public class CocosSensorHandler implements SensorEventListener {
// ===========================================================
// Constants
// ===========================================================
private static final String TAG = "CocosSensorHandler";
private static CocosSensorHandler mSensorHandler;
private static boolean mEnableSensor = false;
private final Context mContext;
private SensorManager mSensorManager;
private Sensor mAcceleration;
private Sensor mAccelerationIncludingGravity;
private Sensor mGyroscope;
private int mSamplingPeriodUs = SensorManager.SENSOR_DELAY_GAME;
private static float[] sDeviceMotionValues = new float[9];
// ===========================================================
// Constructors
// ===========================================================
public CocosSensorHandler(final Context context) {
mContext = context;
mSensorHandler = this;
}
// ===========================================================
// Getter & Setter
// ===========================================================
public void enable() {
if (mEnableSensor) {
if (null == mSensorManager) {
mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
mAcceleration = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mAccelerationIncludingGravity = mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
}
mSensorManager.registerListener(this, mAcceleration, mSamplingPeriodUs);
mSensorManager.registerListener(this, mAccelerationIncludingGravity, mSamplingPeriodUs);
mSensorManager.registerListener(this, mGyroscope, mSamplingPeriodUs);
}
}
public void disable() {
if (mEnableSensor && null != mSensorManager) {
this.mSensorManager.unregisterListener(this);
}
}
public void setInterval(float interval) {
if (android.os.Build.VERSION.SDK_INT >= 11) {
mSamplingPeriodUs = (int) (interval * 1000000);
}
disable();
enable();
}
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
@Override
public void onSensorChanged(final SensorEvent sensorEvent) {
int type = sensorEvent.sensor.getType();
if (type == Sensor.TYPE_ACCELEROMETER) {
sDeviceMotionValues[0] = sensorEvent.values[0];
sDeviceMotionValues[1] = sensorEvent.values[1];
// Issue https://github.com/cocos-creator/2d-tasks/issues/2532
// use negative event.acceleration.z to match iOS value
sDeviceMotionValues[2] = -sensorEvent.values[2];
} else if (type == Sensor.TYPE_LINEAR_ACCELERATION) {
sDeviceMotionValues[3] = sensorEvent.values[0];
sDeviceMotionValues[4] = sensorEvent.values[1];
sDeviceMotionValues[5] = sensorEvent.values[2];
} else if (type == Sensor.TYPE_GYROSCOPE) {
// The unit is rad/s, need to be converted to deg/s
sDeviceMotionValues[6] = (float) Math.toDegrees(sensorEvent.values[0]);
sDeviceMotionValues[7] = (float) Math.toDegrees(sensorEvent.values[1]);
sDeviceMotionValues[8] = (float) Math.toDegrees(sensorEvent.values[2]);
}
}
@Override
public void onAccuracyChanged(final Sensor sensor, final int accuracy) {
}
public void onPause() {
disable();
}
public void onResume() {
enable();
}
public static void setAccelerometerInterval(float interval) {
mSensorHandler.setInterval(interval);
}
public static void setAccelerometerEnabled(boolean enabled) {
mEnableSensor = enabled;
if (enabled) {
mSensorHandler.enable();
} else {
mSensorHandler.disable();
}
}
public static float[] getDeviceMotionValue() {
return sDeviceMotionValues;
}
}

View File

@@ -0,0 +1,99 @@
/****************************************************************************
* Copyright (c) 2020 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;
import android.content.Context;
import android.util.Log;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class CocosSurfaceView extends SurfaceView implements android.view.SurfaceHolder.Callback2 {
private CocosTouchHandler mTouchHandler;
private long mNativeHandle;
private int mWindowId;
public CocosSurfaceView(Context context, int windowId) {
super(context);
mWindowId = windowId;
mNativeHandle = constructNative(windowId);
mTouchHandler = new CocosTouchHandler(mWindowId);
getHolder().addCallback(this);
}
private native long constructNative(int windowId);
private native void destructNative(long handle);
private native void onSizeChangedNative(int windowId, int width, final int height);
private native void onSurfaceRedrawNeededNative(long handle);
private native void onSurfaceCreatedNative(long handle, Surface surface);
private native void onSurfaceChangedNative(long handle, Surface surface, int format, int width, int height);
private native void onSurfaceDestroyedNative(long handle);
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
@Override
public void run() {
onSizeChangedNative(mWindowId, w, h);
}
});
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return mTouchHandler.onTouchEvent(event);
}
@Override
public void surfaceRedrawNeeded(SurfaceHolder holder) {
onSurfaceRedrawNeededNative(mNativeHandle);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
onSurfaceCreatedNative(mNativeHandle, holder.getSurface());
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
onSurfaceChangedNative(mNativeHandle, holder.getSurface(), format, width, height);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
onSurfaceDestroyedNative(mNativeHandle);
}
@Override
protected void finalize() throws Throwable {
super.finalize();
destructNative(mNativeHandle);
mNativeHandle = 0;
}
}

View File

@@ -0,0 +1,184 @@
/****************************************************************************
Copyright (c) 2010-2013 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
package com.cocos.lib;
import android.util.Log;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceView;
public class CocosTouchHandler {
public final static String TAG = "CocosTouchHandler";
private boolean mStopHandleTouchAndKeyEvents = false;
private int mWindowId;
public CocosTouchHandler(int windowId) {
mWindowId = windowId;
}
boolean onTouchEvent(MotionEvent pMotionEvent) {
// these data are used in ACTION_MOVE and ACTION_CANCEL
final int pointerNumber = pMotionEvent.getPointerCount();
final int[] ids = new int[pointerNumber];
final float[] xs = new float[pointerNumber];
final float[] ys = new float[pointerNumber];
for (int i = 0; i < pointerNumber; i++) {
ids[i] = pMotionEvent.getPointerId(i);
xs[i] = pMotionEvent.getX(i);
ys[i] = pMotionEvent.getY(i);
}
switch (pMotionEvent.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_DOWN:
if (mStopHandleTouchAndKeyEvents) {
// Cocos2dxEditBox.complete();
return true;
}
final int indexPointerDown = pMotionEvent.getAction() >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int idPointerDown = pMotionEvent.getPointerId(indexPointerDown);
final float xPointerDown = pMotionEvent.getX(indexPointerDown);
final float yPointerDown = pMotionEvent.getY(indexPointerDown);
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
@Override
public void run() {
handleActionDown(mWindowId, idPointerDown, xPointerDown, yPointerDown);
}
});
break;
case MotionEvent.ACTION_DOWN:
if (mStopHandleTouchAndKeyEvents) {
// Cocos2dxEditBox.complete();
return true;
}
// there are only one finger on the screen
final int idDown = pMotionEvent.getPointerId(0);
final float xDown = xs[0];
final float yDown = ys[0];
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
@Override
public void run() {
handleActionDown(mWindowId, idDown, xDown, yDown);
}
});
break;
case MotionEvent.ACTION_MOVE:
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
@Override
public void run() {
handleActionMove(mWindowId, ids, xs, ys);
}
});
break;
case MotionEvent.ACTION_POINTER_UP:
final int indexPointUp = pMotionEvent.getAction() >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int idPointerUp = pMotionEvent.getPointerId(indexPointUp);
final float xPointerUp = pMotionEvent.getX(indexPointUp);
final float yPointerUp = pMotionEvent.getY(indexPointUp);
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
@Override
public void run() {
handleActionUp(mWindowId, idPointerUp, xPointerUp, yPointerUp);
}
});
break;
case MotionEvent.ACTION_UP:
// there are only one finger on the screen
final int idUp = pMotionEvent.getPointerId(0);
final float xUp = xs[0];
final float yUp = ys[0];
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
@Override
public void run() {
handleActionUp(mWindowId, idUp, xUp, yUp);
}
});
break;
case MotionEvent.ACTION_CANCEL:
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
@Override
public void run() {
handleActionCancel(mWindowId, ids, xs, ys);
}
});
break;
}
if (BuildConfig.DEBUG) {
// CocosTouchHandler.dumpMotionEvent(pMotionEvent);
}
return true;
}
public void setStopHandleTouchAndKeyEvents(boolean value) {
mStopHandleTouchAndKeyEvents = value;
}
private static void dumpMotionEvent(final MotionEvent event) {
final String names[] = {"DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE", "POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?"};
final StringBuilder sb = new StringBuilder();
final int action = event.getAction();
final int actionCode = action & MotionEvent.ACTION_MASK;
sb.append("event ACTION_").append(names[actionCode]);
if (actionCode == MotionEvent.ACTION_POINTER_DOWN || actionCode == MotionEvent.ACTION_POINTER_UP) {
sb.append("(pid ").append(action >> MotionEvent.ACTION_POINTER_INDEX_SHIFT);
sb.append(")");
}
sb.append("[");
for (int i = 0; i < event.getPointerCount(); i++) {
sb.append("#").append(i);
sb.append("(pid ").append(event.getPointerId(i));
sb.append(")=").append((int) event.getX(i));
sb.append(",").append((int) event.getY(i));
if (i + 1 < event.getPointerCount()) {
sb.append(";");
}
}
sb.append("]");
Log.d(TAG, sb.toString());
}
private native void handleActionDown(int windowId, final int id, final float x, final float y);
private native void handleActionMove(int windowId, final int[] ids, final float[] xPointerList, final float[] yPointerList);
private native void handleActionUp(int windowId, final int id, final float x, final float y);
private native void handleActionCancel(int windowId, final int[] ids, final float[] xPointerList, final float[] yPointerList);
}

View File

@@ -0,0 +1,509 @@
/****************************************************************************
Copyright (c) 2014-2016 Chukong Technologies Inc.
Copyright (c) 2017-2020 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
package com.cocos.lib;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.widget.FrameLayout;
import android.app.Activity;
import com.cocos.lib.CocosVideoView.OnVideoEventListener;
import java.lang.ref.WeakReference;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CocosVideoHelper {
private FrameLayout mLayout = null;
private Activity mActivity = null;
private static SparseArray<CocosVideoView> sVideoViews = null;
static VideoHandler mVideoHandler = null;
private static Handler sHandler = null;
CocosVideoHelper(Activity activity, FrameLayout layout)
{
mActivity = activity;
mLayout = layout;
mVideoHandler = new VideoHandler(this);
sVideoViews = new SparseArray<CocosVideoView>();
sHandler = new Handler(Looper.myLooper());
}
private static int videoTag = 0;
private final static int VideoTaskCreate = 0;
private final static int VideoTaskRemove = 1;
private final static int VideoTaskSetSource = 2;
private final static int VideoTaskSetRect = 3;
private final static int VideoTaskStart = 4;
private final static int VideoTaskPause = 5;
private final static int VideoTaskResume = 6;
private final static int VideoTaskStop = 7;
private final static int VideoTaskSeek = 8;
private final static int VideoTaskSetVisible = 9;
private final static int VideoTaskRestart = 10;
private final static int VideoTaskKeepRatio = 11;
private final static int VideoTaskFullScreen = 12;
private final static int VideoTaskSetVolume = 13;
private final static int VideoTaskSetPlaybackRate = 14;
private final static int VideoTaskSetMute = 15;
private final static int VideoTaskSetLoop = 16;
final static int KeyEventBack = 1000;
static class VideoHandler extends Handler{
WeakReference<CocosVideoHelper> mReference;
VideoHandler(CocosVideoHelper helper){
mReference = new WeakReference<CocosVideoHelper>(helper);
}
@Override
public void handleMessage(Message msg) {
CocosVideoHelper helper = mReference.get();
switch (msg.what) {
case VideoTaskCreate: {
helper._createVideoView(msg.arg1);
break;
}
case VideoTaskRemove: {
helper._removeVideoView(msg.arg1);
break;
}
case VideoTaskSetSource: {
helper._setVideoURL(msg.arg1, msg.arg2, (String)msg.obj);
break;
}
case VideoTaskStart: {
helper._startVideo(msg.arg1);
break;
}
case VideoTaskSetRect: {
Rect rect = (Rect)msg.obj;
helper._setVideoRect(msg.arg1, rect.left, rect.top, rect.right, rect.bottom);
break;
}
case VideoTaskFullScreen:{
if (msg.arg2 == 1) {
helper._setFullScreenEnabled(msg.arg1, true);
} else {
helper._setFullScreenEnabled(msg.arg1, false);
}
break;
}
case VideoTaskPause: {
helper._pauseVideo(msg.arg1);
break;
}
case VideoTaskStop: {
helper._stopVideo(msg.arg1);
break;
}
case VideoTaskSeek: {
helper._seekVideoTo(msg.arg1, msg.arg2);
break;
}
case VideoTaskSetVisible: {
if (msg.arg2 == 1) {
helper._setVideoVisible(msg.arg1, true);
} else {
helper._setVideoVisible(msg.arg1, false);
}
break;
}
case VideoTaskKeepRatio: {
if (msg.arg2 == 1) {
helper._setVideoKeepRatio(msg.arg1, true);
} else {
helper._setVideoKeepRatio(msg.arg1, false);
}
break;
}
case KeyEventBack: {
helper.onBackKeyEvent();
break;
}
case VideoTaskSetVolume: {
float volume = (float) msg.arg2 / 10;
helper._setVolume(msg.arg1, volume);
break;
}
case VideoTaskSetPlaybackRate: {
float rate = (float) msg.arg2 / 10;
helper._setPlaybackRate(msg.arg1, rate);
break;
}
case VideoTaskSetMute: {
if (msg.arg2 == 1) {
helper._setMute(msg.arg1, true);
} else {
helper._setMute(msg.arg1, false);
}
break;
}
case VideoTaskSetLoop: {
if (msg.arg2 == 1) {
helper._setLoop(msg.arg1, true);
} else {
helper._setLoop(msg.arg1, false);
}
break;
}
default:
break;
}
super.handleMessage(msg);
}
}
public static native void nativeExecuteVideoCallback(int index,int event);
OnVideoEventListener videoEventListener = new OnVideoEventListener() {
@Override
public void onVideoEvent(int tag,int event) {
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
@Override
public void run() {
nativeExecuteVideoCallback(tag, event);
}
});
}
};
public static int createVideoWidget() {
Message msg = new Message();
msg.what = VideoTaskCreate;
msg.arg1 = videoTag;
mVideoHandler.sendMessage(msg);
return videoTag++;
}
private void _createVideoView(int index) {
CocosVideoView videoView = new CocosVideoView(mActivity,index);
sVideoViews.put(index, videoView);
FrameLayout.LayoutParams lParams = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
mLayout.addView(videoView, lParams);
videoView.setZOrderOnTop(true);
videoView.setVideoViewEventListener(videoEventListener);
}
public static void removeVideoWidget(int index){
Message msg = new Message();
msg.what = VideoTaskRemove;
msg.arg1 = index;
mVideoHandler.sendMessage(msg);
}
private void _removeVideoView(int index) {
CocosVideoView view = sVideoViews.get(index);
if (view != null) {
view.stopPlayback();
sVideoViews.remove(index);
mLayout.removeView(view);
}
}
public static void setVideoUrl(int index, int videoSource, String videoUrl) {
Message msg = new Message();
msg.what = VideoTaskSetSource;
msg.arg1 = index;
msg.arg2 = videoSource;
msg.obj = videoUrl;
mVideoHandler.sendMessage(msg);
}
private void _setVideoURL(int index, int videoSource, String videoUrl) {
CocosVideoView videoView = sVideoViews.get(index);
if (videoView != null) {
switch (videoSource) {
case 0:
videoView.setVideoFileName(videoUrl);
break;
case 1:
videoView.setVideoURL(videoUrl);
break;
default:
break;
}
}
}
public static void setVideoRect(int index, int left, int top, int maxWidth, int maxHeight) {
Message msg = new Message();
msg.what = VideoTaskSetRect;
msg.arg1 = index;
msg.obj = new Rect(left, top, maxWidth, maxHeight);
mVideoHandler.sendMessage(msg);
}
private void _setVideoRect(int index, int left, int top, int maxWidth, int maxHeight) {
CocosVideoView videoView = sVideoViews.get(index);
if (videoView != null) {
videoView.setVideoRect(left, top, maxWidth, maxHeight);
}
}
public static void setFullScreenEnabled(int index, boolean enabled) {
Message msg = new Message();
msg.what = VideoTaskFullScreen;
msg.arg1 = index;
if (enabled) {
msg.arg2 = 1;
} else {
msg.arg2 = 0;
}
mVideoHandler.sendMessage(msg);
}
private void _setFullScreenEnabled(int index, boolean enabled) {
CocosVideoView videoView = sVideoViews.get(index);
if (videoView != null) {
videoView.setFullScreenEnabled(enabled);
}
}
private void onBackKeyEvent() {
int viewCount = sVideoViews.size();
for (int i = 0; i < viewCount; i++) {
int key = sVideoViews.keyAt(i);
CocosVideoView videoView = sVideoViews.get(key);
if (videoView != null) {
videoView.setFullScreenEnabled(false);
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
@Override
public void run() {
nativeExecuteVideoCallback(key, KeyEventBack);
}
});
}
}
}
public static void startVideo(int index) {
Message msg = new Message();
msg.what = VideoTaskStart;
msg.arg1 = index;
mVideoHandler.sendMessage(msg);
}
private void _startVideo(int index) {
CocosVideoView videoView = sVideoViews.get(index);
if (videoView != null) {
videoView.start();
}
}
public static void pauseVideo(int index) {
Message msg = new Message();
msg.what = VideoTaskPause;
msg.arg1 = index;
mVideoHandler.sendMessage(msg);
}
private void _pauseVideo(int index) {
CocosVideoView videoView = sVideoViews.get(index);
if (videoView != null) {
videoView.pause();
}
}
public static void stopVideo(int index) {
Message msg = new Message();
msg.what = VideoTaskStop;
msg.arg1 = index;
mVideoHandler.sendMessage(msg);
}
private void _stopVideo(int index) {
CocosVideoView videoView = sVideoViews.get(index);
if (videoView != null) {
videoView.stop();
}
}
public static void seekVideoTo(int index,int msec) {
Message msg = new Message();
msg.what = VideoTaskSeek;
msg.arg1 = index;
msg.arg2 = msec;
mVideoHandler.sendMessage(msg);
}
private void _seekVideoTo(int index,int msec) {
CocosVideoView videoView = sVideoViews.get(index);
if (videoView != null) {
videoView.seekTo(msec);
}
}
public static float getCurrentTime(final int index) {
CocosVideoView video = sVideoViews.get(index);
float currentPosition = -1;
if (video != null) {
currentPosition = video.getCurrentPosition() / 1000.0f;
}
return currentPosition;
}
public static float getDuration(final int index) {
CocosVideoView video = sVideoViews.get(index);
float duration = -1;
if (video != null) {
duration = video.getDuration() / 1000.0f;
}
if (duration <= 0) {
Log.w("CocosVideoHelper", "Video player's duration is not ready to get now!");
}
return duration;
}
public static void setVideoVisible(int index, boolean visible) {
Message msg = new Message();
msg.what = VideoTaskSetVisible;
msg.arg1 = index;
if (visible) {
msg.arg2 = 1;
} else {
msg.arg2 = 0;
}
mVideoHandler.sendMessage(msg);
}
private void _setVideoVisible(int index, boolean visible) {
CocosVideoView videoView = sVideoViews.get(index);
if (videoView != null) {
if (visible) {
videoView.fixSize();
videoView.setVisibility(View.VISIBLE);
} else {
videoView.setVisibility(View.INVISIBLE);
}
}
}
public static void setVideoKeepRatioEnabled(int index, boolean enable) {
Message msg = new Message();
msg.what = VideoTaskKeepRatio;
msg.arg1 = index;
if (enable) {
msg.arg2 = 1;
} else {
msg.arg2 = 0;
}
mVideoHandler.sendMessage(msg);
}
public static void setPlaybackRate(final int index, final float value) {
Message msg = new Message();
msg.what = VideoTaskSetPlaybackRate;
msg.arg1 = index;
msg.arg2 = (int) (value * 10);
mVideoHandler.sendMessage(msg);
}
private void _setPlaybackRate(final int index, final float value) {
CocosVideoView videoView = sVideoViews.get(index);
if (videoView != null) {
videoView.playbackRate(value);
}
}
public static void setMute(int index, boolean enable) {
Message msg = new Message();
msg.what = VideoTaskSetMute;
msg.arg1 = index;
if (enable) {
msg.arg2 = 1;
} else {
msg.arg2 = 0;
}
mVideoHandler.sendMessage(msg);
}
private void _setMute(int index, boolean enable) {
CocosVideoView videoView = sVideoViews.get(index);
if (videoView != null) {
videoView.setMute(enable);
}
}
public static void setLoop(int index, boolean enable) {
Message msg = new Message();
msg.what = VideoTaskSetLoop;
msg.arg1 = index;
if (enable) {
msg.arg2 = 1;
} else {
msg.arg2 = 0;
}
mVideoHandler.sendMessage(msg);
}
private void _setLoop(int index, boolean enable) {
CocosVideoView videoView = sVideoViews.get(index);
if (videoView != null) {
videoView.setLoop(enable);
}
}
private void _setVideoKeepRatio(int index, boolean enable) {
CocosVideoView videoView = sVideoViews.get(index);
if (videoView != null) {
videoView.setKeepRatio(enable);
}
}
private void _setVolume(final int index, final float volume) {
CocosVideoView videoView = sVideoViews.get(index);
if (videoView != null) {
videoView.setVolume(volume);
}
}
public static void setVolume(final int index, final float volume) {
Message msg = new Message();
msg.what = VideoTaskSetVolume;
msg.arg1 = index;
msg.arg2 = (int) (volume * 10);
mVideoHandler.sendMessage(msg);
}
}

View File

@@ -0,0 +1,637 @@
/*
* Copyright (C) 2006 The Android Open Source Project
* Copyright (c) 2014-2016 Chukong Technologies Inc.
* Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.cocos.lib;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.graphics.Point;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.PlaybackParams;
import android.net.Uri;
import android.os.Build;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.FrameLayout;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Map;
public class CocosVideoView extends SurfaceView {
// ===========================================================
// Internal classes and interfaces.
// ===========================================================
public interface OnVideoEventListener {
void onVideoEvent(int tag,int event);
}
private enum State {
IDLE,
ERROR,
INITIALIZED,
PREPARING,
PREPARED,
STARTED,
PAUSED,
STOPPED,
PLAYBACK_COMPLETED,
}
// ===========================================================
// Constants
// ===========================================================
private static final String AssetResourceRoot = "@assets/";
// ===========================================================
// Fields
// ===========================================================
private String TAG = "CocosVideoView";
private Uri mVideoUri;
private int mDuration;
private int mPosition;
private State mCurrentState = State.IDLE;
// All the stuff we need for playing and showing a video
private SurfaceHolder mSurfaceHolder = null;
private MediaPlayer mMediaPlayer = null;
private int mVideoWidth = 0;
private int mVideoHeight = 0;
private OnVideoEventListener mOnVideoEventListener;
// recording the seek position while preparing
private int mSeekWhenPrepared = 0;
protected Activity mActivity = null;
protected int mViewLeft = 0;
protected int mViewTop = 0;
protected int mViewWidth = 0;
protected int mViewHeight = 0;
protected int mVisibleLeft = 0;
protected int mVisibleTop = 0;
protected int mVisibleWidth = 0;
protected int mVisibleHeight = 0;
protected boolean mFullScreenEnabled = false;
private boolean mIsAssetResource = false;
private String mVideoFilePath = null;
private int mViewTag = 0;
private boolean mKeepRatio = false;
private boolean mMetaUpdated = false;
// MediaPlayer will be released when surface view is destroyed, so should record the position,
// and use it to play after MedialPlayer is created again.
private int mPositionBeforeRelease = 0;
// also need to record play state when surface is destroyed.
private State mStateBeforeRelease = State.IDLE;
// ===========================================================
// Constructors
// ===========================================================
public CocosVideoView(Activity activity, int tag) {
super(activity);
mViewTag = tag;
mActivity = activity;
initVideoView();
}
// ===========================================================
// Getter & Setter
// ===========================================================
public void setVideoRect(int left, int top, int maxWidth, int maxHeight) {
if (mViewLeft == left && mViewTop == top && mViewWidth == maxWidth && mViewHeight == maxHeight)
return;
mViewLeft = left;
mViewTop = top;
mViewWidth = maxWidth;
mViewHeight = maxHeight;
fixSize(mViewLeft, mViewTop, mViewWidth, mViewHeight);
}
public void setFullScreenEnabled(boolean enabled) {
if (mFullScreenEnabled != enabled) {
mFullScreenEnabled = enabled;
fixSize();
}
}
public void setVolume (float volume) {
if (mMediaPlayer != null) {
mMediaPlayer.setVolume(volume, volume);
}
}
public void setKeepRatio(boolean enabled) {
mKeepRatio = enabled;
fixSize();
}
public void playbackRate(float value) {
if (mMediaPlayer != null) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
PlaybackParams params = new PlaybackParams();
params.setSpeed(value);
mMediaPlayer.setPlaybackParams(params);
}
}
}
public void setMute(boolean enabled) {
if (mMediaPlayer != null) {
if (enabled) {
mMediaPlayer.setVolume(0f, 0f);
} else {
mMediaPlayer.setVolume(1f, 1f);
}
}
}
public void setLoop(boolean enabled) {
if (mMediaPlayer != null) {
mMediaPlayer.setLooping(enabled);
}
}
public void setVideoURL(String url) {
mIsAssetResource = false;
setVideoURI(Uri.parse(url), null);
}
public void setVideoFileName(String path) {
if (path.startsWith(AssetResourceRoot)) {
path = path.substring(AssetResourceRoot.length());
}
if (path.startsWith("/")) {
mIsAssetResource = false;
setVideoURI(Uri.parse(path),null);
}
else {
mVideoFilePath = path;
mIsAssetResource = true;
setVideoURI(Uri.parse(path), null);
}
}
public int getCurrentPosition() {
if (! (mCurrentState == State.IDLE ||
mCurrentState == State.ERROR ||
mCurrentState == State.INITIALIZED ||
mCurrentState == State.STOPPED ||
mMediaPlayer == null) ) {
mPosition = mMediaPlayer.getCurrentPosition();
}
return mPosition;
}
public int getDuration() {
if (! (mCurrentState == State.IDLE ||
mCurrentState == State.ERROR ||
mCurrentState == State.INITIALIZED ||
mCurrentState == State.STOPPED ||
mMediaPlayer == null) ) {
mDuration = mMediaPlayer.getDuration();
}
return mDuration;
}
/**
* Register a callback to be invoked when some video event triggered.
*
* @param l The callback that will be run
*/
public void setVideoViewEventListener(OnVideoEventListener l)
{
mOnVideoEventListener = l;
}
// ===========================================================
// Overrides
// ===========================================================
@Override
public void setVisibility(int visibility) {
super.setVisibility(visibility);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(mVisibleWidth, mVisibleHeight);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
this.sendEvent(EVENT_CLICKED);
}
return true;
}
// ===========================================================
// Public functions
// ===========================================================
public void stop() {
if (!(mCurrentState == State.IDLE || mCurrentState == State.INITIALIZED || mCurrentState == State.ERROR || mCurrentState == State.STOPPED)
&& mMediaPlayer != null) {
mCurrentState = State.STOPPED;
mMediaPlayer.stop();
this.sendEvent(EVENT_STOPPED);
// after the video is stop, it shall prepare to be playable again
try {
mMediaPlayer.reset(); // reset to avoid some problems on start.
loadDataSource();
mMediaPlayer.prepare();
this.showFirstFrame();
} catch (Exception ex) {}
}
}
public void stopPlayback() {
this.release();
}
public void start() {
if ((mCurrentState == State.PREPARED ||
mCurrentState == State.PAUSED ||
mCurrentState == State.PLAYBACK_COMPLETED) &&
mMediaPlayer != null) {
mCurrentState = State.STARTED;
mMediaPlayer.start();
this.sendEvent(EVENT_PLAYING);
}
}
public void pause() {
if ((mCurrentState == State.STARTED || mCurrentState == State.PLAYBACK_COMPLETED) &&
mMediaPlayer != null) {
mCurrentState = State.PAUSED;
mMediaPlayer.pause();
this.sendEvent(EVENT_PAUSED);
}
}
public void seekTo(int ms) {
if (mCurrentState == State.IDLE || mCurrentState == State.INITIALIZED ||
mCurrentState == State.STOPPED || mCurrentState == State.ERROR ||
mMediaPlayer == null) {
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
try {
Method seekTo = mMediaPlayer.getClass().getMethod("seekTo", long.class, int.class);
// The mode argument added in API level 26, 3 = MediaPlayer.SEEK_CLOSEST
// https://developer.android.com/reference/android/media/MediaPlayer#seekTo(int)
seekTo.invoke(mMediaPlayer, ms, 3);
} catch (Exception e) {
mMediaPlayer.seekTo(ms);
}
}
else {
mMediaPlayer.seekTo(ms);
}
}
public void fixSize() {
if (mFullScreenEnabled) {
Point screenSize = new Point();
mActivity.getWindowManager().getDefaultDisplay().getSize(screenSize);
fixSize(0, 0, screenSize.x, screenSize.y);
} else {
fixSize(mViewLeft, mViewTop, mViewWidth, mViewHeight);
}
}
public void fixSize(int left, int top, int width, int height) {
if (mVideoWidth == 0 || mVideoHeight == 0) {
mVisibleLeft = left;
mVisibleTop = top;
mVisibleWidth = width;
mVisibleHeight = height;
}
else if (width != 0 && height != 0) {
if (mKeepRatio && !mFullScreenEnabled) {
if ( mVideoWidth * height > width * mVideoHeight ) {
mVisibleWidth = width;
mVisibleHeight = width * mVideoHeight / mVideoWidth;
} else if ( mVideoWidth * height < width * mVideoHeight ) {
mVisibleWidth = height * mVideoWidth / mVideoHeight;
mVisibleHeight = height;
}
mVisibleLeft = left + (width - mVisibleWidth) / 2;
mVisibleTop = top + (height - mVisibleHeight) / 2;
} else {
mVisibleLeft = left;
mVisibleTop = top;
mVisibleWidth = width;
mVisibleHeight = height;
}
}
else {
mVisibleLeft = left;
mVisibleTop = top;
mVisibleWidth = mVideoWidth;
mVisibleHeight = mVideoHeight;
}
getHolder().setFixedSize(mVisibleWidth, mVisibleHeight);
FrameLayout.LayoutParams lParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT);
lParams.leftMargin = mVisibleLeft;
lParams.topMargin = mVisibleTop;
setLayoutParams(lParams);
}
public int resolveAdjustedSize(int desiredSize, int measureSpec) {
int result = desiredSize;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
/* Parent says we can be as big as we want. Just don't be larger
* than max size imposed on ourselves.
*/
result = desiredSize;
break;
case MeasureSpec.AT_MOST:
/* Parent says we can be as big as we want, up to specSize.
* Don't be larger than specSize, and don't be larger than
* the max size imposed on ourselves.
*/
result = Math.min(desiredSize, specSize);
break;
case MeasureSpec.EXACTLY:
// No choice. Do what we are told.
result = specSize;
break;
}
return result;
}
// ===========================================================
// Private functions
// ===========================================================
private void initVideoView() {
mVideoWidth = 0;
mVideoHeight = 0;
getHolder().addCallback(mSHCallback);
//Fix issue#11516:Can't play video on Android 2.3.x
getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
setFocusable(true);
setFocusableInTouchMode(true);
mCurrentState = State.IDLE;
}
/**
* @hide
*/
private void setVideoURI(Uri uri, Map<String, String> headers) {
mVideoUri = uri;
mVideoWidth = 0;
mVideoHeight = 0;
}
private void loadDataSource() throws IOException {
if (mIsAssetResource) {
AssetFileDescriptor afd = mActivity.getAssets().openFd(mVideoFilePath);
mMediaPlayer.setDataSource(afd.getFileDescriptor(),afd.getStartOffset(),afd.getLength());
} else {
mMediaPlayer.setDataSource(mVideoUri.toString());
}
}
private void openVideo() {
if (mSurfaceHolder == null) {
// not ready for playback just yet, will try again later
return;
}
if (mIsAssetResource) {
if(mVideoFilePath == null)
return;
} else if(mVideoUri == null) {
return;
}
this.pausePlaybackService();
try {
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setOnPreparedListener(mPreparedListener);
mMediaPlayer.setOnCompletionListener(mCompletionListener);
mMediaPlayer.setOnErrorListener(mErrorListener);
mMediaPlayer.setDisplay(mSurfaceHolder);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setScreenOnWhilePlaying(true);
loadDataSource();
mCurrentState = State.INITIALIZED;
// Use Prepare() instead of PrepareAsync to make things easy.
mMediaPlayer.prepare();
this.showFirstFrame();
// mMediaPlayer.prepareAsync();
} catch (IOException ex) {
Log.w(TAG, "Unable to open content: " + mVideoUri, ex);
mCurrentState = State.ERROR;
mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
return;
} catch (IllegalArgumentException ex) {
Log.w(TAG, "Unable to open content: " + mVideoUri, ex);
mCurrentState = State.ERROR;
mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
return;
}
}
MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
mVideoWidth = mp.getVideoWidth();
mVideoHeight = mp.getVideoHeight();
if (mVideoWidth != 0 && mVideoHeight != 0) {
fixSize();
}
if(!mMetaUpdated) {
CocosVideoView.this.sendEvent(EVENT_META_LOADED);
CocosVideoView.this.sendEvent(EVENT_READY_TO_PLAY);
mMetaUpdated = true;
}
mCurrentState = State.PREPARED;
if (mStateBeforeRelease == State.STARTED) {
CocosVideoView.this.start();
}
if (mPositionBeforeRelease > 0) {
CocosVideoView.this.seekTo(mPositionBeforeRelease);
}
mStateBeforeRelease = State.IDLE;
mPositionBeforeRelease = 0;
}
};
private MediaPlayer.OnCompletionListener mCompletionListener =
new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
mCurrentState = State.PLAYBACK_COMPLETED;
CocosVideoView.this.sendEvent(EVENT_COMPLETED);
//make it playable again.
CocosVideoView.this.showFirstFrame();
}
};
private static final int EVENT_PLAYING = 0;
private static final int EVENT_PAUSED = 1;
private static final int EVENT_STOPPED = 2;
private static final int EVENT_COMPLETED = 3;
private static final int EVENT_META_LOADED = 4;
private static final int EVENT_CLICKED = 5;
private static final int EVENT_READY_TO_PLAY = 6;
private MediaPlayer.OnErrorListener mErrorListener =
new MediaPlayer.OnErrorListener() {
public boolean onError(MediaPlayer mp, int framework_err, int impl_err) {
Log.d(TAG, "Error: " + framework_err + "," + impl_err);
mCurrentState = State.ERROR;
/* Otherwise, pop up an error dialog so the user knows that
* something bad has happened. Only try and pop up the dialog
* if we're attached to a window. When we're going away and no
* longer have a window, don't bother showing the user an error.
*/
if (getWindowToken() != null) {
Resources r = mActivity.getResources();
int messageId;
if (framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
// messageId = com.android.internal.R.string.VideoView_error_text_invalid_progressive_playback;
messageId = r.getIdentifier("VideoView_error_text_invalid_progressive_playback", "string", "android");
} else {
// messageId = com.android.internal.R.string.VideoView_error_text_unknown;
messageId = r.getIdentifier("VideoView_error_text_unknown", "string", "android");
}
int titleId = r.getIdentifier("VideoView_error_title", "string", "android");
int buttonStringId = r.getIdentifier("VideoView_error_button", "string", "android");
new AlertDialog.Builder(mActivity)
.setTitle(r.getString(titleId))
.setMessage(messageId)
.setPositiveButton(r.getString(buttonStringId),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
/* If we get here, there is no onError listener, so
* at least inform them that the video is over.
*/
CocosVideoView.this.sendEvent(EVENT_COMPLETED);
}
})
.setCancelable(false)
.show();
}
return true;
}
};
SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback()
{
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
}
public void surfaceCreated(SurfaceHolder holder) {
mSurfaceHolder = holder;
CocosVideoView.this.openVideo();
}
public void surfaceDestroyed(SurfaceHolder holder) {
// after we return from this we can't use the surface any more
mSurfaceHolder = null;
mPositionBeforeRelease = getCurrentPosition();
mStateBeforeRelease = mCurrentState;
CocosVideoView.this.release();
}
};
/*
* release the media player in any state
*/
private void release() {
if (mMediaPlayer != null) {
mMediaPlayer.release();
mMediaPlayer = null;
mCurrentState = State.IDLE;
}
}
private void showFirstFrame() {
mMediaPlayer.seekTo(1);
}
private void sendEvent(int event) {
if (this.mOnVideoEventListener != null) {
this.mOnVideoEventListener.onVideoEvent(this.mViewTag, event);
}
}
// Tell the music playback service to pause
// REFINE: these constants need to be published somewhere in the framework.
private void pausePlaybackService() {
Intent i = new Intent("com.android.music.musicservicecommand");
i.putExtra("command", "pause");
mActivity.sendBroadcast(i);
}
}

View File

@@ -0,0 +1,173 @@
/****************************************************************************
Copyright (c) 2017-2018 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 engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
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;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.Log;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.FrameLayout;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.concurrent.CountDownLatch;
class ShouldStartLoadingWorker implements Runnable {
private CountDownLatch mLatch;
private boolean[] mResult;
private final int mViewTag;
private final String mUrlString;
ShouldStartLoadingWorker(CountDownLatch latch, boolean[] result, int viewTag, String urlString) {
this.mLatch = latch;
this.mResult = result;
this.mViewTag = viewTag;
this.mUrlString = urlString;
}
@Override
public void run() {
this.mResult[0] = CocosWebViewHelper._shouldStartLoading(mViewTag, mUrlString);
this.mLatch.countDown(); // notify that result is ready
}
}
public class CocosWebView extends WebView {
private static final String TAG = CocosWebViewHelper.class.getSimpleName();
private int mViewTag;
private String mJSScheme;
public CocosWebView(Context context) {
this(context, -1);
}
@SuppressLint("SetJavaScriptEnabled")
public CocosWebView(Context context, int viewTag) {
super(context);
this.mViewTag = viewTag;
this.mJSScheme = "";
this.setFocusable(true);
this.setFocusableInTouchMode(true);
this.getSettings().setSupportZoom(false);
this.getSettings().setDomStorageEnabled(true);
this.getSettings().setJavaScriptEnabled(true);
// `searchBoxJavaBridge_` has big security risk. http://jvn.jp/en/jp/JVN53768697
try {
Method method = this.getClass().getMethod("removeJavascriptInterface", new Class[]{String.class});
method.invoke(this, "searchBoxJavaBridge_");
} catch (Exception e) {
Log.d(TAG, "This API level do not support `removeJavascriptInterface`");
}
this.setWebViewClient(new Cocos2dxWebViewClient());
this.setWebChromeClient(new WebChromeClient());
}
public void setJavascriptInterfaceScheme(String scheme) {
this.mJSScheme = scheme != null ? scheme : "";
}
public void setScalesPageToFit(boolean scalesPageToFit) {
this.getSettings().setSupportZoom(scalesPageToFit);
}
class Cocos2dxWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, final String urlString) {
try {
URI uri = URI.create(urlString);
if (uri != null && uri.getScheme().equals(mJSScheme)) {
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
@Override
public void run() {
CocosWebViewHelper._onJsCallback(mViewTag, urlString);
}
});
return true;
}
} catch (Exception e) {
Log.d(TAG, "Failed to create URI from url");
}
boolean[] result = new boolean[] { true };
CountDownLatch latch = new CountDownLatch(1);
// run worker on cocos thread
GlobalObject.runOnUiThread(new ShouldStartLoadingWorker(latch, result, mViewTag, urlString));
// wait for result from cocos thread
try {
latch.await();
} catch (InterruptedException ex) {
Log.d(TAG, "'shouldOverrideUrlLoading' failed");
}
return result[0];
}
@Override
public void onPageFinished(WebView view, final String url) {
super.onPageFinished(view, url);
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
@Override
public void run() {
CocosWebViewHelper._didFinishLoading(mViewTag, url);
}
});
}
@Override
public void onReceivedError(WebView view, int errorCode, String description, final String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
@Override
public void run() {
CocosWebViewHelper._didFailLoading(mViewTag, failingUrl);
}
});
}
}
public void setWebViewRect(int left, int top, int maxWidth, int maxHeight) {
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT);
layoutParams.leftMargin = left;
layoutParams.topMargin = top;
layoutParams.width = maxWidth;
layoutParams.height = maxHeight;
this.setLayoutParams(layoutParams);
}
}

View File

@@ -0,0 +1,310 @@
/****************************************************************************
Copyright (c) 2010-2011 cocos2d-x.org
Copyright (c) 2013-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
package com.cocos.lib;
import android.graphics.Color;
import android.os.Handler;
import android.os.Looper;
import android.util.SparseArray;
import android.view.View;
import android.webkit.WebView;
import android.widget.FrameLayout;
import android.widget.PopupWindow;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CocosWebViewHelper {
private static final String TAG = CocosWebViewHelper.class.getSimpleName();
private static Handler sHandler;
private static PopupWindow sPopUp;
private static FrameLayout sLayout;
private static SparseArray<CocosWebView> webViews;
private static int viewTag = 0;
public CocosWebViewHelper(FrameLayout layout) {
CocosWebViewHelper.sLayout = layout;
CocosWebViewHelper.sHandler = new Handler(Looper.myLooper());
CocosWebViewHelper.webViews = new SparseArray<CocosWebView>();
}
private static native boolean shouldStartLoading(int index, String message);
private static native void didFinishLoading(int index, String message);
private static native void didFailLoading(int index, String message);
private static native void onJsCallback(int index, String message);
public static boolean _shouldStartLoading(int index, String message) { return !shouldStartLoading(index, message); }
public static void _didFinishLoading(int index, String message) { didFinishLoading(index, message); }
public static void _didFailLoading(int index, String message) { didFailLoading(index, message); }
public static void _onJsCallback(int index, String message) { onJsCallback(index, message); }
public static int createWebView() {
final int index = viewTag;
GlobalObject.runOnUiThread(new Runnable() {
@Override
public void run() {
CocosWebView webView = new CocosWebView(GlobalObject.getContext(), index);
FrameLayout.LayoutParams lParams = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
sLayout.addView(webView, lParams);
webViews.put(index, webView);
}
});
return viewTag++;
}
public static void removeWebView(final int index) {
GlobalObject.runOnUiThread(new Runnable() {
@Override
public void run() {
CocosWebView webView = webViews.get(index);
if (webView != null) {
webViews.remove(index);
sLayout.removeView(webView);
webView.destroy();
webView = null;
}
}
});
}
public static void setVisible(final int index, final boolean visible) {
GlobalObject.runOnUiThread(new Runnable() {
@Override
public void run() {
CocosWebView webView = webViews.get(index);
if (webView != null) {
webView.setVisibility(visible ? View.VISIBLE : View.GONE);
}
}
});
}
public static void setWebViewRect(final int index, final int left, final int top, final int maxWidth, final int maxHeight) {
GlobalObject.runOnUiThread(new Runnable() {
@Override
public void run() {
CocosWebView webView = webViews.get(index);
if (webView != null) {
webView.setWebViewRect(left, top, maxWidth, maxHeight);
}
}
});
}
public static void setBackgroundTransparent(final int index, final boolean isTransparent) {
GlobalObject.runOnUiThread(new Runnable() {
@Override
public void run() {
CocosWebView webView = webViews.get(index);
if (webView != null) {
webView.setBackgroundColor(isTransparent ? Color.TRANSPARENT : Color.WHITE);
webView.setLayerType(WebView.LAYER_TYPE_SOFTWARE, null);
}
}
});
}
public static void setJavascriptInterfaceScheme(final int index, final String scheme) {
GlobalObject.runOnUiThread(new Runnable() {
@Override
public void run() {
CocosWebView webView = webViews.get(index);
if (webView != null) {
webView.setJavascriptInterfaceScheme(scheme);
}
}
});
}
public static void loadData(final int index, final String data, final String mimeType, final String encoding, final String baseURL) {
GlobalObject.runOnUiThread(new Runnable() {
@Override
public void run() {
CocosWebView webView = webViews.get(index);
if (webView != null) {
webView.loadDataWithBaseURL(baseURL, data, mimeType, encoding, null);
}
}
});
}
public static void loadHTMLString(final int index, final String data, final String baseUrl) {
GlobalObject.runOnUiThread(new Runnable() {
@Override
public void run() {
CocosWebView webView = webViews.get(index);
if (webView != null) {
webView.loadDataWithBaseURL(baseUrl, data, null, null, null);
}
}
});
}
public static void loadUrl(final int index, final String url) {
GlobalObject.runOnUiThread(new Runnable() {
@Override
public void run() {
CocosWebView webView = webViews.get(index);
if (webView != null) {
webView.loadUrl(url);
}
}
});
}
public static void loadFile(final int index, final String filePath) {
GlobalObject.runOnUiThread(new Runnable() {
@Override
public void run() {
CocosWebView webView = webViews.get(index);
if (webView != null) {
webView.loadUrl(filePath);
}
}
});
}
public static void stopLoading(final int index) {
GlobalObject.runOnUiThread(new Runnable() {
@Override
public void run() {
CocosWebView webView = webViews.get(index);
if (webView != null) {
webView.stopLoading();
}
}
});
}
public static void reload(final int index) {
GlobalObject.runOnUiThread(new Runnable() {
@Override
public void run() {
CocosWebView webView = webViews.get(index);
if (webView != null) {
webView.reload();
}
}
});
}
public static <T> T callInMainThread(Callable<T> call) throws ExecutionException, InterruptedException {
FutureTask<T> task = new FutureTask<T>(call);
sHandler.post(task);
return task.get();
}
public static boolean canGoBack(final int index) {
Callable<Boolean> callable = new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
CocosWebView webView = webViews.get(index);
return webView != null && webView.canGoBack();
}
};
try {
return callInMainThread(callable);
} catch (ExecutionException e) {
return false;
} catch (InterruptedException e) {
return false;
}
}
public static boolean canGoForward(final int index) {
Callable<Boolean> callable = new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
CocosWebView webView = webViews.get(index);
return webView != null && webView.canGoForward();
}
};
try {
return callInMainThread(callable);
} catch (ExecutionException e) {
return false;
} catch (InterruptedException e) {
return false;
}
}
public static void goBack(final int index) {
GlobalObject.runOnUiThread(new Runnable() {
@Override
public void run() {
CocosWebView webView = webViews.get(index);
if (webView != null) {
webView.goBack();
}
}
});
}
public static void goForward(final int index) {
GlobalObject.runOnUiThread(new Runnable() {
@Override
public void run() {
CocosWebView webView = webViews.get(index);
if (webView != null) {
webView.goForward();
}
}
});
}
public static void evaluateJS(final int index, final String js) {
GlobalObject.runOnUiThread(new Runnable() {
@Override
public void run() {
CocosWebView webView = webViews.get(index);
if (webView != null) {
webView.loadUrl("javascript:" + js);
}
}
});
}
public static void setScalesPageToFit(final int index, final boolean scalesPageToFit) {
GlobalObject.runOnUiThread(new Runnable() {
@Override
public void run() {
CocosWebView webView = webViews.get(index);
if (webView != null) {
webView.setScalesPageToFit(scalesPageToFit);
}
}
});
}
}

View File

@@ -0,0 +1,80 @@
/****************************************************************************
Copyright (c) 2020 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
package com.cocos.lib;
import android.app.Activity;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
public class GlobalObject {
private static Context sContext = null;
private static Activity sActivity = null;
private static Handler sHandler = null;
private static Thread sUiThread = null;
// Should be invoked in UI thread. The parameter `context` and `activity` could be the same value.
public static void init(Context context, Activity activity) {
sContext = context;
sActivity = activity;
sHandler = new Handler(Looper.getMainLooper());
sUiThread = Thread.currentThread();
if (sUiThread != Looper.getMainLooper().getThread()) {
throw new RuntimeException("GlobalObject.init should be invoked in UI thread");
}
}
public static void destroy() {
sContext = null;
sActivity = null;
if (sHandler != null) {
sHandler.removeCallbacksAndMessages(null);
}
sHandler = null;
}
public static Activity getActivity() {
return sActivity;
}
public static Context getContext() {
return sContext;
}
/**
* Runs the specified action on the UI thread. If the current thread is the UI
* thread, then the action is executed immediately. If the current thread is
* not the UI thread, the action is posted to the event queue of the UI thread.
* This method keeps the same logic as which in Activity.runOnUiThread.
* https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-13.0.0_r37/core/java/android/app/Activity.java#7364
* @param action the action to run on the UI thread
*/
public static void runOnUiThread(Runnable action) {
if (Thread.currentThread() != sUiThread) {
sHandler.post(action);
} else {
action.run();
}
}
}

View File

@@ -0,0 +1,61 @@
/****************************************************************************
Copyright (c) 2018-2021 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 engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
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;
public class JsbBridge {
public interface ICallback{
/**
* Applies this callback to the given argument.
*
* @param arg0 as input
* @param arg1 as input
*/
void onScript(String arg0, String arg1);
}
private static ICallback callback;
private static void callByScript(String arg0, String arg1){
if(JsbBridge.callback != null)
callback.onScript(arg0, arg1);
}
/**Add a callback which you would like to apply
* @param f ICallback, the method which will be actually applied. multiple calls will override
* */
public static void setCallback(ICallback f){
JsbBridge.callback = f;
}
/**
* Java dispatch Js event, use native c++ code
* @param arg0 input values
*/
private static native void nativeSendToScript(String arg0, String arg1);
public static void sendToScript(String arg0, String arg1){
nativeSendToScript(arg0, arg1);
}
public static void sendToScript(String arg0){
nativeSendToScript(arg0, null);
}
}

View File

@@ -0,0 +1,109 @@
/****************************************************************************
Copyright (c) 2018-2021 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 engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
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;
import java.util.ArrayList;
import java.util.HashMap;
public class JsbBridgeWrapper {
//Interface for listener, should be implemented and dispatched
public interface OnScriptEventListener {
void onScriptEvent(String arg);
}
/**
* Get the instance of JsbBridgetWrapper
*/
public static JsbBridgeWrapper getInstance() {
if (instance == null) {
instance = new JsbBridgeWrapper();
}
return instance;
}
/**
* Add a listener to specified event, if the event does not exist, the wrapper will create one. Concurrent listener will be ignored
*/
public void addScriptEventListener(String eventName, OnScriptEventListener listener) {
if (eventMap.get(eventName) == null) {
eventMap.put(eventName, new ArrayList<OnScriptEventListener>());
}
eventMap.get(eventName).add(listener);
}
/**
* Remove listener for specified event, concurrent event will be deleted. Return false only if the event does not exist
*/
public boolean removeScriptEventListener(String eventName, OnScriptEventListener listener) {
ArrayList<OnScriptEventListener> arr = eventMap.get(eventName);
if (arr == null) {
return false;
}
arr.remove(listener);
return true;
}
/**
* Remove all listener for event specified.
*/
public void removeAllListenersForEvent(String eventName) {
this.eventMap.remove(eventName);
}
/**
* Remove all event registered. Use it carefully!
*/
public void removeAllListeners() {
this.eventMap.clear();
}
/**
* Dispatch the event with argument, the event should be registered in javascript, or other script language in future.
*/
public void dispatchEventToScript(String eventName, String arg) {
JsbBridge.sendToScript(eventName, arg);
}
/**
* Dispatch the event which is registered in javascript, or other script language in future.
*/
public void dispatchEventToScript(String eventName) {
JsbBridge.sendToScript(eventName);
}
private JsbBridgeWrapper() {
JsbBridge.setCallback(new JsbBridge.ICallback() {
@Override
public void onScript(String arg0, String arg1) {
triggerEvents(arg0, arg1);
}
});
}
private final HashMap<String, ArrayList<OnScriptEventListener>> eventMap = new HashMap<>();
private static JsbBridgeWrapper instance;
private void triggerEvents(String eventName, String arg) {
ArrayList<OnScriptEventListener> arr = eventMap.get(eventName);
if (arr == null)
return;
for (OnScriptEventListener m : arr) {
m.onScriptEvent(arg);
}
}
}

View File

@@ -0,0 +1,57 @@
/****************************************************************************
Copyright (c) 2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
package com.cocos.lib;
import android.os.Build;
import android.view.View;
public class Utils {
public static void hideVirtualButton() {
if (Build.VERSION.SDK_INT >= 19 &&
null != GlobalObject.getActivity()) {
// use reflection to remove dependence of API level
Class viewClass = View.class;
final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = CocosReflectionHelper.<Integer>getConstantValue(viewClass, "SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION");
final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = CocosReflectionHelper.<Integer>getConstantValue(viewClass, "SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN");
final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = CocosReflectionHelper.<Integer>getConstantValue(viewClass, "SYSTEM_UI_FLAG_HIDE_NAVIGATION");
final int SYSTEM_UI_FLAG_FULLSCREEN = CocosReflectionHelper.<Integer>getConstantValue(viewClass, "SYSTEM_UI_FLAG_FULLSCREEN");
final int SYSTEM_UI_FLAG_IMMERSIVE_STICKY = CocosReflectionHelper.<Integer>getConstantValue(viewClass, "SYSTEM_UI_FLAG_IMMERSIVE_STICKY");
final int SYSTEM_UI_FLAG_LAYOUT_STABLE = CocosReflectionHelper.<Integer>getConstantValue(viewClass, "SYSTEM_UI_FLAG_LAYOUT_STABLE");
// getWindow().getDecorView().setSystemUiVisibility();
final Object[] parameters = new Object[]{SYSTEM_UI_FLAG_LAYOUT_STABLE
| SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
| SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
| SYSTEM_UI_FLAG_IMMERSIVE_STICKY};
CocosReflectionHelper.<Void>invokeInstanceMethod(GlobalObject.getActivity().getWindow().getDecorView(),
"setSystemUiVisibility",
new Class[]{Integer.TYPE},
parameters);
}
}
}

View File

@@ -0,0 +1,65 @@
package com.cocos.lib.websocket;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
/**
* An SSL socket factory that forwards all calls to a delegate. Override {@link
* #configureSocket} to customize a created socket before it is returned.
*/
class CocosDelegatingSSLSocketFactory extends SSLSocketFactory {
protected final SSLSocketFactory delegate;
CocosDelegatingSSLSocketFactory(SSLSocketFactory delegate) {
this.delegate = delegate;
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket socket, String host, int port,
boolean autoClose) throws IOException {
return configureSocket(
(SSLSocket)delegate.createSocket(socket, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException {
return configureSocket((SSLSocket)delegate.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost,
int localPort) throws IOException {
return configureSocket(
(SSLSocket)delegate.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return configureSocket((SSLSocket)delegate.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port,
InetAddress localAddress, int localPort)
throws IOException {
return configureSocket((SSLSocket)delegate.createSocket(
address, port, localAddress, localPort));
}
protected SSLSocket configureSocket(SSLSocket socket) throws IOException {
return socket;
}
}

View File

@@ -0,0 +1,50 @@
package com.cocos.lib.websocket;
import org.cocos2dx.okhttp3.Interceptor;
import org.cocos2dx.okhttp3.MediaType;
import org.cocos2dx.okhttp3.Request;
import org.cocos2dx.okhttp3.RequestBody;
import org.cocos2dx.okhttp3.Response;
import org.cocos2dx.okio.BufferedSink;
import org.cocos2dx.okio.GzipSink;
import org.cocos2dx.okio.Okio;
import java.io.IOException;
public class CocosGzipRequestInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
if (originalRequest.body() == null ||
originalRequest.header("Content-Encoding") != null) {
return chain.proceed(originalRequest);
}
Request compressedRequest =
originalRequest.newBuilder()
.header("Content-Encoding", "gzip")
.method(originalRequest.method(), gzip(originalRequest.body()))
.build();
return chain.proceed(compressedRequest);
}
private RequestBody gzip(final RequestBody body) {
return new RequestBody() {
@Override
public MediaType contentType() {
return body.contentType();
}
@Override
public long contentLength() {
return -1; // 无法提前知道压缩后的数据大小
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
body.writeTo(gzipSink);
gzipSink.close();
}
};
}
}

View File

@@ -0,0 +1,346 @@
package com.cocos.lib.websocket;
import android.os.Build;
import android.util.Log;
import com.cocos.lib.GlobalObject;
import org.cocos2dx.okhttp3.CipherSuite;
import org.cocos2dx.okhttp3.Dispatcher;
import org.cocos2dx.okhttp3.OkHttpClient;
import org.cocos2dx.okhttp3.Protocol;
import org.cocos2dx.okhttp3.Request;
import org.cocos2dx.okhttp3.Response;
import org.cocos2dx.okhttp3.WebSocketListener;
import org.cocos2dx.okio.ByteString;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
@SuppressWarnings("unused")
public class CocosWebSocket extends WebSocketListener {
private final static String _TAG = "cocos-websocket";
static {
NativeInit();
}
private static class _WebSocketContext {
long identifier;
long handlerPtr;
}
private static Dispatcher dispatcher = null;
private final long _timeout;
private final boolean _perMessageDeflate;
private final boolean _tcpNoDelay;
private final String[] _header;
private final _WebSocketContext _wsContext =
new _WebSocketContext();
private org.cocos2dx.okhttp3.WebSocket _webSocket;
private OkHttpClient _client;
CocosWebSocket(long ptr, long handler, String[] header, boolean tcpNoDelay,
boolean perMessageDeflate, long timeout) {
_wsContext.identifier = ptr;
_wsContext.handlerPtr = handler;
_header = header;
_tcpNoDelay = tcpNoDelay;
_perMessageDeflate = perMessageDeflate;
_timeout = timeout;
}
private void _removeHandler() {
synchronized (_wsContext) {
_wsContext.identifier = 0;
_wsContext.handlerPtr = 0;
}
}
private void _send(final byte[] msg) {
// Log.d(_TAG, "try sending binary msg");
if (null == _webSocket) {
Log.e(_TAG, "WebSocket hasn't connected yet");
return;
}
ByteString byteString = ByteString.of(msg);
_webSocket.send(byteString);
}
private void _send(final String msg) {
// Log.d(_TAG, "try sending string msg: " + msg);
if (null == _webSocket) {
Log.e(_TAG, "WebSocket hasn't connected yet");
return;
}
_webSocket.send(msg);
}
/**
* Returns the VM's default SSL socket factory, using {@code trustManager} for
* trusted root certificates.
*/
private SSLSocketFactory
defaultSslSocketFactory(X509TrustManager trustManager)
throws NoSuchAlgorithmException, KeyManagementException {
SSLContext sslContext = SSLContext.getInstance("TLS");
SecureRandom random;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
random = SecureRandom.getInstanceStrong();
} else {
random = SecureRandom.getInstance("SHA1PRNG");
}
sslContext.init(null, new TrustManager[] {trustManager}, random);
return sslContext.getSocketFactory();
}
private String[] javaNames(List<CipherSuite> cipherSuites) {
if (cipherSuites == null) {
return new String[0];
} else {
String[] result = new String[cipherSuites.size()];
for (int i = 0; i < result.length; i++) {
result[i] = cipherSuites.get(i).javaName();
}
return result;
}
}
private void _connect(final String url, final String protocols,
final String caFilePath) {
Log.d(_TAG, "connect ws url: '" + url + "' ,protocols: '" + protocols + "' ,ca_: '" + caFilePath + "'");
Request.Builder requestBuilder = new Request.Builder().url(url);
URI uriObj = null;
try {
requestBuilder = requestBuilder.url(url.trim());
uriObj = URI.create(url);
} catch (NullPointerException | IllegalArgumentException e) {
synchronized (_wsContext) {
nativeOnError("invalid url", _wsContext.identifier,
_wsContext.handlerPtr);
}
return;
}
if (!protocols.isEmpty()) {
requestBuilder.addHeader("Sec-WebSocket-Protocol", protocols);
}
if (_header != null) {
for (int index = 0; index < _header.length; index += 2) {
requestBuilder.header(_header[index], _header[index + 1]);
}
}
String originProtocol =uriObj.getScheme().toLowerCase();
String uriScheme = (originProtocol.equals("wss") || originProtocol.equals("https"))? "https" : "http";
requestBuilder.addHeader("Origin", uriScheme + "://" + uriObj.getHost() + (uriObj.getPort() < 0 ? "" : ":" + uriObj.getPort()));
Request request = requestBuilder.build();
if(dispatcher == null) {
dispatcher = new Dispatcher();
}
OkHttpClient.Builder builder =
new OkHttpClient.Builder()
.dispatcher(dispatcher)
.protocols(Collections.singletonList(Protocol.HTTP_1_1))
.readTimeout(_timeout, TimeUnit.MILLISECONDS)
.writeTimeout(_timeout, TimeUnit.MILLISECONDS)
.connectTimeout(_timeout, TimeUnit.MILLISECONDS);
if (_perMessageDeflate) {
// 开启压缩扩展, 开启 Gzip 压缩
builder.addInterceptor(new CocosGzipRequestInterceptor());
}
KeyStore keyStore = null;
if (url.toLowerCase().startsWith("wss://") && !caFilePath.isEmpty()) {
try {
InputStream caInput = null;
if (caFilePath.startsWith("assets/")) {
caInput = GlobalObject.getContext().getResources().getAssets().open(caFilePath);
} else {
caInput = new FileInputStream(caFilePath);
}
if (caFilePath.toLowerCase().endsWith(".pem")) {
keyStore = CocosWebSocketUtils.GetPEMKeyStore(caInput);
} else {
keyStore = CocosWebSocketUtils.GetCERKeyStore(caInput);
}
} catch (Exception e) {
e.printStackTrace();
String errMsg = e.getMessage();
if (errMsg == null) {
errMsg = "unknown error";
}
synchronized (_wsContext) {
nativeOnError(errMsg, _wsContext.identifier, _wsContext.handlerPtr);
}
return;
}
builder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
Log.d(_TAG, "ca hostname: " + hostname);
HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
return hv.verify(hostname, session);
}
});
}
if (url.toLowerCase().startsWith("wss://") || _tcpNoDelay) {
try {
X509TrustManager trustManager =
CocosWebSocketUtils.GetTrustManager(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
SecureRandom random;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
random = SecureRandom.getInstanceStrong();
} else {
random = SecureRandom.getInstance("SHA1PRNG");
}
sslContext.init(null, new TrustManager[] {trustManager}, random);
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
SSLSocketFactory customSslSocketFactory =
new CocosDelegatingSSLSocketFactory(sslSocketFactory) {
@Override
protected SSLSocket configureSocket(SSLSocket socket)
throws IOException {
socket.setTcpNoDelay(_tcpNoDelay);
// TLSv1.2 is disabled default below API20----
// https://developer.android.com/reference/javax/net/ssl/SSLSocket
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT_WATCH) {
socket.setEnabledProtocols(new String[] {"TLSv1.2"});
}
return socket;
}
};
builder.sslSocketFactory(customSslSocketFactory, trustManager);
} catch (GeneralSecurityException e) {
e.printStackTrace();
String errMsg = e.getMessage();
if (errMsg == null) {
errMsg = "unknown error";
}
synchronized (_wsContext) {
nativeOnError(errMsg, _wsContext.identifier, _wsContext.handlerPtr);
}
return;
}
}
_client = builder.build();
_webSocket = _client.newWebSocket(request, this);
}
private void _close(final int code, final String reason) {
_webSocket.close(code, reason);
// _client.dispatcher().executorService().shutdown();
}
private long _getBufferedAmountID() {
return _webSocket.queueSize();
}
private void output(final String content) {
Log.w(_TAG, content);
}
@Override
public void onOpen(org.cocos2dx.okhttp3.WebSocket _webSocket, Response response) {
output("WebSocket onOpen _client: " + _client);
synchronized (_wsContext) {
nativeOnOpen(response.protocol().toString(),
response.headers().toString(), _wsContext.identifier,
_wsContext.handlerPtr);
}
}
@Override
public void onMessage(org.cocos2dx.okhttp3.WebSocket _webSocket, String text) {
// output("Receiving string msg: " + text);
synchronized (_wsContext) {
nativeOnStringMessage(text, _wsContext.identifier, _wsContext.handlerPtr);
}
}
@Override
public void onMessage(org.cocos2dx.okhttp3.WebSocket _webSocket, ByteString bytes) {
// output("Receiving binary msg");
synchronized (_wsContext) {
nativeOnBinaryMessage(bytes.toByteArray(), _wsContext.identifier,
_wsContext.handlerPtr);
}
}
@Override
public void onClosing(org.cocos2dx.okhttp3.WebSocket _webSocket, int code,
String reason) {
output("Closing : " + code + " / " + reason);
if (_webSocket != null) {
_webSocket.close(code, reason);
}
}
@Override
public void onFailure(org.cocos2dx.okhttp3.WebSocket _webSocket, Throwable t,
Response response) {
String msg = "";
if (t != null) {
msg = t.getMessage() == null ? t.getClass().getSimpleName() : t.getMessage();
}
output("onFailure Error : " + msg);
synchronized (_wsContext) {
nativeOnError(msg, _wsContext.identifier, _wsContext.handlerPtr);
}
}
@Override
public void onClosed(org.cocos2dx.okhttp3.WebSocket _webSocket, int code,
String reason) {
output("onClosed : " + code + " / " + reason);
synchronized (_wsContext) {
nativeOnClosed(code, reason, _wsContext.identifier,
_wsContext.handlerPtr);
}
}
private static native void NativeInit();
private native void nativeOnStringMessage(final String msg, long identifier,
long handler);
private native void nativeOnBinaryMessage(final byte[] msg, long identifier,
long handler);
private native void nativeOnOpen(final String protocol,
final String headerString, long identifier,
long handler);
private native void nativeOnClosed(final int code, final String reason,
long identifier, long handler);
private native void nativeOnError(final String msg, long identifier,
long handler);
}

View File

@@ -0,0 +1,86 @@
package com.cocos.lib.websocket;
import android.util.Base64;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.Arrays;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
class CocosWebSocketUtils {
static X509TrustManager GetTrustManager(KeyStore keyStore)
throws GeneralSecurityException {
String algorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(algorithm);
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 ||
!(trustManagers[0] instanceof X509TrustManager)) {
String prefixErrorMessage = "Unexpected default trust managers:";
throw new IllegalStateException(prefixErrorMessage +
Arrays.toString(trustManagers));
}
return (X509TrustManager)trustManagers[0];
}
static KeyStore GetCERKeyStore(InputStream inputStream)
throws CertificateException, KeyStoreException, IOException,
NoSuchAlgorithmException {
CertificateFactory factory = CertificateFactory.getInstance("X.509");
Certificate certificate = factory.generateCertificate(inputStream);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
keyStore.setCertificateEntry("0", certificate);
return keyStore;
}
static KeyStore GetPEMKeyStore(InputStream inputStream) throws Exception {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
int index = 0;
BufferedReader bufferedReader =
new BufferedReader(new InputStreamReader(inputStream));
String carBegin;
while ((carBegin = bufferedReader.readLine()) != null) {
if (carBegin.contains("BEGIN CERTIFICATE")) {
StringBuilder stringBuilder = new StringBuilder();
while ((carBegin = bufferedReader.readLine()) != null) {
if (carBegin.contains("END CERTIFICATE")) {
String hexString = stringBuilder.toString();
byte[] bytes = Base64.decode(hexString, Base64.DEFAULT);
Certificate certificate = _GenerateCertificateFromDER(bytes);
keyStore.setCertificateEntry(Integer.toString(index++),
certificate);
break;
} else {
stringBuilder.append(carBegin);
}
}
}
}
bufferedReader.close();
if (index == 0) {
throw new IllegalArgumentException("No CERTIFICATE found");
}
return keyStore;
}
private static Certificate _GenerateCertificateFromDER(byte[] certBytes)
throws CertificateException {
CertificateFactory factory = CertificateFactory.getInstance("X.509");
return factory.generateCertificate(new ByteArrayInputStream(certBytes));
}
}