no message
This commit is contained in:
9
cocos/platform/android/java/.classpath
Normal file
9
cocos/platform/android/java/.classpath
Normal 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>
|
||||
33
cocos/platform/android/java/.project
Normal file
33
cocos/platform/android/java/.project
Normal 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>
|
||||
9
cocos/platform/android/java/AndroidManifest.xml
Normal file
9
cocos/platform/android/java/AndroidManifest.xml
Normal 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>
|
||||
83
cocos/platform/android/java/build.xml
Normal file
83
cocos/platform/android/java/build.xml
Normal 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.
BIN
cocos/platform/android/java/libs/game-sdk.jar
Normal file
BIN
cocos/platform/android/java/libs/game-sdk.jar
Normal file
Binary file not shown.
BIN
cocos/platform/android/java/libs/okhttp-3.12.14.jar
Normal file
BIN
cocos/platform/android/java/libs/okhttp-3.12.14.jar
Normal file
Binary file not shown.
BIN
cocos/platform/android/java/libs/okio-1.15.0.jar
Normal file
BIN
cocos/platform/android/java/libs/okio-1.15.0.jar
Normal file
Binary file not shown.
13
cocos/platform/android/java/proguard-project.txt
Normal file
13
cocos/platform/android/java/proguard-project.txt
Normal 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:
|
||||
15
cocos/platform/android/java/project.properties
Executable file
15
cocos/platform/android/java/project.properties
Executable 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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
222
cocos/platform/android/java/src/com/cocos/lib/CocosActivity.java
Normal file
222
cocos/platform/android/java/src/com/cocos/lib/CocosActivity.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
105
cocos/platform/android/java/src/com/cocos/lib/CocosHandler.java
Normal file
105
cocos/platform/android/java/src/com/cocos/lib/CocosHandler.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
435
cocos/platform/android/java/src/com/cocos/lib/CocosHelper.java
Normal file
435
cocos/platform/android/java/src/com/cocos/lib/CocosHelper.java
Normal 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");
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
173
cocos/platform/android/java/src/com/cocos/lib/CocosWebView.java
Executable file
173
cocos/platform/android/java/src/com/cocos/lib/CocosWebView.java
Executable 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);
|
||||
}
|
||||
}
|
||||
310
cocos/platform/android/java/src/com/cocos/lib/CocosWebViewHelper.java
Executable file
310
cocos/platform/android/java/src/com/cocos/lib/CocosWebViewHelper.java
Executable 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
61
cocos/platform/android/java/src/com/cocos/lib/JsbBridge.java
Normal file
61
cocos/platform/android/java/src/com/cocos/lib/JsbBridge.java
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
57
cocos/platform/android/java/src/com/cocos/lib/Utils.java
Normal file
57
cocos/platform/android/java/src/com/cocos/lib/Utils.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user