初始化

This commit is contained in:
SmallMain
2022-06-25 00:23:03 +08:00
commit ef0589e8e5
2264 changed files with 617829 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := cocos2dxandroid_static
LOCAL_MODULE_FILENAME := libcocos2dandroid
LOCAL_SRC_FILES := \
CCDevice-android.cpp \
CCFileUtils-android.cpp \
CCApplication-android.cpp \
CCCanvasRenderingContext2D-android.cpp \
jni/JniImp.cpp \
jni/JniHelper.cpp \
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
LOCAL_C_INCLUDES := $(LOCAL_PATH) \
$(LOCAL_PATH)/.. \
$(LOCAL_PATH)/../.. \
$(LOCAL_PATH)/../../..
LOCAL_EXPORT_LDLIBS := -lGLESv2 \
-lEGL \
-llog \
-landroid
LOCAL_STATIC_LIBRARIES := v8_static
include $(BUILD_STATIC_LIBRARY)

View File

@@ -0,0 +1,307 @@
/****************************************************************************
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.
****************************************************************************/
#include "platform/CCApplication.h"
#include <EGL/egl.h>
#include <cstring>
#include <jni.h>
#include "platform/android/jni/JniImp.h"
#include "platform/android/CCGL-android.h"
#include "base/CCScheduler.h"
#include "base/CCConfiguration.h"
#include "audio/include/AudioEngine.h"
#include "scripting/js-bindings/jswrapper/SeApi.h"
#include "scripting/js-bindings/event/EventDispatcher.h"
#define LOG_APP_TAG "CCApplication_android Debug"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_APP_TAG,__VA_ARGS__)
// IDEA: using ndk-r10c will cause the next function could not be found. It may be a bug of ndk-r10c.
// Here is the workaround method to fix the problem.
#ifdef __aarch64__
extern "C" size_t __ctype_get_mb_cur_max(void)
{
return (size_t) sizeof(wchar_t);
}
#endif
PFNGLGENVERTEXARRAYSOESPROC glGenVertexArraysOESEXT = 0;
PFNGLBINDVERTEXARRAYOESPROC glBindVertexArrayOESEXT = 0;
PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArraysOESEXT = 0;
NS_CC_BEGIN
void Application::updateViewSize(int width, int height)
{
_viewSize.x = width;
_viewSize.y = height;
// handle resize event
Application::getInstance()->getScheduler()->performFunctionInCocosThread([=]() {
EventDispatcher::dispatchResizeEvent(width, height);
});
}
extern "C" {
void Java_org_cocos2dx_lib_Cocos2dxGLSurfaceView_nativeOnSizeChanged(JNIEnv * env, jobject obj, jint width, jint height) {
auto inst = Application::getInstance();
// nativeOnSizeChanged is firstly called before Application initiating.
if (inst != nullptr) {
inst->updateViewSize(width, height);
}
}
void Java_org_cocos2dx_lib_Cocos2dxOrientationHelper_nativeOnOrientationChanged(JNIEnv * env, jobject obj, jint rotation) {
auto inst = Application::getInstance();
// nativeOnSizeChanged is firstly called before Application initiating.
if (inst != nullptr) {
// handle orientation change event
inst->getScheduler()->performFunctionInCocosThread([=]() {
EventDispatcher::dispatchOrientationChangeEvent(rotation);
});
}
}
}
Application* Application::_instance = nullptr;
std::shared_ptr<Scheduler> Application::_scheduler = nullptr;
Application::Application(const std::string& name, int width, int height)
{
Application::_instance = this;
Configuration::getInstance();
_scheduler = std::make_shared<Scheduler>();
PFNGLGENVERTEXARRAYSOESPROC glGenVertexArraysOESEXT = (PFNGLGENVERTEXARRAYSOESPROC)eglGetProcAddress("glGenVertexArraysOES");
PFNGLBINDVERTEXARRAYOESPROC glBindVertexArrayOESEXT = (PFNGLBINDVERTEXARRAYOESPROC)eglGetProcAddress("glBindVertexArrayOES");
PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArraysOESEXT = (PFNGLDELETEVERTEXARRAYSOESPROC)eglGetProcAddress("glDeleteVertexArraysOES");
_renderTexture = new RenderTexture(width, height);
updateViewSize(width, height);
}
Application::~Application()
{
#if USE_AUDIO
AudioEngine::end();
#endif
EventDispatcher::destroy();
se::ScriptEngine::destroyInstance();
delete _renderTexture;
_renderTexture = nullptr;
Application::_instance = nullptr;
}
void Application::start()
{
if(!applicationDidFinishLaunching())
return;
}
void Application::restart()
{
restartJSVM();
}
void Application::end()
{
exitApplication();
}
void Application::setMultitouch(bool /*value*/)
{
}
bool Application::applicationDidFinishLaunching()
{
return true;
}
void Application::onPause()
{
}
void Application::onResume()
{
}
void Application::setPreferredFramesPerSecond(int fps)
{
_fps = fps;
setPreferredFramesPerSecondJNI(_fps);
}
std::string Application::getCurrentLanguageCode() const
{
return getCurrentLanguageCodeJNI();
}
bool Application::isDisplayStats() {
se::AutoHandleScope hs;
se::Value ret;
char commandBuf[100] = "cc.debug.isDisplayStats();";
se::ScriptEngine::getInstance()->evalString(commandBuf, 100, &ret);
return ret.toBoolean();
}
void Application::setDisplayStats(bool isShow) {
se::AutoHandleScope hs;
char commandBuf[100] = {0};
sprintf(commandBuf, "cc.debug.setDisplayStats(%s);", isShow ? "true" : "false");
se::ScriptEngine::getInstance()->evalString(commandBuf);
}
Application::LanguageType Application::getCurrentLanguage() const
{
std::string languageName = getCurrentLanguageJNI();
const char* pLanguageName = languageName.c_str();
LanguageType ret = LanguageType::ENGLISH;
if (0 == strcmp("zh", pLanguageName))
{
ret = LanguageType::CHINESE;
}
else if (0 == strcmp("en", pLanguageName))
{
ret = LanguageType::ENGLISH;
}
else if (0 == strcmp("fr", pLanguageName))
{
ret = LanguageType::FRENCH;
}
else if (0 == strcmp("it", pLanguageName))
{
ret = LanguageType::ITALIAN;
}
else if (0 == strcmp("de", pLanguageName))
{
ret = LanguageType::GERMAN;
}
else if (0 == strcmp("es", pLanguageName))
{
ret = LanguageType::SPANISH;
}
else if (0 == strcmp("ru", pLanguageName))
{
ret = LanguageType::RUSSIAN;
}
else if (0 == strcmp("nl", pLanguageName))
{
ret = LanguageType::DUTCH;
}
else if (0 == strcmp("ko", pLanguageName))
{
ret = LanguageType::KOREAN;
}
else if (0 == strcmp("ja", pLanguageName))
{
ret = LanguageType::JAPANESE;
}
else if (0 == strcmp("hu", pLanguageName))
{
ret = LanguageType::HUNGARIAN;
}
else if (0 == strcmp("pt", pLanguageName))
{
ret = LanguageType::PORTUGUESE;
}
else if (0 == strcmp("ar", pLanguageName))
{
ret = LanguageType::ARABIC;
}
else if (0 == strcmp("nb", pLanguageName))
{
ret = LanguageType::NORWEGIAN;
}
else if (0 == strcmp("pl", pLanguageName))
{
ret = LanguageType::POLISH;
}
else if (0 == strcmp("tr", pLanguageName))
{
ret = LanguageType::TURKISH;
}
else if (0 == strcmp("uk", pLanguageName))
{
ret = LanguageType::UKRAINIAN;
}
else if (0 == strcmp("ro", pLanguageName))
{
ret = LanguageType::ROMANIAN;
}
else if (0 == strcmp("bg", pLanguageName))
{
ret = LanguageType::BULGARIAN;
}
return ret;
}
Application::Platform Application::getPlatform() const
{
return Platform::ANDROIDOS;
}
float Application::getScreenScale() const
{
return 1.f;
}
GLint Application::getMainFBO() const
{
return _mainFBO;
}
void Application::onCreateView(PixelFormat& /*pixelformat*/, DepthFormat& /*depthFormat*/, int& /*multisamplingCount*/)
{
}
bool Application::openURL(const std::string &url)
{
return openURLJNI(url);
}
void Application::copyTextToClipboard(const std::string &text)
{
copyTextToClipboardJNI(text);
}
std::string Application::getSystemVersion()
{
return getSystemVersionJNI();
}
const cocos2d::Vec2& Application::getViewSize() const
{
return _viewSize;
}
NS_CC_END

View File

@@ -0,0 +1,619 @@
#include "platform/CCCanvasRenderingContext2D.h"
#include "base/ccTypes.h"
#include "base/csscolorparser.hpp"
#include "cocos/scripting/js-bindings/jswrapper/SeApi.h"
#include "platform/android/jni/JniHelper.h"
#include "platform/android/jni/JniImp.h"
#include <regex>
#ifndef JCLS_CANVASIMPL
#define JCLS_CANVASIMPL "org/cocos2dx/lib/CanvasRenderingContext2DImpl"
#endif
using namespace cocos2d;
enum class CanvasTextAlign {
LEFT,
CENTER,
RIGHT
};
enum class CanvasTextBaseline {
TOP,
MIDDLE,
BOTTOM
};
class CanvasRenderingContext2DImpl
{
public:
CanvasRenderingContext2DImpl()
{
jobject obj = JniHelper::newObject(JCLS_CANVASIMPL);
_obj = JniHelper::getEnv()->NewGlobalRef(obj);
JniHelper::getEnv()->DeleteLocalRef(obj);
}
~CanvasRenderingContext2DImpl()
{
JniHelper::getEnv()->DeleteGlobalRef(_obj);
}
void recreateBuffer(float w, float h)
{
_bufferWidth = w;
_bufferHeight = h;
if (_bufferWidth < 1.0f || _bufferHeight < 1.0f)
return;
JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "recreateBuffer", w, h);
fillData();
}
void beginPath()
{
JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "beginPath");
}
void closePath()
{
JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "closePath");
}
void moveTo(float x, float y)
{
JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "moveTo", x, y);
}
void lineTo(float x, float y)
{
JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "lineTo", x, y);
}
void stroke()
{
if (_bufferWidth < 1.0f || _bufferHeight < 1.0f)
return;
JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "stroke");
fillData();
}
void fill()
{
if (_bufferWidth < 1.0f || _bufferHeight < 1.0f)
return;
JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "fill");
fillData();
}
void saveContext()
{
JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "saveContext");
}
void restoreContext()
{
JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "restoreContext");
}
void rect(float x, float y, float w, float h)
{
if (_bufferWidth < 1.0f || _bufferHeight < 1.0f)
return;
JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "rect", x, y, w, h);
fillData();
}
void clearRect(float x, float y, float w, float h)
{
if (_bufferWidth < 1.0f || _bufferHeight < 1.0f)
return;
if (x >= _bufferWidth || y >= _bufferHeight)
return;
if (x + w > _bufferWidth)
w = _bufferWidth - x;
if (y + h > _bufferHeight)
h = _bufferHeight - y;
JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "clearRect", x, y, w, h);
fillData();
}
void fillRect(float x, float y, float w, float h)
{
if (_bufferWidth < 1.0f || _bufferHeight < 1.0f)
return;
if (x >= _bufferWidth || y >= _bufferHeight)
return;
if (x + w > _bufferWidth)
w = _bufferWidth - x;
if (y + h > _bufferHeight)
h = _bufferHeight - y;
JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "fillRect", x, y, w, h);
fillData();
}
void fillText(const std::string& text, float x, float y, float maxWidth)
{
if (text.empty() || _bufferWidth < 1.0f || _bufferHeight < 1.0f)
return;
JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "fillText", text, x, y, maxWidth);
fillData();
}
void strokeText(const std::string& text, float x, float y, float maxWidth)
{
if (text.empty() || _bufferWidth < 1.0f || _bufferHeight < 1.0f)
return;
JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "strokeText", text, x, y, maxWidth);
fillData();
}
float measureText(const std::string& text)
{
if (text.empty())
return 0.0f;
return JniHelper::callObjectFloatMethod(_obj, JCLS_CANVASIMPL, "measureText", text);
}
void updateFont(const std::string& fontName, float fontSize, bool bold, bool italic, bool oblique, bool smallCaps)
{
JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "updateFont", fontName, fontSize, bold, italic, oblique, smallCaps);
}
void setLineCap(const std::string& lineCap) {
JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "setLineCap", lineCap);
}
void setLineJoin(const std::string& lineJoin) {
JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "setLineJoin", lineJoin);
}
void setTextAlign(CanvasTextAlign align)
{
JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "setTextAlign", (int)align);
}
void setTextBaseline(CanvasTextBaseline baseline)
{
JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "setTextBaseline", (int)baseline);
}
void setFillStyle(float r, float g, float b, float a)
{
JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "setFillStyle", r, g, b, a);
}
void setStrokeStyle(float r, float g, float b, float a)
{
JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "setStrokeStyle", r, g, b, a);
}
void setLineWidth(float lineWidth)
{
JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "setLineWidth", lineWidth);
}
void _fillImageData(const Data &imageData, float imageWidth, float imageHeight, float offsetX, float offsetY) {
if (_bufferWidth < 1.0f || _bufferHeight < 1.0f)
return;
jbyteArray arr = JniHelper::getEnv()->NewByteArray(imageData.getSize());
JniHelper::getEnv()->SetByteArrayRegion(arr, 0, imageData.getSize(),
(const jbyte *) imageData.getBytes());
JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "_fillImageData", arr, imageWidth,
imageHeight, offsetX, offsetY);
JniHelper::getEnv()->DeleteLocalRef(arr);
fillData();
}
const Data& getDataRef() const
{
return _data;
}
void setPremultiply(bool multiply)
{
_premultiply = multiply;
}
#define CLAMP(V, HI) std::min( (V), (HI) )
void unMultiplyAlpha(unsigned char* ptr, ssize_t size)
{
// Android source data is not premultiplied alpha when API >= 19
// please refer CanvasRenderingContext2DImpl::recreateBuffer(float w, float h)
// in CanvasRenderingContext2DImpl.java
// if (getAndroidSDKInt() >= 19)
// return;
float alpha;
for (int i = 0; i < size; i += 4)
{
alpha = (float)ptr[i + 3];
if (alpha > 0)
{
ptr[i] = CLAMP((int)((float)ptr[i] / alpha * 255), 255);
ptr[i+1] = CLAMP((int)((float)ptr[i+1] / alpha * 255), 255);
ptr[i+2] = CLAMP((int)((float)ptr[i+2] / alpha * 255), 255);
}
}
}
void fillData()
{
jbyteArray arr = JniHelper::callObjectByteArrayMethod(_obj, JCLS_CANVASIMPL, "getDataRef");
jsize len = JniHelper::getEnv()->GetArrayLength(arr);
jbyte* jbarray = (jbyte *)malloc(len * sizeof(jbyte));
JniHelper::getEnv()->GetByteArrayRegion(arr,0,len,jbarray);
if (!_premultiply)
{
unMultiplyAlpha( (unsigned char*) jbarray, len);
}
_data.fastSet((unsigned char*) jbarray, len); //IDEA: DON'T create new jbarray every time.
JniHelper::getEnv()->DeleteLocalRef(arr);
}
private:
jobject _obj = nullptr;
Data _data;
float _bufferWidth = 0.0f;
float _bufferHeight = 0.0f;
bool _premultiply = true;
};
namespace {
void fillRectWithColor(uint8_t* buf, uint32_t totalWidth, uint32_t totalHeight, uint32_t x, uint32_t y, uint32_t width, uint32_t height, uint8_t r, uint8_t g, uint8_t b)
{
assert(x + width <= totalWidth);
assert(y + height <= totalHeight);
uint32_t y0 = totalHeight - (y + height);
uint32_t y1 = totalHeight - y;
uint8_t* p;
for (uint32_t offsetY = y0; offsetY < y1; ++offsetY)
{
for (uint32_t offsetX = x; offsetX < (x + width); ++offsetX)
{
p = buf + (totalWidth * offsetY + offsetX) * 3;
*p++ = r;
*p++ = g;
*p++ = b;
}
}
}
}
NS_CC_BEGIN
CanvasGradient::CanvasGradient()
{
// SE_LOGD("CanvasGradient constructor: %p\n", this);
}
CanvasGradient::~CanvasGradient()
{
// SE_LOGD("CanvasGradient destructor: %p\n", this);
}
void CanvasGradient::addColorStop(float offset, const std::string& color)
{
// SE_LOGD("CanvasGradient::addColorStop: %p\n", this);
}
// CanvasRenderingContext2D
CanvasRenderingContext2D::CanvasRenderingContext2D(float width, float height)
: __width(width)
, __height(height)
{
// SE_LOGD("CanvasRenderingContext2D constructor: %p, width: %f, height: %f\n", this, width, height);
_impl = new CanvasRenderingContext2DImpl();
recreateBufferIfNeeded();
}
CanvasRenderingContext2D::~CanvasRenderingContext2D()
{
// SE_LOGD("CanvasRenderingContext2D destructor: %p\n", this);
delete _impl;
}
void CanvasRenderingContext2D::recreateBufferIfNeeded()
{
if (_isBufferSizeDirty)
{
_isBufferSizeDirty = false;
// SE_LOGD("Recreate buffer %p, w: %f, h:%f\n", this, __width, __height);
_impl->recreateBuffer(__width, __height);
if (_canvasBufferUpdatedCB != nullptr)
_canvasBufferUpdatedCB(_impl->getDataRef());
}
}
void CanvasRenderingContext2D::rect(float x, float y, float width, float height)
{
// SE_LOGD("CanvasRenderingContext2D::rect: %p, %f, %f, %f, %f\n", this, x, y, width, height);
recreateBufferIfNeeded();
_impl->rect(x, y, width, height);
}
void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height)
{
// SE_LOGD("CanvasRenderingContext2D::clearRect: %p, %f, %f, %f, %f\n", this, x, y, width, height);
recreateBufferIfNeeded();
_impl->clearRect(x, y, width, height);
}
void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height)
{
recreateBufferIfNeeded();
_impl->fillRect(x, y, width, height);
if (_canvasBufferUpdatedCB != nullptr)
_canvasBufferUpdatedCB(_impl->getDataRef());
}
void CanvasRenderingContext2D::fillText(const std::string& text, float x, float y, float maxWidth)
{
// SE_LOGD("CanvasRenderingContext2D::fillText: %s, %f, %f, %f\n", text.c_str(), x, y, maxWidth);
if (text.empty())
return;
recreateBufferIfNeeded();
_impl->fillText(text, x, y, maxWidth);
if (_canvasBufferUpdatedCB != nullptr)
_canvasBufferUpdatedCB(_impl->getDataRef());
}
void CanvasRenderingContext2D::strokeText(const std::string& text, float x, float y, float maxWidth)
{
// SE_LOGD("CanvasRenderingContext2D::strokeText: %s, %f, %f, %f\n", text.c_str(), x, y, maxWidth);
if (text.empty())
return;
recreateBufferIfNeeded();
_impl->strokeText(text, x, y, maxWidth);
if (_canvasBufferUpdatedCB != nullptr)
_canvasBufferUpdatedCB(_impl->getDataRef());
}
cocos2d::Size CanvasRenderingContext2D::measureText(const std::string& text)
{
// SE_LOGD("CanvasRenderingContext2D::measureText: %s\n", text.c_str());
return cocos2d::Size(_impl->measureText(text), 0);
}
CanvasGradient* CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1)
{
return nullptr;
}
void CanvasRenderingContext2D::save()
{
_impl->saveContext();
}
void CanvasRenderingContext2D::beginPath()
{
_impl->beginPath();
}
void CanvasRenderingContext2D::closePath()
{
_impl->closePath();
}
void CanvasRenderingContext2D::moveTo(float x, float y)
{
_impl->moveTo(x, y);
}
void CanvasRenderingContext2D::lineTo(float x, float y)
{
_impl->lineTo(x, y);
}
void CanvasRenderingContext2D::stroke()
{
_impl->stroke();
if (_canvasBufferUpdatedCB != nullptr)
_canvasBufferUpdatedCB(_impl->getDataRef());
}
void CanvasRenderingContext2D::fill()
{
_impl->fill();
if (_canvasBufferUpdatedCB != nullptr)
_canvasBufferUpdatedCB(_impl->getDataRef());
}
void CanvasRenderingContext2D::restore()
{
_impl->restoreContext();
}
void CanvasRenderingContext2D::setCanvasBufferUpdatedCallback(const CanvasBufferUpdatedCallback& cb)
{
_canvasBufferUpdatedCB = cb;
}
void CanvasRenderingContext2D::setPremultiply(bool multiply)
{
_impl->setPremultiply(multiply);
}
void CanvasRenderingContext2D::set__width(float width)
{
// SE_LOGD("CanvasRenderingContext2D::set__width: %f\n", width);
__width = width;
_isBufferSizeDirty = true;
recreateBufferIfNeeded();
}
void CanvasRenderingContext2D::set__height(float height)
{
// SE_LOGD("CanvasRenderingContext2D::set__height: %f\n", height);
__height = height;
_isBufferSizeDirty = true;
recreateBufferIfNeeded();
}
void CanvasRenderingContext2D::set_lineWidth(float lineWidth)
{
_lineWidth = lineWidth;
_impl->setLineWidth(lineWidth);
}
void CanvasRenderingContext2D::set_lineJoin(const std::string& lineJoin)
{
if(lineJoin.empty()) return ;
_impl->setLineJoin(lineJoin);
}
void CanvasRenderingContext2D::set_lineCap(const std::string& lineCap)
{
if(lineCap.empty()) return ;
_impl->setLineCap(lineCap);
}
/*
* support format e.g.: "oblique bold small-caps 18px Arial"
* "italic bold small-caps 25px Arial"
* "italic 25px Arial"
* */
void CanvasRenderingContext2D::set_font(const std::string& font)
{
if (_font != font)
{
_font = font;
std::string fontName = "sans-serif";
std::string fontSizeStr = "30";
std::regex re("\\s*((\\d+)([\\.]\\d+)?)px\\s+([^\\r\\n]*)");
std::match_results<std::string::const_iterator> results;
if (std::regex_search(_font.cbegin(), _font.cend(), results, re))
{
fontSizeStr = results[2].str();
// support get font name from `60px American` or `60px "American abc-abc_abc"`
// support get font name contain space,example `times new roman`
// if regex rule that does not conform to the rules,such as Chinese,it defaults to sans-serif
std::match_results<std::string::const_iterator> fontResults;
std::regex fontRe("([\\w\\s-]+|\"[\\w\\s-]+\"$)");
if(std::regex_match(results[4].str(), fontResults, fontRe))
{
fontName = results[4].str();
}
}
float fontSize = atof(fontSizeStr.c_str());
bool isBold = font.find("bold", 0) != std::string::npos;
bool isItalic = font.find("italic", 0) != std::string::npos;
bool isSmallCaps = font.find("small-caps", 0) != std::string::npos;
bool isOblique = font.find("oblique", 0) != std::string::npos;
//font-style: italic, oblique, normal
//font-weight: normal, bold
//font-variant: normal, small-caps
_impl->updateFont(fontName, fontSize, isBold, isItalic, isOblique, isSmallCaps);
}
}
void CanvasRenderingContext2D::set_textAlign(const std::string& textAlign)
{
// SE_LOGD("CanvasRenderingContext2D::set_textAlign: %s\n", textAlign.c_str());
if (textAlign == "left")
{
_impl->setTextAlign(CanvasTextAlign::LEFT);
}
else if (textAlign == "center" || textAlign == "middle")
{
_impl->setTextAlign(CanvasTextAlign::CENTER);
}
else if (textAlign == "right")
{
_impl->setTextAlign(CanvasTextAlign::RIGHT);
}
else
{
assert(false);
}
}
void CanvasRenderingContext2D::set_textBaseline(const std::string& textBaseline)
{
// SE_LOGD("CanvasRenderingContext2D::set_textBaseline: %s\n", textBaseline.c_str());
if (textBaseline == "top")
{
_impl->setTextBaseline(CanvasTextBaseline::TOP);
}
else if (textBaseline == "middle")
{
_impl->setTextBaseline(CanvasTextBaseline::MIDDLE);
}
else if (textBaseline == "bottom" || textBaseline == "alphabetic") //REFINE:, how to deal with alphabetic, currently we handle it as bottom mode.
{
_impl->setTextBaseline(CanvasTextBaseline::BOTTOM);
}
else
{
assert(false);
}
}
void CanvasRenderingContext2D::set_fillStyle(const std::string& fillStyle)
{
CSSColorParser::Color color = CSSColorParser::parse(fillStyle);
_impl->setFillStyle(color.r/255.0f, color.g/255.0f, color.b/255.0f, color.a);
// SE_LOGD("CanvasRenderingContext2D::set_fillStyle: %s, (%d, %d, %d, %f)\n", fillStyle.c_str(), color.r, color.g, color.b, color.a);
}
void CanvasRenderingContext2D::set_strokeStyle(const std::string& strokeStyle)
{
CSSColorParser::Color color = CSSColorParser::parse(strokeStyle);
_impl->setStrokeStyle(color.r/255.0f, color.g/255.0f, color.b/255.0f, color.a);
}
void CanvasRenderingContext2D::set_globalCompositeOperation(const std::string& globalCompositeOperation)
{
// SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
}
void CanvasRenderingContext2D::_fillImageData(const Data& imageData, float imageWidth, float imageHeight, float offsetX, float offsetY)
{
_impl->_fillImageData(imageData, imageWidth, imageHeight, offsetX, offsetY);
if (_canvasBufferUpdatedCB != nullptr)
_canvasBufferUpdatedCB(_impl->getDataRef());
}
// transform
//REFINE:
void CanvasRenderingContext2D::translate(float x, float y)
{
// SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
}
void CanvasRenderingContext2D::scale(float x, float y)
{
// SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
}
void CanvasRenderingContext2D::rotate(float angle)
{
// SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
}
void CanvasRenderingContext2D::transform(float a, float b, float c, float d, float e, float f)
{
// SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
}
void CanvasRenderingContext2D::setTransform(float a, float b, float c, float d, float e, float f)
{
// SE_LOGE("%s isn't implemented!\n", __FUNCTION__);
}
NS_CC_END

View File

@@ -0,0 +1,136 @@
/****************************************************************************
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.
****************************************************************************/
#include "platform/CCPlatformConfig.h"
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
#include "platform/CCDevice.h"
#include <string.h>
#include <android/log.h>
#include <jni.h>
#include "base/ccTypes.h"
#include "platform/android/jni/JniHelper.h"
#include "platform/CCFileUtils.h"
#include "base/ccUTF8.h"
#include "platform/CCApplication.h"
#ifndef JCLS_HELPER
#define JCLS_HELPER "org/cocos2dx/lib/Cocos2dxHelper"
#endif
NS_CC_BEGIN
int Device::getDPI()
{
static int dpi = -1;
if (dpi == -1)
{
dpi = JniHelper::callStaticIntMethod(JCLS_HELPER, "getDPI");
}
return dpi;
}
void Device::setAccelerometerEnabled(bool isEnabled)
{
if (isEnabled)
{
JniHelper::callStaticVoidMethod(JCLS_HELPER, "enableAccelerometer");
}
else
{
JniHelper::callStaticVoidMethod(JCLS_HELPER, "disableAccelerometer");
}
}
void Device::setAccelerometerInterval(float interval)
{
JniHelper::callStaticVoidMethod(JCLS_HELPER, "setAccelerometerInterval", interval);
}
const Device::MotionValue& Device::getDeviceMotionValue()
{
static MotionValue __motionValue;
float* v = JniHelper::callStaticFloatArrayMethod(JCLS_HELPER, "getDeviceMotionValue");
__motionValue.accelerationX = v[0];
__motionValue.accelerationY = v[1];
__motionValue.accelerationZ = v[2];
__motionValue.accelerationIncludingGravityX = v[3];
__motionValue.accelerationIncludingGravityY = v[4];
__motionValue.accelerationIncludingGravityZ = v[5];
__motionValue.rotationRateAlpha = v[6];
__motionValue.rotationRateBeta = v[7];
__motionValue.rotationRateGamma = v[8];
return __motionValue;
}
Device::Rotation Device::getDeviceRotation()
{
int rotation = JniHelper::callStaticIntMethod(JCLS_HELPER, "getDeviceRotation");
return (Device::Rotation)rotation;
}
std::string Device::getDeviceModel()
{
return JniHelper::callStaticStringMethod(JCLS_HELPER, "getDeviceModel");
}
void Device::setKeepScreenOn(bool value)
{
JniHelper::callStaticVoidMethod(JCLS_HELPER, "setKeepScreenOn", value);
}
void Device::vibrate(float duration)
{
JniHelper::callStaticVoidMethod(JCLS_HELPER, "vibrate", duration);
}
float Device::getBatteryLevel()
{
return JniHelper::callStaticFloatMethod(JCLS_HELPER, "getBatteryLevel");
}
Device::NetworkType Device::getNetworkType()
{
return (Device::NetworkType)JniHelper::callStaticIntMethod(JCLS_HELPER, "getNetworkType");
}
cocos2d::Vec4 Device::getSafeAreaEdge()
{
float *data = JniHelper::callStaticFloatArrayMethod(JCLS_HELPER, "getSafeArea");
return cocos2d::Vec4(data[0], data[1], data[2],data[3]);
}
int Device::getDevicePixelRatio()
{
return 1;
}
NS_CC_END
#endif // CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID

View File

@@ -0,0 +1,333 @@
/****************************************************************************
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.
****************************************************************************/
#include "platform/CCPlatformConfig.h"
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
#include "platform/android/CCFileUtils-android.h"
#include "platform/android/jni/JniHelper.h"
#include "platform/android/jni/JniImp.h"
#include "android/asset_manager.h"
#include "android/asset_manager_jni.h"
#include "base/ZipUtils.h"
#include <stdlib.h>
#include <sys/stat.h>
#define LOG_TAG "CCFileUtils-android.cpp"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define ASSETS_FOLDER_NAME "@assets/"
#ifndef JCLS_HELPER
#define JCLS_HELPER "org/cocos2dx/lib/Cocos2dxHelper"
#endif
NS_CC_BEGIN
AAssetManager* FileUtilsAndroid::assetmanager = nullptr;
ZipFile* FileUtilsAndroid::obbfile = nullptr;
void FileUtilsAndroid::setassetmanager(AAssetManager* a) {
if (nullptr == a) {
LOGD("setassetmanager : received unexpected nullptr parameter");
return;
}
cocos2d::FileUtilsAndroid::assetmanager = a;
}
FileUtils* FileUtils::getInstance()
{
if (s_sharedFileUtils == nullptr)
{
s_sharedFileUtils = new FileUtilsAndroid();
if (!s_sharedFileUtils->init())
{
delete s_sharedFileUtils;
s_sharedFileUtils = nullptr;
CCLOG("ERROR: Could not init CCFileUtilsAndroid");
}
}
return s_sharedFileUtils;
}
FileUtilsAndroid::FileUtilsAndroid()
{
}
FileUtilsAndroid::~FileUtilsAndroid()
{
if (obbfile)
{
delete obbfile;
obbfile = nullptr;
}
}
bool FileUtilsAndroid::init()
{
_defaultResRootPath = ASSETS_FOLDER_NAME;
std::string assetsPath(getApkPathJNI());
if (assetsPath.find("/obb/") != std::string::npos)
{
obbfile = new ZipFile(assetsPath);
}
return FileUtils::init();
}
std::string FileUtilsAndroid::getNewFilename(const std::string &filename) const
{
std::string newFileName = FileUtils::getNewFilename(filename);
// ../xxx do not fix this path
auto pos = newFileName.find("../");
if (pos == std::string::npos || pos == 0)
{
return newFileName;
}
std::vector<std::string> v(3);
v.resize(0);
auto change = false;
size_t size = newFileName.size();
size_t idx = 0;
bool noexit = true;
while (noexit)
{
pos = newFileName.find('/', idx);
std::string tmp;
if (pos == std::string::npos)
{
tmp = newFileName.substr(idx, size - idx);
noexit = false;
}else
{
tmp = newFileName.substr(idx, pos - idx + 1);
}
auto t = v.size();
if (t > 0 && v[t-1].compare("../") != 0 &&
(tmp.compare("../") == 0 || tmp.compare("..") == 0))
{
v.pop_back();
change = true;
}else
{
v.push_back(tmp);
}
idx = pos + 1;
}
if (change)
{
newFileName.clear();
for (auto &s : v)
{
newFileName.append(s);
}
}
return newFileName;
}
bool FileUtilsAndroid::isFileExistInternal(const std::string& strFilePath) const
{
if (strFilePath.empty())
{
return false;
}
bool bFound = false;
// Check whether file exists in apk.
if (strFilePath[0] != '/')
{
const char* s = strFilePath.c_str();
// Found "@assets/" at the beginning of the path and we don't want it
if (strFilePath.find(ASSETS_FOLDER_NAME) == 0) s += strlen(ASSETS_FOLDER_NAME);
if (obbfile && obbfile->fileExists(s))
{
bFound = true;
}
else if (FileUtilsAndroid::assetmanager)
{
AAsset* aa = AAssetManager_open(FileUtilsAndroid::assetmanager, s, AASSET_MODE_UNKNOWN);
if (aa)
{
bFound = true;
AAsset_close(aa);
} else {
// CCLOG("[AssetManager] ... in APK %s, found = false!", strFilePath.c_str());
}
}
}
else
{
FILE *fp = fopen(strFilePath.c_str(), "r");
if (fp)
{
bFound = true;
fclose(fp);
}
}
return bFound;
}
bool FileUtilsAndroid::isDirectoryExistInternal(const std::string& dirPath_) const
{
if (dirPath_.empty())
{
return false;
}
std::string dirPath = dirPath_;
if (dirPath[dirPath.length() - 1] == '/')
{
dirPath[dirPath.length() - 1] = '\0';
}
// find absolute path in flash memory
if (dirPath[0] == '/')
{
CCLOG("find in flash memory dirPath(%s)", dirPath.c_str());
struct stat st;
if (stat(dirPath.c_str(), &st) == 0)
{
return S_ISDIR(st.st_mode);
}
}
else
{
// find it in apk's assets dir
// Found "@assets/" at the beginning of the path and we don't want it
CCLOG("find in apk dirPath(%s)", dirPath.c_str());
const char* s = dirPath.c_str();
if (dirPath.find(_defaultResRootPath) == 0)
{
s += _defaultResRootPath.length();
}
if (FileUtilsAndroid::assetmanager)
{
AAssetDir* aa = AAssetManager_openDir(FileUtilsAndroid::assetmanager, s);
if (aa && AAssetDir_getNextFileName(aa))
{
AAssetDir_close(aa);
return true;
}
}
}
return false;
}
bool FileUtilsAndroid::isAbsolutePath(const std::string& strPath) const
{
// On Android, there are two situations for full path.
// 1) Files in APK, e.g. assets/path/path/file.png
// 2) Files not in APK, e.g. /data/data/org.cocos2dx.hellocpp/cache/path/path/file.png, or /sdcard/path/path/file.png.
// So these two situations need to be checked on Android.
if (strPath[0] == '/' || strPath.find(ASSETS_FOLDER_NAME) == 0)
{
return true;
}
return false;
}
FileUtils::Status FileUtilsAndroid::getContents(const std::string& filename, ResizableBuffer* buffer)
{
if (filename.empty())
return FileUtils::Status::NotExists;
std::string fullPath = fullPathForFilename(filename);
if (fullPath.empty())
return FileUtils::Status::NotExists;
if (fullPath[0] == '/')
return FileUtils::getContents(fullPath, buffer);
std::string relativePath;
size_t position = fullPath.find(ASSETS_FOLDER_NAME);
if (0 == position) {
// "@assets/" is at the beginning of the path and we don't want it
relativePath += fullPath.substr(strlen(ASSETS_FOLDER_NAME));
} else {
relativePath = fullPath;
}
if (obbfile)
{
if (obbfile->getFileData(relativePath, buffer))
return FileUtils::Status::OK;
}
if (nullptr == assetmanager) {
LOGD("... FileUtilsAndroid::assetmanager is nullptr");
return FileUtils::Status::NotInitialized;
}
AAsset* asset = AAssetManager_open(assetmanager, relativePath.data(), AASSET_MODE_UNKNOWN);
if (nullptr == asset) {
LOGD("asset (%s) is nullptr", filename.c_str());
return FileUtils::Status::OpenFailed;
}
auto size = AAsset_getLength(asset);
buffer->resize(size);
int readsize = AAsset_read(asset, buffer->buffer(), size);
AAsset_close(asset);
if (readsize < size) {
if (readsize >= 0)
buffer->resize(readsize);
return FileUtils::Status::ReadFailed;
}
return FileUtils::Status::OK;
}
std::string FileUtilsAndroid::getWritablePath() const
{
// Fix for Nexus 10 (Android 4.2 multi-user environment)
// the path is retrieved through Java Context.getCacheDir() method
std::string dir("");
std::string tmp = JniHelper::callStaticStringMethod(JCLS_HELPER, "getWritablePath");
if (tmp.length() > 0)
{
dir.append(tmp).append("/");
return dir;
}
else
{
return "";
}
}
NS_CC_END
#endif // CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID

View File

@@ -0,0 +1,90 @@
/****************************************************************************
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.
****************************************************************************/
#ifndef __CC_FILEUTILS_ANDROID_H__
#define __CC_FILEUTILS_ANDROID_H__
#include "platform/CCPlatformConfig.h"
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
#include "platform/CCFileUtils.h"
#include "base/ccMacros.h"
#include "base/ccTypes.h"
#include <string>
#include <vector>
#include "jni.h"
#include "android/asset_manager.h"
NS_CC_BEGIN
class ZipFile;
/**
* @addtogroup platform
* @{
*/
//! @brief Helper class to handle file operations
class CC_DLL FileUtilsAndroid : public FileUtils
{
friend class FileUtils;
public:
FileUtilsAndroid();
/**
* @js NA
* @lua NA
*/
virtual ~FileUtilsAndroid();
static void setassetmanager(AAssetManager* a);
static AAssetManager* getAssetManager() { return assetmanager; }
static ZipFile* getObbFile() { return obbfile; }
/* override functions */
bool init() override;
virtual std::string getNewFilename(const std::string &filename) const override;
virtual FileUtils::Status getContents(const std::string& filename, ResizableBuffer* buffer) override;
virtual std::string getWritablePath() const override;
virtual bool isAbsolutePath(const std::string& strPath) const override;
private:
virtual bool isFileExistInternal(const std::string& strFilePath) const override;
virtual bool isDirectoryExistInternal(const std::string& dirPath) const override;
static AAssetManager* assetmanager;
static ZipFile* obbfile;
};
// end of platform group
/// @}
NS_CC_END
#endif // CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
#endif // __CC_FILEUTILS_ANDROID_H__

View File

@@ -0,0 +1,79 @@
/****************************************************************************
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.
****************************************************************************/
#ifndef __CCGL_H__
#define __CCGL_H__
#include "platform/CCPlatformConfig.h"
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
#define glClearDepth glClearDepthf
#define glDeleteVertexArrays glDeleteVertexArraysOES
#define glGenVertexArrays glGenVertexArraysOES
#define glBindVertexArray glBindVertexArrayOES
#define glMapBuffer glMapBufferOES
#define glUnmapBuffer glUnmapBufferOES
#define glTexImage3D glTexImage3DOES
#define glCompressedTexImage3D glCompressedTexImage3DOES
#define glCompressedTexSubImage3D glCompressedTexSubImage3DOES
#define glTexSubImage3D glTexSubImage3DOES
#define glDepthRange glDepthRangef
#define glSubImage3D glSubImage3DOES
#define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_OES
#define GL_WRITE_ONLY GL_WRITE_ONLY_OES
// GL_GLEXT_PROTOTYPES isn't defined in glplatform.h on android ndk r7
// we manually define it here
#include <GLES2/gl2platform.h>
#ifndef GL_GLEXT_PROTOTYPES
#define GL_GLEXT_PROTOTYPES 1
#endif
// normal process
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
// gl2.h doesn't define GLchar on Android
typedef char GLchar;
// android defines GL_BGRA_EXT but not GL_BRGA
#ifndef GL_BGRA
#define GL_BGRA 0x80E1
#endif
//declare here while define in EGLView_android.cpp
extern PFNGLGENVERTEXARRAYSOESPROC glGenVertexArraysOESEXT;
extern PFNGLBINDVERTEXARRAYOESPROC glBindVertexArrayOESEXT;
extern PFNGLDELETEVERTEXARRAYSOESPROC glDeleteVertexArraysOESEXT;
#define glGenVertexArraysOES glGenVertexArraysOESEXT
#define glBindVertexArrayOES glBindVertexArrayOESEXT
#define glDeleteVertexArraysOES glDeleteVertexArraysOESEXT
#endif // CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
#endif // __CCGL_H__

View File

@@ -0,0 +1,68 @@
/****************************************************************************
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.
****************************************************************************/
#ifndef __CCPLATFORMDEFINE_H__
#define __CCPLATFORMDEFINE_H__
#include "platform/CCPlatformConfig.h"
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
#include <android/log.h>
#define CC_DLL
#define CC_NO_MESSAGE_PSEUDOASSERT(cond) \
if (!(cond)) { \
__android_log_print(ANDROID_LOG_ERROR, \
"assert", \
"%s function:%s line:%d", \
__FILE__, __FUNCTION__, __LINE__); \
}
#define CC_MESSAGE_PSEUDOASSERT(cond, msg) \
if (!(cond)) { \
__android_log_print(ANDROID_LOG_ERROR, \
"assert", \
"file:%s function:%s line:%d, %s", \
__FILE__, __FUNCTION__, __LINE__, msg); \
}
#define CC_ASSERT(cond) CC_NO_MESSAGE_PSEUDOASSERT(cond)
#define CC_UNUSED_PARAM(unusedparam) (void)unusedparam
/* Define NULL pointer value */
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
#endif // CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
#endif /* __CCPLATFORMDEFINE_H__*/

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,20 @@
# 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:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

View File

@@ -0,0 +1,510 @@
/****************************************************************************
* 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 org.cocos2dx.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.nio.ByteBuffer;
import java.nio.ByteOrder;
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 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 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);
String key = fontName;
if (enableBold) {
key += "-Bold";
paint.setFakeBoldText(true);
}
if (enableItalic) {
key += "-Italic";
}
Typeface typeFace;
if (sTypefaceCache.containsKey(key)) {
typeFace = sTypefaceCache.get(key);
} else {
int style = Typeface.NORMAL;
if (enableBold && enableItalic) {
style = Typeface.BOLD_ITALIC;
} else if (enableBold) {
style = Typeface.BOLD;
} else if (enableItalic) {
style = Typeface.ITALIC;
}
typeFace = Typeface.create(fontName, style);
}
paint.setTypeface(typeFace);
if(obliqueFont) {
paint.setTextSkewX(_sApproximatingOblique);
}
if(smallCapsFontVariant && Build.VERSION.SDK_INT >= 21) {
Cocos2dxReflectionHelper.<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) {
// Cocos2dxReflectionHelper.<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 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();
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();
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 float measureText(String text) {
createTextPaintIfNeeded();
float ret = mTextPaint.measureText(text);
// Log.d(TAG, "measureText: " + text + ", return: " + ret);
return ret;
}
private Size measureTextReturnSize(String text) {
createTextPaintIfNeeded();
Paint.FontMetrics fm = mTextPaint.getFontMetrics();
// Use descent & ascent for clipping the transparent region.
// So don't use bottom & top which will make text be cut.
return new Size(measureText(text), fm.descent - fm.ascent);
}
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(float r, float g, float b, float a) {
// Log.d(TAG, "setFillStyle: " + r + ", " + g + ", " + b + ", " + a);
mFillStyleR = (int)(r * 255.0f);
mFillStyleG = (int)(g * 255.0f);
mFillStyleB = (int)(b * 255.0f);
mFillStyleA = (int)(a * 255.0f);
}
private void setStrokeStyle(float r, float g, float b, float a) {
// Log.d(TAG, "setStrokeStyle: " + r + ", " + g + ", " + b + ", " + a);
mStrokeStyleR = (int)(r * 255.0f);
mStrokeStyleG = (int)(g * 255.0f);
mStrokeStyleB = (int)(b * 255.0f);
mStrokeStyleA = (int)(a * 255.0f);
}
private void setLineWidth(float lineWidth) {
mLineWidth = lineWidth;
}
private void _fillImageData(byte[] 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)
r = ((int)imageData[4 * i + 0]) & 0xff;
g = ((int)imageData[4 * i + 1]) & 0xff;
b = ((int)imageData[4 * i + 2]) & 0xff;
a = ((int)imageData[4 * i + 3]) & 0xff;
fillColors[i] = (a & 0xff) << 24 | (r & 0xff) << 16 | (g & 0xff) << 8 | (b & 0xff);
}
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);
Size textSize = measureTextReturnSize(text);
// Log.d(TAG,"textSize: " + textSize.width + ", " + textSize.height);
if (mTextAlign == TEXT_ALIGN_CENTER)
{
ret.x -= textSize.width / 2;
}
else if (mTextAlign == TEXT_ALIGN_RIGHT)
{
ret.x -= textSize.width;
}
if (mTextBaseline == TEXT_BASELINE_TOP)
{
ret.y += textSize.height;
}
else if (mTextBaseline == TEXT_BASELINE_MIDDLE)
{
ret.y += textSize.height / 2;
}
return ret;
}
private byte[] getDataRef() {
// Log.d(TAG, "this: " + this + ", getDataRef ...");
if (mBitmap != null) {
final int len = mBitmap.getWidth() * mBitmap.getHeight() * 4;
final byte[] pixels = new byte[len];
final ByteBuffer buf = ByteBuffer.wrap(pixels);
buf.order(ByteOrder.nativeOrder());
mBitmap.copyPixelsToBuffer(buf);
return pixels;
}
Log.e(TAG, "getDataRef return null");
return null;
}
}

View File

@@ -0,0 +1,147 @@
/****************************************************************************
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 org.cocos2dx.lib;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
public class Cocos2dxAccelerometer implements SensorEventListener {
// ===========================================================
// Constants
// ===========================================================
private static final String TAG = Cocos2dxAccelerometer.class.getSimpleName();
// ===========================================================
// Fields
// ===========================================================
private final Context mContext;
private final SensorManager mSensorManager;
private final Sensor mAcceleration;
private final Sensor mAccelerationIncludingGravity;
private final Sensor mGyroscope;
private int mSamplingPeriodUs = SensorManager.SENSOR_DELAY_GAME;
class Acceleration {
public float x = 0.0f;
public float y = 0.0f;
public float z = 0.0f;
}
class RotationRate {
public float alpha = 0.0f;
public float beta = 0.0f;
public float gamma = 0.0f;
}
class DeviceMotionEvent {
public Acceleration acceleration = new Acceleration();
public Acceleration accelerationIncludingGravity = new Acceleration();
public RotationRate rotationRate = new RotationRate();
}
private DeviceMotionEvent mDeviceMotionEvent = new DeviceMotionEvent();
// ===========================================================
// Constructors
// ===========================================================
public Cocos2dxAccelerometer(final Context context) {
mContext = context;
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);
}
// ===========================================================
// Getter & Setter
// ===========================================================
public void enable() {
mSensorManager.registerListener(this, mAcceleration, mSamplingPeriodUs);
mSensorManager.registerListener(this, mAccelerationIncludingGravity, mSamplingPeriodUs);
mSensorManager.registerListener(this, mGyroscope, mSamplingPeriodUs);
}
public void disable() {
this.mSensorManager.unregisterListener(this);
}
public void setInterval(float interval) {
if (android.os.Build.VERSION.SDK_INT >= 11) {
mSamplingPeriodUs = (int) (interval * 1000000);
}
disable();
enable();
}
public DeviceMotionEvent getDeviceMotionEvent() {
return mDeviceMotionEvent;
}
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
@Override
public void onSensorChanged(final SensorEvent sensorEvent) {
int type = sensorEvent.sensor.getType();
if (type == Sensor.TYPE_ACCELEROMETER) {
mDeviceMotionEvent.accelerationIncludingGravity.x = sensorEvent.values[0];
mDeviceMotionEvent.accelerationIncludingGravity.y = sensorEvent.values[1];
mDeviceMotionEvent.accelerationIncludingGravity.z = sensorEvent.values[2];
}
else if (type == Sensor.TYPE_LINEAR_ACCELERATION) {
mDeviceMotionEvent.acceleration.x = sensorEvent.values[0];
mDeviceMotionEvent.acceleration.y = sensorEvent.values[1];
mDeviceMotionEvent.acceleration.z = sensorEvent.values[2];
}
else if (type == Sensor.TYPE_GYROSCOPE) {
// The unit is rad/s, need to be converted to deg/s
mDeviceMotionEvent.rotationRate.alpha = (float)Math.toDegrees(sensorEvent.values[0]);
mDeviceMotionEvent.rotationRate.beta = (float)Math.toDegrees(sensorEvent.values[1]);
mDeviceMotionEvent.rotationRate.gamma = (float)Math.toDegrees(sensorEvent.values[2]);
}
}
@Override
public void onAccuracyChanged(final Sensor sensor, final int accuracy) {
}
// ===========================================================
// Methods
// Native method called from Cocos2dxGLSurfaceView (To be in the same thread)
// ===========================================================
public static native void onSensorChanged(final float x, final float y, final float z, final long timestamp);
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
}

View File

@@ -0,0 +1,652 @@
/****************************************************************************
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 org.cocos2dx.lib;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.media.AudioManager;
import android.opengl.GLSurfaceView;
import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.preference.PreferenceManager.OnActivityResultListener;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.cocos2dx.lib.Cocos2dxHelper.Cocos2dxHelperListener;
import java.lang.reflect.Field;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
public abstract class Cocos2dxActivity extends Activity implements Cocos2dxHelperListener {
// ===========================================================
// Constants
// ===========================================================
private final static String TAG = Cocos2dxActivity.class.getSimpleName();
// ===========================================================
// Fields
// ===========================================================
private static Cocos2dxActivity sContext = null;
protected FrameLayout mFrameLayout = null;
private Cocos2dxGLSurfaceView mGLSurfaceView = null;
private int[] mGLContextAttrs = null;
private Cocos2dxHandler mHandler = null;
private Cocos2dxVideoHelper mVideoHelper = null;
private Cocos2dxWebViewHelper mWebViewHelper = null;
private boolean hasFocus = false;
private Cocos2dxEditBox mEditBox = null;
private boolean gainAudioFocus = false;
private boolean paused = true;
// DEBUG VIEW BEGIN
private LinearLayout mLinearLayoutForDebugView;
private TextView mFPSTextView;
private TextView mJSBInvocationTextView;
private TextView mGLOptModeTextView;
private TextView mGameInfoTextView_0;
private TextView mGameInfoTextView_1;
private TextView mGameInfoTextView_2;
// DEBUG VIEW END
private Cocos2dxOrientationHelper mCocos2dxOrientationHelper = null;
// ===========================================================
// Inner class
// ===========================================================
public class Cocos2dxEGLConfigChooser implements GLSurfaceView.EGLConfigChooser {
protected int[] configAttribs;
public Cocos2dxEGLConfigChooser(int redSize, int greenSize, int blueSize, int alphaSize, int depthSize, int stencilSize)
{
configAttribs = new int[] {redSize, greenSize, blueSize, alphaSize, depthSize, stencilSize};
}
public Cocos2dxEGLConfigChooser(int[] attribs)
{
configAttribs = attribs;
}
private int findConfigAttrib(EGL10 egl, EGLDisplay display,
EGLConfig config, int attribute, int defaultValue) {
int[] value = new int[1];
if (egl.eglGetConfigAttrib(display, config, attribute, value)) {
return value[0];
}
return defaultValue;
}
class ConfigValue implements Comparable<ConfigValue> {
public EGLConfig config = null;
public int[] configAttribs = null;
public int value = 0;
private void calcValue() {
// depth factor 29bit and [6,12)bit
if (configAttribs[4] > 0) {
value = value + (1 << 29) + ((configAttribs[4]%64) << 6);
}
// stencil factor 28bit and [0, 6)bit
if (configAttribs[5] > 0) {
value = value + (1 << 28) + ((configAttribs[5]%64));
}
// alpha factor 30bit and [24, 28)bit
if (configAttribs[3] > 0) {
value = value + (1 << 30) + ((configAttribs[3]%16) << 24);
}
// green factor [20, 24)bit
if (configAttribs[1] > 0) {
value = value + ((configAttribs[1]%16) << 20);
}
// blue factor [16, 20)bit
if (configAttribs[2] > 0) {
value = value + ((configAttribs[2]%16) << 16);
}
// red factor [12, 16)bit
if (configAttribs[0] > 0) {
value = value + ((configAttribs[0]%16) << 12);
}
}
public ConfigValue(int[] attribs) {
configAttribs = attribs;
calcValue();
}
public ConfigValue(EGL10 egl, EGLDisplay display, EGLConfig config) {
this.config = config;
configAttribs = new int[6];
configAttribs[0] = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);
configAttribs[1] = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
configAttribs[2] = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
configAttribs[3] = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
configAttribs[4] = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0);
configAttribs[5] = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0);
calcValue();
}
@Override
public int compareTo(ConfigValue another) {
if (value < another.value) {
return -1;
} else if (value > another.value) {
return 1;
} else {
return 0;
}
}
@Override
public String toString() {
return "{ color: " + configAttribs[3] + configAttribs[2] + configAttribs[1] + configAttribs[0] +
"; depth: " + configAttribs[4] + "; stencil: " + configAttribs[5] + ";}";
}
}
@Override
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display)
{
int[] EGLattribs = {
EGL10.EGL_RED_SIZE, configAttribs[0],
EGL10.EGL_GREEN_SIZE, configAttribs[1],
EGL10.EGL_BLUE_SIZE, configAttribs[2],
EGL10.EGL_ALPHA_SIZE, configAttribs[3],
EGL10.EGL_DEPTH_SIZE, configAttribs[4],
EGL10.EGL_STENCIL_SIZE,configAttribs[5],
EGL10.EGL_RENDERABLE_TYPE, 4, //EGL_OPENGL_ES2_BIT
EGL10.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] numConfigs = new int[1];
boolean eglChooseResult = egl.eglChooseConfig(display, EGLattribs, configs, 1, numConfigs);
if (eglChooseResult && numConfigs[0] > 0)
{
return configs[0];
}
// there's no config match the specific configAttribs, we should choose a closest one
int[] EGLV2attribs = {
EGL10.EGL_RENDERABLE_TYPE, 4, //EGL_OPENGL_ES2_BIT
EGL10.EGL_NONE
};
eglChooseResult = egl.eglChooseConfig(display, EGLV2attribs, null, 0, numConfigs);
if(eglChooseResult && numConfigs[0] > 0) {
int num = numConfigs[0];
ConfigValue[] cfgVals = new ConfigValue[num];
// convert all config to ConfigValue
configs = new EGLConfig[num];
egl.eglChooseConfig(display, EGLV2attribs, configs, num, numConfigs);
for (int i = 0; i < num; ++i) {
cfgVals[i] = new ConfigValue(egl, display, configs[i]);
}
ConfigValue e = new ConfigValue(configAttribs);
// bin search
int lo = 0;
int hi = num;
int mi;
while (lo < hi - 1) {
mi = (lo + hi) / 2;
if (e.compareTo(cfgVals[mi]) < 0) {
hi = mi;
} else {
lo = mi;
}
}
if (lo != num - 1) {
lo = lo + 1;
}
Log.w("cocos2d", "Can't find EGLConfig match: " + e + ", instead of closest one:" + cfgVals[lo]);
return cfgVals[lo].config;
}
Log.e(DEVICE_POLICY_SERVICE, "Can not select an EGLConfig for rendering.");
return null;
}
}
// ===========================================================
// Member methods
// ===========================================================
public Cocos2dxGLSurfaceView getGLSurfaceView(){
return mGLSurfaceView;
}
public static Context getContext() {
return sContext;
}
public void init() {
ViewGroup.LayoutParams frameLayoutParams =
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
mFrameLayout = new FrameLayout(this);
mFrameLayout.setLayoutParams(frameLayoutParams);
Cocos2dxRenderer renderer = this.addSurfaceView();
this.addDebugInfo(renderer);
// Should create EditBox after adding SurfaceView, or EditBox will be hidden by SurfaceView.
mEditBox = new Cocos2dxEditBox(this, mFrameLayout);
// Set frame layout as the content view
setContentView(mFrameLayout);
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 e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
mCocos2dxOrientationHelper = new Cocos2dxOrientationHelper(this);
}
public void setKeepScreenOn(boolean value) {
final boolean newValue = value;
runOnUiThread(new Runnable() {
@Override
public void run() {
mGLSurfaceView.setKeepScreenOn(newValue);
}
});
}
public void setEnableAudioFocusGain(boolean value) {
if(gainAudioFocus != value) {
if(!paused) {
if (value)
Cocos2dxAudioFocusManager.registerAudioFocusListener(this);
else
Cocos2dxAudioFocusManager.unregisterAudioFocusListener(this);
}
gainAudioFocus = value;
}
}
public Cocos2dxGLSurfaceView onCreateView() {
Cocos2dxGLSurfaceView glSurfaceView = new Cocos2dxGLSurfaceView(this);
//this line is need on some device if we specify an alpha bits
if(this.mGLContextAttrs[3] > 0) glSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
Cocos2dxEGLConfigChooser chooser = new Cocos2dxEGLConfigChooser(this.mGLContextAttrs);
glSurfaceView.setEGLConfigChooser(chooser);
return glSurfaceView;
}
// ===========================================================
// Override functions
// ===========================================================
@Override
protected void onCreate(final Bundle savedInstanceState) {
Log.d(TAG, "Cocos2dxActivity onCreate: " + this + ", savedInstanceState: " + savedInstanceState);
super.onCreate(savedInstanceState);
// Workaround in https://stackoverflow.com/questions/16283079/re-launch-of-activity-on-home-button-but-only-the-first-time/16447508
if (!isTaskRoot()) {
// Android launched another instance of the root activity into an existing task
// so just quietly finish and go away, dropping the user back into the activity
// at the top of the stack (ie: the last state of this task)
finish();
Log.w(TAG, "[Workaround] Ignore the activity started from icon!");
return;
}
Utils.setActivity(this);
Utils.hideVirtualButton();
Cocos2dxHelper.registerBatteryLevelReceiver(this);
onLoadNativeLibraries();
sContext = this;
this.mHandler = new Cocos2dxHandler(this);
Cocos2dxHelper.init(this);
CanvasRenderingContext2DImpl.init(this);
this.mGLContextAttrs = getGLContextAttrs();
this.init();
if (mVideoHelper == null) {
mVideoHelper = new Cocos2dxVideoHelper(this, mFrameLayout);
}
if(mWebViewHelper == null){
mWebViewHelper = new Cocos2dxWebViewHelper(mFrameLayout);
}
Window window = this.getWindow();
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
this.setVolumeControlStream(AudioManager.STREAM_MUSIC);
}
@Override
protected void onResume() {
Log.d(TAG, "onResume()");
paused = false;
super.onResume();
if(gainAudioFocus)
Cocos2dxAudioFocusManager.registerAudioFocusListener(this);
Utils.hideVirtualButton();
resumeIfHasFocus();
mCocos2dxOrientationHelper.onResume();
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
Log.d(TAG, "onWindowFocusChanged() hasFocus=" + hasFocus);
super.onWindowFocusChanged(hasFocus);
this.hasFocus = hasFocus;
resumeIfHasFocus();
}
private void resumeIfHasFocus() {
if(hasFocus && !paused) {
Utils.hideVirtualButton();
Cocos2dxHelper.onResume();
mGLSurfaceView.onResume();
}
}
@Override
protected void onPause() {
Log.d(TAG, "onPause()");
paused = true;
super.onPause();
if(gainAudioFocus)
Cocos2dxAudioFocusManager.unregisterAudioFocusListener(this);
Cocos2dxHelper.onPause();
mGLSurfaceView.onPause();
mCocos2dxOrientationHelper.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
// Workaround in https://stackoverflow.com/questions/16283079/re-launch-of-activity-on-home-button-but-only-the-first-time/16447508
if (!isTaskRoot()) {
return;
}
if(gainAudioFocus)
Cocos2dxAudioFocusManager.unregisterAudioFocusListener(this);
Cocos2dxHelper.unregisterBatteryLevelReceiver(this);;
CanvasRenderingContext2DImpl.destroy();
Log.d(TAG, "Cocos2dxActivity onDestroy: " + this + ", mGLSurfaceView" + mGLSurfaceView);
if (mGLSurfaceView != null) {
Cocos2dxHelper.terminateProcess();
}
}
@Override
public void showDialog(final String pTitle, final String pMessage) {
Message msg = new Message();
msg.what = Cocos2dxHandler.HANDLER_SHOW_DIALOG;
msg.obj = new Cocos2dxHandler.DialogMessage(pTitle, pMessage);
this.mHandler.sendMessage(msg);
}
@Override
public void runOnGLThread(final Runnable runnable) {
this.mGLSurfaceView.queueEvent(runnable);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
for (OnActivityResultListener listener : Cocos2dxHelper.getOnActivityResultListeners()) {
listener.onActivityResult(requestCode, resultCode, data);
}
super.onActivityResult(requestCode, resultCode, data);
}
// ===========================================================
// Protected and private methods
// ===========================================================
protected void onLoadNativeLibraries() {
try {
ApplicationInfo ai = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
Bundle bundle = ai.metaData;
String libName = bundle.getString("android.app.lib_name");
System.loadLibrary(libName);
} catch (Exception e) {
e.printStackTrace();
}
}
private Cocos2dxRenderer addSurfaceView() {
this.mGLSurfaceView = this.onCreateView();
this.mGLSurfaceView.setPreserveEGLContextOnPause(true);
// Should set to transparent, or it will hide EditText
// https://stackoverflow.com/questions/2978290/androids-edittext-is-hidden-when-the-virtual-keyboard-is-shown-and-a-surfacevie
mGLSurfaceView.setBackgroundColor(Color.TRANSPARENT);
// Switch to supported OpenGL (ARGB888) mode on emulator
if (isAndroidEmulator())
this.mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
Cocos2dxRenderer renderer = new Cocos2dxRenderer();
this.mGLSurfaceView.setCocos2dxRenderer(renderer);
mFrameLayout.addView(this.mGLSurfaceView);
return renderer;
}
private void addDebugInfo(Cocos2dxRenderer renderer) {
LinearLayout.LayoutParams linearLayoutParam = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
linearLayoutParam.setMargins(30, 0, 0, 0);
Cocos2dxHelper.setOnGameInfoUpdatedListener(new Cocos2dxHelper.OnGameInfoUpdatedListener() {
@Override
public void onFPSUpdated(float fps) {
Cocos2dxActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
if (mFPSTextView != null) {
mFPSTextView.setText("FPS: " + (int)Math.ceil(fps));
}
}
});
}
@Override
public void onJSBInvocationCountUpdated(int count) {
Cocos2dxActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
if (mJSBInvocationTextView != null) {
mJSBInvocationTextView.setText("JSB: " + count);
}
}
});
}
@Override
public void onOpenDebugView() {
Cocos2dxActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
if (mLinearLayoutForDebugView != null || mFrameLayout == null) {
Log.e(TAG, "onOpenDebugView: failed!");
return;
}
mLinearLayoutForDebugView = new LinearLayout(Cocos2dxActivity.this);
mLinearLayoutForDebugView.setOrientation(LinearLayout.VERTICAL);
mFrameLayout.addView(mLinearLayoutForDebugView);
mFPSTextView = new TextView(Cocos2dxActivity.this);
mFPSTextView.setBackgroundColor(Color.RED);
mFPSTextView.setTextColor(Color.WHITE);
mLinearLayoutForDebugView.addView(mFPSTextView, linearLayoutParam);
mJSBInvocationTextView = new TextView(Cocos2dxActivity.this);
mJSBInvocationTextView.setBackgroundColor(Color.GREEN);
mJSBInvocationTextView.setTextColor(Color.WHITE);
mLinearLayoutForDebugView.addView(mJSBInvocationTextView, linearLayoutParam);
mGLOptModeTextView = new TextView(Cocos2dxActivity.this);
mGLOptModeTextView.setBackgroundColor(Color.BLUE);
mGLOptModeTextView.setTextColor(Color.WHITE);
mGLOptModeTextView.setText("GL Opt: Enabled");
mLinearLayoutForDebugView.addView(mGLOptModeTextView, linearLayoutParam);
mGameInfoTextView_0 = new TextView(Cocos2dxActivity.this);
mGameInfoTextView_0.setBackgroundColor(Color.RED);
mGameInfoTextView_0.setTextColor(Color.WHITE);
mLinearLayoutForDebugView.addView(mGameInfoTextView_0, linearLayoutParam);
mGameInfoTextView_1 = new TextView(Cocos2dxActivity.this);
mGameInfoTextView_1.setBackgroundColor(Color.GREEN);
mGameInfoTextView_1.setTextColor(Color.WHITE);
mLinearLayoutForDebugView.addView(mGameInfoTextView_1, linearLayoutParam);
mGameInfoTextView_2 = new TextView(Cocos2dxActivity.this);
mGameInfoTextView_2.setBackgroundColor(Color.BLUE);
mGameInfoTextView_2.setTextColor(Color.WHITE);
mLinearLayoutForDebugView.addView(mGameInfoTextView_2, linearLayoutParam);
}
});
renderer.showFPS();
}
@Override
public void onDisableBatchGLCommandsToNative() {
Cocos2dxActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
if (mGLOptModeTextView != null) {
mGLOptModeTextView.setText("GL Opt: Disabled");
}
}
});
}
@Override
public void onGameInfoUpdated_0(final String text) {
Cocos2dxActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
if (mGameInfoTextView_0 != null) {
mGameInfoTextView_0.setText(text);
}
}
});
}
@Override
public void onGameInfoUpdated_1(String text) {
Cocos2dxActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
if (mGameInfoTextView_1 != null) {
mGameInfoTextView_1.setText(text);
}
}
});
}
@Override
public void onGameInfoUpdated_2(String text) {
Cocos2dxActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
if (mGameInfoTextView_2 != null) {
mGameInfoTextView_2.setText(text);
}
}
});
}
});
}
private final static boolean isAndroidEmulator() {
String model = Build.MODEL;
Log.d(TAG, "model=" + model);
String product = Build.PRODUCT;
Log.d(TAG, "product=" + product);
boolean isEmulator = false;
if (product != null) {
isEmulator = product.equals("sdk") || product.contains("_sdk") || product.contains("sdk_");
}
Log.d(TAG, "isEmulator=" + isEmulator);
return isEmulator;
}
// ===========================================================
// Native methods
// ===========================================================
//native method,call GLViewImpl::getGLContextAttrs() to get the OpenGL ES context attributions
private static native int[] getGLContextAttrs();
}

View File

@@ -0,0 +1,127 @@
/****************************************************************************
* 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 org.cocos2dx.lib;
import android.content.Context;
import android.media.AudioManager;
import android.util.Log;
class Cocos2dxAudioFocusManager {
private final static String TAG = "AudioFocusManager";
// Audio focus values synchronized with which in cocos/platform/android/javaactivity-android.cpp
private final static int AUDIOFOCUS_GAIN = 0;
private final static int AUDIOFOCUS_LOST = 1;
private final static int AUDIOFOCUS_LOST_TRANSIENT = 2;
private final static int AUDIOFOCUS_LOST_TRANSIENT_CAN_DUCK = 3;
private static AudioManager.OnAudioFocusChangeListener sAfChangeListener =
new AudioManager.OnAudioFocusChangeListener() {
public void onAudioFocusChange(int 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");
Cocos2dxHelper.runOnGLThread(new Runnable() {
@Override
public void run() {
nativeOnAudioFocusChange(AUDIOFOCUS_LOST);
}
});
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
// Pause playback
Log.d(TAG, "Pause music by AUDIOFOCUS_LOSS_TRANSILENT");
Cocos2dxHelper.runOnGLThread(new Runnable() {
@Override
public void run() {
nativeOnAudioFocusChange(AUDIOFOCUS_LOST_TRANSIENT);
}
});
} 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");
Cocos2dxHelper.runOnGLThread(new Runnable() {
@Override
public void run() {
nativeOnAudioFocusChange(AUDIOFOCUS_LOST_TRANSIENT_CAN_DUCK);
}
});
} 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");
Cocos2dxHelper.runOnGLThread(new Runnable() {
@Override
public void run() {
nativeOnAudioFocusChange(AUDIOFOCUS_GAIN);
}
});
}
}
};
static boolean registerAudioFocusListener(Context context) {
AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
// Request audio focus for playback
int result = am.requestAudioFocus(sAfChangeListener,
// Use the music stream.
AudioManager.STREAM_MUSIC,
// Request permanent focus.
AudioManager.AUDIOFOCUS_GAIN);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.d(TAG, "requestAudioFocus succeed");
return true;
}
Log.e(TAG, "requestAudioFocus failed!");
return false;
}
static void unregisterAudioFocusListener(Context context) {
AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
int result = am.abandonAudioFocus(sAfChangeListener);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.d(TAG, "abandonAudioFocus succeed!");
} else {
Log.e(TAG, "abandonAudioFocus failed!");
}
Cocos2dxHelper.runOnGLThread(new Runnable() {
@Override
public void run() {
nativeOnAudioFocusChange(AUDIOFOCUS_GAIN);
}
});
}
private static native void nativeOnAudioFocusChange(int focusChange);
}

View File

@@ -0,0 +1,369 @@
/****************************************************************************
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 org.cocos2dx.lib;
import android.util.Log;
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.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
import org.cocos2dx.okhttp3.Call;
import org.cocos2dx.okhttp3.Callback;
import org.cocos2dx.okhttp3.OkHttpClient;
import org.cocos2dx.okhttp3.Request;
import org.cocos2dx.okhttp3.Response;
public class Cocos2dxDownloader {
private int _id;
private OkHttpClient _httpClient = 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 static ConcurrentHashMap<String, Boolean> _resumingSupport = new ConcurrentHashMap<>();
private void onProgress(final int id, final long downloadBytes, final long downloadNow, final long downloadTotal) {
Cocos2dxHelper.runOnGLThread(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;
Cocos2dxHelper.runOnGLThread(new Runnable() {
@Override
public void run() {
nativeOnFinish(_id, id, errCode, errStr, data);
}
});
runNextTaskIfExists();
}
public static Cocos2dxDownloader createDownloader(int id, int timeoutInSeconds, String tempFileSuffix, int maxProcessingTasks) {
Cocos2dxDownloader downloader = new Cocos2dxDownloader();
downloader._id = id;
if (timeoutInSeconds > 0) {
downloader._httpClient = new OkHttpClient().newBuilder()
.followRedirects(true)
.followSslRedirects(true)
.callTimeout(timeoutInSeconds, TimeUnit.SECONDS)
.build();
} else {
downloader._httpClient = new OkHttpClient().newBuilder()
.followRedirects(true)
.followSslRedirects(true)
.build();
}
downloader._tempFileNameSuffix = tempFileSuffix;
downloader._countOfMaxProcessingTasks = maxProcessingTasks;
return downloader;
}
public static void createTask(final Cocos2dxDownloader downloader, int id_, String url_, String path_, String []header_) {
final int id = id_;
final String url = url_;
final String path = path_;
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(path + downloader._tempFileNameSuffix);
if (tempFile.isDirectory()) break;
File parent = tempFile.getParentFile();
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) {
if (_resumingSupport.containsKey(host) && _resumingSupport.get(host)) {
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;
Cocos2dxHelper.runOnGLThread(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(path + downloader._tempFileNameSuffix);
if (file.exists() && file.isFile()) {
file.delete();
}
}
downloader.onFinish(id, -2, response.message(), null);
return;
}
long total = response.body().contentLength();
if (path.length() > 0 && !_resumingSupport.containsKey(host)) {
if (total > 0) {
_resumingSupport.put(host, true);
} else {
_resumingSupport.put(host, false);
}
}
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);
} else {
// 非文件
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("Cocos2dxDownloader", e.toString());
}
}
}
});
} while (false);
}
};
downloader.enqueueTask(taskRunnable);
}
public static void abort(final Cocos2dxDownloader downloader, final int id) {
Cocos2dxHelper.getActivity().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 Cocos2dxDownloader downloader) {
Cocos2dxHelper.getActivity().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) {
Cocos2dxHelper.getActivity().runOnUiThread(taskRunnable);
_runningTaskCount++;
} else {
_taskQueue.add(taskRunnable);
}
}
}
private void runNextTaskIfExists() {
synchronized (_taskQueue) {
while (_runningTaskCount < _countOfMaxProcessingTasks &&
Cocos2dxDownloader.this._taskQueue.size() > 0) {
Runnable taskRunnable = Cocos2dxDownloader.this._taskQueue.poll();
Cocos2dxHelper.getActivity().runOnUiThread(taskRunnable);
_runningTaskCount += 1;
}
}
}
native void nativeOnProgress(int id, int taskId, long dl, long dlnow, long dltotal);
native void nativeOnFinish(int id, int taskId, int errCode, String errStr, final byte[] data);
}

View File

@@ -0,0 +1,479 @@
/****************************************************************************
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 org.cocos2dx.lib;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Canvas;
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.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.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.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
public class Cocos2dxEditBox {
// 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 Cocos2dxEditBox sThis = null;
private Cocos2dxEditText mEditText = null;
private Button mButton = null;
private String mButtonTitle = null;
private boolean mConfirmHold = true;
private Cocos2dxActivity mActivity = null;
private RelativeLayout mButtonLayout = null;
private RelativeLayout.LayoutParams mButtonParams;
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 Paint mPaint;
private int mLineColor = DARK_GREEN;
private float mLineWidth = 2f;
private boolean keyboardVisible = false;
private int mScreenHeight;
private int mTopMargin = 0;
private int mOrientation;
public Cocos2dxEditText(Cocos2dxActivity context){
super(context);
//remove focus border
this.setBackground(null);
this.setTextColor(Color.BLACK);
mScreenHeight = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).
getDefaultDisplay().getHeight();
mPaint = new Paint();
mPaint.setStrokeWidth(mLineWidth);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(mLineColor);
mOrientation = this.getResources().getConfiguration().orientation;
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++.
Cocos2dxEditBox.this.onKeyboardInput(s.toString());
}
};
registKeyboardVisible();
}
/***************************************************************************************
Override functions.
**************************************************************************************/
@Override
protected void onDraw(Canvas canvas) {
// draw the underline
int padB = this.getPaddingBottom();
canvas.drawLine(getScrollX(), this.getHeight() - padB / 2 - mLineWidth,
getScrollX() + this.getWidth(),
this.getHeight() - padB / 2 - mLineWidth, mPaint);
super.onDraw(canvas);
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
int newOrientation = newConfig.orientation;
if (mOrientation != newOrientation) {
mOrientation = newOrientation;
mTopMargin = 0; // clear top margin cache
}
}
/***************************************************************************************
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 = mActivity.getResources().getString(R.string.done);
} else if (confirmType.contentEquals("next")) {
this.setImeOptions(EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
mButtonTitle = mActivity.getResources().getString(R.string.next);
} else if (confirmType.contentEquals("search")) {
this.setImeOptions(EditorInfo.IME_ACTION_SEARCH | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
mButtonTitle = mActivity.getResources().getString(R.string.search);
} else if (confirmType.contentEquals("go")) {
this.setImeOptions(EditorInfo.IME_ACTION_GO | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
mButtonTitle = mActivity.getResources().getString(R.string.go);
} else if (confirmType.contentEquals("send")) {
this.setImeOptions(EditorInfo.IME_ACTION_SEND | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
mButtonTitle = mActivity.getResources().getString(R.string.send);
} else{
mButtonTitle = null;
Log.e(TAG, "unknown confirm type " + confirmType);
}
}
private void setInputType(final String inputType, boolean isMultiLine){
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"))
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) {
Cocos2dxEditBox.this.hide();
}
return false; // pass on to other listeners.
}
});
this.addTextChangedListener(mTextWatcher);
}
private void removeListeners() {
this.setOnEditorActionListener(null);
this.removeTextChangedListener(mTextWatcher);
}
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;
}
} else {
if (keyboardVisible) {
keyboardVisible = false;
Cocos2dxEditBox.this.hide();
}
}
if (Cocos2dxEditText.this.mTopMargin == 0 && r.bottom != getRootView().getHeight()) {
Cocos2dxEditText.this.setTopMargin(r.bottom);
}
}
});
}
private void setTopMargin(int topMargin) {
mTopMargin = topMargin;
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) mEditText.getLayoutParams();
layoutParams.topMargin = mTopMargin - getHeight();
setLayoutParams(layoutParams);
requestLayout();
}
}
public Cocos2dxEditBox(Cocos2dxActivity context, FrameLayout layout) {
Cocos2dxEditBox.sThis = this;
mActivity = context;
this.addItems(context, layout);
}
/***************************************************************************************
Public functions.
**************************************************************************************/
// Invoked by surface view to send a complete message to CPP.
public static void complete() {
Cocos2dxEditBox.sThis.hide();
}
/***************************************************************************************
Private functions.
**************************************************************************************/
private void addItems(Cocos2dxActivity context, FrameLayout layout) {
RelativeLayout myLayout = new RelativeLayout(context);
myLayout.setFitsSystemWindows(true);
this.addEditText(context, myLayout);
this.addButton(context, myLayout);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
layout.addView(myLayout, layoutParams);
//FXI ME: Is it needed?
// When touch area outside EditText and soft keyboard, then hide.
// layout.setOnTouchListener(new View.OnTouchListener() {
// @Override
// public boolean onTouch(View v, MotionEvent event) {
// Cocos2dxEditBox.this.hide();
// return true;
// }
//
// });
}
private void addEditText(Cocos2dxActivity context, RelativeLayout layout) {
mEditText = new Cocos2dxEditText(context);
mEditText.setVisibility(View.INVISIBLE);
mEditText.setBackgroundColor(Color.WHITE);
mEditText.setId(mEditTextID);
RelativeLayout.LayoutParams editParams = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
editParams.addRule(RelativeLayout.LEFT_OF, mButtonLayoutID);
layout.addView(mEditText, editParams);
}
private void addButton(Cocos2dxActivity context, RelativeLayout layout) {
mButton = new Button(context);
mButtonParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
mButton.setTextColor(Color.WHITE);
mButton.setBackground(getRoundRectShape());
mButtonLayout = new RelativeLayout(Cocos2dxHelper.getActivity());
mButtonLayout.setVisibility(View.INVISIBLE);
mButtonLayout.setBackgroundColor(Color.WHITE);
RelativeLayout.LayoutParams buttonLayoutParams = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
buttonLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
buttonLayoutParams.addRule(RelativeLayout.ALIGN_BOTTOM, mEditTextID);
buttonLayoutParams.addRule(RelativeLayout.ALIGN_TOP, mEditTextID);
mButtonLayout.addView(mButton, mButtonParams);
mButtonLayout.setId(mButtonLayoutID);
layout.addView(mButtonLayout, buttonLayoutParams);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Cocos2dxEditBox.this.onKeyboardConfirm(mEditText.getText().toString());
if (!Cocos2dxEditBox.this.mConfirmHold)
Cocos2dxEditBox.this.hide();
}
});
}
private Drawable getRoundRectShape() {
int radius = 7;
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(DARK_GREEN);
ShapeDrawable shapeDrawablePress = new ShapeDrawable();
shapeDrawablePress.setShape(roundRectShape);
shapeDrawablePress.getPaint().setStyle(Paint.Style.FILL);
shapeDrawablePress.getPaint().setColor(DARK_GREEN_PRESS);
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();
mEditText.hide();
mButtonLayout.setVisibility(View.INVISIBLE);
this.closeKeyboard();
mActivity.getGLSurfaceView().requestFocus();
mActivity.getGLSurfaceView().setStopHandleTouchAndKeyEvents(false);
}
private void show(String defaultValue, int maxLength, boolean isMultiline, boolean confirmHold, String confirmType, String inputType) {
mConfirmHold = confirmHold;
mEditText.show(defaultValue, maxLength, isMultiline, confirmHold, confirmType, inputType);
int editPaddingBottom = mEditText.getPaddingBottom();
int editPadding = mEditText.getPaddingTop();
mEditText.setPadding(editPadding, editPadding, editPadding, editPaddingBottom);
mButton.setText(mButtonTitle);
if (TextUtils.isEmpty(mButtonTitle)) {
mButton.setPadding(0, 0, 0, 0);
mButtonParams.setMargins(0, 0, 0, 0);
mButtonLayout.setVisibility(View.INVISIBLE);
} else {
int buttonTextPadding = mEditText.getPaddingBottom() / 2;
mButton.setPadding(editPadding, buttonTextPadding, editPadding, buttonTextPadding);
mButtonParams.setMargins(0, buttonTextPadding, 2, 0);
mButtonLayout.setVisibility(View.VISIBLE);
}
mActivity.getGLSurfaceView().setStopHandleTouchAndKeyEvents(true);
this.openKeyboard();
}
private void closeKeyboard() {
InputMethodManager imm = (InputMethodManager) Cocos2dxEditBox.this.mActivity.getSystemService(Cocos2dxEditBox.this.mActivity.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
this.onKeyboardComplete(mEditText.getText().toString());
}
private void openKeyboard() {
InputMethodManager imm = (InputMethodManager) Cocos2dxEditBox.this.mActivity.getSystemService(Cocos2dxEditBox.this.mActivity.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) {
if (null != Cocos2dxEditBox.sThis) {
Cocos2dxEditBox.sThis.mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
Cocos2dxEditBox.sThis.show(defaultValue, maxLength, isMultiline, confirmHold, confirmType, inputType);
}
});
}
}
private static void hideNative() {
if (null != Cocos2dxEditBox.sThis) {
Cocos2dxEditBox.sThis.mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
Cocos2dxEditBox.sThis.hide();
}
});
}
}
/***************************************************************************************
Native functions invoked by UI.
**************************************************************************************/
private void onKeyboardInput(String text) {
mActivity.runOnGLThread(new Runnable() {
@Override
public void run() {
Cocos2dxEditBox.onKeyboardInputNative(text);
}
});
}
private void onKeyboardComplete(String text) {
mActivity.getGLSurfaceView().requestFocus();
mActivity.getGLSurfaceView().setStopHandleTouchAndKeyEvents(false);
mActivity.runOnGLThread(new Runnable() {
@Override
public void run() {
Cocos2dxEditBox.onKeyboardCompleteNative(text);
}
});
}
private void onKeyboardConfirm(String text) {
mActivity.runOnGLThread(new Runnable() {
@Override
public void run() {
Cocos2dxEditBox.onKeyboardConfirmNative(text);
}
});
}
private static native void onKeyboardInputNative(String text);
private static native void onKeyboardCompleteNative(String text);
private static native void onKeyboardConfirmNative(String text);
}

View File

@@ -0,0 +1,343 @@
/****************************************************************************
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 org.cocos2dx.lib;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
public class Cocos2dxGLSurfaceView extends GLSurfaceView {
// ===========================================================
// Constants
// ===========================================================
private static final String TAG = Cocos2dxGLSurfaceView.class.getSimpleName();
private final static int HANDLER_OPEN_IME_KEYBOARD = 2;
private final static int HANDLER_CLOSE_IME_KEYBOARD = 3;
// ===========================================================
// Fields
// ===========================================================
// REFINE: Static handler -> Potential leak!
private static Handler sHandler;
private static Cocos2dxGLSurfaceView mCocos2dxGLSurfaceView;
private Cocos2dxRenderer mCocos2dxRenderer;
private boolean mStopHandleTouchAndKeyEvents = false;
public static native void nativeOnSizeChanged(int width, int height);
// ===========================================================
// Constructors
// ===========================================================
public Cocos2dxGLSurfaceView(final Context context) {
super(context);
this.initView();
}
public Cocos2dxGLSurfaceView(final Context context, final AttributeSet attrs) {
super(context, attrs);
this.initView();
}
protected void initView() {
this.setEGLContextClientVersion(2);
this.setFocusableInTouchMode(true);
Cocos2dxGLSurfaceView.mCocos2dxGLSurfaceView = this;
}
// ===========================================================
// Getter & Setter
// ===========================================================
public static Cocos2dxGLSurfaceView getInstance() {
return mCocos2dxGLSurfaceView;
}
public static void queueAccelerometer(final float x, final float y, final float z, final long timestamp) {
mCocos2dxGLSurfaceView.queueEvent(new Runnable() {
@Override
public void run() {
Cocos2dxAccelerometer.onSensorChanged(x, y, z, timestamp);
}
});
}
public void setCocos2dxRenderer(final Cocos2dxRenderer renderer) {
this.mCocos2dxRenderer = renderer;
this.setRenderer(this.mCocos2dxRenderer);
}
private String getContentText() {
return this.mCocos2dxRenderer.getContentText();
}
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
@Override
public void onResume() {
super.onResume();
this.setRenderMode(RENDERMODE_CONTINUOUSLY);
this.queueEvent(new Runnable() {
@Override
public void run() {
Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleOnResume();
}
});
mCocos2dxRenderer.setPauseInMainThread(false);
}
@Override
public void onPause() {
this.queueEvent(new Runnable() {
@Override
public void run() {
Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleOnPause();
}
});
this.setRenderMode(RENDERMODE_WHEN_DIRTY);
mCocos2dxRenderer.setPauseInMainThread(true);
//super.onPause();
}
@Override
public boolean onTouchEvent(final 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);
this.queueEvent(new Runnable() {
@Override
public void run() {
Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleActionDown(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];
this.queueEvent(new Runnable() {
@Override
public void run() {
Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleActionDown(idDown, xDown, yDown);
}
});
break;
case MotionEvent.ACTION_MOVE:
this.queueEvent(new Runnable() {
@Override
public void run() {
Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleActionMove(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);
this.queueEvent(new Runnable() {
@Override
public void run() {
Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleActionUp(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];
this.queueEvent(new Runnable() {
@Override
public void run() {
Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleActionUp(idUp, xUp, yUp);
}
});
break;
case MotionEvent.ACTION_CANCEL:
this.queueEvent(new Runnable() {
@Override
public void run() {
Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleActionCancel(ids, xs, ys);
}
});
break;
}
/*
if (BuildConfig.DEBUG) {
Cocos2dxGLSurfaceView.dumpMotionEvent(pMotionEvent);
}
*/
return true;
}
/*
* This function is called before Cocos2dxRenderer.nativeInit(), so the
* width and height is correct.
*/
@Override
protected void onSizeChanged(final int pNewSurfaceWidth, final int pNewSurfaceHeight, final int pOldSurfaceWidth, final int pOldSurfaceHeight) {
if(!this.isInEditMode()) {
this.mCocos2dxRenderer.setScreenWidthAndHeight(pNewSurfaceWidth, pNewSurfaceHeight);
nativeOnSizeChanged(pNewSurfaceWidth, pNewSurfaceHeight);
}
}
@Override
public boolean onKeyDown(final int pKeyCode, final KeyEvent pKeyEvent) {
switch (pKeyCode) {
case KeyEvent.KEYCODE_BACK:
Cocos2dxVideoHelper.mVideoHandler.sendEmptyMessage(Cocos2dxVideoHelper.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:
this.queueEvent(new Runnable() {
@Override
public void run() {
Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleKeyDown(pKeyCode);
}
});
return true;
default:
return super.onKeyDown(pKeyCode, pKeyEvent);
}
}
@Override
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:
this.queueEvent(new Runnable() {
@Override
public void run() {
Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleKeyUp(keyCode);
}
});
return true;
default:
return super.onKeyUp(keyCode, event);
}
}
// ===========================================================
// Methods
// ===========================================================
public void setStopHandleTouchAndKeyEvents(boolean value) {
mStopHandleTouchAndKeyEvents = value;
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
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(Cocos2dxGLSurfaceView.TAG, sb.toString());
}
}

View File

@@ -0,0 +1,104 @@
/****************************************************************************
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 org.cocos2dx.lib;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Handler;
import android.os.Message;
import java.lang.ref.WeakReference;
public class Cocos2dxHandler extends Handler {
// ===========================================================
// Constants
// ===========================================================
public final static int HANDLER_SHOW_DIALOG = 1;
// ===========================================================
// Fields
// ===========================================================
private WeakReference<Cocos2dxActivity> mActivity;
// ===========================================================
// Constructors
// ===========================================================
public Cocos2dxHandler(Cocos2dxActivity activity) {
this.mActivity = new WeakReference<Cocos2dxActivity>(activity);
}
// ===========================================================
// Getter & Setter
// ===========================================================
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
// ===========================================================
// Methods
// ===========================================================
public void handleMessage(Message msg) {
switch (msg.what) {
case Cocos2dxHandler.HANDLER_SHOW_DIALOG:
showDialog(msg);
break;
}
}
private void showDialog(Message msg) {
Cocos2dxActivity theActivity = this.mActivity.get();
DialogMessage dialogMessage = (DialogMessage)msg.obj;
new AlertDialog.Builder(theActivity)
.setTitle(dialogMessage.title)
.setMessage(dialogMessage.message)
.setPositiveButton("Ok",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// REFINE: Auto-generated method stub
}
}).create().show();
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
public static class DialogMessage {
public String title;
public String message;
public DialogMessage(String title, String message) {
this.title = title;
this.message = message;
}
}
}

View File

@@ -0,0 +1,664 @@
/****************************************************************************
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 org.cocos2dx.lib;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ClipboardManager;
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.content.res.AssetManager;
import android.media.AudioManager;
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.ParcelFileDescriptor;
import android.os.Vibrator;
import android.preference.PreferenceManager.OnActivityResultListener;
import android.util.DisplayMetrics;
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.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Set;
public class Cocos2dxHelper {
// ===========================================================
// Constants
// ===========================================================
private static final String PREFS_NAME = "Cocos2dxPrefsFile";
private static final int RUNNABLES_PER_FRAME = 5;
private static final String TAG = Cocos2dxHelper.class.getSimpleName();
// ===========================================================
// Fields
// ===========================================================
private static AssetManager sAssetManager;
private static Cocos2dxAccelerometer sCocos2dxAccelerometer;
private static boolean sAccelerometerEnabled;
private static boolean sCompassEnabled;
private static boolean sActivityVisible;
private static String sPackageName;
private static String sFileDirectory;
private static Activity sActivity = null;
private static Cocos2dxHelperListener sCocos2dxHelperListener;
private static Set<OnActivityResultListener> onActivityResultListeners = new LinkedHashSet<OnActivityResultListener>();
private static Vibrator sVibrateService = null;
// The absolute path to the OBB if it exists, else the absolute path to the APK.
private static String sAssetsPath = "";
// The OBB file
private static ZipResourceFile sOBBFile = null;
/**
* 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);
}
}
}
private static BatteryReceiver sBatteryReceiver = new BatteryReceiver();
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);
}
public static float getBatteryLevel() {
return sBatteryReceiver.sBatteryLevel;
}
public static final int NETWORK_TYPE_NONE = 0;
public static final int NETWORK_TYPE_LAN = 1;
public static final int NETWORK_TYPE_WWAN = 2;
public static int getNetworkType() {
int status = NETWORK_TYPE_NONE;
NetworkInfo networkInfo;
try {
ConnectivityManager connMgr = (ConnectivityManager) sActivity.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
// ===========================================================
public static void runOnGLThread(final Runnable r) {
((Cocos2dxActivity)sActivity).runOnGLThread(r);
}
private static boolean sInited = false;
public static void init(final Activity activity) {
sActivity = activity;
Cocos2dxHelper.sCocos2dxHelperListener = (Cocos2dxHelperListener)activity;
if (!sInited) {
PackageManager pm = activity.getPackageManager();
boolean isSupportLowLatency = pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY);
Log.d(TAG, "isSupportLowLatency:" + isSupportLowLatency);
int sampleRate = 44100;
int bufferSizeInFrames = 192;
if (Build.VERSION.SDK_INT >= 17) {
AudioManager am = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);
// use reflection to remove dependence of API 17 when compiling
// AudioManager.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
final Class audioManagerClass = AudioManager.class;
Object[] parameters = new Object[]{Cocos2dxReflectionHelper.<String>getConstantValue(audioManagerClass, "PROPERTY_OUTPUT_SAMPLE_RATE")};
final String strSampleRate = Cocos2dxReflectionHelper.<String>invokeInstanceMethod(am, "getProperty", new Class[]{String.class}, parameters);
// AudioManager.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
parameters = new Object[]{Cocos2dxReflectionHelper.<String>getConstantValue(audioManagerClass, "PROPERTY_OUTPUT_FRAMES_PER_BUFFER")};
final String strBufferSizeInFrames = Cocos2dxReflectionHelper.<String>invokeInstanceMethod(am, "getProperty", new Class[]{String.class}, parameters);
sampleRate = Integer.parseInt(strSampleRate);
bufferSizeInFrames = Integer.parseInt(strBufferSizeInFrames);
Log.d(TAG, "sampleRate: " + sampleRate + ", framesPerBuffer: " + bufferSizeInFrames);
} else {
Log.d(TAG, "android version is lower than 17");
}
nativeSetAudioDeviceInfo(isSupportLowLatency, sampleRate, bufferSizeInFrames);
final ApplicationInfo applicationInfo = activity.getApplicationInfo();
Cocos2dxHelper.sPackageName = applicationInfo.packageName;
Cocos2dxHelper.sFileDirectory = activity.getFilesDir().getAbsolutePath();
Cocos2dxHelper.nativeSetApkPath(Cocos2dxHelper.getAssetsPath());
Cocos2dxHelper.sCocos2dxAccelerometer = new Cocos2dxAccelerometer(activity);
Cocos2dxHelper.sAssetManager = activity.getAssets();
Cocos2dxHelper.nativeSetContext((Context)activity, Cocos2dxHelper.sAssetManager);
Cocos2dxHelper.sVibrateService = (Vibrator)activity.getSystemService(Context.VIBRATOR_SERVICE);
sInited = true;
int versionCode = 1;
try {
versionCode = Cocos2dxActivity.getContext().getPackageManager().getPackageInfo(Cocos2dxHelper.getPackageName(), 0).versionCode;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
try {
Cocos2dxHelper.sOBBFile = APKExpansionSupport.getAPKExpansionZipFile(Cocos2dxActivity.getContext(), versionCode, 0);
} catch (IOException e) {
e.printStackTrace();
}
}
}
// This function returns the absolute path to the OBB if it exists,
// else it returns the absolute path to the APK.
public static String getAssetsPath()
{
if (Cocos2dxHelper.sAssetsPath == "") {
int versionCode = 1;
try {
versionCode = Cocos2dxHelper.sActivity.getPackageManager().getPackageInfo(Cocos2dxHelper.sPackageName, 0).versionCode;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
String pathToOBB = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/obb/" + Cocos2dxHelper.sPackageName + "/main." + versionCode + "." + Cocos2dxHelper.sPackageName + ".obb";
File obbFile = new File(pathToOBB);
if (obbFile.exists())
Cocos2dxHelper.sAssetsPath = pathToOBB;
else
Cocos2dxHelper.sAssetsPath = Cocos2dxHelper.sActivity.getApplicationInfo().sourceDir;
}
return Cocos2dxHelper.sAssetsPath;
}
public static ZipResourceFile getObbFile()
{
return Cocos2dxHelper.sOBBFile;
}
public static Activity getActivity() {
return sActivity;
}
public static void addOnActivityResultListener(OnActivityResultListener listener) {
onActivityResultListeners.add(listener);
}
public static Set<OnActivityResultListener> getOnActivityResultListeners() {
return onActivityResultListeners;
}
public static boolean isActivityVisible(){
return sActivityVisible;
}
// ===========================================================
// Getter & Setter
// ===========================================================
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
// ===========================================================
// Methods
// ===========================================================
private static native void nativeSetApkPath(final String pApkPath);
private static native void nativeSetEditTextDialogResult(final byte[] pBytes);
private static native void nativeSetContext(final Context pContext, final AssetManager pAssetManager);
private static native void nativeSetAudioDeviceInfo(boolean isSupportLowLatency, int deviceSampleRate, int audioBufferSizeInFames);
public static String getPackageName() {
return Cocos2dxHelper.sPackageName;
}
public static String getWritablePath() {
return Cocos2dxHelper.sFileDirectory;
}
public static String getCurrentLanguage() {
return Locale.getDefault().getLanguage();
}
public static String getCurrentLanguageCode() {
return Locale.getDefault().toString();
}
public static String getDeviceModel(){
return Build.MODEL;
}
public static AssetManager getAssetManager() {
return Cocos2dxHelper.sAssetManager;
}
public static void enableAccelerometer() {
Cocos2dxHelper.sAccelerometerEnabled = true;
Cocos2dxHelper.sCocos2dxAccelerometer.enable();
}
public static void setAccelerometerInterval(float interval) {
Cocos2dxHelper.sCocos2dxAccelerometer.setInterval(interval);
}
public static void disableAccelerometer() {
Cocos2dxHelper.sAccelerometerEnabled = false;
Cocos2dxHelper.sCocos2dxAccelerometer.disable();
}
public static void setKeepScreenOn(boolean value) {
((Cocos2dxActivity)sActivity).setKeepScreenOn(value);
}
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 = Cocos2dxReflectionHelper.<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);
Cocos2dxReflectionHelper.invokeInstanceMethod(sVibrateService,"vibrate",
new Class[]{type}, new Object[]{(effect)});
}
} else {
sVibrateService.vibrate((long) (duration * 1000));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getVersion() {
try {
String version = Cocos2dxActivity.getContext().getPackageManager().getPackageInfo(Cocos2dxActivity.getContext().getPackageName(), 0).versionName;
return version;
} catch(Exception e) {
return "";
}
}
public static boolean openURL(String url) {
boolean ret = false;
try {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
sActivity.startActivity(i);
ret = true;
} catch (Exception e) {
}
return ret;
}
// 复制文本到剪切板
public static void copyTextToClipboard(final String text){
sActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
ClipboardManager myClipboard = (ClipboardManager)sActivity.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 (Cocos2dxHelper.sOBBFile != null) {
AssetFileDescriptor descriptor = Cocos2dxHelper.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(Cocos2dxHelper.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(Cocos2dxHelper.TAG, e.toString());
} catch (InvocationTargetException e) {
Log.e(Cocos2dxHelper.TAG, e.toString());
}
}
}
return array;
}
public static void endApplication() {
if (sActivity != null)
sActivity.finish();
}
public static void onResume() {
sActivityVisible = true;
if (Cocos2dxHelper.sAccelerometerEnabled) {
Cocos2dxHelper.sCocos2dxAccelerometer.enable();
}
}
public static void onPause() {
sActivityVisible = false;
if (Cocos2dxHelper.sAccelerometerEnabled) {
Cocos2dxHelper.sCocos2dxAccelerometer.disable();
}
}
public static void onEnterBackground() {
}
public static void onEnterForeground() {
}
public static void terminateProcess() {
android.os.Process.killProcess(android.os.Process.myPid());
}
private static void showDialog(final String pTitle, final String pMessage) {
Cocos2dxHelper.sCocos2dxHelperListener.showDialog(pTitle, pMessage);
}
public static void setEditTextDialogResult(final String pResult) {
try {
final byte[] bytesUTF8 = pResult.getBytes("UTF8");
Cocos2dxHelper.sCocos2dxHelperListener.runOnGLThread(new Runnable() {
@Override
public void run() {
Cocos2dxHelper.nativeSetEditTextDialogResult(bytesUTF8);
}
});
} catch (UnsupportedEncodingException pUnsupportedEncodingException) {
/* Nothing. */
}
}
private static int displayMetricsToDPI(DisplayMetrics metrics)
{
if(metrics.xdpi != metrics.ydpi) {
Log.w(Cocos2dxHelper.TAG, "xdpi != ydpi, use (xdpi + ydpi)/2 instead.");
return (int) ((metrics.xdpi + metrics.ydpi) / 2.0);
} else {
return (int)metrics.xdpi;
}
}
public static int getDPI()
{
if (sActivity != null)
{
DisplayMetrics metrics = new DisplayMetrics();
WindowManager wm = sActivity.getWindowManager();
if (wm != null)
{
Display d = wm.getDefaultDisplay();
if (d != null)
{
try {
Method getRealMetrics = d.getClass().getMethod("getRealMetrics", metrics.getClass());
getRealMetrics.invoke(d, metrics);
return displayMetricsToDPI(metrics);
} catch (Exception e) {
e.printStackTrace();
}
d.getMetrics(metrics);
return displayMetricsToDPI(metrics);
}
}
}
return -1;
}
public static byte[] conversionEncoding(byte[] text, String fromCharset,String newCharset)
{
try {
String str = new String(text,fromCharset);
return str.getBytes(newCharset);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
private static void setGameInfoDebugViewText(int index, String text) {
if (sOnGameInfoUpdatedListener != null) {
if (index == 0) {
sOnGameInfoUpdatedListener.onGameInfoUpdated_0(text);
}
else if (index == 1) {
sOnGameInfoUpdatedListener.onGameInfoUpdated_1(text);
}
else if (index == 2) {
sOnGameInfoUpdatedListener.onGameInfoUpdated_2(text);
}
}
}
private static void setJSBInvocationCount(int count) {
if (sOnGameInfoUpdatedListener != null) {
sOnGameInfoUpdatedListener.onJSBInvocationCountUpdated(count);
}
}
private static void openDebugView() {
if (sOnGameInfoUpdatedListener != null) {
sOnGameInfoUpdatedListener.onOpenDebugView();
}
}
private static void disableBatchGLCommandsToNative() {
if (sOnGameInfoUpdatedListener != null) {
sOnGameInfoUpdatedListener.onDisableBatchGLCommandsToNative();
}
}
public interface OnGameInfoUpdatedListener {
void onFPSUpdated(float fps);
void onJSBInvocationCountUpdated(int count);
void onOpenDebugView();
void onDisableBatchGLCommandsToNative();
void onGameInfoUpdated_0(String text);
void onGameInfoUpdated_1(String text);
void onGameInfoUpdated_2(String text);
}
private static OnGameInfoUpdatedListener sOnGameInfoUpdatedListener;
public static void setOnGameInfoUpdatedListener(OnGameInfoUpdatedListener listener) {
sOnGameInfoUpdatedListener = listener;
}
public static OnGameInfoUpdatedListener getOnGameInfoUpdatedListener() {
return sOnGameInfoUpdatedListener;
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
public static interface Cocos2dxHelperListener {
public void showDialog(final String pTitle, final String pMessage);
public void runOnGLThread(final Runnable pRunnable);
}
private static float[] sDeviceMotionValues = new float[9];
private static float[] getDeviceMotionValue() {
Cocos2dxAccelerometer.DeviceMotionEvent event = Cocos2dxHelper.sCocos2dxAccelerometer.getDeviceMotionEvent();
sDeviceMotionValues[0] = event.acceleration.x;
sDeviceMotionValues[1] = event.acceleration.y;
// Issue https://github.com/cocos-creator/2d-tasks/issues/2532
// use negative event.acceleration.z to match iOS value
sDeviceMotionValues[2] = - event.acceleration.z;
sDeviceMotionValues[3] = event.accelerationIncludingGravity.x;
sDeviceMotionValues[4] = event.accelerationIncludingGravity.y;
sDeviceMotionValues[5] = event.accelerationIncludingGravity.z;
sDeviceMotionValues[6] = event.rotationRate.alpha;
sDeviceMotionValues[7] = event.rotationRate.beta;
sDeviceMotionValues[8] = event.rotationRate.gamma;
return sDeviceMotionValues;
}
public static int getSDKVersion() {
return Build.VERSION.SDK_INT;
}
public static String getSystemVersion() {
return Build.VERSION.RELEASE;
}
public static int getDeviceRotation() {
try {
Display display = ((WindowManager) sActivity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
return display.getRotation();
} catch (NullPointerException e) {
e.printStackTrace();
}
return Surface.ROTATION_0;
}
public static float[] getSafeArea() {
if (android.os.Build.VERSION.SDK_INT >= 28) {
do {
Object windowInsectObj = 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};
}
}

View File

@@ -0,0 +1,413 @@
/****************************************************************************
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 org.cocos2dx.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 Cocos2dxHttpURLConnection
{
private static String TAG = "Cocos2dxHttpURLConnection";
private static final String POST_METHOD = "POST" ;
private static final String PUT_METHOD = "PUT" ;
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)) {
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(Cocos2dxHelper.getActivity().getAssets().open(assetsfilenameString));
}
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate ca;
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
caInput.close();
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
httpsURLConnection.setSSLSocketFactory(context.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "setVerifySSL:" + e.toString());
}
}
//Add header
static void addRequestHeader(HttpURLConnection urlConnection, String key, String value) {
urlConnection.setRequestProperty(key, value);
}
static int connect(HttpURLConnection http) {
int suc = 0;
try {
http.connect();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "connect" + e.toString());
suc = 1;
}
return suc;
}
static void disconnect(HttpURLConnection http) {
http.disconnect();
}
static void sendRequest(HttpURLConnection http, byte[] byteArray) {
try {
OutputStream out = http.getOutputStream();
if(null != byteArray) {
out.write(byteArray);
out.flush();
}
out.close();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "sendRequest:" + e.toString());
}
}
static String getResponseHeaders(HttpURLConnection http) {
Map<String, List<String>> headers = http.getHeaderFields();
if (null == headers) {
return null;
}
String header = "";
for (Entry<String, List<String>> entry: headers.entrySet()) {
String key = entry.getKey();
if (null == key) {
header += listToString(entry.getValue(), ",") + "\n";
} else {
header += key + ":" + listToString(entry.getValue(), ",") + "\n";
}
}
return header;
}
static String getResponseHeaderByIdx(HttpURLConnection http, int idx) {
Map<String, List<String>> headers = http.getHeaderFields();
if (null == headers) {
return null;
}
String header = null;
int counter = 0;
for (Entry<String, List<String>> entry: headers.entrySet()) {
if (counter == idx) {
String key = entry.getKey();
if (null == key) {
header = listToString(entry.getValue(), ",") + "\n";
} else {
header = key + ":" + listToString(entry.getValue(), ",") + "\n";
}
break;
}
counter++;
}
return header;
}
static String getResponseHeaderByKey(HttpURLConnection http, String key) {
if (null == key) {
return null;
}
Map<String, List<String>> headers = http.getHeaderFields();
if (null == headers) {
return null;
}
String header = null;
for (Entry<String, List<String>> entry: headers.entrySet()) {
if (key.equalsIgnoreCase(entry.getKey())) {
if ("set-cookie".equalsIgnoreCase(key)) {
header = combinCookies(entry.getValue(), http.getURL().getHost());
} else {
header = listToString(entry.getValue(), ",");
}
break;
}
}
return header;
}
static int getResponseHeaderByKeyInt(HttpURLConnection http, String key) {
String value = http.getHeaderField(key);
if (null == value) {
return 0;
} else {
return Integer.parseInt(value);
}
}
static byte[] getResponseContent(HttpURLConnection http) {
InputStream in;
try {
in = http.getInputStream();
String contentEncoding = http.getContentEncoding();
if (contentEncoding != null) {
if(contentEncoding.equalsIgnoreCase("gzip")){
in = new GZIPInputStream(http.getInputStream()); //reads 2 bytes to determine GZIP stream!
}
else if(contentEncoding.equalsIgnoreCase("deflate")){
in = new InflaterInputStream(http.getInputStream());
}
}
} catch (IOException e) {
in = http.getErrorStream();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "1 getResponseContent: " + e.toString());
return null;
}
try {
byte[] buffer = new byte[1024];
int size = 0;
ByteArrayOutputStream bytestream = new ByteArrayOutputStream();
while((size = in.read(buffer, 0 , 1024)) != -1)
{
bytestream.write(buffer, 0, size);
}
byte retbuffer[] = bytestream.toByteArray();
bytestream.close();
return retbuffer;
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "2 getResponseContent:" + e.toString());
}
return null;
}
static int getResponseCode(HttpURLConnection http) {
int code = 0;
try {
code = http.getResponseCode();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "getResponseCode:" + e.toString());
}
return code;
}
static String getResponseMessage(HttpURLConnection http) {
String msg;
try {
msg = http.getResponseMessage();
} catch (Exception e) {
e.printStackTrace();
msg = e.toString();
Log.e(TAG, "getResponseMessage: " + msg);
}
return msg;
}
public static String listToString(List<String> list, String strInterVal) {
if (list == null) {
return null;
}
StringBuilder result = new StringBuilder();
boolean flag = false;
for (String str : list) {
if (flag) {
result.append(strInterVal);
}
if (null == str) {
str = "";
}
result.append(str);
flag = true;
}
return result.toString();
}
public static String combinCookies(List<String> list, String hostDomain) {
StringBuilder sbCookies = new StringBuilder();
String domain = hostDomain;
String tailmatch = "FALSE";
String path = "/";
String secure = "FALSE";
String key = null;
String value = null;
String expires = null;
for (String str : list) {
String[] parts = str.split(";");
for (String part : parts) {
int firstIndex = part.indexOf("=");
if (-1 == firstIndex)
continue;
String[] item = {part.substring(0, firstIndex), part.substring(firstIndex + 1)};
if ("expires".equalsIgnoreCase(item[0].trim())) {
expires = str2Seconds(item[1].trim());
} else if("path".equalsIgnoreCase(item[0].trim())) {
path = item[1];
} else if("secure".equalsIgnoreCase(item[0].trim())) {
secure = item[1];
} else if("domain".equalsIgnoreCase(item[0].trim())) {
domain = item[1];
} else if("version".equalsIgnoreCase(item[0].trim()) || "max-age".equalsIgnoreCase(item[0].trim())) {
//do nothing
} else {
key = item[0];
value = item[1];
}
}
if (null == domain) {
domain = "none";
}
sbCookies.append(domain);
sbCookies.append('\t');
sbCookies.append(tailmatch); //access
sbCookies.append('\t');
sbCookies.append(path); //path
sbCookies.append('\t');
sbCookies.append(secure); //secure
sbCookies.append('\t');
sbCookies.append(expires); //expires
sbCookies.append("\t");
sbCookies.append(key); //key
sbCookies.append("\t");
sbCookies.append(value); //value
sbCookies.append('\n');
}
return sbCookies.toString();
}
private static String str2Seconds(String strTime) {
Calendar c = Calendar.getInstance();
long milliseconds = 0;
try {
c.setTime(new SimpleDateFormat("EEE, dd-MMM-yy hh:mm:ss zzz", Locale.US).parse(strTime));
milliseconds = c.getTimeInMillis() / 1000;
} catch (ParseException e) {
Log.e(TAG, "str2Seconds: " + e.toString());
}
return Long.toString(milliseconds);
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2013-2016 Chukong Technologies Inc.
* Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.cocos2dx.lib;
public class Cocos2dxJavascriptJavaBridge {
public static native int evalString(String value);
}

View File

@@ -0,0 +1,174 @@
/****************************************************************************
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 org.cocos2dx.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 Cocos2dxLocalStorage {
private static final String TAG = "Cocos2dxLocalStorage";
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;
/**
* Constructor
* @param context The Context within which to work, used to create the DB
* @return
*/
public static boolean init(String dbName, String tableName) {
if (Cocos2dxActivity.getContext() != null) {
DATABASE_NAME = dbName;
TABLE_NAME = tableName;
mDatabaseOpenHelper = new DBOpenHelper(Cocos2dxActivity.getContext());
mDatabase = mDatabaseOpenHelper.getWritableDatabase();
return true;
}
return false;
}
public static void destroy() {
if (mDatabase != null) {
mDatabase.close();
}
}
public static void setItem(String key, String value) {
try {
String sql = "replace into "+TABLE_NAME+"(key,value)values(?,?)";
mDatabase.execSQL(sql, new Object[] { key, value });
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getItem(String key) {
String ret = null;
try {
String sql = "select value from "+TABLE_NAME+" where key=?";
Cursor c = mDatabase.rawQuery(sql, new String[]{key});
while (c.moveToNext()) {
// only return the first value
if (ret != null)
{
Log.e(TAG, "The key contains more than one value.");
break;
}
ret = c.getString(c.getColumnIndex("value"));
}
c.close();
} catch (Exception e) {
e.printStackTrace();
}
return ret;
}
public static void removeItem(String key) {
try {
String sql = "delete from "+TABLE_NAME+" where key=?";
mDatabase.execSQL(sql, new Object[] {key});
} catch (Exception e) {
e.printStackTrace();
}
}
public static void clear() {
try {
String sql = "delete from "+TABLE_NAME;
mDatabase.execSQL(sql);
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getKey(int nIndex) {
String ret = null;
try {
int nCount = 0;
String sql = "select key from "+TABLE_NAME + " order by rowid asc";
Cursor c = mDatabase.rawQuery(sql, null);
if(nIndex < 0 || nIndex >= c.getCount()) {
return null;
}
while (c.moveToNext()) {
if(nCount == nIndex) {
ret = c.getString(c.getColumnIndex("key"));
break;
}
nCount++;
}
c.close();
} catch (Exception e) {
e.printStackTrace();
}
return ret;
}
public static int getLength() {
int res = 0;
try {
String sql = "select count(*) as nums from "+TABLE_NAME;
Cursor c = mDatabase.rawQuery(sql, null);
if (c.moveToNext()){
res = c.getInt(c.getColumnIndex("nums"));
}
c.close();
} catch (Exception e) {
e.printStackTrace();
}
return res;
}
/**
* This creates/opens the database.
*/
private static class DBOpenHelper extends SQLiteOpenHelper {
DBOpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS "+TABLE_NAME+"(key TEXT PRIMARY KEY,value TEXT);");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
//db.execSQL("DROP TABLE IF EXISTS " + VIRTUAL_TABLE);
//onCreate(db);
}
}
}

View File

@@ -0,0 +1,33 @@
package org.cocos2dx.lib;
import android.content.Context;
import android.view.OrientationEventListener;
public class Cocos2dxOrientationHelper extends OrientationEventListener {
private int currentOrientation;
public Cocos2dxOrientationHelper(Context context) {
super(context);
currentOrientation = Cocos2dxHelper.getDeviceRotation();
}
public void onPause() {
this.disable();
}
public void onResume() {
this.enable();
}
@Override
public void onOrientationChanged(int orientation) {
int curOrientation = Cocos2dxHelper.getDeviceRotation();
if (curOrientation != currentOrientation) {
currentOrientation = Cocos2dxHelper.getDeviceRotation();
nativeOnOrientationChanged(currentOrientation);
}
}
public static native void nativeOnOrientationChanged(int rotation);
}

View File

@@ -0,0 +1,134 @@
/****************************************************************************
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 org.cocos2dx.lib;
import android.util.Log;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Cocos2dxReflectionHelper {
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;
}
public static <T> T invokeInterfaceMethod(final Class clazz, final String methodName,
final Class[] parameterTypes, final Object[] parameters) {
try {
final Method method = clazz.getMethod(methodName, parameterTypes);
return (T)method.invoke(null, parameters);
} catch (NoSuchMethodException e) {
Log.e("error", "can not find " + methodName + " in " + clazz.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;
}
public static Object createInstanceInterface(final String className, final String interfaceName) {
try {
final Class clazz = Class.forName(className + "$" + interfaceName);
return Proxy.newProxyInstance(clazz.getClassLoader(),
new Class<?>[]{clazz}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
});
} catch (ClassNotFoundException ex) {
Log.e("error", "can not find " + className + "." + interfaceName);
}
return null;
}
public static Class getClassByName(final String className) {
try {
return Class.forName(className);
} catch (ClassNotFoundException ex) {
Log.e("error", "can not find " + className);
}
return null;
}
public static Class getInterfaceByName(final String className, final String interfaceName) {
try {
return Class.forName(className + "$" + interfaceName);
} catch (ClassNotFoundException ex) {
Log.e("error", "can not find " + className + "." + interfaceName);
}
return null;
}
}

View File

@@ -0,0 +1,270 @@
/****************************************************************************
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 org.cocos2dx.lib;
import android.opengl.GLSurfaceView;
import java.lang.ref.WeakReference;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class Cocos2dxRenderer implements GLSurfaceView.Renderer {
// ===========================================================
// Constants
// ===========================================================
private final static String TAG = "Cocos2dxRenderer";
private final static long NANOSECONDSPERSECOND = 1000000000L;
private final static long NANOSECONDSPERMICROSECOND = 1000000;
private static final long INTERVAL_60_FPS = (long) (1.0 / 60 * Cocos2dxRenderer.NANOSECONDSPERSECOND);
private static long sAnimationInterval = INTERVAL_60_FPS;
private static WeakReference<Cocos2dxRenderer> sRenderer;
// ===========================================================
// Fields
// ===========================================================
private long mLastTickInNanoSeconds;
private int mScreenWidth;
private int mScreenHeight;
private boolean mNativeInitCompleted = false;
private boolean mNeedShowFPS = false;
private String mDefaultResourcePath = "";
private long mOldNanoTime = 0;
private long mFrameCount = 0;
private boolean mNeedToPause = false;
// ===========================================================
// Constructors
// ===========================================================
// ===========================================================
// Getter & Setter
// ===========================================================
public static void setPreferredFramesPerSecond(int fps) {
Cocos2dxRenderer.sAnimationInterval = (long) (1.0 / fps * Cocos2dxRenderer.NANOSECONDSPERSECOND);
}
public void setScreenWidthAndHeight(final int surfaceWidth, final int surfaceHeight) {
this.mScreenWidth = surfaceWidth;
this.mScreenHeight = surfaceHeight;
}
public void setDefaultResourcePath(String path) {
if (path == null)
return;
mDefaultResourcePath = path;
}
public void showFPS() {
mNeedShowFPS = true;
}
public interface OnGameEngineInitializedListener {
void onGameEngineInitialized();
}
private OnGameEngineInitializedListener mGameEngineInitializedListener;
public void setOnGameEngineInitializedListener(OnGameEngineInitializedListener listener) {
mGameEngineInitializedListener = listener;
}
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
@Override
public void onSurfaceCreated(final GL10 GL10, final EGLConfig EGLConfig) {
mNativeInitCompleted = false;
Cocos2dxRenderer.nativeInit(this.mScreenWidth, this.mScreenHeight, mDefaultResourcePath);
mOldNanoTime = System.nanoTime();
this.mLastTickInNanoSeconds = System.nanoTime();
mNativeInitCompleted = true;
if (mGameEngineInitializedListener != null) {
Cocos2dxHelper.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
mGameEngineInitializedListener.onGameEngineInitialized();
}
});
}
}
@Override
public void onSurfaceChanged(final GL10 GL10, final int width, final int height) {
Cocos2dxRenderer.nativeOnSurfaceChanged(width, height);
}
@Override
public void onDrawFrame(final GL10 gl) {
if (mNeedToPause)
return;
if (mNeedShowFPS) {
/////////////////////////////////////////////////////////////////////
//IDEA: show FPS in Android Text control rather than outputing log.
++mFrameCount;
long nowFpsTime = System.nanoTime();
long fpsTimeInterval = nowFpsTime - mOldNanoTime;
if (fpsTimeInterval > 1000000000L) {
double frameRate = 1000000000.0 * mFrameCount / fpsTimeInterval;
Cocos2dxHelper.OnGameInfoUpdatedListener listener = Cocos2dxHelper.getOnGameInfoUpdatedListener();
if (listener != null) {
listener.onFPSUpdated((float) frameRate);
}
mFrameCount = 0;
mOldNanoTime = System.nanoTime();
}
/////////////////////////////////////////////////////////////////////
}
/*
* No need to use algorithm in default(60 FPS) situation,
* since onDrawFrame() was called by system 60 times per second by default.
*/
if (sAnimationInterval <= INTERVAL_60_FPS) {
Cocos2dxRenderer.nativeRender();
} else {
final long now = System.nanoTime();
final long interval = now - this.mLastTickInNanoSeconds;
if (interval < Cocos2dxRenderer.sAnimationInterval) {
try {
Thread.sleep((Cocos2dxRenderer.sAnimationInterval - interval) / Cocos2dxRenderer.NANOSECONDSPERMICROSECOND);
} catch (final Exception e) {
}
}
/*
* Render time MUST be counted in, or the FPS will slower than appointed.
*/
this.mLastTickInNanoSeconds = System.nanoTime();
Cocos2dxRenderer.nativeRender();
}
}
// ===========================================================
// Methods
// ===========================================================
private static native void nativeTouchesBegin(final int id, final float x, final float y);
private static native void nativeTouchesEnd(final int id, final float x, final float y);
private static native void nativeTouchesMove(final int[] ids, final float[] xs, final float[] ys);
private static native void nativeTouchesCancel(final int[] ids, final float[] xs, final float[] ys);
private static native boolean nativeKeyEvent(final int keyCode,boolean isPressed);
private static native void nativeRender();
private static native void nativeInit(final int width, final int height, final String resourcePath);
private static native void nativeOnSurfaceChanged(final int width, final int height);
private static native void nativeOnPause();
private static native void nativeOnResume();
// This function will be invoked in main thread.
public void setPauseInMainThread(boolean value) {
mNeedToPause = value;
}
public void handleActionDown(final int id, final float x, final float y) {
if (! mNativeInitCompleted)
return;
Cocos2dxRenderer.nativeTouchesBegin(id, x, y);
}
public void handleActionUp(final int id, final float x, final float y) {
if (! mNativeInitCompleted)
return;
Cocos2dxRenderer.nativeTouchesEnd(id, x, y);
}
public void handleActionCancel(final int[] ids, final float[] xs, final float[] ys) {
if (! mNativeInitCompleted)
return;
Cocos2dxRenderer.nativeTouchesCancel(ids, xs, ys);
}
public void handleActionMove(final int[] ids, final float[] xs, final float[] ys) {
if (! mNativeInitCompleted)
return;
Cocos2dxRenderer.nativeTouchesMove(ids, xs, ys);
}
public void handleKeyDown(final int keyCode) {
if (! mNativeInitCompleted)
return;
Cocos2dxRenderer.nativeKeyEvent(keyCode, true);
}
public void handleKeyUp(final int keyCode) {
if (! mNativeInitCompleted)
return;
Cocos2dxRenderer.nativeKeyEvent(keyCode, false);
}
public void handleOnPause() {
/**
* onPause may be invoked before onSurfaceCreated,
* and engine will be initialized correctly after
* onSurfaceCreated is invoked. Can not invoke any
* native method before onSurfaceCreated is invoked
*/
if (! mNativeInitCompleted)
return;
Cocos2dxHelper.onEnterBackground();
Cocos2dxRenderer.nativeOnPause();
}
public void handleOnResume() {
Cocos2dxHelper.onEnterForeground();
Cocos2dxRenderer.nativeOnResume();
}
private static native void nativeInsertText(final String text);
private static native void nativeDeleteBackward();
private static native String nativeGetContentText();
public void handleInsertText(final String text) {
Cocos2dxRenderer.nativeInsertText(text);
}
public void handleDeleteBackward() {
Cocos2dxRenderer.nativeDeleteBackward();
}
public String getContentText() {
return Cocos2dxRenderer.nativeGetContentText();
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
}

View File

@@ -0,0 +1,81 @@
/****************************************************************************
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 org.cocos2dx.lib;
import android.content.Context;
import android.graphics.Typeface;
import java.util.HashMap;
public class Cocos2dxTypefaces {
// ===========================================================
// Constants
// ===========================================================
// ===========================================================
// Fields
// ===========================================================
private static final HashMap<String, Typeface> sTypefaceCache = new HashMap<String, Typeface>();
// ===========================================================
// Constructors
// ===========================================================
// ===========================================================
// Getter & Setter
// ===========================================================
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
// ===========================================================
// Methods
// ===========================================================
public static synchronized Typeface get(final Context context, final String assetName) {
if (!Cocos2dxTypefaces.sTypefaceCache.containsKey(assetName)) {
Typeface typeface = null;
if (assetName.startsWith("/"))
{
typeface = Typeface.createFromFile(assetName);
}
else
{
typeface = Typeface.createFromAsset(context.getAssets(), assetName);
}
Cocos2dxTypefaces.sTypefaceCache.put(assetName, typeface);
}
return Cocos2dxTypefaces.sTypefaceCache.get(assetName);
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
}

View File

@@ -0,0 +1,575 @@
/****************************************************************************
Copyright (c) 2014-2016 Chukong Technologies Inc.
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
package org.cocos2dx.lib;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.widget.FrameLayout;
import org.cocos2dx.lib.Cocos2dxVideoView.OnVideoEventListener;
import java.lang.ref.WeakReference;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Cocos2dxVideoHelper {
private FrameLayout mLayout = null;
private Cocos2dxActivity mActivity = null;
private static SparseArray<Cocos2dxVideoView> sVideoViews = null;
static VideoHandler mVideoHandler = null;
private static Handler sHandler = null;
Cocos2dxVideoHelper(Cocos2dxActivity activity, FrameLayout layout)
{
mActivity = activity;
mLayout = layout;
mVideoHandler = new VideoHandler(this);
sVideoViews = new SparseArray<Cocos2dxVideoView>();
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;
final static int KeyEventBack = 1000;
static class VideoHandler extends Handler{
WeakReference<Cocos2dxVideoHelper> mReference;
VideoHandler(Cocos2dxVideoHelper helper){
mReference = new WeakReference<Cocos2dxVideoHelper>(helper);
}
@Override
public void handleMessage(Message msg) {
Cocos2dxVideoHelper 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;
}
default:
break;
}
super.handleMessage(msg);
}
}
private class VideoEventRunnable implements Runnable
{
private int mVideoTag;
private int mVideoEvent;
public VideoEventRunnable(int tag,int event) {
mVideoTag = tag;
mVideoEvent = event;
}
@Override
public void run() {
nativeExecuteVideoCallback(mVideoTag, mVideoEvent);
}
}
public static native void nativeExecuteVideoCallback(int index,int event);
OnVideoEventListener videoEventListener = new OnVideoEventListener() {
@Override
public void onVideoEvent(int tag,int event) {
mActivity.runOnGLThread(new VideoEventRunnable(tag, event));
}
};
public static int createVideoWidget() {
Message msg = new Message();
msg.what = VideoTaskCreate;
msg.arg1 = videoTag;
mVideoHandler.sendMessage(msg);
return videoTag++;
}
private void _createVideoView(int index) {
Cocos2dxVideoView videoView = new Cocos2dxVideoView(mActivity,index);
sVideoViews.put(index, videoView);
FrameLayout.LayoutParams lParams = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
mLayout.addView(videoView, 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) {
Cocos2dxVideoView view = sVideoViews.get(index);
if (view != null) {
view.stopPlayback();
sVideoViews.remove(index);
mLayout.removeView(view);
}
}
public static void setVideoUrl(int index, int videoSource, String videoUrl) {
Message msg = 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) {
Cocos2dxVideoView videoView = sVideoViews.get(index);
if (videoView != null) {
switch (videoSource) {
case 0:
videoView.setVideoFileName(videoUrl);
break;
case 1:
videoView.setVideoURL(videoUrl);
break;
default:
break;
}
}
}
public static void setVideoRect(int index, int left, int top, int maxWidth, int maxHeight) {
Message msg = 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) {
Cocos2dxVideoView 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) {
Cocos2dxVideoView 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);
Cocos2dxVideoView videoView = sVideoViews.get(key);
if (videoView != null) {
videoView.setFullScreenEnabled(false);
mActivity.runOnGLThread(new VideoEventRunnable(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) {
Cocos2dxVideoView 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) {
Cocos2dxVideoView 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) {
Cocos2dxVideoView 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) {
Cocos2dxVideoView videoView = sVideoViews.get(index);
if (videoView != null) {
videoView.seekTo(msec);
}
}
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 float getCurrentTime(final int index) {
Callable<Float> callable = new Callable<Float>() {
@Override
public Float call() throws Exception {
Cocos2dxVideoView video = sVideoViews.get(index);
float currentPosition = -1;
if (video != null) {
currentPosition = video.getCurrentPosition() / 1000.0f;
}
return new Float(currentPosition);
}
};
try {
return callInMainThread(callable);
} catch (ExecutionException e) {
return -1;
} catch (InterruptedException e) {
return -1;
}
}
public static float getDuration(final int index) {
Callable<Float> callable = new Callable<Float>() {
@Override
public Float call() throws Exception {
Cocos2dxVideoView video = sVideoViews.get(index);
float duration = -1;
if (video != null) {
duration = video.getDuration() / 1000.0f;
}
if (duration <= 0) {
Log.w("Cocos2dxVideoHelper", "Video player's duration is not ready to get now!");
}
return new Float(duration);
}
};
try {
return callInMainThread(callable);
} catch (ExecutionException e) {
return -1;
} catch (InterruptedException e) {
return -1;
}
}
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) {
Cocos2dxVideoView videoView = sVideoViews.get(index);
if (videoView != null) {
if (visible) {
videoView.fixSize();
videoView.setVisibility(View.VISIBLE);
} else {
videoView.setVisibility(View.INVISIBLE);
}
}
}
public static void setVideoKeepRatioEnabled(int index, boolean enable) {
Message msg = new Message();
msg.what = VideoTaskKeepRatio;
msg.arg1 = index;
if (enable) {
msg.arg2 = 1;
} else {
msg.arg2 = 0;
}
mVideoHandler.sendMessage(msg);
}
private void _setVideoKeepRatio(int index, boolean enable) {
Cocos2dxVideoView videoView = sVideoViews.get(index);
if (videoView != null) {
videoView.setKeepRatio(enable);
}
}
private void _setVolume(final int index, final float volume) {
Cocos2dxVideoView 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);
}
private byte[] getFrame(final int index) {
Callable<byte[]> callable = new Callable<byte[]>() {
@Override
public byte[] call() throws Exception {
Cocos2dxVideoView video = sVideoViews.get(index);
byte datas[] = null;
if (video != null) datas = video.getFrame();
if (datas == null) {
Log.w("Cocos2dxVideoHelper", "Video player's frame is not ready to get now!");
}
return datas;
}
};
try {
return callInMainThread(callable);
} catch (ExecutionException e) {
Log.w("ExecutionException", e.getMessage());
return null;
} catch (InterruptedException e) {
Log.w("InterruptedException", e.getMessage());
return null;
}
}
private float getFrameChannel(final int index) {
Callable<Float> callable = new Callable<Float>() {
@Override
public Float call() throws Exception {
Cocos2dxVideoView video = sVideoViews.get(index);
int channel = 0;
if (video != null) {
channel = video.getFrameChannel();
}
if (channel == 0) {
Log.w("Cocos2dxVideoHelper", "Video player's frame channel is unknown!");
}
return new Float((float)channel);
}
};
try {
return callInMainThread(callable);
} catch (ExecutionException e) {
return -1.0f;
} catch (InterruptedException e) {
return -1.0f;
}
}
private float getFrameWidth(final int index) {
Callable<Float> callable = new Callable<Float>() {
@Override
public Float call() {
Cocos2dxVideoView video = sVideoViews.get(index);
int width = 0;
if (video != null) {
width = video.getFrameWidth();
}
if (width == 0) {
Log.w("Cocos2dxVideoHelper", "Video player's frame width is not ready to get now!");
}
return new Float((float)width);
}
};
try {
return callInMainThread(callable);
} catch (ExecutionException e) {
return -1.0f;
} catch (InterruptedException e) {
return -1.0f;
}
}
private float getFrameHeight(final int index) {
Callable<Float> callable = new Callable<Float>() {
@Override
public Float call() {
Cocos2dxVideoView video = sVideoViews.get(index);
int height = 0;
if (video != null) {
height = video.getFrameHeight();
}
if (height == 0) {
Log.w("Cocos2dxVideoHelper", "Video player's frame height is not ready to get now!");
}
return new Float((float)height);
}
};
try {
return callInMainThread(callable);
} catch (ExecutionException e) {
return -1.0f;
} catch (InterruptedException e) {
return -1.0f;
}
}
private void setShowRawFrame(final int index, final boolean show) {
Cocos2dxVideoView video = sVideoViews.get(index);
if (video != null) {
video.setShowRawFrame(show);
}
}
}

View File

@@ -0,0 +1,717 @@
/*
* Copyright (C) 2006 The Android Open Source Project
* Copyright (c) 2014-2016 Chukong Technologies Inc.
* Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.cocos2dx.lib;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.FrameLayout;
import android.graphics.Bitmap;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.nio.ByteBuffer;
public class Cocos2dxVideoView 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 = "Cocos2dxVideoView";
private Uri mVideoUri;
private int mDuration;
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 Cocos2dxActivity mCocos2dxActivity = null;
protected int mViewLeft = 0;
protected int mViewTop = 0;
protected int mViewWidth = 0;
protected int mViewHeight = 0;
protected int mVisibleLeft = 0;
protected int mVisibleTop = 0;
protected int mVisibleWidth = 0;
protected int mVisibleHeight = 0;
protected boolean mFullScreenEnabled = false;
protected int mFullScreenWidth = 0;
protected int mFullScreenHeight = 0;
private boolean mIsAssetRouse = false;
private String mVideoFilePath = null;
private int mViewTag = 0;
private boolean mKeepRatio = false;
private boolean mMetaUpdated = false;
private MediaMetadataRetriever mRetriever = null;
private ByteBuffer mFrameBuf = null;
private byte mPixels[] = null;
private Bitmap mFrame = null;
private Bitmap mCurFrame = null;
private boolean mShowRaw = true;
private Object mCopyListener = null;
private Surface mSurface = null;
private static final int PX_INVALID = 0;
private static final int PX_ALPHA = 1;
private static final int PX_ARGB_4444 = 2;
private static final int PX_ARGB_8888 = 3;
private static final int PX_HARDWARE = 4;
private static final int PX_RGBA_F16 = 5;
private static final int PX_RGB_565 = 6;
// 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;
// ===========================================================
// Constructors
// ===========================================================
public Cocos2dxVideoView(Cocos2dxActivity activity,int tag) {
super(activity);
mViewTag = tag;
mCocos2dxActivity = activity;
initVideoView();
setShowRawFrame(true); // Show mediaplayer window.
}
// ===========================================================
// 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 setVideoURL(String url) {
mIsAssetRouse = false;
setVideoURI(Uri.parse(url), null);
}
public void setVideoFileName(String path) {
if (path.startsWith(AssetResourceRoot)) {
path = path.substring(AssetResourceRoot.length());
}
if (path.startsWith("/")) {
mIsAssetRouse = false;
setVideoURI(Uri.parse(path),null);
}
else {
mVideoFilePath = path;
mIsAssetRouse = true;
setVideoURI(Uri.parse(path), null);
}
}
public int getCurrentPosition() {
if (! (mCurrentState == State.ERROR |
mMediaPlayer == null) ) {
return mMediaPlayer.getCurrentPosition();
}
return -1;
}
public int getDuration() {
if (! (mCurrentState == State.IDLE ||
mCurrentState == State.ERROR ||
mCurrentState == State.INITIALIZED ||
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.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;
}
mMediaPlayer.seekTo(ms);
}
public void fixSize() {
if (mFullScreenEnabled) {
mFullScreenWidth = mCocos2dxActivity.getGLSurfaceView().getWidth();
mFullScreenHeight = mCocos2dxActivity.getGLSurfaceView().getHeight();
fixSize(0, 0, mFullScreenWidth, mFullScreenHeight);
} else {
fixSize(mViewLeft, mViewTop, mViewWidth, mViewHeight);
}
}
public void fixSize(int left, int top, int width, int height) {
if (mVideoWidth == 0 || mVideoHeight == 0) {
mVisibleLeft = left;
mVisibleTop = top;
mVisibleWidth = width;
mVisibleHeight = height;
}
else if (width != 0 && height != 0) {
if (mKeepRatio && !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;
}
public byte[] getFrame() {
if(mRetriever == null) {
return null;
} else {
try {
if (Build.VERSION.SDK_INT < 24) {
mFrame = mRetriever.getFrameAtTime(mMediaPlayer.getCurrentPosition() * 1000, MediaMetadataRetriever.OPTION_CLOSEST);
} else {
if(mFrame == null) {
Bitmap firstFrame = mRetriever.getFrameAtTime(0, MediaMetadataRetriever.OPTION_CLOSEST);
mFrame = Bitmap.createBitmap(getWidth(), getHeight(), firstFrame.getConfig());
}
if (mCopyListener == null) {
mCopyListener = Cocos2dxReflectionHelper.createInstanceInterface("android.view.PixelCopy", "OnPixelCopyFinishedListener");
}
final Class clazz = Cocos2dxReflectionHelper.getClassByName("android.view.PixelCopy");
final Class intef = Cocos2dxReflectionHelper.getInterfaceByName("android.view.PixelCopy", "OnPixelCopyFinishedListener");
final Class types[] = {SurfaceView.class, Bitmap.class, intef, Handler.class};
final Object params[] = {this, mFrame, mCopyListener, this.getHandler()};
Cocos2dxReflectionHelper.invokeInterfaceMethod(clazz, "request", types, params);
}
} catch (Exception e) {
Log.e("Grab error", e.getStackTrace().toString());
return mPixels;
}
if(mFrame == null) {
return mPixels;
}
mCurFrame = mFrame;
if(mFrameBuf == null) {
mFrameBuf = ByteBuffer.allocate(mCurFrame.getByteCount());
}
mFrameBuf.position(0);
mCurFrame.copyPixelsToBuffer(mFrameBuf);
mPixels = mFrameBuf.array();
return mPixels;
}
}
public int getFrameChannel() throws NoSuchFieldException, IllegalAccessException {
if (mCurFrame == null) {
return 0;
} else {
Bitmap.Config cfg = mCurFrame.getConfig();
if(Build.VERSION.SDK_INT > 26) {
Field hardware = Bitmap.Config.class.getDeclaredField("HARDWARE");
Field rgba_f16 = Bitmap.Config.class.getDeclaredField("RGBA_F16");
if(hardware != null && cfg == hardware.get(null)) return PX_HARDWARE;
if(rgba_f16 != null && cfg == rgba_f16.get(null)) return PX_RGBA_F16;
}
switch (cfg) {
case ALPHA_8:
return PX_ALPHA;
case ARGB_4444:
return PX_ARGB_4444;
case ARGB_8888:
return PX_ARGB_8888;
case RGB_565:
return PX_RGB_565;
default:
return PX_INVALID;
}
}
}
public int getFrameWidth() {
if (mCurFrame != null) {
return mCurFrame.getWidth();
} else {
return 0;
}
}
public int getFrameHeight() {
if(mCurFrame != null) {
return mCurFrame.getHeight();
} else {
return 0;
}
}
public void setShowRawFrame(boolean show) {
if(mShowRaw == show) { // Now show state == Before show state
return; // Do nothing
}
setTranslationY(0); // Show view
if(!show) {
setTranslationY(-1000); // Hide view
}
mShowRaw = show;
}
// ===========================================================
// 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 openVideo() {
if (mSurfaceHolder == null) {
// not ready for playback just yet, will try again later
return;
}
if (mIsAssetRouse) {
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);
mRetriever = new MediaMetadataRetriever();
mFrameBuf = null;
mPixels = null;
mCurFrame = null;
mFrame = null;
if (mIsAssetRouse) {
AssetFileDescriptor afd = mCocos2dxActivity.getAssets().openFd(mVideoFilePath);
mMediaPlayer.setDataSource(afd.getFileDescriptor(),afd.getStartOffset(),afd.getLength());
mRetriever.setDataSource(afd.getFileDescriptor(),afd.getStartOffset(),afd.getLength());
} else {
mMediaPlayer.setDataSource(mVideoUri.toString());
if (mVideoUri.getHost().length() > 0) {
mRetriever.setDataSource(mVideoUri.toString(), new HashMap<String, String>());
} else {
mRetriever.setDataSource(mCocos2dxActivity.getContext(), mVideoUri);
}
}
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) {
Cocos2dxVideoView.this.sendEvent(EVENT_META_LOADED);
Cocos2dxVideoView.this.sendEvent(EVENT_READY_TO_PLAY);
mMetaUpdated = true;
}
mCurrentState = State.PREPARED;
}
};
private MediaPlayer.OnCompletionListener mCompletionListener =
new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
mCurrentState = State.PLAYBACK_COMPLETED;
Cocos2dxVideoView.this.sendEvent(EVENT_COMPLETED);
}
};
private static final int EVENT_PLAYING = 0;
private static final int EVENT_PAUSED = 1;
private static final int EVENT_STOPPED = 2;
private static final int EVENT_COMPLETED = 3;
private static final int EVENT_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 = mCocos2dxActivity.getResources();
int messageId;
if (framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
// messageId = com.android.internal.R.string.VideoView_error_text_invalid_progressive_playback;
messageId = r.getIdentifier("VideoView_error_text_invalid_progressive_playback", "string", "android");
} else {
// messageId = com.android.internal.R.string.VideoView_error_text_unknown;
messageId = r.getIdentifier("VideoView_error_text_unknown", "string", "android");
}
int titleId = r.getIdentifier("VideoView_error_title", "string", "android");
int buttonStringId = r.getIdentifier("VideoView_error_button", "string", "android");
new AlertDialog.Builder(mCocos2dxActivity)
.setTitle(r.getString(titleId))
.setMessage(messageId)
.setPositiveButton(r.getString(buttonStringId),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
/* If we get here, there is no onError listener, so
* at least inform them that the video is over.
*/
Cocos2dxVideoView.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;
Cocos2dxVideoView.this.openVideo();
if (mPositionBeforeRelease > 0)
mMediaPlayer.seekTo(mPositionBeforeRelease);
}
public void surfaceDestroyed(SurfaceHolder holder) {
// after we return from this we can't use the surface any more
mSurfaceHolder = null;
mPositionBeforeRelease = getCurrentPosition();
Cocos2dxVideoView.this.release();
}
};
/*
* release the media player in any state
*/
private void release() {
if (mMediaPlayer != null) {
mMediaPlayer.release();
mMediaPlayer = null;
}
}
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");
mCocos2dxActivity.sendBroadcast(i);
}
}

View File

@@ -0,0 +1,174 @@
/****************************************************************************
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 org.cocos2dx.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] = Cocos2dxWebViewHelper._shouldStartLoading(mViewTag, mUrlString);
this.mLatch.countDown(); // notify that result is ready
}
}
public class Cocos2dxWebView extends WebView {
private static final String TAG = Cocos2dxWebViewHelper.class.getSimpleName();
private int mViewTag;
private String mJSScheme;
public Cocos2dxWebView(Context context) {
this(context, -1);
}
@SuppressLint("SetJavaScriptEnabled")
public Cocos2dxWebView(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) {
Cocos2dxActivity activity = (Cocos2dxActivity)getContext();
try {
URI uri = URI.create(urlString);
if (uri != null && uri.getScheme().equals(mJSScheme)) {
activity.runOnGLThread(new Runnable() {
@Override
public void run() {
Cocos2dxWebViewHelper._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
activity.runOnGLThread(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);
Cocos2dxActivity activity = (Cocos2dxActivity)getContext();
activity.runOnGLThread(new Runnable() {
@Override
public void run() {
Cocos2dxWebViewHelper._didFinishLoading(mViewTag, url);
}
});
}
@Override
public void onReceivedError(WebView view, int errorCode, String description, final String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
Cocos2dxActivity activity = (Cocos2dxActivity)getContext();
activity.runOnGLThread(new Runnable() {
@Override
public void run() {
Cocos2dxWebViewHelper._didFailLoading(mViewTag, failingUrl);
}
});
}
}
public void setWebViewRect(int left, int top, int maxWidth, int maxHeight) {
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT);
layoutParams.leftMargin = left;
layoutParams.topMargin = top;
layoutParams.width = maxWidth;
layoutParams.height = maxHeight;
this.setLayoutParams(layoutParams);
}
}

View File

@@ -0,0 +1,323 @@
/****************************************************************************
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 org.cocos2dx.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 java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Cocos2dxWebViewHelper {
private static final String TAG = Cocos2dxWebViewHelper.class.getSimpleName();
private static Handler sHandler;
private static Cocos2dxActivity sCocos2dxActivity;
private static FrameLayout sLayout;
private static SparseArray<Cocos2dxWebView> webViews;
private static int viewTag = 0;
public Cocos2dxWebViewHelper(FrameLayout layout) {
Cocos2dxWebViewHelper.sLayout = layout;
Cocos2dxWebViewHelper.sHandler = new Handler(Looper.myLooper());
Cocos2dxWebViewHelper.sCocos2dxActivity = (Cocos2dxActivity) Cocos2dxActivity.getContext();
Cocos2dxWebViewHelper.webViews = new SparseArray<Cocos2dxWebView>();
}
private static native boolean shouldStartLoading(int index, String message);
public static boolean _shouldStartLoading(int index, String message) {
return !shouldStartLoading(index, message);
}
private static native void didFinishLoading(int index, String message);
public static void _didFinishLoading(int index, String message) {
didFinishLoading(index, message);
}
private static native void didFailLoading(int index, String message);
public static void _didFailLoading(int index, String message) {
didFailLoading(index, message);
}
private static native void onJsCallback(int index, String message);
public static void _onJsCallback(int index, String message) {
onJsCallback(index, message);
}
public static int createWebView() {
final int index = viewTag;
sCocos2dxActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
Cocos2dxWebView webView = new Cocos2dxWebView(sCocos2dxActivity, 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) {
sCocos2dxActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
Cocos2dxWebView 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) {
sCocos2dxActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
Cocos2dxWebView 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) {
sCocos2dxActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
Cocos2dxWebView webView = webViews.get(index);
if (webView != null) {
webView.setWebViewRect(left, top, maxWidth, maxHeight);
}
}
});
}
public static void setBackgroundTransparent(final int index, final boolean isTransparent) {
sCocos2dxActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
Cocos2dxWebView 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) {
sCocos2dxActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
Cocos2dxWebView 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) {
sCocos2dxActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
Cocos2dxWebView 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) {
sCocos2dxActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
Cocos2dxWebView webView = webViews.get(index);
if (webView != null) {
webView.loadDataWithBaseURL(baseUrl, data, null, null, null);
}
}
});
}
public static void loadUrl(final int index, final String url) {
sCocos2dxActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
Cocos2dxWebView webView = webViews.get(index);
if (webView != null) {
webView.loadUrl(url);
}
}
});
}
public static void loadFile(final int index, final String filePath) {
sCocos2dxActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
Cocos2dxWebView webView = webViews.get(index);
if (webView != null) {
webView.loadUrl(filePath);
}
}
});
}
public static void stopLoading(final int index) {
sCocos2dxActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
Cocos2dxWebView webView = webViews.get(index);
if (webView != null) {
webView.stopLoading();
}
}
});
}
public static void reload(final int index) {
sCocos2dxActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
Cocos2dxWebView 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 {
Cocos2dxWebView 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 {
Cocos2dxWebView 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) {
sCocos2dxActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
Cocos2dxWebView webView = webViews.get(index);
if (webView != null) {
webView.goBack();
}
}
});
}
public static void goForward(final int index) {
sCocos2dxActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
Cocos2dxWebView webView = webViews.get(index);
if (webView != null) {
webView.goForward();
}
}
});
}
public static void evaluateJS(final int index, final String js) {
sCocos2dxActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
Cocos2dxWebView webView = webViews.get(index);
if (webView != null) {
webView.loadUrl("javascript:" + js);
}
}
});
}
public static void setScalesPageToFit(final int index, final boolean scalesPageToFit) {
sCocos2dxActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
Cocos2dxWebView webView = webViews.get(index);
if (webView != null) {
webView.setScalesPageToFit(scalesPageToFit);
}
}
});
}
}

View File

@@ -0,0 +1,63 @@
/****************************************************************************
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 org.cocos2dx.lib;
import android.app.Activity;
import android.os.Build;
import android.view.View;
public class Utils {
private static Activity sActivity = null;
public static void setActivity(final Activity activity) {
Utils.sActivity = activity;
}
public static void hideVirtualButton() {
if (Build.VERSION.SDK_INT >= 19 &&
null != Utils.sActivity) {
// use reflection to remove dependence of API level
Class viewClass = View.class;
final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = Cocos2dxReflectionHelper.<Integer>getConstantValue(viewClass, "SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION");
final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = Cocos2dxReflectionHelper.<Integer>getConstantValue(viewClass, "SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN");
final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = Cocos2dxReflectionHelper.<Integer>getConstantValue(viewClass, "SYSTEM_UI_FLAG_HIDE_NAVIGATION");
final int SYSTEM_UI_FLAG_FULLSCREEN = Cocos2dxReflectionHelper.<Integer>getConstantValue(viewClass, "SYSTEM_UI_FLAG_FULLSCREEN");
final int SYSTEM_UI_FLAG_IMMERSIVE_STICKY = Cocos2dxReflectionHelper.<Integer>getConstantValue(viewClass, "SYSTEM_UI_FLAG_IMMERSIVE_STICKY");
final int SYSTEM_UI_FLAG_LAYOUT_STABLE = Cocos2dxReflectionHelper.<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};
Cocos2dxReflectionHelper.<Void>invokeInstanceMethod(Utils.sActivity.getWindow().getDecorView(),
"setSystemUiVisibility",
new Class[]{Integer.TYPE},
parameters);
}
}
}

View File

@@ -0,0 +1,319 @@
/****************************************************************************
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.
****************************************************************************/
#include "platform/android/jni/JniHelper.h"
#include <android/log.h>
#include <string.h>
#include <pthread.h>
#include "base/ccUTF8.h"
#define LOG_TAG "JniHelper"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
static pthread_key_t g_key;
jclass _getClassID(const char *className) {
if (nullptr == className) {
return nullptr;
}
JNIEnv* env = cocos2d::JniHelper::getEnv();
jstring _jstrClassName = env->NewStringUTF(className);
jclass _clazz = (jclass) env->CallObjectMethod(cocos2d::JniHelper::classloader,
cocos2d::JniHelper::loadclassMethod_methodID,
_jstrClassName);
if (nullptr == _clazz || env->ExceptionCheck()) {
LOGE("Classloader failed to find class of %s", className);
env->ExceptionClear();
_clazz = nullptr;
}
env->DeleteLocalRef(_jstrClassName);
return _clazz;
}
void _detachCurrentThread(void* a) {
cocos2d::JniHelper::getJavaVM()->DetachCurrentThread();
}
namespace cocos2d {
JavaVM* JniHelper::_psJavaVM = nullptr;
jmethodID JniHelper::loadclassMethod_methodID = nullptr;
jobject JniHelper::classloader = nullptr;
std::function<void()> JniHelper::classloaderCallback = nullptr;
jobject JniHelper::_activity = nullptr;
JavaVM* JniHelper::getJavaVM() {
pthread_t thisthread = pthread_self();
LOGD("JniHelper::getJavaVM(), pthread_self() = %ld", thisthread);
return _psJavaVM;
}
void JniHelper::setJavaVM(JavaVM *javaVM) {
pthread_t thisthread = pthread_self();
LOGD("JniHelper::setJavaVM(%p), pthread_self() = %ld", javaVM, thisthread);
_psJavaVM = javaVM;
pthread_key_create(&g_key, _detachCurrentThread);
}
JNIEnv* JniHelper::cacheEnv(JavaVM* jvm) {
JNIEnv* _env = nullptr;
// get jni environment
jint ret = jvm->GetEnv((void**)&_env, JNI_VERSION_1_4);
switch (ret) {
case JNI_OK :
// Success!
pthread_setspecific(g_key, _env);
return _env;
case JNI_EDETACHED :
// Thread not attached
if (jvm->AttachCurrentThread(&_env, nullptr) < 0)
{
LOGE("Failed to get the environment using AttachCurrentThread()");
return nullptr;
} else {
// Success : Attached and obtained JNIEnv!
pthread_setspecific(g_key, _env);
return _env;
}
case JNI_EVERSION :
// Cannot recover from this error
LOGE("JNI interface version 1.4 not supported");
default :
LOGE("Failed to get the environment using GetEnv()");
return nullptr;
}
}
JNIEnv* JniHelper::getEnv() {
JNIEnv *_env = (JNIEnv *)pthread_getspecific(g_key);
if (_env == nullptr)
_env = JniHelper::cacheEnv(_psJavaVM);
return _env;
}
jobject JniHelper::getActivity() {
return _activity;
}
bool JniHelper::setClassLoaderFrom(jobject activityinstance) {
JniMethodInfo _getclassloaderMethod;
if (!JniHelper::getMethodInfo_DefaultClassLoader(_getclassloaderMethod,
"android/content/Context",
"getClassLoader",
"()Ljava/lang/ClassLoader;")) {
return false;
}
jobject _c = cocos2d::JniHelper::getEnv()->CallObjectMethod(activityinstance,
_getclassloaderMethod.methodID);
if (nullptr == _c) {
return false;
}
JniMethodInfo _m;
if (!JniHelper::getMethodInfo_DefaultClassLoader(_m,
"java/lang/ClassLoader",
"loadClass",
"(Ljava/lang/String;)Ljava/lang/Class;")) {
return false;
}
JniHelper::classloader = cocos2d::JniHelper::getEnv()->NewGlobalRef(_c);
JniHelper::loadclassMethod_methodID = _m.methodID;
JniHelper::_activity = cocos2d::JniHelper::getEnv()->NewGlobalRef(activityinstance);
if (JniHelper::classloaderCallback != nullptr){
JniHelper::classloaderCallback();
}
return true;
}
bool JniHelper::getStaticMethodInfo(JniMethodInfo &methodinfo,
const char *className,
const char *methodName,
const char *paramCode) {
if ((nullptr == className) ||
(nullptr == methodName) ||
(nullptr == paramCode)) {
return false;
}
JNIEnv *env = JniHelper::getEnv();
if (!env) {
LOGE("Failed to get JNIEnv");
return false;
}
jclass classID = _getClassID(className);
if (! classID) {
LOGE("Failed to find class %s", className);
env->ExceptionClear();
return false;
}
jmethodID methodID = env->GetStaticMethodID(classID, methodName, paramCode);
if (! methodID) {
LOGE("Failed to find static method id of %s", methodName);
env->ExceptionClear();
return false;
}
methodinfo.classID = classID;
methodinfo.env = env;
methodinfo.methodID = methodID;
return true;
}
bool JniHelper::getMethodInfo_DefaultClassLoader(JniMethodInfo &methodinfo,
const char *className,
const char *methodName,
const char *paramCode) {
if ((nullptr == className) ||
(nullptr == methodName) ||
(nullptr == paramCode)) {
return false;
}
JNIEnv *env = JniHelper::getEnv();
if (!env) {
return false;
}
jclass classID = env->FindClass(className);
if (! classID) {
LOGE("Failed to find class %s", className);
env->ExceptionClear();
return false;
}
jmethodID methodID = env->GetMethodID(classID, methodName, paramCode);
if (! methodID) {
LOGE("Failed to find method id of %s", methodName);
env->ExceptionClear();
return false;
}
methodinfo.classID = classID;
methodinfo.env = env;
methodinfo.methodID = methodID;
return true;
}
bool JniHelper::getMethodInfo(JniMethodInfo &methodinfo,
const char *className,
const char *methodName,
const char *paramCode) {
if ((nullptr == className) ||
(nullptr == methodName) ||
(nullptr == paramCode)) {
return false;
}
JNIEnv *env = JniHelper::getEnv();
if (!env) {
return false;
}
jclass classID = _getClassID(className);
if (! classID) {
LOGE("Failed to find class %s", className);
env->ExceptionClear();
return false;
}
jmethodID methodID = env->GetMethodID(classID, methodName, paramCode);
if (! methodID) {
LOGE("Failed to find method id of %s", methodName);
env->ExceptionClear();
return false;
}
methodinfo.classID = classID;
methodinfo.env = env;
methodinfo.methodID = methodID;
return true;
}
std::string JniHelper::jstring2string(jstring jstr) {
if (jstr == nullptr) {
return "";
}
JNIEnv *env = JniHelper::getEnv();
if (!env) {
return "";
}
std::string strValue = cocos2d::StringUtils::getStringUTFCharsJNI(env, jstr);
return strValue;
}
jstring JniHelper::convert(JniHelper::LocalRefMapType &localRefs, cocos2d::JniMethodInfo& t, const char* x) {
jstring ret = nullptr;
if (x)
ret = cocos2d::StringUtils::newStringUTFJNI(t.env, x);
localRefs[t.env].push_back(ret);
return ret;
}
jstring JniHelper::convert(JniHelper::LocalRefMapType &localRefs, cocos2d::JniMethodInfo& t, const std::string& x) {
return convert(localRefs, t, x.c_str());
}
void JniHelper::deleteLocalRefs(JNIEnv* env, JniHelper::LocalRefMapType &localRefs) {
if (!env) {
return;
}
for (const auto& ref : localRefs[env]) {
env->DeleteLocalRef(ref);
}
localRefs[env].clear();
}
void JniHelper::reportError(const std::string& className, const std::string& methodName, const std::string& signature) {
LOGE("Failed to find static java method. Class name: %s, method name: %s, signature: %s ", className.c_str(), methodName.c_str(), signature.c_str());
}
} //namespace cocos2d

View File

@@ -0,0 +1,399 @@
/****************************************************************************
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.
****************************************************************************/
#ifndef __ANDROID_JNI_HELPER_H__
#define __ANDROID_JNI_HELPER_H__
#include <jni.h>
#include <string>
#include <vector>
#include <unordered_map>
#include <functional>
#include "base/ccMacros.h"
#include "math/Vec3.h"
//The macro must be used this way to find the native method. The principle is not well understood.
#define JNI_METHOD2(CLASS2,FUNC2) Java_##CLASS2##_##FUNC2
#define JNI_METHOD1(CLASS1,FUNC1) JNI_METHOD2(CLASS1,FUNC1)
NS_CC_BEGIN
typedef struct JniMethodInfo_
{
JNIEnv * env;
jclass classID;
jmethodID methodID;
} JniMethodInfo;
class CC_DLL JniHelper
{
public:
typedef std::unordered_map<JNIEnv *, std::vector<jobject >> LocalRefMapType;
static void setJavaVM(JavaVM *javaVM);
static JavaVM* getJavaVM();
static JNIEnv* getEnv();
static jobject getActivity();
static bool setClassLoaderFrom(jobject activityInstance);
static bool getStaticMethodInfo(JniMethodInfo &methodinfo,
const char *className,
const char *methodName,
const char *paramCode);
static bool getMethodInfo(JniMethodInfo &methodinfo,
const char *className,
const char *methodName,
const char *paramCode);
static std::string jstring2string(jstring str);
static jmethodID loadclassMethod_methodID;
static jobject classloader;
static std::function<void()> classloaderCallback;
template <typename... Ts>
static jobject newObject(const std::string& className, Ts... xs)
{
jobject ret = nullptr;
static const char* methodName = "<init>";
cocos2d::JniMethodInfo t;
std::string signature = "(" + std::string(getJNISignature(xs...)) + ")V";
if (cocos2d::JniHelper::getMethodInfo(t, className.c_str(), methodName, signature.c_str())) {
LocalRefMapType localRefs;
ret = t.env->NewObject(t.classID, t.methodID, convert(t, xs)...);
t.env->DeleteLocalRef(t.classID);
deleteLocalRefs(t.env, localRefs);
} else {
reportError(className, methodName, signature);
}
return ret;
}
template <typename... Ts>
static void callObjectVoidMethod(jobject object,
const std::string& className,
const std::string& methodName,
Ts... xs) {
cocos2d::JniMethodInfo t;
std::string signature = "(" + std::string(getJNISignature(xs...)) + ")V";
if (cocos2d::JniHelper::getMethodInfo(t, className.c_str(), methodName.c_str(), signature.c_str())) {
LocalRefMapType localRefs;
t.env->CallVoidMethod(object, t.methodID, convert(localRefs, t, xs)...);
t.env->DeleteLocalRef(t.classID);
deleteLocalRefs(t.env, localRefs);
} else {
reportError(className, methodName, signature);
}
}
template <typename... Ts>
static float callObjectFloatMethod(jobject object,
const std::string& className,
const std::string& methodName,
Ts... xs) {
float ret = 0.0f;
cocos2d::JniMethodInfo t;
std::string signature = "(" + std::string(getJNISignature(xs...)) + ")F";
if (cocos2d::JniHelper::getMethodInfo(t, className.c_str(), methodName.c_str(), signature.c_str())) {
LocalRefMapType localRefs;
ret = t.env->CallFloatMethod(object, t.methodID, convert(localRefs, t, xs)...);
t.env->DeleteLocalRef(t.classID);
deleteLocalRefs(t.env, localRefs);
} else {
reportError(className, methodName, signature);
}
return ret;
}
template <typename... Ts>
static jbyteArray callObjectByteArrayMethod(jobject object,
const std::string& className,
const std::string& methodName,
Ts... xs) {
jbyteArray ret = nullptr;
cocos2d::JniMethodInfo t;
std::string signature = "(" + std::string(getJNISignature(xs...)) + ")[B";
if (cocos2d::JniHelper::getMethodInfo(t, className.c_str(), methodName.c_str(), signature.c_str())) {
LocalRefMapType localRefs;
ret = (jbyteArray)t.env->CallObjectMethod(object, t.methodID, convert(localRefs, t, xs)...);
t.env->DeleteLocalRef(t.classID);
deleteLocalRefs(t.env, localRefs);
} else {
reportError(className, methodName, signature);
}
return ret;
}
template <typename... Ts>
static void callStaticVoidMethod(const std::string& className,
const std::string& methodName,
Ts... xs) {
cocos2d::JniMethodInfo t;
std::string signature = "(" + std::string(getJNISignature(xs...)) + ")V";
if (cocos2d::JniHelper::getStaticMethodInfo(t, className.c_str(), methodName.c_str(), signature.c_str())) {
LocalRefMapType localRefs;
t.env->CallStaticVoidMethod(t.classID, t.methodID, convert(localRefs, t, xs)...);
t.env->DeleteLocalRef(t.classID);
deleteLocalRefs(t.env, localRefs);
} else {
reportError(className, methodName, signature);
}
}
template <typename... Ts>
static bool callStaticBooleanMethod(const std::string& className,
const std::string& methodName,
Ts... xs) {
jboolean jret = JNI_FALSE;
cocos2d::JniMethodInfo t;
std::string signature = "(" + std::string(getJNISignature(xs...)) + ")Z";
if (cocos2d::JniHelper::getStaticMethodInfo(t, className.c_str(), methodName.c_str(), signature.c_str())) {
LocalRefMapType localRefs;
jret = t.env->CallStaticBooleanMethod(t.classID, t.methodID, convert(localRefs, t, xs)...);
t.env->DeleteLocalRef(t.classID);
deleteLocalRefs(t.env, localRefs);
} else {
reportError(className, methodName, signature);
}
return (jret == JNI_TRUE);
}
template <typename... Ts>
static int callStaticIntMethod(const std::string& className,
const std::string& methodName,
Ts... xs) {
jint ret = 0;
cocos2d::JniMethodInfo t;
std::string signature = "(" + std::string(getJNISignature(xs...)) + ")I";
if (cocos2d::JniHelper::getStaticMethodInfo(t, className.c_str(), methodName.c_str(), signature.c_str())) {
LocalRefMapType localRefs;
ret = t.env->CallStaticIntMethod(t.classID, t.methodID, convert(t, xs)...);
t.env->DeleteLocalRef(t.classID);
deleteLocalRefs(t.env, localRefs);
} else {
reportError(className, methodName, signature);
}
return ret;
}
template <typename... Ts>
static float callStaticFloatMethod(const std::string& className,
const std::string& methodName,
Ts... xs) {
jfloat ret = 0.0;
cocos2d::JniMethodInfo t;
std::string signature = "(" + std::string(getJNISignature(xs...)) + ")F";
if (cocos2d::JniHelper::getStaticMethodInfo(t, className.c_str(), methodName.c_str(), signature.c_str())) {
LocalRefMapType localRefs;
ret = t.env->CallStaticFloatMethod(t.classID, t.methodID, convert(localRefs, t, xs)...);
t.env->DeleteLocalRef(t.classID);
deleteLocalRefs(t.env, localRefs);
} else {
reportError(className, methodName, signature);
}
return ret;
}
template <typename... Ts>
static float* callStaticFloatArrayMethod(const std::string& className,
const std::string& methodName,
Ts... xs) {
static float ret[32];
cocos2d::JniMethodInfo t;
std::string signature = "(" + std::string(getJNISignature(xs...)) + ")[F";
if (cocos2d::JniHelper::getStaticMethodInfo(t, className.c_str(), methodName.c_str(), signature.c_str())) {
LocalRefMapType localRefs;
jfloatArray array = (jfloatArray) t.env->CallStaticObjectMethod(t.classID, t.methodID, convert(localRefs, t, xs)...);
jsize len = t.env->GetArrayLength(array);
if (len <= 32) {
jfloat* elems = t.env->GetFloatArrayElements(array, 0);
if (elems) {
memcpy(ret, elems, sizeof(float) * len);
t.env->ReleaseFloatArrayElements(array, elems, 0);
};
}
t.env->DeleteLocalRef(array);
t.env->DeleteLocalRef(t.classID);
deleteLocalRefs(t.env, localRefs);
return &ret[0];
} else {
reportError(className, methodName, signature);
}
return nullptr;
}
template <typename... Ts>
static Vec3 callStaticVec3Method(const std::string& className,
const std::string& methodName,
Ts... xs) {
Vec3 ret;
cocos2d::JniMethodInfo t;
std::string signature = "(" + std::string(getJNISignature(xs...)) + ")[F";
if (cocos2d::JniHelper::getStaticMethodInfo(t, className.c_str(), methodName.c_str(), signature.c_str())) {
LocalRefMapType localRefs;
jfloatArray array = (jfloatArray) t.env->CallStaticObjectMethod(t.classID, t.methodID, convert(localRefs, t, xs)...);
jsize len = t.env->GetArrayLength(array);
if (len == 3) {
jfloat* elems = t.env->GetFloatArrayElements(array, 0);
ret.x = elems[0];
ret.y = elems[1];
ret.z = elems[2];
t.env->ReleaseFloatArrayElements(array, elems, 0);
}
t.env->DeleteLocalRef(array);
t.env->DeleteLocalRef(t.classID);
deleteLocalRefs(t.env, localRefs);
} else {
reportError(className, methodName, signature);
}
return ret;
}
template <typename... Ts>
static double callStaticDoubleMethod(const std::string& className,
const std::string& methodName,
Ts... xs) {
jdouble ret = 0.0;
cocos2d::JniMethodInfo t;
std::string signature = "(" + std::string(getJNISignature(xs...)) + ")D";
if (cocos2d::JniHelper::getStaticMethodInfo(t, className.c_str(), methodName.c_str(), signature.c_str())) {
LocalRefMapType localRefs;
ret = t.env->CallStaticDoubleMethod(t.classID, t.methodID, convert(localRefs, t, xs)...);
t.env->DeleteLocalRef(t.classID);
deleteLocalRefs(t.env, localRefs);
} else {
reportError(className, methodName, signature);
}
return ret;
}
template <typename... Ts>
static std::string callStaticStringMethod(const std::string& className,
const std::string& methodName,
Ts... xs) {
std::string ret;
cocos2d::JniMethodInfo t;
std::string signature = "(" + std::string(getJNISignature(xs...)) + ")Ljava/lang/String;";
if (cocos2d::JniHelper::getStaticMethodInfo(t, className.c_str(), methodName.c_str(), signature.c_str())) {
LocalRefMapType localRefs;
jstring jret = (jstring)t.env->CallStaticObjectMethod(t.classID, t.methodID, convert(localRefs, t, xs)...);
ret = cocos2d::JniHelper::jstring2string(jret);
t.env->DeleteLocalRef(t.classID);
t.env->DeleteLocalRef(jret);
deleteLocalRefs(t.env, localRefs);
} else {
reportError(className, methodName, signature);
}
return ret;
}
private:
static JNIEnv* cacheEnv(JavaVM* jvm);
static bool getMethodInfo_DefaultClassLoader(JniMethodInfo &methodinfo,
const char *className,
const char *methodName,
const char *paramCode);
static JavaVM* _psJavaVM;
static jobject _activity;
static jstring convert(LocalRefMapType &localRefs, cocos2d::JniMethodInfo& t, const char* x);
static jstring convert(LocalRefMapType &localRefs, cocos2d::JniMethodInfo& t, const std::string& x);
template <typename T>
static T convert(LocalRefMapType &localRefs, cocos2d::JniMethodInfo&, T x) {
return x;
}
static void deleteLocalRefs(JNIEnv* env, LocalRefMapType &localRefs);
static std::string getJNISignature() {
return "";
}
static std::string getJNISignature(bool) {
return "Z";
}
// jchar is unsigned 16 bits, we do char => jchar conversion on purpose
static std::string getJNISignature(char) {
return "C";
}
static std::string getJNISignature(jshort) {
return "S";
}
static std::string getJNISignature(jint) {
return "I";
}
static std::string getJNISignature(jlong) {
return "J";
}
static std::string getJNISignature(jfloat) {
return "F";
}
static std::string getJNISignature(jdouble) {
return "D";
}
static std::string getJNISignature(jbyteArray) {
return "[B";
}
static std::string getJNISignature(const char*) {
return "Ljava/lang/String;";
}
static std::string getJNISignature(const std::string&) {
return "Ljava/lang/String;";
}
template <typename T>
static std::string getJNISignature(T x) {
// This template should never be instantiated
static_assert(sizeof(x) == 0, "Unsupported argument type");
return "";
}
template <typename T, typename... Ts>
static std::string getJNISignature(T x, Ts... xs) {
return getJNISignature(x) + getJNISignature(xs...);
}
static void reportError(const std::string& className, const std::string& methodName, const std::string& signature);
};
NS_CC_END
#endif // __ANDROID_JNI_HELPER_H__

View File

@@ -0,0 +1,680 @@
/****************************************************************************
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 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.
****************************************************************************/
#include "JniImp.h"
#include <unordered_map>
#include <android/log.h>
#include <android/asset_manager_jni.h>
#include <jni.h>
#include <mutex>
#include "JniHelper.h"
#include "platform/CCApplication.h"
#include "scripting/js-bindings/jswrapper/SeApi.h"
#include "scripting/js-bindings/event/EventDispatcher.h"
#include "platform/android/CCFileUtils-android.h"
#include "base/CCScheduler.h"
#include "base/CCAutoreleasePool.h"
#include "base/CCGLUtils.h"
#define JNI_IMP_LOG_TAG "JniImp"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,JNI_IMP_LOG_TAG,__VA_ARGS__)
#ifndef ORG_RENDER_CLASS_NAME
#define ORG_RENDER_CLASS_NAME org_cocos2dx_lib_Cocos2dxRenderer
#endif
#define JNI_RENDER(FUNC) JNI_METHOD1(ORG_RENDER_CLASS_NAME,FUNC)
#ifndef ORG_ACTIVITY_CLASS_NAME
#define ORG_ACTIVITY_CLASS_NAME org_cocos2dx_lib_Cocos2dxActivity
#endif
#define JNI_ACTIVITY(FUNC) JNI_METHOD1(ORG_ACTIVITY_CLASS_NAME,FUNC)
#ifndef ORG_ACCELEROMETER_CLASS_NAME
#define ORG_ACCELEROMETER_CLASS_NAME org_cocos2dx_lib_Cocos2dxAccelerometer
#endif
#define JNI_ACCELEROMETER(FUNC) JNI_METHOD1(ORG_ACCELEROMETER_CLASS_NAME,FUNC)
#ifndef ORG_HELPER_CLASS_NAME
#define ORG_HELPER_CLASS_NAME org_cocos2dx_lib_Cocos2dxHelper
#endif
#define JNI_HELPER(FUNC) JNI_METHOD1(ORG_HELPER_CLASS_NAME,FUNC)
#ifndef ORG_AUDIOFOCUS_CLASS_NAME
#define ORG_AUDIOFOCUS_CLASS_NAME org_cocos2dx_lib_Cocos2dxAudioFocusManager
#endif
#define JNI_AUDIO(FUNC) JNI_METHOD1(ORG_AUDIOFOCUS_CLASS_NAME,FUNC)
#ifndef JCLS_HELPER
#define JCLS_HELPER "org/cocos2dx/lib/Cocos2dxHelper"
#endif
#ifndef JCLS_RENDERER
#define JCLS_RENDERER "org/cocos2dx/lib/Cocos2dxRenderer"
#endif
#define KEYCODE_BACK 0x04
#define KEYCODE_MENU 0x52
#define KEYCODE_DPAD_UP 0x13
#define KEYCODE_DPAD_DOWN 0x14
#define KEYCODE_DPAD_LEFT 0x15
#define KEYCODE_DPAD_RIGHT 0x16
#define KEYCODE_ENTER 0x42
#define KEYCODE_DPAD_CENTER 0x17
using namespace cocos2d;
extern uint32_t __jsbInvocationCount;
namespace
{
bool __isOpenDebugView = false;
bool __isGLOptModeEnabled = true;
std::string g_apkPath;
EditTextCallback s_editTextCallback = nullptr;
void* s_ctx = nullptr;
int g_deviceSampleRate = 44100;
int g_deviceAudioBufferSizeInFrames = 192;
int g_width = 0;
int g_height = 0;
bool g_isStarted = false;
bool g_isGameFinished = false;
int g_SDKInt = 0;
cocos2d::Application* g_app = nullptr;
bool setCanvasCallback(se::Object* global)
{
se::AutoHandleScope scope;
se::ScriptEngine* se = se::ScriptEngine::getInstance();
char commandBuf[200] = {0};
uint8_t devicePixelRatio = Application::getInstance()->getDevicePixelRatio();
sprintf(commandBuf, "window.innerWidth = %d; window.innerHeight = %d;",
g_width / devicePixelRatio,
g_height / devicePixelRatio);
se->evalString(commandBuf);
glViewport(0, 0, g_width / devicePixelRatio, g_height / devicePixelRatio);
glDepthMask(GL_TRUE);
return true;
}
}
void cocos_jni_env_init (JNIEnv* env);
Application* cocos_android_app_init(JNIEnv* env, int width, int height);
extern "C"
{
void getSDKInt(JNIEnv* env)
{
if (env && g_SDKInt == 0)
{
// VERSION is a nested class within android.os.Build (hence "$" rather than "/")
jclass versionClass = env->FindClass("android/os/Build$VERSION");
if (NULL == versionClass)
return;
jfieldID sdkIntFieldID = env->GetStaticFieldID(versionClass, "SDK_INT", "I");
if (NULL == sdkIntFieldID)
return;
g_SDKInt = env->GetStaticIntField(versionClass, sdkIntFieldID);
}
}
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
JniHelper::setJavaVM(vm);
cocos_jni_env_init(JniHelper::getEnv());
getSDKInt(JniHelper::getEnv());
return JNI_VERSION_1_4;
}
/*****************************************************
* Cocos2dxActivity native functions implementation.
*****************************************************/
JNIEXPORT jintArray JNICALL JNI_ACTIVITY(getGLContextAttrs)(JNIEnv* env, jobject thiz)
{
//REFINE
int tmp[7] = {8, 8, 8,
8, 0, 0, 0};
jintArray glContextAttrsJava = env->NewIntArray(7);
env->SetIntArrayRegion(glContextAttrsJava, 0, 7, tmp);
return glContextAttrsJava;
}
/*****************************************************
* Cocos2dxRenderer native functions implementation.
*****************************************************/
JNIEXPORT void JNICALL JNI_RENDER(nativeInit)(JNIEnv* env, jobject thiz, jint w, jint h, jstring jDefaultResourcePath)
{
g_width = w;
g_height = h;
g_app = cocos_android_app_init(env, w, h);
g_isGameFinished = false;
ccInvalidateStateCache();
std::string defaultResourcePath = JniHelper::jstring2string(jDefaultResourcePath);
LOGD("nativeInit: %d, %d, %s", w, h, defaultResourcePath.c_str());
if (!defaultResourcePath.empty())
FileUtils::getInstance()->setDefaultResourceRootPath(defaultResourcePath);
se::ScriptEngine* se = se::ScriptEngine::getInstance();
se->addRegisterCallback(setCanvasCallback);
EventDispatcher::init();
g_app->start();
g_isStarted = true;
}
JNIEXPORT void JNICALL JNI_RENDER(nativeRender)(JNIEnv* env)
{
if (g_isGameFinished)
{
// with Application destructor called, native resource will be released
delete g_app;
g_app = nullptr;
JniHelper::callStaticVoidMethod(JCLS_HELPER, "endApplication");
return;
}
if (!g_isStarted)
{
auto scheduler = Application::getInstance()->getScheduler();
scheduler->removeAllFunctionsToBePerformedInCocosThread();
scheduler->unscheduleAll();
se::ScriptEngine::getInstance()->cleanup();
cocos2d::PoolManager::getInstance()->getCurrentPool()->clear();
//REFINE: Wait HttpClient, WebSocket, Audio thread to exit
ccInvalidateStateCache();
se::ScriptEngine* se = se::ScriptEngine::getInstance();
se->addRegisterCallback(setCanvasCallback);
EventDispatcher::init();
if(!g_app->applicationDidFinishLaunching())
{
g_isGameFinished = true;
return;
}
g_isStarted = true;
}
static std::chrono::steady_clock::time_point prevTime;
static std::chrono::steady_clock::time_point now;
static float dt = 0.f;
static float dtSum = 0.f;
static uint32_t jsbInvocationTotalCount = 0;
static uint32_t jsbInvocationTotalFrames = 0;
bool downsampleEnabled = g_app->isDownsampleEnabled();
if (downsampleEnabled)
g_app->getRenderTexture()->prepare();
g_app->getScheduler()->update(dt);
EventDispatcher::dispatchTickEvent(dt);
if (downsampleEnabled)
g_app->getRenderTexture()->draw();
PoolManager::getInstance()->getCurrentPool()->clear();
now = std::chrono::steady_clock::now();
dt = std::chrono::duration_cast<std::chrono::microseconds>(now - prevTime).count() / 1000000.f;
prevTime = std::chrono::steady_clock::now();
if (__isOpenDebugView)
{
dtSum += dt;
++jsbInvocationTotalFrames;
jsbInvocationTotalCount += __jsbInvocationCount;
if (dtSum > 1.0f)
{
dtSum = 0.0f;
setJSBInvocationCountJNI(jsbInvocationTotalCount / jsbInvocationTotalFrames);
jsbInvocationTotalCount = 0;
jsbInvocationTotalFrames = 0;
}
}
__jsbInvocationCount = 0;
}
JNIEXPORT void JNICALL JNI_RENDER(nativeOnPause)()
{
if (g_isGameFinished) {
return;
}
if (g_app)
g_app->onPause();
}
JNIEXPORT void JNICALL JNI_RENDER(nativeOnResume)()
{
if (g_isGameFinished) {
return;
}
if (g_app)
g_app->onResume();
}
JNIEXPORT void JNICALL JNI_RENDER(nativeInsertText)(JNIEnv* env, jobject thiz, jstring text)
{
//REFINE
}
JNIEXPORT void JNICALL JNI_RENDER(nativeDeleteBackward)(JNIEnv* env, jobject thiz)
{
//REFINE
}
JNIEXPORT jstring JNICALL JNI_RENDER(nativeGetContentText)()
{
//REFINE
}
JNIEXPORT void JNICALL JNI_RENDER(nativeOnSurfaceChanged)(JNIEnv* env, jobject thiz, jint w, jint h)
{
//REFINE
}
/***********************************************************
* Cocos2dxAccelerometer native functions implementation.
***********************************************************/
JNIEXPORT void JNICALL JNI_ACCELEROMETER(onSensorChanged)(JNIEnv* env, jobject thiz, jfloat x, jfloat y, jfloat z, jlong timeStamp)
{
//REFINE
}
/***********************************************************
* Touches native functions implementation.
***********************************************************/
static void dispatchTouchEventWithOnePoint(JNIEnv* env, cocos2d::TouchEvent::Type type, jint id, jfloat x, jfloat y)
{
if (g_isGameFinished) {
return;
}
cocos2d::TouchEvent touchEvent;
touchEvent.type = type;
uint8_t devicePixelRatio = Application::getInstance()->getDevicePixelRatio();
cocos2d::TouchInfo touchInfo;
touchInfo.index = id;
touchInfo.x = x / devicePixelRatio;
touchInfo.y = y / devicePixelRatio;
touchEvent.touches.push_back(touchInfo);
cocos2d::EventDispatcher::dispatchTouchEvent(touchEvent);
}
static void dispatchTouchEventWithPoints(JNIEnv* env, cocos2d::TouchEvent::Type type, jintArray ids, jfloatArray xs, jfloatArray ys)
{
if (g_isGameFinished) {
return;
}
cocos2d::TouchEvent touchEvent;
touchEvent.type = type;
int size = env->GetArrayLength(ids);
jint id[size];
jfloat x[size];
jfloat y[size];
env->GetIntArrayRegion(ids, 0, size, id);
env->GetFloatArrayRegion(xs, 0, size, x);
env->GetFloatArrayRegion(ys, 0, size, y);
uint8_t devicePixelRatio = Application::getInstance()->getDevicePixelRatio();
for(int i = 0; i < size; i++)
{
cocos2d::TouchInfo touchInfo;
touchInfo.index = id[i];
touchInfo.x = x[i] / devicePixelRatio;
touchInfo.y = y[i] / devicePixelRatio;
touchEvent.touches.push_back(touchInfo);
}
cocos2d::EventDispatcher::dispatchTouchEvent(touchEvent);
}
JNIEXPORT void JNICALL JNI_RENDER(nativeTouchesBegin)(JNIEnv * env, jobject thiz, jint id, jfloat x, jfloat y)
{
if (g_isGameFinished) {
return;
}
dispatchTouchEventWithOnePoint(env, cocos2d::TouchEvent::Type::BEGAN, id, x, y);
}
JNIEXPORT void JNICALL JNI_RENDER(nativeTouchesEnd)(JNIEnv * env, jobject thiz, jint id, jfloat x, jfloat y)
{
if (g_isGameFinished) {
return;
}
dispatchTouchEventWithOnePoint(env, cocos2d::TouchEvent::Type::ENDED, id, x, y);
}
JNIEXPORT void JNICALL JNI_RENDER(nativeTouchesMove)(JNIEnv * env, jobject thiz, jintArray ids, jfloatArray xs, jfloatArray ys)
{
if (g_isGameFinished) {
return;
}
dispatchTouchEventWithPoints(env, cocos2d::TouchEvent::Type::MOVED, ids, xs, ys);
}
JNIEXPORT void JNICALL JNI_RENDER(nativeTouchesCancel)(JNIEnv * env, jobject thiz, jintArray ids, jfloatArray xs, jfloatArray ys)
{
if (g_isGameFinished) {
return;
}
dispatchTouchEventWithPoints(env, cocos2d::TouchEvent::Type::CANCELLED, ids, xs, ys);
}
JNIEXPORT jboolean JNICALL JNI_RENDER(nativeKeyEvent)(JNIEnv * env, jobject thiz, jint keyCode, jboolean isPressed)
{
if (g_isGameFinished) {
return JNI_TRUE;
}
int keyInWeb = -1;
// key values in web, refer to http://docs.cocos.com/creator/api/en/enums/KEY.html
switch(keyCode)
{
case KEYCODE_BACK:
keyInWeb = 6;
break;
case KEYCODE_ENTER:
keyInWeb = 13;
break;
case KEYCODE_MENU:
keyInWeb = 18;
break;
case KEYCODE_DPAD_UP:
keyInWeb = 1003;
break;
case KEYCODE_DPAD_DOWN:
keyInWeb = 1004;
break;
case KEYCODE_DPAD_LEFT:
keyInWeb = 1000;
break;
case KEYCODE_DPAD_RIGHT:
keyInWeb = 1001;
break;
case KEYCODE_DPAD_CENTER:
keyInWeb = 1005;
break;
default:
keyInWeb = 0; // If the key can't be identified, this value is 0
}
KeyboardEvent event;
event.key = keyInWeb;
event.action = isPressed ? KeyboardEvent::Action::PRESS : KeyboardEvent::Action::RELEASE;
EventDispatcher::dispatchKeyboardEvent(event);
return JNI_TRUE;
}
/***********************************************************
* Cocos2dxHelper native functions implementation.
***********************************************************/
JNIEXPORT void JNICALL JNI_HELPER(nativeSetApkPath)(JNIEnv* env, jobject thiz, jstring apkPath)
{
g_apkPath = JniHelper::jstring2string(apkPath);
}
JNIEXPORT void JNICALL JNI_HELPER(nativeSetContext)(JNIEnv* env, jobject thiz, jobject context, jobject assetManager)
{
JniHelper::setClassLoaderFrom(context);
FileUtilsAndroid::setassetmanager(AAssetManager_fromJava(env, assetManager));
}
JNIEXPORT void JNICALL JNI_HELPER(nativeSetAudioDeviceInfo)(JNIEnv* env, jobject thiz, jboolean isSupportLowLatency, jint deviceSampleRate, jint deviceAudioBufferSizeInFrames)
{
g_deviceSampleRate = deviceSampleRate;
g_deviceAudioBufferSizeInFrames = deviceAudioBufferSizeInFrames;
LOGD("nativeSetAudioDeviceInfo: sampleRate: %d, bufferSizeInFrames: %d", g_deviceSampleRate, g_deviceAudioBufferSizeInFrames);
}
JNIEXPORT void JNICALL JNI_HELPER(nativeSetEditTextDialogResult)(JNIEnv* env, jobject obj, jbyteArray text)
{
jsize size = env->GetArrayLength(text);
if (size > 0)
{
jbyte * data = (jbyte*)env->GetByteArrayElements(text, 0);
char* buffer = (char*)malloc(size+1);
if (buffer != nullptr)
{
memcpy(buffer, data, size);
buffer[size] = '\0';
// pass data to edittext's delegate
if (s_editTextCallback)
s_editTextCallback(buffer, s_ctx);
free(buffer);
}
env->ReleaseByteArrayElements(text, data, 0);
}
else
{
if (s_editTextCallback)
s_editTextCallback("", s_ctx);
}
}
/***********************************************************
* Cocos2dxAudioFocusManager native functions implementation.
***********************************************************/
JNIEXPORT void JNICALL JNI_AUDIO(nativeOnAudioFocusChange)(JNIEnv* env, jobject thiz, jint focusChange)
{
// cocos_audioengine_focus_change(focusChange);
}
} // end of extern "C"
void restartJSVM()
{
g_isStarted = false;
}
/***********************************************************
* Functions invoke from cpp to Java.
***********************************************************/
std::string getApkPathJNI()
{
return g_apkPath;
}
std::string getPackageNameJNI()
{
return JniHelper::callStaticStringMethod(JCLS_HELPER, "getPackageName");
}
int getObbAssetFileDescriptorJNI(const std::string& path, long* startOffset, long* size)
{
JniMethodInfo methodInfo;
int fd = 0;
if (JniHelper::getStaticMethodInfo(methodInfo, JCLS_HELPER, "getObbAssetFileDescriptor", "(Ljava/lang/String;)[J"))
{
jstring stringArg = methodInfo.env->NewStringUTF(path.c_str());
jlongArray newArray = (jlongArray)methodInfo.env->CallStaticObjectMethod(methodInfo.classID, methodInfo.methodID, stringArg);
jsize theArrayLen = methodInfo.env->GetArrayLength(newArray);
if (3 == theArrayLen)
{
jboolean copy = JNI_FALSE;
jlong *array = methodInfo.env->GetLongArrayElements(newArray, &copy);
fd = static_cast<int>(array[0]);
*startOffset = array[1];
*size = array[2];
methodInfo.env->ReleaseLongArrayElements(newArray, array, 0);
}
methodInfo.env->DeleteLocalRef(methodInfo.classID);
methodInfo.env->DeleteLocalRef(stringArg);
}
return fd;
}
int getDeviceSampleRateJNI()
{
return g_deviceSampleRate;
}
int getDeviceAudioBufferSizeInFramesJNI()
{
return g_deviceAudioBufferSizeInFrames;
}
void convertEncodingJNI(const std::string& src, int byteSize, const std::string& fromCharset, std::string& dst, const std::string& newCharset)
{
JniMethodInfo methodInfo;
if (JniHelper::getStaticMethodInfo(methodInfo, JCLS_HELPER, "conversionEncoding", "([BLjava/lang/String;Ljava/lang/String;)[B"))
{
jbyteArray strArray = methodInfo.env->NewByteArray(byteSize);
methodInfo.env->SetByteArrayRegion(strArray, 0, byteSize, reinterpret_cast<const jbyte*>(src.c_str()));
jstring stringArg1 = methodInfo.env->NewStringUTF(fromCharset.c_str());
jstring stringArg2 = methodInfo.env->NewStringUTF(newCharset.c_str());
jbyteArray newArray = (jbyteArray)methodInfo.env->CallStaticObjectMethod(methodInfo.classID, methodInfo.methodID, strArray, stringArg1, stringArg2);
jsize theArrayLen = methodInfo.env->GetArrayLength(newArray);
methodInfo.env->GetByteArrayRegion(newArray, 0, theArrayLen, (jbyte*)dst.c_str());
methodInfo.env->DeleteLocalRef(strArray);
methodInfo.env->DeleteLocalRef(stringArg1);
methodInfo.env->DeleteLocalRef(stringArg2);
methodInfo.env->DeleteLocalRef(newArray);
methodInfo.env->DeleteLocalRef(methodInfo.classID);
}
}
std::string getCurrentLanguageJNI()
{
return JniHelper::callStaticStringMethod(JCLS_HELPER, "getCurrentLanguage");
}
std::string getCurrentLanguageCodeJNI()
{
return JniHelper::callStaticStringMethod(JCLS_HELPER, "getCurrentLanguageCode");
}
std::string getSystemVersionJNI()
{
return JniHelper::callStaticStringMethod(JCLS_HELPER, "getSystemVersion");
}
bool openURLJNI(const std::string& url)
{
return JniHelper::callStaticBooleanMethod(JCLS_HELPER, "openURL", url);
}
void copyTextToClipboardJNI(const std::string& text)
{
JniHelper::callStaticVoidMethod(JCLS_HELPER, "copyTextToClipboard", text);
}
void setPreferredFramesPerSecondJNI(int fps)
{
JniHelper::callStaticVoidMethod(JCLS_RENDERER, "setPreferredFramesPerSecond", fps);
}
void setGameInfoDebugViewTextJNI(int index, const std::string& text)
{
if (!__isOpenDebugView)
return;
JniHelper::callStaticVoidMethod(JCLS_HELPER, "setGameInfoDebugViewText", index, text);
}
void setJSBInvocationCountJNI(int count)
{
if (!__isOpenDebugView)
return;
JniHelper::callStaticVoidMethod(JCLS_HELPER, "setJSBInvocationCount", count);
}
void openDebugViewJNI()
{
if (!__isOpenDebugView)
{
LOGD("openDebugViewJNI ...");
__isOpenDebugView = true;
JniHelper::callStaticVoidMethod(JCLS_HELPER, "openDebugView");
if (!__isGLOptModeEnabled)
{
JniHelper::callStaticVoidMethod(JCLS_HELPER, "disableBatchGLCommandsToNative");
}
}
}
void disableBatchGLCommandsToNativeJNI()
{
__isGLOptModeEnabled = false;
if (__isOpenDebugView)
{
JniHelper::callStaticVoidMethod(JCLS_HELPER, "disableBatchGLCommandsToNative");
}
}
void exitApplication()
{
g_isGameFinished = true;
}
bool getApplicationExited()
{
return g_isGameFinished;
}
int getAndroidSDKInt()
{
return g_SDKInt;
}

View File

@@ -0,0 +1,54 @@
/****************************************************************************
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 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.
****************************************************************************/
#pragma once
#include <string>
typedef void (*EditTextCallback)(const std::string& text, void* ctx);
extern bool getApplicationExited();
extern void restartJSVM();
extern void exitApplication();
extern std::string getApkPathJNI();
extern std::string getPackageNameJNI();
extern int getObbAssetFileDescriptorJNI(const std::string& path, long* startOffset, long* size);
extern void convertEncodingJNI(const std::string& src, int byteSize, const std::string& fromCharset, std::string& dst, const std::string& newCharset);
extern int getDeviceSampleRateJNI();
extern int getDeviceAudioBufferSizeInFramesJNI();
extern std::string getCurrentLanguageJNI();
extern std::string getCurrentLanguageCodeJNI();
extern std::string getSystemVersionJNI();
extern bool openURLJNI(const std::string& url);
extern void copyTextToClipboardJNI(const std::string& text);
extern void setPreferredFramesPerSecondJNI(int fps);
extern void setGameInfoDebugViewTextJNI(int index, const std::string& text);
extern void setJSBInvocationCountJNI(int count);
extern void openDebugViewJNI();
extern void disableBatchGLCommandsToNativeJNI();
extern int getAndroidSDKInt();

View File

@@ -0,0 +1,7 @@
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures

View File

@@ -0,0 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.cocos2dx.lib">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
</manifest>

View File

@@ -0,0 +1,34 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion PROP_COMPILE_SDK_VERSION.toInteger()
buildToolsVersion PROP_BUILD_TOOLS_VERSION
defaultConfig {
minSdkVersion PROP_MIN_SDK_VERSION
targetSdkVersion PROP_TARGET_SDK_VERSION
versionCode 1
versionName "1.0"
}
sourceSets.main {
aidl.srcDir "../java/src"
java.srcDir "../java/src"
manifest.srcFile "AndroidManifest.xml"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: '../java/libs')
}

View File

@@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in E:\developSoftware\Android\SDK/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--EditBox Confirm Button Name-->
<string name="done" translatable="false">完成</string>
<string name="next" translatable="false">下一个</string>
<string name="search" translatable="false">搜索</string>
<string name="go" translatable="false">前往</string>
<string name="send" translatable="false">发送</string>
</resources>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--EditBox Confirm Button Title-->
<string name="done" translatable="false">Done</string>
<string name="next" translatable="false">Next</string>
<string name="search" translatable="false">Search</string>
<string name="go" translatable="false">Go</string>
<string name="send" translatable="false">Send</string>
</resources>