no message
This commit is contained in:
87
cocos/platform/BasePlatform.cpp
Normal file
87
cocos/platform/BasePlatform.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "platform/BasePlatform.h"
|
||||
#if (CC_PLATFORM == CC_PLATFORM_WINDOWS)
|
||||
#include "platform/win32/WindowsPlatform.h"
|
||||
#elif (CC_PLATFORM == CC_PLATFORM_ANDROID)
|
||||
#include "platform/android/AndroidPlatform.h"
|
||||
#elif (CC_PLATFORM == CC_PLATFORM_OHOS)
|
||||
#include "platform/ohos/OhosPlatform.h"
|
||||
#elif (CC_PLATFORM == CC_PLATFORM_MACOS)
|
||||
#include "platform/mac/MacPlatform.h"
|
||||
#elif (CC_PLATFORM == CC_PLATFORM_IOS)
|
||||
#include "platform/ios/IOSPlatform.h"
|
||||
#elif (CC_PLATFORM == CC_PLATFORM_LINUX)
|
||||
#include "platform/linux/LinuxPlatform.h"
|
||||
#elif (CC_PLATFORM == CC_PLATFORM_QNX)
|
||||
#include "platform/qnx/QnxPlatform.h"
|
||||
#elif (CC_PLATFORM == CC_PLATFORM_OPENHARMONY)
|
||||
#include "platform/openharmony/OpenHarmonyPlatform.h"
|
||||
#endif
|
||||
|
||||
namespace cc {
|
||||
BasePlatform* BasePlatform::_currentPlatform = nullptr;
|
||||
|
||||
BasePlatform::BasePlatform() {
|
||||
// Only one platform can be initialized.
|
||||
CC_ASSERT_NULL(_currentPlatform);
|
||||
_currentPlatform = this;
|
||||
}
|
||||
|
||||
BasePlatform::~BasePlatform() {
|
||||
_currentPlatform = nullptr;
|
||||
}
|
||||
|
||||
BasePlatform* BasePlatform::createDefaultPlatform() {
|
||||
#if (CC_PLATFORM == CC_PLATFORM_WINDOWS)
|
||||
static WindowsPlatform platform;
|
||||
#elif (CC_PLATFORM == CC_PLATFORM_ANDROID)
|
||||
static AndroidPlatform platform;
|
||||
#elif (CC_PLATFORM == CC_PLATFORM_OHOS)
|
||||
static OhosPlatform platform;
|
||||
#elif (CC_PLATFORM == CC_PLATFORM_MACOS)
|
||||
static MacPlatform platform;
|
||||
#elif (CC_PLATFORM == CC_PLATFORM_IOS)
|
||||
static IOSPlatform platform;
|
||||
#elif (CC_PLATFORM == CC_PLATFORM_LINUX)
|
||||
static LinuxPlatform platform;
|
||||
#elif (CC_PLATFORM == CC_PLATFORM_QNX)
|
||||
static QnxPlatform platform;
|
||||
#elif (CC_PLATFORM == CC_PLATFORM_OPENHARMONY)
|
||||
static OpenHarmonyPlatform platform;
|
||||
#endif
|
||||
return &platform;
|
||||
}
|
||||
|
||||
BasePlatform* BasePlatform::getPlatform() {
|
||||
if (_currentPlatform) {
|
||||
return _currentPlatform;
|
||||
}
|
||||
createDefaultPlatform();
|
||||
CC_ASSERT_NOT_NULL(_currentPlatform);
|
||||
return _currentPlatform;
|
||||
}
|
||||
|
||||
} // namespace cc
|
||||
181
cocos/platform/BasePlatform.h
Normal file
181
cocos/platform/BasePlatform.h
Normal file
@@ -0,0 +1,181 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "base/Log.h"
|
||||
#include "base/Macros.h"
|
||||
|
||||
#include "engine/EngineEvents.h"
|
||||
#include "platform/interfaces/modules/ISystem.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace cc {
|
||||
|
||||
class OSInterface;
|
||||
class ISystemWindow;
|
||||
|
||||
class CC_DLL BasePlatform {
|
||||
public:
|
||||
BasePlatform();
|
||||
/**
|
||||
* @brief Destructor of AbstratctPlatform.
|
||||
*/
|
||||
virtual ~BasePlatform();
|
||||
/**
|
||||
* @brief Get default system platform.
|
||||
*/
|
||||
static BasePlatform *getPlatform();
|
||||
/**
|
||||
* @brief Initialize system platform.
|
||||
*/
|
||||
virtual int32_t init() = 0;
|
||||
/**
|
||||
* @brief Run system platform.
|
||||
*/
|
||||
virtual int32_t run(int argc, const char **argv) = 0;
|
||||
/**
|
||||
* @brief Main business logic.
|
||||
*/
|
||||
virtual int32_t loop() = 0;
|
||||
|
||||
/**
|
||||
* @brief Polling event.
|
||||
*/
|
||||
virtual void pollEvent() = 0;
|
||||
|
||||
/**
|
||||
* @brief Exit platform.
|
||||
*/
|
||||
virtual void exit() = 0;
|
||||
|
||||
/**
|
||||
* @brief Get target system type.
|
||||
*/
|
||||
using OSType = ISystem::OSType;
|
||||
|
||||
virtual OSType getOSType() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the SDK version for Android.Other systems also have sdk versions,
|
||||
but they are not currently used.
|
||||
*/
|
||||
virtual int getSdkVersion() const = 0;
|
||||
/**
|
||||
* @brief Run the task in the platform thread,
|
||||
* @brief most platforms are the main thread, android is the non-main thread
|
||||
* @param task : Tasks running in platform threads
|
||||
* @param fps : Task call frequency
|
||||
*/
|
||||
using ThreadCallback = std::function<void(void)>;
|
||||
virtual void runInPlatformThread(const ThreadCallback &task) = 0;
|
||||
/**
|
||||
* @brief Get task call frequency.
|
||||
*/
|
||||
virtual int32_t getFps() const = 0;
|
||||
/**
|
||||
* @brief Set task call frequency.
|
||||
*/
|
||||
virtual void setFps(int32_t fps) = 0;
|
||||
|
||||
/**
|
||||
* @brief Get target system interface(Non thread safe.).
|
||||
*/
|
||||
template <class T>
|
||||
std::enable_if_t<std::is_base_of<OSInterface, T>::value, T *>
|
||||
getInterface() const {
|
||||
for (const auto &it : _osInterfaces) {
|
||||
T *intf = dynamic_cast<T *>(it.get());
|
||||
if (intf) {
|
||||
return intf;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::enable_if_t<std::is_base_of<OSInterface, T>::value, T *>
|
||||
getInterface() {
|
||||
for (const auto &it : _osInterfaces) {
|
||||
T *intf = dynamic_cast<T *>(it.get());
|
||||
if (intf) {
|
||||
return intf;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Registration system interface.
|
||||
*/
|
||||
bool registerInterface(const OSInterface::Ptr &osInterface) {
|
||||
CC_ASSERT_NOT_NULL(osInterface);
|
||||
auto it = std::find(_osInterfaces.begin(), _osInterfaces.end(), osInterface);
|
||||
if (it != _osInterfaces.end()) {
|
||||
CC_LOG_WARNING("Duplicate registration interface");
|
||||
return false;
|
||||
}
|
||||
_osInterfaces.push_back(osInterface);
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* @brief Unregistration system interface.
|
||||
*/
|
||||
void unregisterInterface(const OSInterface::Ptr &osInterface) {
|
||||
CC_ASSERT_NOT_NULL(osInterface);
|
||||
auto it = std::find(_osInterfaces.begin(), _osInterfaces.end(), osInterface);
|
||||
if (it != _osInterfaces.end()) {
|
||||
CC_LOG_WARNING("Interface is not registrated");
|
||||
return;
|
||||
}
|
||||
_osInterfaces.erase(it);
|
||||
}
|
||||
|
||||
void unregisterAllInterfaces() {
|
||||
_osInterfaces.clear();
|
||||
}
|
||||
|
||||
virtual ISystemWindow *createNativeWindow(uint32_t windowId, void *externalHandle) = 0;
|
||||
|
||||
private:
|
||||
static BasePlatform *createDefaultPlatform();
|
||||
|
||||
static BasePlatform *_currentPlatform; // NOLINT(readability-identifier-naming)
|
||||
std::vector<OSInterface::Ptr> _osInterfaces;
|
||||
CC_DISALLOW_COPY_MOVE_ASSIGN(BasePlatform);
|
||||
};
|
||||
} // namespace cc
|
||||
|
||||
#define START_PLATFORM(argc, argv) \
|
||||
do { \
|
||||
cc::BasePlatform *platform = cc::BasePlatform::getPlatform(); \
|
||||
if (platform->init()) { \
|
||||
CC_LOG_FATAL("Platform initialization failed"); \
|
||||
return -1; \
|
||||
} \
|
||||
return platform->run(argc, argv); \
|
||||
} while (0)
|
||||
1175
cocos/platform/FileUtils.cpp
Normal file
1175
cocos/platform/FileUtils.cpp
Normal file
File diff suppressed because it is too large
Load Diff
636
cocos/platform/FileUtils.h
Normal file
636
cocos/platform/FileUtils.h
Normal file
@@ -0,0 +1,636 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include "base/Data.h"
|
||||
#include "base/Macros.h"
|
||||
#include "base/Value.h"
|
||||
#include "base/std/container/string.h"
|
||||
#include "base/std/container/unordered_map.h"
|
||||
#include "base/std/container/vector.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
class ResizableBuffer {
|
||||
public:
|
||||
~ResizableBuffer() = default;
|
||||
virtual void resize(size_t size) = 0;
|
||||
virtual void *buffer() const = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class ResizableBufferAdapter {};
|
||||
|
||||
template <typename CharT, typename Traits, typename Allocator>
|
||||
class ResizableBufferAdapter<std::basic_string<CharT, Traits, Allocator>> : public ResizableBuffer {
|
||||
using BufferType = std::basic_string<CharT, Traits, Allocator>;
|
||||
BufferType *_buffer;
|
||||
|
||||
public:
|
||||
explicit ResizableBufferAdapter(BufferType *buffer) : _buffer(buffer) {}
|
||||
void resize(size_t size) override {
|
||||
_buffer->resize((size + sizeof(CharT) - 1) / sizeof(CharT));
|
||||
}
|
||||
void *buffer() const override {
|
||||
// can not invoke string::front() if it is empty
|
||||
return _buffer->empty() ? nullptr : &_buffer->front();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class ResizableBufferAdapter<ccstd::vector<T>> : public ResizableBuffer {
|
||||
using BufferType = ccstd::vector<T>;
|
||||
BufferType *_buffer;
|
||||
|
||||
public:
|
||||
explicit ResizableBufferAdapter(BufferType *buffer) : _buffer(buffer) {}
|
||||
void resize(size_t size) override {
|
||||
_buffer->resize((size + sizeof(T) - 1) / sizeof(T));
|
||||
}
|
||||
void *buffer() const override {
|
||||
// can not invoke vector::front() if it is empty
|
||||
return _buffer->empty() ? nullptr : &_buffer->front();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class ResizableBufferAdapter<Data> : public ResizableBuffer {
|
||||
using BufferType = Data;
|
||||
BufferType *_buffer;
|
||||
|
||||
public:
|
||||
explicit ResizableBufferAdapter(BufferType *buffer) : _buffer(buffer) {}
|
||||
void resize(size_t size) override {
|
||||
size_t oldSize = _buffer->getSize();
|
||||
if (oldSize != size) {
|
||||
// need to take buffer ownership for outer memory control
|
||||
auto *old = _buffer->takeBuffer();
|
||||
void *buffer = realloc(old, size);
|
||||
if (buffer) {
|
||||
_buffer->fastSet(static_cast<unsigned char *>(buffer), static_cast<uint32_t>(size));
|
||||
}
|
||||
}
|
||||
}
|
||||
void *buffer() const override {
|
||||
return _buffer->getBytes();
|
||||
}
|
||||
};
|
||||
|
||||
/** Helper class to handle file operations. */
|
||||
class CC_DLL FileUtils {
|
||||
public:
|
||||
/**
|
||||
* Gets the instance of FileUtils.
|
||||
*/
|
||||
static FileUtils *getInstance();
|
||||
|
||||
/**
|
||||
* Destroys the instance of FileUtils.
|
||||
*/
|
||||
CC_DEPRECATED(3.6.0)
|
||||
static void destroyInstance();
|
||||
|
||||
/**
|
||||
* You can inherit from platform dependent implementation of FileUtils, such as FileUtilsAndroid,
|
||||
* and use this function to set delegate, then FileUtils will invoke delegate's implementation.
|
||||
* For example, your resources are encrypted, so you need to decrypt it after reading data from
|
||||
* resources, then you can implement all getXXX functions, and engine will invoke your own getXX
|
||||
* functions when reading data of resources.
|
||||
*
|
||||
* If you don't want to system default implementation after setting delegate, you can just pass nullptr
|
||||
* to this function.
|
||||
*
|
||||
* @warning It will delete previous delegate
|
||||
*/
|
||||
static void setDelegate(FileUtils *delegate);
|
||||
|
||||
/**
|
||||
* The default constructor.
|
||||
*/
|
||||
FileUtils();
|
||||
|
||||
/**
|
||||
* The destructor of FileUtils.
|
||||
*/
|
||||
virtual ~FileUtils();
|
||||
|
||||
/**
|
||||
* Purges full path caches.
|
||||
*/
|
||||
virtual void purgeCachedEntries();
|
||||
|
||||
/**
|
||||
* Gets string from a file.
|
||||
*/
|
||||
virtual ccstd::string getStringFromFile(const ccstd::string &filename);
|
||||
|
||||
/**
|
||||
* Creates binary data from a file.
|
||||
* @return A data object.
|
||||
*/
|
||||
virtual Data getDataFromFile(const ccstd::string &filename);
|
||||
|
||||
enum class Status {
|
||||
OK = 0,
|
||||
NOT_EXISTS = 1, // File not exists
|
||||
OPEN_FAILED = 2, // Open file failed.
|
||||
READ_FAILED = 3, // Read failed
|
||||
NOT_INITIALIZED = 4, // FileUtils is not initializes
|
||||
TOO_LARGE = 5, // The file is too large (great than 2^32-1)
|
||||
OBTAIN_SIZE_FAILED = 6 // Failed to obtain the file size.
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets whole file contents as string from a file.
|
||||
*
|
||||
* Unlike getStringFromFile, these getContents methods:
|
||||
* - read file in binary mode (does not convert CRLF to LF).
|
||||
* - does not truncate the string when '\0' is found (returned string of getContents may have '\0' in the middle.).
|
||||
*
|
||||
* The template version of can accept cc::Data, std::basic_string and ccstd::vector.
|
||||
*
|
||||
* @code
|
||||
* ccstd::string sbuf;
|
||||
* FileUtils::getInstance()->getContents("path/to/file", &sbuf);
|
||||
*
|
||||
* ccstd::vector<int> vbuf;
|
||||
* FileUtils::getInstance()->getContents("path/to/file", &vbuf);
|
||||
*
|
||||
* Data dbuf;
|
||||
* FileUtils::getInstance()->getContents("path/to/file", &dbuf);
|
||||
* @endcode
|
||||
*
|
||||
* Note: if you read to ccstd::vector<T> and std::basic_string<T> where T is not 8 bit type,
|
||||
* you may get 0 ~ sizeof(T)-1 bytes padding.
|
||||
*
|
||||
* - To write a new buffer class works with getContents, just extend ResizableBuffer.
|
||||
* - To write a adapter for existing class, write a specialized ResizableBufferAdapter for that class, see follow code.
|
||||
*
|
||||
* @code
|
||||
* namespace cc { // ResizableBufferAdapter needed in cocos2d namespace.
|
||||
* template<>
|
||||
* class ResizableBufferAdapter<AlreadyExistsBuffer> : public ResizableBuffer {
|
||||
* public:
|
||||
* ResizableBufferAdapter(AlreadyExistsBuffer* buffer) {
|
||||
* // your code here
|
||||
* }
|
||||
* virtual void resize(size_t size) override {
|
||||
* // your code here
|
||||
* }
|
||||
* virtual void* buffer() const override {
|
||||
* // your code here
|
||||
* }
|
||||
* };
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] filename The resource file name which contains the path.
|
||||
* @param[out] buffer The buffer where the file contents are store to.
|
||||
* @return Returns:
|
||||
* - Status::OK when there is no error, the buffer is filled with the contents of file.
|
||||
* - Status::NotExists when file not exists, the buffer will not changed.
|
||||
* - Status::OpenFailed when cannot open file, the buffer will not changed.
|
||||
* - Status::ReadFailed when read end up before read whole, the buffer will fill with already read bytes.
|
||||
* - Status::NotInitialized when FileUtils is not initializes, the buffer will not changed.
|
||||
* - Status::TooLarge when there file to be read is too large (> 2^32-1), the buffer will not changed.
|
||||
* - Status::ObtainSizeFailed when failed to obtain the file size, the buffer will not changed.
|
||||
*/
|
||||
template <
|
||||
typename T,
|
||||
typename Enable = typename std::enable_if<
|
||||
std::is_base_of<ResizableBuffer, ResizableBufferAdapter<T>>::value>::type>
|
||||
Status getContents(const ccstd::string &filename, T *buffer) {
|
||||
ResizableBufferAdapter<T> buf(buffer);
|
||||
return getContents(filename, &buf);
|
||||
}
|
||||
virtual Status getContents(const ccstd::string &filename, ResizableBuffer *buffer);
|
||||
|
||||
/**
|
||||
* Gets resource file data from a zip file.
|
||||
*
|
||||
* @param[in] filename The resource file name which contains the relative path of the zip file.
|
||||
* @param[out] size If the file read operation succeeds, it will be the data size, otherwise 0.
|
||||
* @return Upon success, a pointer to the data is returned, otherwise nullptr.
|
||||
* @warning Recall: you are responsible for calling free() on any Non-nullptr pointer returned.
|
||||
*/
|
||||
virtual unsigned char *getFileDataFromZip(const ccstd::string &zipFilePath, const ccstd::string &filename, uint32_t *size);
|
||||
|
||||
/** Returns the fullpath for a given filename.
|
||||
|
||||
First it will try to get a new filename from the "filenameLookup" dictionary.
|
||||
If a new filename can't be found on the dictionary, it will use the original filename.
|
||||
Then it will try to obtain the full path of the filename using the FileUtils search rules: resolutions, and search paths.
|
||||
The file search is based on the array element order of search paths and resolution directories.
|
||||
|
||||
For instance:
|
||||
|
||||
We set two elements("/mnt/sdcard/", "internal_dir/") to search paths vector by setSearchPaths,
|
||||
and set three elements("resources-ipadhd/", "resources-ipad/", "resources-iphonehd")
|
||||
to resolutions vector by setSearchResolutionsOrder. The "internal_dir" is relative to "Resources/".
|
||||
|
||||
If we have a file named 'sprite.png', the mapping in fileLookup dictionary contains `key: sprite.png -> value: sprite.pvr.gz`.
|
||||
Firstly, it will replace 'sprite.png' with 'sprite.pvr.gz', then searching the file sprite.pvr.gz as follows:
|
||||
|
||||
/mnt/sdcard/resources-ipadhd/sprite.pvr.gz (if not found, search next)
|
||||
/mnt/sdcard/resources-ipad/sprite.pvr.gz (if not found, search next)
|
||||
/mnt/sdcard/resources-iphonehd/sprite.pvr.gz (if not found, search next)
|
||||
/mnt/sdcard/sprite.pvr.gz (if not found, search next)
|
||||
internal_dir/resources-ipadhd/sprite.pvr.gz (if not found, search next)
|
||||
internal_dir/resources-ipad/sprite.pvr.gz (if not found, search next)
|
||||
internal_dir/resources-iphonehd/sprite.pvr.gz (if not found, search next)
|
||||
internal_dir/sprite.pvr.gz (if not found, return "sprite.png")
|
||||
|
||||
If the filename contains relative path like "gamescene/uilayer/sprite.png",
|
||||
and the mapping in fileLookup dictionary contains `key: gamescene/uilayer/sprite.png -> value: gamescene/uilayer/sprite.pvr.gz`.
|
||||
The file search order will be:
|
||||
|
||||
/mnt/sdcard/gamescene/uilayer/resources-ipadhd/sprite.pvr.gz (if not found, search next)
|
||||
/mnt/sdcard/gamescene/uilayer/resources-ipad/sprite.pvr.gz (if not found, search next)
|
||||
/mnt/sdcard/gamescene/uilayer/resources-iphonehd/sprite.pvr.gz (if not found, search next)
|
||||
/mnt/sdcard/gamescene/uilayer/sprite.pvr.gz (if not found, search next)
|
||||
internal_dir/gamescene/uilayer/resources-ipadhd/sprite.pvr.gz (if not found, search next)
|
||||
internal_dir/gamescene/uilayer/resources-ipad/sprite.pvr.gz (if not found, search next)
|
||||
internal_dir/gamescene/uilayer/resources-iphonehd/sprite.pvr.gz (if not found, search next)
|
||||
internal_dir/gamescene/uilayer/sprite.pvr.gz (if not found, return "gamescene/uilayer/sprite.png")
|
||||
|
||||
If the new file can't be found on the file system, it will return the parameter filename directly.
|
||||
|
||||
This method was added to simplify multiplatform support. Whether you are using cocos2d-js or any cross-compilation toolchain like StellaSDK or Apportable,
|
||||
you might need to load different resources for a given file in the different platforms.
|
||||
|
||||
@since v2.1
|
||||
*/
|
||||
virtual ccstd::string fullPathForFilename(const ccstd::string &filename) const;
|
||||
|
||||
/**
|
||||
* Gets full path from a file name and the path of the relative file.
|
||||
* @param filename The file name.
|
||||
* @param relativeFile The path of the relative file.
|
||||
* @return The full path.
|
||||
* e.g. filename: hello.png, pszRelativeFile: /User/path1/path2/hello.plist
|
||||
* Return: /User/path1/path2/hello.pvr (If there a a key(hello.png)-value(hello.pvr) in FilenameLookup dictionary. )
|
||||
*
|
||||
*/
|
||||
virtual ccstd::string fullPathFromRelativeFile(const ccstd::string &filename, const ccstd::string &relativeFile);
|
||||
|
||||
/**
|
||||
* Sets the array of search paths.
|
||||
*
|
||||
* You can use this array to modify the search path of the resources.
|
||||
* If you want to use "themes" or search resources in the "cache", you can do it easily by adding new entries in this array.
|
||||
*
|
||||
* @note This method could access relative path and absolute path.
|
||||
* If the relative path was passed to the vector, FileUtils will add the default resource directory before the relative path.
|
||||
* For instance:
|
||||
* On Android, the default resource root path is "@assets/".
|
||||
* If "/mnt/sdcard/" and "resources-large" were set to the search paths vector,
|
||||
* "resources-large" will be converted to "@assets/resources-large" since it was a relative path.
|
||||
*
|
||||
* @param searchPaths The array contains search paths.
|
||||
* @see fullPathForFilename(const char*)
|
||||
* @since v2.1
|
||||
*/
|
||||
virtual void setSearchPaths(const ccstd::vector<ccstd::string> &searchPaths);
|
||||
|
||||
/**
|
||||
* Get default resource root path.
|
||||
*/
|
||||
const ccstd::string &getDefaultResourceRootPath() const;
|
||||
|
||||
/**
|
||||
* Set default resource root path.
|
||||
*/
|
||||
void setDefaultResourceRootPath(const ccstd::string &path);
|
||||
|
||||
/**
|
||||
* Add search path.
|
||||
*
|
||||
* @since v2.1
|
||||
*/
|
||||
void addSearchPath(const ccstd::string &path, bool front = false);
|
||||
|
||||
/**
|
||||
* Gets the array of search paths.
|
||||
*
|
||||
* @return The array of search paths which may contain the prefix of default resource root path.
|
||||
* @note In best practise, getter function should return the value of setter function passes in.
|
||||
* But since we should not break the compatibility, we keep using the old logic.
|
||||
* Therefore, If you want to get the original search paths, please call 'getOriginalSearchPaths()' instead.
|
||||
* @see fullPathForFilename(const char*).
|
||||
*/
|
||||
virtual const ccstd::vector<ccstd::string> &getSearchPaths() const;
|
||||
|
||||
/**
|
||||
* Gets the original search path array set by 'setSearchPaths' or 'addSearchPath'.
|
||||
* @return The array of the original search paths
|
||||
*/
|
||||
virtual const ccstd::vector<ccstd::string> &getOriginalSearchPaths() const;
|
||||
|
||||
/**
|
||||
* Gets the writable path.
|
||||
* @return The path that can be write/read a file in
|
||||
*/
|
||||
virtual ccstd::string getWritablePath() const = 0;
|
||||
|
||||
/**
|
||||
* Sets writable path.
|
||||
*/
|
||||
virtual void setWritablePath(const ccstd::string &writablePath);
|
||||
|
||||
/**
|
||||
* Converts the contents of a file to a ValueMap.
|
||||
* @param filename The filename of the file to gets content.
|
||||
* @return ValueMap of the file contents.
|
||||
* @note This method is used internally.
|
||||
*/
|
||||
virtual ValueMap getValueMapFromFile(const ccstd::string &filename);
|
||||
|
||||
/** Converts the contents of a file to a ValueMap.
|
||||
* This method is used internally.
|
||||
*/
|
||||
virtual ValueMap getValueMapFromData(const char *filedata, int filesize);
|
||||
|
||||
/**
|
||||
* write a ValueMap into a plist file
|
||||
*
|
||||
*@param dict the ValueMap want to save
|
||||
*@param fullPath The full path to the file you want to save a string
|
||||
*@return bool
|
||||
*/
|
||||
virtual bool writeToFile(const ValueMap &dict, const ccstd::string &fullPath);
|
||||
|
||||
/**
|
||||
* write a string into a file
|
||||
*
|
||||
* @param dataStr the string want to save
|
||||
* @param fullPath The full path to the file you want to save a string
|
||||
* @return bool True if write success
|
||||
*/
|
||||
virtual bool writeStringToFile(const ccstd::string &dataStr, const ccstd::string &fullPath);
|
||||
|
||||
/**
|
||||
* write Data into a file
|
||||
*
|
||||
*@param data the data want to save
|
||||
*@param fullPath The full path to the file you want to save a string
|
||||
*@return bool
|
||||
*/
|
||||
virtual bool writeDataToFile(const Data &data, const ccstd::string &fullPath);
|
||||
|
||||
/**
|
||||
* write ValueMap into a plist file
|
||||
*
|
||||
*@param dict the ValueMap want to save
|
||||
*@param fullPath The full path to the file you want to save a string
|
||||
*@return bool
|
||||
*/
|
||||
virtual bool writeValueMapToFile(const ValueMap &dict, const ccstd::string &fullPath);
|
||||
|
||||
/**
|
||||
* write ValueVector into a plist file
|
||||
*
|
||||
*@param vecData the ValueVector want to save
|
||||
*@param fullPath The full path to the file you want to save a string
|
||||
*@return bool
|
||||
*/
|
||||
virtual bool writeValueVectorToFile(const ValueVector &vecData, const ccstd::string &fullPath);
|
||||
|
||||
/**
|
||||
* Windows fopen can't support UTF-8 filename
|
||||
* Need convert all parameters fopen and other 3rd-party libs
|
||||
*
|
||||
* @param filenameUtf8 ccstd::string name file for conversion from utf-8
|
||||
* @return ccstd::string ansi filename in current locale
|
||||
*/
|
||||
virtual ccstd::string getSuitableFOpen(const ccstd::string &filenameUtf8) const;
|
||||
|
||||
// Converts the contents of a file to a ValueVector.
|
||||
// This method is used internally.
|
||||
virtual ValueVector getValueVectorFromFile(const ccstd::string &filename);
|
||||
|
||||
/**
|
||||
* Checks whether a file exists.
|
||||
*
|
||||
* @note If a relative path was passed in, it will be inserted a default root path at the beginning.
|
||||
* @param filename The path of the file, it could be a relative or absolute path.
|
||||
* @return True if the file exists, false if not.
|
||||
*/
|
||||
virtual bool isFileExist(const ccstd::string &filename) const;
|
||||
|
||||
/**
|
||||
* Gets filename extension is a suffix (separated from the base filename by a dot) in lower case.
|
||||
* Examples of filename extensions are .png, .jpeg, .exe, .dmg and .txt.
|
||||
* @param filePath The path of the file, it could be a relative or absolute path.
|
||||
* @return suffix for filename in lower case or empty if a dot not found.
|
||||
*/
|
||||
virtual ccstd::string getFileExtension(const ccstd::string &filePath) const;
|
||||
|
||||
/**
|
||||
* Checks whether the path is an absolute path.
|
||||
*
|
||||
* @note On Android, if the parameter passed in is relative to "@assets/", this method will treat it as an absolute path.
|
||||
* Also on Blackberry, path starts with "app/native/Resources/" is treated as an absolute path.
|
||||
*
|
||||
* @param path The path that needs to be checked.
|
||||
* @return True if it's an absolute path, false if not.
|
||||
*/
|
||||
virtual bool isAbsolutePath(const ccstd::string &path) const;
|
||||
|
||||
/**
|
||||
* Checks whether the path is a directory.
|
||||
*
|
||||
* @param dirPath The path of the directory, it could be a relative or an absolute path.
|
||||
* @return True if the directory exists, false if not.
|
||||
*/
|
||||
virtual bool isDirectoryExist(const ccstd::string &dirPath) const;
|
||||
|
||||
/**
|
||||
* List all files in a directory.
|
||||
*
|
||||
* @param dirPath The path of the directory, it could be a relative or an absolute path.
|
||||
* @return File paths in a string vector
|
||||
*/
|
||||
virtual ccstd::vector<ccstd::string> listFiles(const ccstd::string &dirPath) const;
|
||||
|
||||
/**
|
||||
* List all files recursively in a directory.
|
||||
*
|
||||
* @param dirPath The path of the directory, it could be a relative or an absolute path.
|
||||
* @return File paths in a string vector
|
||||
*/
|
||||
virtual void listFilesRecursively(const ccstd::string &dirPath, ccstd::vector<ccstd::string> *files) const;
|
||||
|
||||
/**
|
||||
* Creates a directory.
|
||||
*
|
||||
* @param dirPath The path of the directory, it must be an absolute path.
|
||||
* @return True if the directory have been created successfully, false if not.
|
||||
*/
|
||||
virtual bool createDirectory(const ccstd::string &dirPath);
|
||||
|
||||
/**
|
||||
* Removes a directory.
|
||||
*
|
||||
* @param dirPath The full path of the directory, it must be an absolute path.
|
||||
* @return True if the directory have been removed successfully, false if not.
|
||||
*/
|
||||
virtual bool removeDirectory(const ccstd::string &dirPath);
|
||||
|
||||
/**
|
||||
* Removes a file.
|
||||
*
|
||||
* @param filepath The full path of the file, it must be an absolute path.
|
||||
* @return True if the file have been removed successfully, false if not.
|
||||
*/
|
||||
virtual bool removeFile(const ccstd::string &filepath);
|
||||
|
||||
/**
|
||||
* Renames a file under the given directory.
|
||||
*
|
||||
* @param path The parent directory path of the file, it must be an absolute path.
|
||||
* @param oldname The current name of the file.
|
||||
* @param name The new name of the file.
|
||||
* @return True if the file have been renamed successfully, false if not.
|
||||
*/
|
||||
virtual bool renameFile(const ccstd::string &path, const ccstd::string &oldname, const ccstd::string &name);
|
||||
|
||||
/**
|
||||
* Renames a file under the given directory.
|
||||
*
|
||||
* @param oldfullpath The current fullpath of the file. Includes path and name.
|
||||
* @param newfullpath The new fullpath of the file. Includes path and name.
|
||||
* @return True if the file have been renamed successfully, false if not.
|
||||
*/
|
||||
virtual bool renameFile(const ccstd::string &oldfullpath, const ccstd::string &newfullpath);
|
||||
|
||||
/**
|
||||
* Retrieve the file size.
|
||||
*
|
||||
* @note If a relative path was passed in, it will be inserted a default root path at the beginning.
|
||||
* @param filepath The path of the file, it could be a relative or absolute path.
|
||||
* @return The file size.
|
||||
*/
|
||||
virtual long getFileSize(const ccstd::string &filepath); //NOLINT(google-runtime-int)
|
||||
|
||||
/** Returns the full path cache. */
|
||||
const ccstd::unordered_map<ccstd::string, ccstd::string> &getFullPathCache() const { return _fullPathCache; }
|
||||
|
||||
virtual ccstd::string normalizePath(const ccstd::string &path) const;
|
||||
virtual ccstd::string getFileDir(const ccstd::string &path) const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Initializes the instance of FileUtils. It will set _searchPathArray and _searchResolutionsOrderArray to default values.
|
||||
*
|
||||
* @note When you are porting Cocos2d-x to a new platform, you may need to take care of this method.
|
||||
* You could assign a default value to _defaultResRootPath in the subclass of FileUtils(e.g. FileUtilsAndroid). Then invoke the FileUtils::init().
|
||||
* @return true if succeed, otherwise it returns false.
|
||||
*
|
||||
*/
|
||||
virtual bool init();
|
||||
|
||||
/**
|
||||
* Checks whether a file exists without considering search paths and resolution orders.
|
||||
* @param filename The file (with absolute path) to look up for
|
||||
* @return Returns true if the file found at the given absolute path, otherwise returns false
|
||||
*/
|
||||
virtual bool isFileExistInternal(const ccstd::string &filename) const = 0;
|
||||
|
||||
/**
|
||||
* Checks whether a directory exists without considering search paths and resolution orders.
|
||||
* @param dirPath The directory (with absolute path) to look up for
|
||||
* @return Returns true if the directory found at the given absolute path, otherwise returns false
|
||||
*/
|
||||
virtual bool isDirectoryExistInternal(const ccstd::string &dirPath) const;
|
||||
|
||||
/**
|
||||
* Gets full path for filename, resolution directory and search path.
|
||||
*
|
||||
* @param filename The file name.
|
||||
* @param searchPath The search path.
|
||||
* @return The full path of the file. It will return an empty string if the full path of the file doesn't exist.
|
||||
*/
|
||||
virtual ccstd::string getPathForFilename(const ccstd::string &filename, const ccstd::string &searchPath) const;
|
||||
|
||||
/**
|
||||
* Gets full path for the directory and the filename.
|
||||
*
|
||||
* @note Only iOS and Mac need to override this method since they are using
|
||||
* `[[NSBundle mainBundle] pathForResource: ofType: inDirectory:]` to make a full path.
|
||||
* Other platforms will use the default implementation of this method.
|
||||
* @param directory The directory contains the file we are looking for.
|
||||
* @param filename The name of the file.
|
||||
* @return The full path of the file, if the file can't be found, it will return an empty string.
|
||||
*/
|
||||
virtual ccstd::string getFullPathForDirectoryAndFilename(const ccstd::string &directory, const ccstd::string &filename) const;
|
||||
|
||||
/**
|
||||
* The vector contains search paths.
|
||||
* The lower index of the element in this vector, the higher priority for this search path.
|
||||
*/
|
||||
ccstd::vector<ccstd::string> _searchPathArray;
|
||||
|
||||
/**
|
||||
* The search paths which was set by 'setSearchPaths' / 'addSearchPath'.
|
||||
*/
|
||||
ccstd::vector<ccstd::string> _originalSearchPaths;
|
||||
|
||||
/**
|
||||
* The default root path of resources.
|
||||
* If the default root path of resources needs to be changed, do it in the `init` method of FileUtils's subclass.
|
||||
* For instance:
|
||||
* On Android, the default root path of resources will be assigned with "@assets/" in FileUtilsAndroid::init().
|
||||
* Similarly on Blackberry, we assign "app/native/Resources/" to this variable in FileUtilsBlackberry::init().
|
||||
*/
|
||||
ccstd::string _defaultResRootPath;
|
||||
|
||||
/**
|
||||
* The full path cache. When a file is found, it will be added into this cache.
|
||||
* This variable is used for improving the performance of file search.
|
||||
*/
|
||||
mutable ccstd::unordered_map<ccstd::string, ccstd::string> _fullPathCache;
|
||||
|
||||
/**
|
||||
* Writable path.
|
||||
*/
|
||||
ccstd::string _writablePath;
|
||||
|
||||
/**
|
||||
* The singleton pointer of FileUtils.
|
||||
*/
|
||||
static FileUtils *sharedFileUtils;
|
||||
|
||||
/**
|
||||
* Remove null value key (for iOS)
|
||||
*/
|
||||
virtual void valueMapCompact(ValueMap &valueMap);
|
||||
virtual void valueVectorCompact(ValueVector &valueVector);
|
||||
};
|
||||
|
||||
// Can remove this function when refactoring file system.
|
||||
FileUtils *createFileUtils();
|
||||
|
||||
} // namespace cc
|
||||
1182
cocos/platform/Image.cpp
Normal file
1182
cocos/platform/Image.cpp
Normal file
File diff suppressed because it is too large
Load Diff
136
cocos/platform/Image.h
Normal file
136
cocos/platform/Image.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2016-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "base/RefCounted.h"
|
||||
#include "base/std/container/string.h"
|
||||
#include "gfx-base/GFXDef.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
class Image : public RefCounted {
|
||||
public:
|
||||
Image();
|
||||
~Image() override;
|
||||
|
||||
/** Supported formats for Image */
|
||||
enum class Format {
|
||||
//! JPEG
|
||||
JPG,
|
||||
//! PNG
|
||||
PNG,
|
||||
//! WebP
|
||||
WEBP,
|
||||
//! PVR
|
||||
PVR,
|
||||
//! ETC
|
||||
ETC,
|
||||
//! ETC2
|
||||
ETC2,
|
||||
//! ASTC
|
||||
ASTC,
|
||||
//! Compressed Data
|
||||
COMPRESSED,
|
||||
//! Raw Data
|
||||
RAW_DATA,
|
||||
//! Unknown format
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
bool initWithImageFile(const ccstd::string &path);
|
||||
bool initWithImageData(const unsigned char *data, uint32_t dataLen);
|
||||
|
||||
// @warning kFmtRawData only support RGBA8888
|
||||
bool initWithRawData(const unsigned char *data, uint32_t dataLen, int width, int height, int bitsPerComponent, bool preMulti = false);
|
||||
|
||||
// data will be free outside.
|
||||
inline void takeData(unsigned char **outData) {
|
||||
*outData = _data;
|
||||
_data = nullptr;
|
||||
}
|
||||
|
||||
// Getters
|
||||
inline unsigned char *getData() const { return _data; }
|
||||
inline uint32_t getDataLen() const { return _dataLen; }
|
||||
inline Format getFileType() const { return _fileType; }
|
||||
inline gfx::Format getRenderFormat() const { return _renderFormat; }
|
||||
inline int getWidth() const { return _width; }
|
||||
inline int getHeight() const { return _height; }
|
||||
inline ccstd::string getFilePath() const { return _filePath; }
|
||||
inline bool isCompressed() const { return _isCompressed; }
|
||||
inline const ccstd::vector<uint32_t> &getMipmapLevelDataSize() const { return _mipmapLevelDataSize; }
|
||||
|
||||
/**
|
||||
@brief Save Image data to the specified file, with specified format.
|
||||
@param filename the file's absolute path, including file suffix.
|
||||
@param isToRGB whether the image is saved as RGB format.
|
||||
*/
|
||||
bool saveToFile(const std::string &filename, bool isToRGB = true);
|
||||
|
||||
protected:
|
||||
bool initWithJpgData(const unsigned char *data, uint32_t dataLen);
|
||||
bool initWithPngData(const unsigned char *data, uint32_t dataLen);
|
||||
#if CC_USE_WEBP
|
||||
bool initWithWebpData(const unsigned char *data, uint32_t dataLen);
|
||||
#endif
|
||||
bool initWithPVRData(const unsigned char *data, uint32_t dataLen);
|
||||
bool initWithPVRv2Data(const unsigned char *data, uint32_t dataLen);
|
||||
bool initWithPVRv3Data(const unsigned char *data, uint32_t dataLen);
|
||||
bool initWithETCData(const unsigned char *data, uint32_t dataLen);
|
||||
bool initWithETC2Data(const unsigned char *data, uint32_t dataLen);
|
||||
bool initWithASTCData(const unsigned char *data, uint32_t dataLen);
|
||||
bool initWithCompressedMipsData(const unsigned char *data, uint32_t dataLen);
|
||||
|
||||
bool saveImageToPNG(const std::string &filePath, bool isToRGB = true);
|
||||
bool saveImageToJPG(const std::string &filePath);
|
||||
|
||||
unsigned char *_data = nullptr;
|
||||
uint32_t _dataLen = 0;
|
||||
int _width = 0;
|
||||
int _height = 0;
|
||||
Format _fileType = Format::UNKNOWN;
|
||||
gfx::Format _renderFormat;
|
||||
ccstd::string _filePath;
|
||||
bool _isCompressed = false;
|
||||
ccstd::vector<uint32_t> _mipmapLevelDataSize;
|
||||
|
||||
static Format detectFormat(const unsigned char *data, uint32_t dataLen);
|
||||
static bool isPng(const unsigned char *data, uint32_t dataLen);
|
||||
static bool isJpg(const unsigned char *data, uint32_t dataLen);
|
||||
static bool isWebp(const unsigned char *data, uint32_t dataLen);
|
||||
static bool isPvr(const unsigned char *data, uint32_t dataLen);
|
||||
static bool isEtc(const unsigned char *data, uint32_t dataLen);
|
||||
static bool isEtc2(const unsigned char *data, uint32_t dataLen);
|
||||
static bool isASTC(const unsigned char *data, uint32_t detaLen);
|
||||
static bool isCompressed(const unsigned char *data, uint32_t detaLen);
|
||||
|
||||
static gfx::Format getASTCFormat(const unsigned char *pHeader);
|
||||
|
||||
friend class ImageUtils;
|
||||
};
|
||||
|
||||
} //namespace cc
|
||||
129
cocos/platform/SAXParser.cpp
Normal file
129
cocos/platform/SAXParser.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010 Максим Аксенов
|
||||
Copyright (c) 2010 cocos2d-x.org
|
||||
Copyright (c) 2013 Martell Malone
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "platform/SAXParser.h"
|
||||
#include "platform/FileUtils.h"
|
||||
#include "tinyxml2/tinyxml2.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
class XmlSaxHander : public tinyxml2::XMLVisitor {
|
||||
public:
|
||||
XmlSaxHander() : _ccsaxParserImp(0){};
|
||||
|
||||
virtual bool VisitEnter(const tinyxml2::XMLElement &element, const tinyxml2::XMLAttribute *firstAttribute);
|
||||
virtual bool VisitExit(const tinyxml2::XMLElement &element);
|
||||
virtual bool Visit(const tinyxml2::XMLText &text);
|
||||
virtual bool Visit(const tinyxml2::XMLUnknown &) { return true; }
|
||||
|
||||
void setSAXParserImp(SAXParser *parser) {
|
||||
_ccsaxParserImp = parser;
|
||||
}
|
||||
|
||||
private:
|
||||
SAXParser *_ccsaxParserImp;
|
||||
};
|
||||
|
||||
bool XmlSaxHander::VisitEnter(const tinyxml2::XMLElement &element, const tinyxml2::XMLAttribute *firstAttribute) {
|
||||
//log(" VisitEnter %s",element.Value());
|
||||
|
||||
ccstd::vector<const char *> attsVector;
|
||||
for (const tinyxml2::XMLAttribute *attrib = firstAttribute; attrib; attrib = attrib->Next()) {
|
||||
//log("%s", attrib->Name());
|
||||
attsVector.push_back(attrib->Name());
|
||||
//log("%s",attrib->Value());
|
||||
attsVector.push_back(attrib->Value());
|
||||
}
|
||||
|
||||
// nullptr is used in c++11
|
||||
//attsVector.push_back(nullptr);
|
||||
attsVector.push_back(nullptr);
|
||||
|
||||
SAXParser::startElement(_ccsaxParserImp, (const CC_XML_CHAR *)element.Value(), (const CC_XML_CHAR **)(&attsVector[0]));
|
||||
return true;
|
||||
}
|
||||
bool XmlSaxHander::VisitExit(const tinyxml2::XMLElement &element) {
|
||||
//log("VisitExit %s",element.Value());
|
||||
|
||||
SAXParser::endElement(_ccsaxParserImp, (const CC_XML_CHAR *)element.Value());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool XmlSaxHander::Visit(const tinyxml2::XMLText &text) {
|
||||
//log("Visit %s",text.Value());
|
||||
SAXParser::textHandler(_ccsaxParserImp, (const CC_XML_CHAR *)text.Value(), static_cast<int>(strlen(text.Value())));
|
||||
return true;
|
||||
}
|
||||
|
||||
SAXParser::SAXParser() {
|
||||
_delegator = nullptr;
|
||||
}
|
||||
|
||||
SAXParser::~SAXParser(void) {
|
||||
}
|
||||
|
||||
bool SAXParser::init(const char *encoding) {
|
||||
CC_UNUSED_PARAM(encoding);
|
||||
// nothing to do
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SAXParser::parse(const char *xmlData, size_t dataLength) {
|
||||
tinyxml2::XMLDocument tinyDoc;
|
||||
tinyDoc.Parse(xmlData, dataLength);
|
||||
XmlSaxHander printer;
|
||||
printer.setSAXParserImp(this);
|
||||
|
||||
return tinyDoc.Accept(&printer);
|
||||
}
|
||||
|
||||
bool SAXParser::parse(const ccstd::string &filename) {
|
||||
bool ret = false;
|
||||
Data data = FileUtils::getInstance()->getDataFromFile(filename);
|
||||
if (!data.isNull()) {
|
||||
ret = parse((const char *)data.getBytes(), data.getSize());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SAXParser::startElement(void *ctx, const CC_XML_CHAR *name, const CC_XML_CHAR **atts) {
|
||||
((SAXParser *)(ctx))->_delegator->startElement(ctx, (char *)name, (const char **)atts);
|
||||
}
|
||||
|
||||
void SAXParser::endElement(void *ctx, const CC_XML_CHAR *name) {
|
||||
((SAXParser *)(ctx))->_delegator->endElement(ctx, (char *)name);
|
||||
}
|
||||
void SAXParser::textHandler(void *ctx, const CC_XML_CHAR *name, int len) {
|
||||
((SAXParser *)(ctx))->_delegator->textHandler(ctx, (char *)name, len);
|
||||
}
|
||||
void SAXParser::setDelegator(SAXDelegator *delegator) {
|
||||
_delegator = delegator;
|
||||
}
|
||||
|
||||
} // namespace cc
|
||||
122
cocos/platform/SAXParser.h
Normal file
122
cocos/platform/SAXParser.h
Normal file
@@ -0,0 +1,122 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010 cocos2d-x.org
|
||||
Copyright (c) 2010 Максим Аксенов
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __CCSAXPARSER_H__
|
||||
#define __CCSAXPARSER_H__
|
||||
/// @cond DO_NOT_SHOW
|
||||
|
||||
#include "base/Macros.h"
|
||||
#include "base/std/container/string.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
/**
|
||||
* @addtogroup platform
|
||||
* @{
|
||||
*/
|
||||
|
||||
typedef unsigned char CC_XML_CHAR;
|
||||
|
||||
class CC_DLL SAXDelegator {
|
||||
public:
|
||||
virtual ~SAXDelegator() {}
|
||||
|
||||
/**
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
virtual void startElement(void *ctx, const char *name, const char **atts) = 0;
|
||||
/**
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
virtual void endElement(void *ctx, const char *name) = 0;
|
||||
/**
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
virtual void textHandler(void *ctx, const char *s, int len) = 0;
|
||||
};
|
||||
|
||||
class CC_DLL SAXParser {
|
||||
SAXDelegator *_delegator;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
SAXParser();
|
||||
/**
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
~SAXParser(void);
|
||||
/**
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
bool init(const char *encoding);
|
||||
/**
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
bool parse(const char *xmlData, size_t dataLength);
|
||||
/**
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
bool parse(const ccstd::string &filename);
|
||||
/**
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
void setDelegator(SAXDelegator *delegator);
|
||||
/**
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
static void startElement(void *ctx, const CC_XML_CHAR *name, const CC_XML_CHAR **atts);
|
||||
/**
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
static void endElement(void *ctx, const CC_XML_CHAR *name);
|
||||
/**
|
||||
* @js NA
|
||||
* @lua NA
|
||||
*/
|
||||
static void textHandler(void *ctx, const CC_XML_CHAR *name, int len);
|
||||
};
|
||||
|
||||
// end of platform group
|
||||
/// @}
|
||||
|
||||
} // namespace cc
|
||||
|
||||
/// @endcond
|
||||
#endif //__CCSAXPARSER_H__
|
||||
423
cocos/platform/SDLHelper.cpp
Normal file
423
cocos/platform/SDLHelper.cpp
Normal file
@@ -0,0 +1,423 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "platform/SDLHelper.h"
|
||||
#include "SDL2/SDL.h"
|
||||
#include "SDL2/SDL_main.h"
|
||||
#include "SDL2/SDL_syswm.h"
|
||||
#include "base/Log.h"
|
||||
#include "engine/EngineEvents.h"
|
||||
#include "platform/BasePlatform.h"
|
||||
#include "platform/interfaces/modules/IScreen.h"
|
||||
#include "platform/interfaces/modules/ISystemWindow.h"
|
||||
#include "platform/interfaces/modules/ISystemWindowManager.h"
|
||||
|
||||
namespace {
|
||||
std::unordered_map<int, cc::KeyCode> gKeyMap = {
|
||||
{SDLK_APPLICATION, cc::KeyCode::CONTEXT_MENU},
|
||||
{SDLK_SCROLLLOCK, cc::KeyCode::SCROLLLOCK},
|
||||
{SDLK_PAUSE, cc::KeyCode::PAUSE},
|
||||
{SDLK_PRINTSCREEN, cc::KeyCode::PRINT_SCREEN},
|
||||
{SDLK_INSERT, cc::KeyCode::INSERT},
|
||||
{SDLK_ESCAPE, cc::KeyCode::ESCAPE},
|
||||
{SDLK_MINUS, cc::KeyCode::MINUS},
|
||||
{SDLK_LSHIFT, cc::KeyCode::SHIFT_LEFT},
|
||||
{SDLK_RSHIFT, cc::KeyCode::SHIFT_RIGHT},
|
||||
{SDLK_EQUALS, cc::KeyCode::EQUAL},
|
||||
{SDLK_BACKSLASH, cc::KeyCode::BACKSLASH},
|
||||
{SDLK_BACKQUOTE, cc::KeyCode::BACKQUOTE},
|
||||
{SDLK_BACKSPACE, cc::KeyCode::BACKSPACE},
|
||||
{SDLK_RETURN, cc::KeyCode::ENTER},
|
||||
{SDLK_RETURN2, cc::KeyCode::ENTER},
|
||||
{SDLK_LEFTBRACKET, cc::KeyCode::BRACKET_LEFT},
|
||||
{SDLK_RIGHTBRACKET, cc::KeyCode::BRACKET_RIGHT},
|
||||
{SDLK_SEMICOLON, cc::KeyCode::SEMICOLON},
|
||||
{SDLK_QUOTE, cc::KeyCode::QUOTE},
|
||||
{SDLK_TAB, cc::KeyCode::TAB},
|
||||
{SDLK_LCTRL, cc::KeyCode::CONTROL_LEFT},
|
||||
{SDLK_RCTRL, cc::KeyCode::CONTROL_RIGHT},
|
||||
{SDLK_LALT, cc::KeyCode::ALT_LEFT},
|
||||
{SDLK_RALT, cc::KeyCode::ALT_RIGHT},
|
||||
{SDLK_LEFT, cc::KeyCode::ARROW_LEFT},
|
||||
{SDLK_RIGHT, cc::KeyCode::ARROW_RIGHT},
|
||||
{SDLK_UP, cc::KeyCode::ARROW_UP},
|
||||
{SDLK_DOWN, cc::KeyCode::ARROW_DOWN},
|
||||
{SDLK_KP_ENTER, cc::KeyCode::NUMPAD_ENTER},
|
||||
{SDLK_KP_PLUS, cc::KeyCode::NUMPAD_PLUS},
|
||||
{SDLK_KP_MULTIPLY, cc::KeyCode::NUMPAD_MULTIPLY},
|
||||
{SDLK_KP_DIVIDE, cc::KeyCode::NUMPAD_DIVIDE},
|
||||
{SDLK_KP_MINUS, cc::KeyCode::NUMPAD_MINUS},
|
||||
{SDLK_KP_PERIOD, cc::KeyCode::NUMPAD_DECIMAL},
|
||||
{SDLK_KP_BACKSPACE, cc::KeyCode::BACKSPACE},
|
||||
{SDLK_NUMLOCKCLEAR, cc::KeyCode::NUM_LOCK},
|
||||
{SDLK_HOME, cc::KeyCode::HOME},
|
||||
{SDLK_PAGEUP, cc::KeyCode::PAGE_UP},
|
||||
{SDLK_PAGEDOWN, cc::KeyCode::PAGE_DOWN},
|
||||
{SDLK_END, cc::KeyCode::END},
|
||||
{SDLK_COMMA, cc::KeyCode::COMMA},
|
||||
{SDLK_PERIOD, cc::KeyCode::PERIOD},
|
||||
{SDLK_SLASH, cc::KeyCode::SLASH},
|
||||
{SDLK_SPACE, cc::KeyCode::SPACE},
|
||||
{SDLK_DELETE, cc::KeyCode::DELETE_KEY},
|
||||
{SDLK_CAPSLOCK, cc::KeyCode::CAPS_LOCK},
|
||||
{SDLK_KP_0, cc::KeyCode::NUMPAD_0},
|
||||
{SDLK_KP_1, cc::KeyCode::NUMPAD_1},
|
||||
{SDLK_KP_2, cc::KeyCode::NUMPAD_2},
|
||||
{SDLK_KP_3, cc::KeyCode::NUMPAD_3},
|
||||
{SDLK_KP_4, cc::KeyCode::NUMPAD_4},
|
||||
{SDLK_KP_5, cc::KeyCode::NUMPAD_5},
|
||||
{SDLK_KP_6, cc::KeyCode::NUMPAD_6},
|
||||
{SDLK_KP_7, cc::KeyCode::NUMPAD_7},
|
||||
{SDLK_KP_8, cc::KeyCode::NUMPAD_8},
|
||||
{SDLK_KP_9, cc::KeyCode::NUMPAD_9}};
|
||||
|
||||
int sdlKeycodeToCocosCode(int sdlCode, int mode) {
|
||||
auto it = gKeyMap.find(sdlCode);
|
||||
if (it != gKeyMap.end()) {
|
||||
return static_cast<int>(it->second);
|
||||
}
|
||||
|
||||
if (sdlCode >= SDLK_F1 && sdlCode <= SDLK_F12) {
|
||||
// F1 ~ F12
|
||||
return 112 + (sdlCode - SDLK_F1);
|
||||
} else {
|
||||
int code = sdlCode & (~(1 << 30));
|
||||
if (code >= SDLK_a && code <= SDLK_z) {
|
||||
return 'A' + (code - SDLK_a);
|
||||
} else {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_map<cc::ISystemWindow::WindowFlags, SDL_WindowFlags> gWindowFlagMap = {
|
||||
{cc::ISystemWindow::CC_WINDOW_FULLSCREEN, SDL_WINDOW_FULLSCREEN},
|
||||
{cc::ISystemWindow::CC_WINDOW_OPENGL, SDL_WINDOW_OPENGL},
|
||||
{cc::ISystemWindow::CC_WINDOW_SHOWN, SDL_WINDOW_SHOWN},
|
||||
{cc::ISystemWindow::CC_WINDOW_HIDDEN, SDL_WINDOW_HIDDEN},
|
||||
{cc::ISystemWindow::CC_WINDOW_BORDERLESS, SDL_WINDOW_BORDERLESS},
|
||||
{cc::ISystemWindow::CC_WINDOW_RESIZABLE, SDL_WINDOW_RESIZABLE},
|
||||
{cc::ISystemWindow::CC_WINDOW_MINIMIZED, SDL_WINDOW_MINIMIZED},
|
||||
{cc::ISystemWindow::CC_WINDOW_MAXIMIZED, SDL_WINDOW_MAXIMIZED},
|
||||
{cc::ISystemWindow::CC_WINDOW_INPUT_GRABBED, SDL_WINDOW_INPUT_GRABBED},
|
||||
{cc::ISystemWindow::CC_WINDOW_INPUT_FOCUS, SDL_WINDOW_INPUT_FOCUS},
|
||||
{cc::ISystemWindow::CC_WINDOW_MOUSE_FOCUS, SDL_WINDOW_MOUSE_FOCUS},
|
||||
{cc::ISystemWindow::CC_WINDOW_FULLSCREEN_DESKTOP, SDL_WINDOW_FULLSCREEN_DESKTOP},
|
||||
{cc::ISystemWindow::CC_WINDOW_FOREIGN, SDL_WINDOW_FOREIGN},
|
||||
{cc::ISystemWindow::CC_WINDOW_ALLOW_HIGHDPI, SDL_WINDOW_ALLOW_HIGHDPI},
|
||||
{cc::ISystemWindow::CC_WINDOW_MOUSE_CAPTURE, SDL_WINDOW_MOUSE_CAPTURE},
|
||||
{cc::ISystemWindow::CC_WINDOW_ALWAYS_ON_TOP, SDL_WINDOW_ALWAYS_ON_TOP},
|
||||
{cc::ISystemWindow::CC_WINDOW_SKIP_TASKBAR, SDL_WINDOW_SKIP_TASKBAR},
|
||||
{cc::ISystemWindow::CC_WINDOW_UTILITY, SDL_WINDOW_UTILITY},
|
||||
{cc::ISystemWindow::CC_WINDOW_TOOLTIP, SDL_WINDOW_TOOLTIP},
|
||||
{cc::ISystemWindow::CC_WINDOW_POPUP_MENU, SDL_WINDOW_POPUP_MENU},
|
||||
{cc::ISystemWindow::CC_WINDOW_VULKAN, SDL_WINDOW_VULKAN}};
|
||||
|
||||
int windowFlagsToSDLWindowFlag(int flags) {
|
||||
int sdlFlags = 0;
|
||||
for (auto it : gWindowFlagMap) {
|
||||
if ((it.first & flags) == it.first) {
|
||||
sdlFlags |= it.second;
|
||||
}
|
||||
}
|
||||
return sdlFlags;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace cc {
|
||||
SDLHelper::SDLHelper() {}
|
||||
|
||||
SDLHelper::~SDLHelper() {
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
int SDLHelper::init() {
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
||||
// Display error message
|
||||
CC_LOG_ERROR("SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
// (1) Disable IDE on windows platform.
|
||||
// (2) On mac platform, SDL has an internal implementation of textinput ,
|
||||
// which internally sends the SDL_TEXTINPUT event. Causing two events to be sent.
|
||||
// So we need to stop the implementation of TextInput.
|
||||
// (3) Other platforms do not use textinput in sdl.
|
||||
stopTextInput();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SDLHelper::dispatchWindowEvent(uint32_t windowId, const SDL_WindowEvent &wevent) {
|
||||
WindowEvent ev;
|
||||
ev.windowId = windowId;
|
||||
|
||||
switch (wevent.event) {
|
||||
case SDL_WINDOWEVENT_SHOWN: {
|
||||
ev.type = WindowEvent::Type::SHOW;
|
||||
events::WindowEvent::broadcast(ev);
|
||||
break;
|
||||
}
|
||||
case SDL_WINDOWEVENT_RESTORED: {
|
||||
ev.type = WindowEvent::Type::RESTORED;
|
||||
events::WindowEvent::broadcast(ev);
|
||||
break;
|
||||
}
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED: {
|
||||
auto *screen = BasePlatform::getPlatform()->getInterface<IScreen>();
|
||||
CC_ASSERT(screen != nullptr);
|
||||
ev.type = WindowEvent::Type::SIZE_CHANGED;
|
||||
ev.width = wevent.data1 * screen->getDevicePixelRatio();
|
||||
ev.height = wevent.data2 * screen->getDevicePixelRatio();
|
||||
events::WindowEvent::broadcast(ev);
|
||||
break;
|
||||
}
|
||||
case SDL_WINDOWEVENT_RESIZED: {
|
||||
auto *screen = BasePlatform::getPlatform()->getInterface<IScreen>();
|
||||
CC_ASSERT(screen != nullptr);
|
||||
ev.type = WindowEvent::Type::RESIZED;
|
||||
ev.width = wevent.data1 * screen->getDevicePixelRatio();
|
||||
ev.height = wevent.data2 * screen->getDevicePixelRatio();
|
||||
events::WindowEvent::broadcast(ev);
|
||||
break;
|
||||
}
|
||||
case SDL_WINDOWEVENT_HIDDEN: {
|
||||
SDL_Window *window = SDL_GetWindowFromID(windowId);
|
||||
if (!isWindowMinimized(window)) {
|
||||
int32_t v = SDL_GetWindowFlags(window);
|
||||
ev.type = WindowEvent::Type::HIDDEN;
|
||||
events::WindowEvent::broadcast(ev);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_WINDOWEVENT_MINIMIZED: {
|
||||
ev.type = WindowEvent::Type::MINIMIZED;
|
||||
events::WindowEvent::broadcast(ev);
|
||||
break;
|
||||
}
|
||||
case SDL_WINDOWEVENT_CLOSE: {
|
||||
ev.type = WindowEvent::Type::CLOSE;
|
||||
events::WindowEvent::broadcast(ev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDLHelper::dispatchSDLEvent(uint32_t windowId, const SDL_Event &sdlEvent) {
|
||||
cc::TouchEvent touch;
|
||||
cc::MouseEvent mouse;
|
||||
cc::KeyboardEvent keyboard;
|
||||
|
||||
touch.windowId = windowId;
|
||||
mouse.windowId = windowId;
|
||||
keyboard.windowId = windowId;
|
||||
|
||||
SDL_Window *window = SDL_GetWindowFromID(sdlEvent.window.windowID);
|
||||
|
||||
switch (sdlEvent.type) {
|
||||
case SDL_QUIT: {
|
||||
WindowEvent ev;
|
||||
ev.type = WindowEvent::Type::QUIT;
|
||||
events::WindowEvent::broadcast(ev);
|
||||
break;
|
||||
}
|
||||
case SDL_WINDOWEVENT: {
|
||||
dispatchWindowEvent(windowId, sdlEvent.window);
|
||||
break;
|
||||
}
|
||||
case SDL_MOUSEBUTTONDOWN: {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
SDL_GetWindowSize(window, &width, &height);
|
||||
const SDL_MouseButtonEvent &event = sdlEvent.button;
|
||||
if (0 > event.x || event.x > width || 0 > event.y || event.y > height) {
|
||||
break;
|
||||
}
|
||||
mouse.type = MouseEvent::Type::DOWN;
|
||||
mouse.x = static_cast<float>(event.x);
|
||||
mouse.y = static_cast<float>(event.y);
|
||||
mouse.button = event.button - 1;
|
||||
events::Mouse::broadcast(mouse);
|
||||
break;
|
||||
}
|
||||
case SDL_MOUSEBUTTONUP: {
|
||||
const SDL_MouseButtonEvent &event = sdlEvent.button;
|
||||
mouse.type = MouseEvent::Type::UP;
|
||||
mouse.x = static_cast<float>(event.x);
|
||||
mouse.y = static_cast<float>(event.y);
|
||||
mouse.button = event.button - 1;
|
||||
events::Mouse::broadcast(mouse);
|
||||
break;
|
||||
}
|
||||
case SDL_MOUSEMOTION: {
|
||||
const SDL_MouseMotionEvent &event = sdlEvent.motion;
|
||||
mouse.type = MouseEvent::Type::MOVE;
|
||||
mouse.button = -1; // BUTTON_MISSING
|
||||
// Needs to be consistent with event-mouse.ts definition
|
||||
// Multiple button presses at the same time are not supported.
|
||||
// if we are pressed at the same time, the result is indeterminate.
|
||||
if (event.state & SDL_BUTTON_LMASK) {
|
||||
mouse.button |= 0x00; // BUTTON_LEFT
|
||||
} else if (event.state & SDL_BUTTON_RMASK) {
|
||||
mouse.button |= 0x02; // BUTTON_RGIHT
|
||||
} else if (event.state & SDL_BUTTON_MIDDLE) {
|
||||
mouse.button |= 0x01; // BUTTON_MIDDLE
|
||||
}
|
||||
|
||||
mouse.x = static_cast<float>(event.x);
|
||||
mouse.y = static_cast<float>(event.y);
|
||||
mouse.xDelta = static_cast<float>(event.xrel);
|
||||
mouse.yDelta = static_cast<float>(event.yrel);
|
||||
events::Mouse::broadcast(mouse);
|
||||
break;
|
||||
}
|
||||
case SDL_MOUSEWHEEL: {
|
||||
const SDL_MouseWheelEvent &event = sdlEvent.wheel;
|
||||
mouse.type = MouseEvent::Type::WHEEL;
|
||||
mouse.x = static_cast<float>(event.x);
|
||||
mouse.y = static_cast<float>(event.y);
|
||||
mouse.button = 0; //TODO: direction
|
||||
events::Mouse::broadcast(mouse);
|
||||
break;
|
||||
}
|
||||
case SDL_FINGERUP: {
|
||||
const SDL_TouchFingerEvent &event = sdlEvent.tfinger;
|
||||
touch.type = TouchEvent::Type::ENDED;
|
||||
touch.touches = {TouchInfo(event.x, event.y, (int)event.fingerId)};
|
||||
events::Touch::broadcast(touch);
|
||||
break;
|
||||
}
|
||||
case SDL_FINGERDOWN: {
|
||||
const SDL_TouchFingerEvent &event = sdlEvent.tfinger;
|
||||
touch.type = TouchEvent::Type::BEGAN;
|
||||
touch.touches = {TouchInfo(event.x, event.y, (int)event.fingerId)};
|
||||
events::Touch::broadcast(touch);
|
||||
break;
|
||||
}
|
||||
case SDL_FINGERMOTION: {
|
||||
const SDL_TouchFingerEvent &event = sdlEvent.tfinger;
|
||||
touch.type = TouchEvent::Type::MOVED;
|
||||
touch.touches = {TouchInfo(event.x, event.y, (int)event.fingerId)};
|
||||
events::Touch::broadcast(touch);
|
||||
break;
|
||||
}
|
||||
case SDL_KEYDOWN: {
|
||||
const SDL_KeyboardEvent &event = sdlEvent.key;
|
||||
auto mode = event.keysym.mod;
|
||||
keyboard.action = KeyboardEvent::Action::PRESS;
|
||||
keyboard.key = sdlKeycodeToCocosCode(event.keysym.sym, mode);
|
||||
keyboard.altKeyActive = mode & KMOD_ALT;
|
||||
keyboard.ctrlKeyActive = mode & KMOD_CTRL;
|
||||
keyboard.shiftKeyActive = mode & KMOD_SHIFT;
|
||||
//CC_LOG_DEBUG("==> key %d -> code %d", event.keysym.sym, keyboard.key);
|
||||
events::Keyboard::broadcast(keyboard);
|
||||
break;
|
||||
}
|
||||
case SDL_KEYUP: {
|
||||
const SDL_KeyboardEvent &event = sdlEvent.key;
|
||||
auto mode = event.keysym.mod;
|
||||
keyboard.action = KeyboardEvent::Action::RELEASE;
|
||||
keyboard.key = sdlKeycodeToCocosCode(event.keysym.sym, mode);
|
||||
keyboard.altKeyActive = mode & KMOD_ALT;
|
||||
keyboard.ctrlKeyActive = mode & KMOD_CTRL;
|
||||
keyboard.shiftKeyActive = mode & KMOD_SHIFT;
|
||||
events::Keyboard::broadcast(keyboard);
|
||||
break;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Window *SDLHelper::createWindow(const char *title,
|
||||
int w, int h, int flags) {
|
||||
SDL_Rect screenRect;
|
||||
if (SDL_GetDisplayUsableBounds(0, &screenRect) != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
int x = screenRect.x;
|
||||
int y = screenRect.y + screenRect.h - h;
|
||||
return createWindow(title, x, y, w, h, flags);
|
||||
}
|
||||
|
||||
SDL_Window *SDLHelper::createWindow(const char *title,
|
||||
int x, int y, int w,
|
||||
int h, int flags) {
|
||||
// Create window
|
||||
int sdlFlags = windowFlagsToSDLWindowFlag(flags);
|
||||
SDL_Window *handle = SDL_CreateWindow(title, x, y, w, h, sdlFlags);
|
||||
if (handle == nullptr) {
|
||||
// Display error message
|
||||
CC_LOG_ERROR("Window could not be created! SDL_Error: %s\n", SDL_GetError());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void SDLHelper::setCursorEnabled(bool value) {
|
||||
SDL_SetRelativeMouseMode(value ? SDL_FALSE : SDL_TRUE);
|
||||
events::PointerLock::broadcast(!value);
|
||||
}
|
||||
|
||||
#if (CC_PLATFORM == CC_PLATFORM_LINUX)
|
||||
uintptr_t SDLHelper::getDisplay(SDL_Window *window) {
|
||||
SDL_SysWMinfo wmInfo;
|
||||
SDL_VERSION(&wmInfo.version);
|
||||
SDL_GetWindowWMInfo(window, &wmInfo);
|
||||
return reinterpret_cast<uintptr_t>(wmInfo.info.x11.display);
|
||||
}
|
||||
#endif
|
||||
|
||||
uintptr_t SDLHelper::getWindowHandle(SDL_Window *window) {
|
||||
SDL_SysWMinfo wmInfo;
|
||||
SDL_VERSION(&wmInfo.version);
|
||||
SDL_GetWindowWMInfo(window, &wmInfo);
|
||||
|
||||
#if CC_PLATFORM == CC_PLATFORM_WINDOWS
|
||||
return reinterpret_cast<uintptr_t>(wmInfo.info.win.window);
|
||||
#elif (CC_PLATFORM == CC_PLATFORM_LINUX)
|
||||
return reinterpret_cast<uintptr_t>(wmInfo.info.x11.window);
|
||||
#elif (CC_PLATFORM == CC_PLATFORM_MACOS)
|
||||
return reinterpret_cast<uintptr_t>(wmInfo.info.cocoa.window);
|
||||
#endif
|
||||
CC_ABORT();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Vec2 SDLHelper::getWindowPosition(SDL_Window *window) {
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
SDL_GetWindowPosition(window, &x, &y);
|
||||
return Vec2(x, y);
|
||||
}
|
||||
|
||||
void SDLHelper::stopTextInput() {
|
||||
SDL_StopTextInput();
|
||||
}
|
||||
|
||||
bool SDLHelper::isWindowMinimized(SDL_Window *window) {
|
||||
return SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED;
|
||||
}
|
||||
|
||||
} // namespace cc
|
||||
64
cocos/platform/SDLHelper.h
Normal file
64
cocos/platform/SDLHelper.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
#include <iostream>
|
||||
#include "engine/EngineEvents.h"
|
||||
#include "math/Vec2.h"
|
||||
|
||||
struct SDL_Window;
|
||||
union SDL_Event;
|
||||
struct SDL_WindowEvent;
|
||||
|
||||
namespace cc {
|
||||
|
||||
class SDLHelper {
|
||||
friend class SystemWindowManager;
|
||||
|
||||
public:
|
||||
SDLHelper();
|
||||
~SDLHelper();
|
||||
|
||||
static int init();
|
||||
|
||||
static SDL_Window* createWindow(const char* title,
|
||||
int w, int h, int flags);
|
||||
static SDL_Window* createWindow(const char* title,
|
||||
int x, int y, int w,
|
||||
int h, int flags);
|
||||
|
||||
static uintptr_t getWindowHandle(SDL_Window* window);
|
||||
#if (CC_PLATFORM == CC_PLATFORM_LINUX)
|
||||
static uintptr_t getDisplay(SDL_Window* window);
|
||||
#endif
|
||||
static void setCursorEnabled(bool value);
|
||||
static Vec2 getWindowPosition(SDL_Window* window);
|
||||
static void stopTextInput();
|
||||
static bool isWindowMinimized(SDL_Window* window);
|
||||
|
||||
private:
|
||||
static void dispatchSDLEvent(uint32_t windowId, const SDL_Event& sdlEvent);
|
||||
static void dispatchWindowEvent(uint32_t windowId, const SDL_WindowEvent& wevent);
|
||||
};
|
||||
} // namespace cc
|
||||
82
cocos/platform/StdC.h
Normal file
82
cocos/platform/StdC.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2020 cocos.com Xiamen Yaji Software Co., Ltd.
|
||||
Copyright (c) undefined-2022 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated engine source code (the "Software"), a limited,
|
||||
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
||||
to use Cocos Creator solely to develop games on your target platforms. You shall
|
||||
not use Cocos Creator software for developing other software or tools that's
|
||||
used for developing games. You are not granted to publish, distribute,
|
||||
sublicense, and/or sell copies of Cocos Creator.
|
||||
|
||||
The software or tools in this License Agreement are licensed, not sold.
|
||||
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if CC_PLATFORM == CC_PLATFORM_WINDOWS
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
|
||||
#if _MSC_VER < 1800
|
||||
#if !defined(isnan)
|
||||
#define isnan _isnan
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#if _MSC_VER >= 1600
|
||||
#include <stdint.h>
|
||||
#else
|
||||
#include "platform/win32/compat/stdint.h"
|
||||
#endif
|
||||
|
||||
// Conflicted with ParticleSystem::PositionType::RELATIVE, so we need to undef it.
|
||||
#ifdef RELATIVE
|
||||
#undef RELATIVE
|
||||
#endif
|
||||
|
||||
// Conflicted with CCBReader::SizeType::RELATIVE and CCBReader::ScaleType::RELATIVE, so we need to undef it.
|
||||
#ifdef ABSOLUTE
|
||||
#undef ABSOLUTE
|
||||
#endif
|
||||
|
||||
// Conflicted with HttpRequest::Type::DELETE, so we need to undef it.
|
||||
#ifdef DELETE
|
||||
#undef DELETE
|
||||
#endif
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
#else
|
||||
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#endif // CC_PLATFORM == CC_PLATFORM_WINDOWS
|
||||
86
cocos/platform/UniversalPlatform.cpp
Normal file
86
cocos/platform/UniversalPlatform.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "platform/UniversalPlatform.h"
|
||||
|
||||
#include "platform/interfaces/OSInterface.h"
|
||||
|
||||
extern int cocos_main(int argc, const char** argv); // NOLINT(readability-identifier-naming)
|
||||
extern void cocos_destory(); // NOLINT(readability-identifier-naming)
|
||||
|
||||
namespace cc {
|
||||
UniversalPlatform::OSType UniversalPlatform::getOSType() const {
|
||||
return getInterface<ISystem>()->getOSType();
|
||||
}
|
||||
|
||||
int32_t UniversalPlatform::run(int argc, const char** argv) {
|
||||
if (cocos_main(argc, argv) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return loop();
|
||||
}
|
||||
|
||||
int UniversalPlatform::getSdkVersion() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void UniversalPlatform::runInPlatformThread(const ThreadCallback& task) {
|
||||
_mainTask = task;
|
||||
}
|
||||
|
||||
void UniversalPlatform::runTask() {
|
||||
if (_mainTask) {
|
||||
_mainTask();
|
||||
}
|
||||
}
|
||||
|
||||
int32_t UniversalPlatform::getFps() const {
|
||||
return _fps;
|
||||
}
|
||||
|
||||
void UniversalPlatform::setFps(int32_t fps) {
|
||||
_fps = fps;
|
||||
}
|
||||
|
||||
void UniversalPlatform::pollEvent() {
|
||||
}
|
||||
|
||||
void UniversalPlatform::onPause() {
|
||||
}
|
||||
|
||||
void UniversalPlatform::onResume() {
|
||||
}
|
||||
|
||||
void UniversalPlatform::onClose() {
|
||||
}
|
||||
|
||||
void UniversalPlatform::onDestroy() {
|
||||
cocos_destory();
|
||||
}
|
||||
|
||||
void UniversalPlatform::exit() {
|
||||
|
||||
}
|
||||
|
||||
} // namespace cc
|
||||
94
cocos/platform/UniversalPlatform.h
Normal file
94
cocos/platform/UniversalPlatform.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "platform/BasePlatform.h"
|
||||
|
||||
namespace cc {
|
||||
class CC_DLL UniversalPlatform : public BasePlatform {
|
||||
public:
|
||||
/**
|
||||
* @brief Start base platform initialization.
|
||||
*/
|
||||
int32_t run(int argc, const char **argv) override;
|
||||
|
||||
/**
|
||||
* @brief Get targe platform type.
|
||||
*/
|
||||
OSType getOSType() const override;
|
||||
/**
|
||||
* @brief Get the SDK version for Android.Other systems also have sdk versions,
|
||||
but they are not currently used.
|
||||
*/
|
||||
int getSdkVersion() const override;
|
||||
/**
|
||||
* @brief Polling event
|
||||
*/
|
||||
void pollEvent() override;
|
||||
/**
|
||||
* @brief Run the task in the platform thread,
|
||||
* @brief most platforms are the main thread, android is the non-main thread
|
||||
* @param task : Tasks running in platform threads
|
||||
*/
|
||||
void runInPlatformThread(const ThreadCallback &task) override;
|
||||
/**
|
||||
* @brief Get task call frequency.
|
||||
*/
|
||||
int32_t getFps() const override;
|
||||
/**
|
||||
* @brief Set task call frequency.
|
||||
*/
|
||||
void setFps(int32_t fps) override;
|
||||
|
||||
virtual void runTask();
|
||||
/**
|
||||
* @brief Processing pause message
|
||||
*/
|
||||
virtual void onPause();
|
||||
/**
|
||||
* @brief Processing resume message
|
||||
*/
|
||||
virtual void onResume();
|
||||
/**
|
||||
* @brief Processing close message
|
||||
*/
|
||||
virtual void onClose();
|
||||
/**
|
||||
* @brief Processing destroy message
|
||||
*/
|
||||
virtual void onDestroy();
|
||||
|
||||
/**
|
||||
* @brief Exit platform.
|
||||
*/
|
||||
void exit() override;
|
||||
|
||||
private:
|
||||
ThreadCallback _mainTask{nullptr};
|
||||
|
||||
int32_t _fps{60};
|
||||
};
|
||||
|
||||
} // namespace cc
|
||||
133
cocos/platform/android/AndroidKeyCodes.cpp
Normal file
133
cocos/platform/android/AndroidKeyCodes.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include <android/input.h>
|
||||
#include <android/keycodes.h>
|
||||
#include <unordered_map>
|
||||
#include "base/std/container/unordered_map.h"
|
||||
|
||||
namespace cc {
|
||||
ccstd::unordered_map<int32_t, const char *> androidKeyCodes = {
|
||||
{AKEYCODE_BACK, "Backspace"},
|
||||
{AKEYCODE_TAB, "Tab"},
|
||||
{AKEYCODE_ENTER, "Enter"},
|
||||
{AKEYCODE_SHIFT_LEFT, "ShiftLeft"},
|
||||
{AKEYCODE_CTRL_LEFT, "ControlLeft"},
|
||||
{AKEYCODE_ALT_LEFT, "AltLeft"},
|
||||
{AKEYCODE_SHIFT_RIGHT, "ShiftRight"},
|
||||
{AKEYCODE_CTRL_RIGHT, "ControlRight"},
|
||||
{AKEYCODE_ALT_RIGHT, "AltRight"},
|
||||
{AKEYCODE_MEDIA_PLAY_PAUSE, "Pause"},
|
||||
{AKEYCODE_CAPS_LOCK, "CapsLock"},
|
||||
{AKEYCODE_ESCAPE, "Escape"},
|
||||
{AKEYCODE_SPACE, "Space"},
|
||||
{AKEYCODE_PAGE_UP, "PageUp"},
|
||||
{AKEYCODE_PAGE_DOWN, "PageDown"},
|
||||
{AKEYCODE_MOVE_END, "End"},
|
||||
{AKEYCODE_MOVE_HOME, "Home"},
|
||||
{AKEYCODE_DPAD_LEFT, "ArrowLeft"},
|
||||
{AKEYCODE_DPAD_UP, "ArrowUp"},
|
||||
{AKEYCODE_DPAD_RIGHT, "ArrowRight"},
|
||||
{AKEYCODE_DPAD_DOWN, "ArrowDown"},
|
||||
{AKEYCODE_INSERT, "Insert"},
|
||||
{AKEYCODE_DEL, "Delete"},
|
||||
{AKEYCODE_0, "Digit0"},
|
||||
{AKEYCODE_1, "Digit1"},
|
||||
{AKEYCODE_2, "Digit2"},
|
||||
{AKEYCODE_3, "Digit3"},
|
||||
{AKEYCODE_4, "Digit4"},
|
||||
{AKEYCODE_5, "Digit5"},
|
||||
{AKEYCODE_6, "Digit6"},
|
||||
{AKEYCODE_7, "Digit7"},
|
||||
{AKEYCODE_8, "Digit8"},
|
||||
{AKEYCODE_9, "Digit9"},
|
||||
{AKEYCODE_A, "KeyA"},
|
||||
{AKEYCODE_B, "KeyB"},
|
||||
{AKEYCODE_C, "KeyC"},
|
||||
{AKEYCODE_D, "KeyD"},
|
||||
{AKEYCODE_E, "KeyE"},
|
||||
{AKEYCODE_F, "KeyF"},
|
||||
{AKEYCODE_G, "KeyG"},
|
||||
{AKEYCODE_H, "KeyH"},
|
||||
{AKEYCODE_I, "KeyI"},
|
||||
{AKEYCODE_J, "KeyJ"},
|
||||
{AKEYCODE_K, "KeyK"},
|
||||
{AKEYCODE_L, "KeyL"},
|
||||
{AKEYCODE_M, "KeyM"},
|
||||
{AKEYCODE_N, "KeyN"},
|
||||
{AKEYCODE_O, "KeyO"},
|
||||
{AKEYCODE_P, "KeyP"},
|
||||
{AKEYCODE_Q, "KeyQ"},
|
||||
{AKEYCODE_R, "KeyR"},
|
||||
{AKEYCODE_S, "KeyS"},
|
||||
{AKEYCODE_T, "KeyT"},
|
||||
{AKEYCODE_U, "KeyU"},
|
||||
{AKEYCODE_V, "KeyV"},
|
||||
{AKEYCODE_W, "KeyW"},
|
||||
{AKEYCODE_X, "KeyX"},
|
||||
{AKEYCODE_Y, "KeyY"},
|
||||
{AKEYCODE_Z, "KeyZ"},
|
||||
{AKEYCODE_NUMPAD_0, "Numpad0"},
|
||||
{AKEYCODE_NUMPAD_1, "Numpad1"},
|
||||
{AKEYCODE_NUMPAD_2, "Numpad2"},
|
||||
{AKEYCODE_NUMPAD_3, "Numpad3"},
|
||||
{AKEYCODE_NUMPAD_4, "Numpad4"},
|
||||
{AKEYCODE_NUMPAD_5, "Numpad5"},
|
||||
{AKEYCODE_NUMPAD_6, "Numpad6"},
|
||||
{AKEYCODE_NUMPAD_7, "Numpad7"},
|
||||
{AKEYCODE_NUMPAD_8, "Numpad8"},
|
||||
{AKEYCODE_NUMPAD_9, "Numpad9"},
|
||||
{AKEYCODE_NUMPAD_MULTIPLY, "NumpadMultiply"},
|
||||
{AKEYCODE_NUMPAD_ADD, "NumpadAdd"},
|
||||
{AKEYCODE_NUMPAD_SUBTRACT, "NumpadSubtract"},
|
||||
{AKEYCODE_NUMPAD_DOT, "NumpadDecimal"},
|
||||
{AKEYCODE_NUMPAD_DIVIDE, "NumpadDivide"},
|
||||
{AKEYCODE_NUMPAD_ENTER, "NumpadEnter"},
|
||||
{AKEYCODE_F1, "F1"},
|
||||
{AKEYCODE_F2, "F2"},
|
||||
{AKEYCODE_F3, "F3"},
|
||||
{AKEYCODE_F4, "F4"},
|
||||
{AKEYCODE_F5, "F5"},
|
||||
{AKEYCODE_F6, "F6"},
|
||||
{AKEYCODE_F7, "F7"},
|
||||
{AKEYCODE_F8, "F8"},
|
||||
{AKEYCODE_F9, "F9"},
|
||||
{AKEYCODE_F10, "F10"},
|
||||
{AKEYCODE_F11, "F11"},
|
||||
{AKEYCODE_F12, "F12"},
|
||||
{AKEYCODE_NUM_LOCK, "NumLock"},
|
||||
{AKEYCODE_SCROLL_LOCK, "ScrollLock"},
|
||||
{AKEYCODE_SEMICOLON, "Semicolon"},
|
||||
{AKEYCODE_EQUALS, "Equal"},
|
||||
{AKEYCODE_COMMA, "Comma"},
|
||||
{AKEYCODE_MINUS, "Minus"},
|
||||
{AKEYCODE_PERIOD, "Period"},
|
||||
{AKEYCODE_SLASH, "Slash"},
|
||||
{AKEYCODE_GRAVE, "Backquote"},
|
||||
{AKEYCODE_LEFT_BRACKET, "BracketLeft"},
|
||||
{AKEYCODE_BACKSLASH, "Backslash"},
|
||||
{AKEYCODE_RIGHT_BRACKET, "BracketRight"},
|
||||
{AKEYCODE_APOSTROPHE, "Quote"},
|
||||
};
|
||||
} // namespace cc
|
||||
884
cocos/platform/android/AndroidPlatform.cpp
Normal file
884
cocos/platform/android/AndroidPlatform.cpp
Normal file
@@ -0,0 +1,884 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "platform/android/AndroidPlatform.h"
|
||||
#include <android/native_window_jni.h>
|
||||
#include <thread>
|
||||
#include "application/ApplicationManager.h"
|
||||
#include "base/Log.h"
|
||||
#include "base/memory/Memory.h"
|
||||
#include "base/std/container/unordered_map.h"
|
||||
#include "game-activity/native_app_glue/android_native_app_glue.h"
|
||||
#include "java/jni/JniHelper.h"
|
||||
#include "modules/Screen.h"
|
||||
#include "modules/System.h"
|
||||
#include "platform/BasePlatform.h"
|
||||
#include "platform/android/FileUtils-android.h"
|
||||
#include "platform/java/jni/JniImp.h"
|
||||
#include "platform/java/modules/Accelerometer.h"
|
||||
#include "platform/java/modules/Battery.h"
|
||||
#include "platform/java/modules/Network.h"
|
||||
#include "platform/java/modules/SystemWindow.h"
|
||||
#include "platform/java/modules/SystemWindowManager.h"
|
||||
#include "platform/java/modules/Vibrator.h"
|
||||
|
||||
#include "platform/interfaces/modules/IXRInterface.h"
|
||||
#if CC_USE_XR
|
||||
#include "platform/java/modules/XRInterface.h"
|
||||
#endif
|
||||
|
||||
#include "base/StringUtil.h"
|
||||
#include "engine/EngineEvents.h"
|
||||
#include "paddleboat.h"
|
||||
|
||||
#define ABORT_GAME \
|
||||
{ \
|
||||
CC_LOG_ERROR("*** GAME ABORTING."); \
|
||||
*((volatile char *)0) = 'a'; \
|
||||
}
|
||||
|
||||
#define ABORT_IF(cond) \
|
||||
{ \
|
||||
if (!(cond)) { \
|
||||
CC_LOG_ERROR("ASSERTION FAILED: %s", #cond); \
|
||||
ABORT_GAME; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define INPUT_ACTION_COUNT 6
|
||||
|
||||
// Interval time per frame, in milliseconds
|
||||
#define LOW_FREQUENCY_TIME_INTERVAL 50
|
||||
|
||||
// Maximum runtime of game threads while in the background, in seconds
|
||||
#define LOW_FREQUENCY_EXPIRED_DURATION_SECONDS 60
|
||||
|
||||
#define CC_ENABLE_SUSPEND_GAME_THREAD true
|
||||
|
||||
extern int cocos_main(int argc, const char **argv); // NOLINT(readability-identifier-naming)
|
||||
|
||||
namespace cc {
|
||||
|
||||
struct cc::KeyboardEvent keyboardEvent;
|
||||
|
||||
struct InputAction {
|
||||
uint32_t buttonMask{0};
|
||||
int32_t actionCode{-1};
|
||||
};
|
||||
|
||||
extern ccstd::unordered_map<int32_t, const char *> androidKeyCodes;
|
||||
|
||||
static const InputAction PADDLEBOAT_ACTIONS[INPUT_ACTION_COUNT] = {
|
||||
{PADDLEBOAT_BUTTON_DPAD_UP, static_cast<int>(KeyCode::DPAD_UP)},
|
||||
{PADDLEBOAT_BUTTON_DPAD_LEFT, static_cast<int>(KeyCode::DPAD_LEFT)},
|
||||
{PADDLEBOAT_BUTTON_DPAD_DOWN, static_cast<int>(KeyCode::DPAD_DOWN)},
|
||||
{PADDLEBOAT_BUTTON_DPAD_RIGHT, static_cast<int>(KeyCode::DPAD_RIGHT)},
|
||||
};
|
||||
|
||||
struct ControllerKeyRemap {
|
||||
Paddleboat_Buttons buttonMask;
|
||||
StickKeyCode actionCode{StickKeyCode::UNDEFINE};
|
||||
const char *name;
|
||||
};
|
||||
|
||||
#define REMAP_WITH_NAME(btn, key) \
|
||||
{ btn, key, #btn }
|
||||
|
||||
static const ControllerKeyRemap PADDLEBOAT_MAPKEY[] = {
|
||||
REMAP_WITH_NAME(PADDLEBOAT_BUTTON_A, StickKeyCode::A),
|
||||
REMAP_WITH_NAME(PADDLEBOAT_BUTTON_B, StickKeyCode::B),
|
||||
REMAP_WITH_NAME(PADDLEBOAT_BUTTON_X, StickKeyCode::X),
|
||||
REMAP_WITH_NAME(PADDLEBOAT_BUTTON_Y, StickKeyCode::Y),
|
||||
REMAP_WITH_NAME(PADDLEBOAT_BUTTON_L1, StickKeyCode::L1),
|
||||
// REMAP_WITH_NAME(PADDLEBOAT_BUTTON_L2, StickKeyCode::TRIGGER_LEFT),
|
||||
REMAP_WITH_NAME(PADDLEBOAT_BUTTON_L3, StickKeyCode::L3),
|
||||
REMAP_WITH_NAME(PADDLEBOAT_BUTTON_R1, StickKeyCode::R1),
|
||||
// REMAP_WITH_NAME(PADDLEBOAT_BUTTON_R2, StickKeyCode::TRIGGER_RIGHT),
|
||||
REMAP_WITH_NAME(PADDLEBOAT_BUTTON_R3, StickKeyCode::R3),
|
||||
REMAP_WITH_NAME(PADDLEBOAT_BUTTON_SELECT, StickKeyCode::MINUS),
|
||||
REMAP_WITH_NAME(PADDLEBOAT_BUTTON_START, StickKeyCode::PLUS),
|
||||
REMAP_WITH_NAME(PADDLEBOAT_BUTTON_SYSTEM, StickKeyCode::MENU),
|
||||
REMAP_WITH_NAME(PADDLEBOAT_BUTTON_TOUCHPAD, StickKeyCode::UNDEFINE),
|
||||
REMAP_WITH_NAME(PADDLEBOAT_BUTTON_AUX1, StickKeyCode::UNDEFINE),
|
||||
REMAP_WITH_NAME(PADDLEBOAT_BUTTON_AUX2, StickKeyCode::UNDEFINE),
|
||||
REMAP_WITH_NAME(PADDLEBOAT_BUTTON_AUX3, StickKeyCode::UNDEFINE),
|
||||
REMAP_WITH_NAME(PADDLEBOAT_BUTTON_AUX4, StickKeyCode::UNDEFINE),
|
||||
};
|
||||
#undef REMAP_WITH_NAME
|
||||
|
||||
const int INPUT_ACTION_REMAP_COUNT = sizeof(PADDLEBOAT_MAPKEY) / sizeof(ControllerKeyRemap);
|
||||
|
||||
static const InputAction INPUT_KEY_ACTIONS[] = {
|
||||
{AKEYCODE_BACK, static_cast<int>(KeyCode::MOBILE_BACK)},
|
||||
{AKEYCODE_ENTER, static_cast<int>(KeyCode::ENTER)},
|
||||
{AKEYCODE_MENU, static_cast<int>(KeyCode::ALT_LEFT)},
|
||||
{AKEYCODE_DPAD_UP, static_cast<int>(KeyCode::DPAD_UP)},
|
||||
{AKEYCODE_DPAD_DOWN, static_cast<int>(KeyCode::DPAD_DOWN)},
|
||||
{AKEYCODE_DPAD_LEFT, static_cast<int>(KeyCode::DPAD_LEFT)},
|
||||
{AKEYCODE_DPAD_RIGHT, static_cast<int>(KeyCode::DPAD_RIGHT)},
|
||||
{AKEYCODE_DPAD_CENTER, static_cast<int>(KeyCode::DPAD_CENTER)},
|
||||
};
|
||||
|
||||
static bool keyState[INPUT_ACTION_COUNT] = {false};
|
||||
|
||||
extern void gameControllerStatusCallback(int32_t controllerIndex,
|
||||
Paddleboat_ControllerStatus status,
|
||||
void *userData);
|
||||
|
||||
class GameInputProxy {
|
||||
public:
|
||||
explicit GameInputProxy(AndroidPlatform *platform) {
|
||||
_androidPlatform = platform;
|
||||
if (0 != platform->_app->activity->vm->AttachCurrentThread(&_jniEnv, nullptr)) {
|
||||
CC_LOG_FATAL("*** FATAL ERROR: Failed to attach thread to JNI.");
|
||||
ABORT_GAME
|
||||
}
|
||||
ABORT_IF(_jniEnv != nullptr)
|
||||
|
||||
Paddleboat_init(_jniEnv, platform->_app->activity->javaGameActivity);
|
||||
Paddleboat_setControllerStatusCallback(gameControllerStatusCallback, this);
|
||||
// This is needed to allow controller events through to us.
|
||||
// By default, only touch-screen events are passed through, to match the
|
||||
// behaviour of NativeActivity.
|
||||
android_app_set_motion_event_filter(platform->_app, nullptr);
|
||||
|
||||
// Flags to control how the IME behaves.
|
||||
static constexpr int INPUTTYPE_DOT_TYPE_CLASS_TEXT = 1;
|
||||
static constexpr int IME_ACTION_NONE = 1;
|
||||
static constexpr int IME_FLAG_NO_FULLSCREEN = 33554432;
|
||||
|
||||
GameActivity_setImeEditorInfo(platform->_app->activity, INPUTTYPE_DOT_TYPE_CLASS_TEXT,
|
||||
IME_ACTION_NONE, IME_FLAG_NO_FULLSCREEN);
|
||||
}
|
||||
|
||||
~GameInputProxy() {
|
||||
Paddleboat_setControllerStatusCallback(nullptr, nullptr);
|
||||
Paddleboat_destroy(_jniEnv);
|
||||
if (_jniEnv) {
|
||||
CC_LOG_INFO("Detaching current thread from JNI.");
|
||||
_androidPlatform->_app->activity->vm->DetachCurrentThread();
|
||||
CC_LOG_INFO("Current thread detached from JNI.");
|
||||
_jniEnv = nullptr;
|
||||
}
|
||||
}
|
||||
void checkForNewAxis() {
|
||||
// Tell GameActivity about any new axis ids so it reports
|
||||
// their events
|
||||
const uint64_t activeAxisIds = Paddleboat_getActiveAxisMask();
|
||||
uint64_t newAxisIds = activeAxisIds ^ _activeAxisIds;
|
||||
if (newAxisIds != 0) {
|
||||
_activeAxisIds = activeAxisIds;
|
||||
int32_t currentAxisId = 0;
|
||||
while (newAxisIds != 0) {
|
||||
if ((newAxisIds & 1) != 0) {
|
||||
CC_LOG_INFO("Enable Axis: %d", currentAxisId);
|
||||
GameActivityPointerAxes_enableAxis(currentAxisId);
|
||||
}
|
||||
++currentAxisId;
|
||||
newAxisIds >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleInput() {
|
||||
checkForNewAxis();
|
||||
Paddleboat_update(_jniEnv);
|
||||
// If we get any key or motion events that were handled by a game controller,
|
||||
// read controller data and cook it into an event
|
||||
bool controllerEvent = false;
|
||||
|
||||
// Swap input buffers so we don't miss any events while processing inputBuffer.
|
||||
android_input_buffer *inputBuffer = android_app_swap_input_buffers(
|
||||
_androidPlatform->_app);
|
||||
// Early exit if no events.
|
||||
if (inputBuffer == nullptr) return;
|
||||
|
||||
if (inputBuffer->keyEventsCount != 0) {
|
||||
for (uint64_t i = 0; i < inputBuffer->keyEventsCount; ++i) {
|
||||
GameActivityKeyEvent *keyEvent = &inputBuffer->keyEvents[i];
|
||||
if (_gameControllerIndex >= 0 && Paddleboat_processGameActivityKeyInputEvent(keyEvent,
|
||||
sizeof(GameActivityKeyEvent))) {
|
||||
controllerEvent = true;
|
||||
} else {
|
||||
cookGameActivityKeyEvent(keyEvent);
|
||||
}
|
||||
}
|
||||
android_app_clear_key_events(inputBuffer);
|
||||
}
|
||||
if (inputBuffer->motionEventsCount != 0) {
|
||||
for (uint64_t i = 0; i < inputBuffer->motionEventsCount; ++i) {
|
||||
GameActivityMotionEvent *motionEvent = &inputBuffer->motionEvents[i];
|
||||
|
||||
if (_gameControllerIndex >= 0 && Paddleboat_processGameActivityMotionInputEvent(motionEvent,
|
||||
sizeof(GameActivityMotionEvent))) {
|
||||
controllerEvent = true;
|
||||
} else {
|
||||
// Didn't belong to a game controller, process it ourselves if it is a touch event
|
||||
bool isMouseEvent = motionEvent->pointerCount > 0 && (motionEvent->pointers[0].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || motionEvent->pointers[0].toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE);
|
||||
if (isMouseEvent) {
|
||||
cookGameActivityMouseEvent(motionEvent);
|
||||
} else {
|
||||
cookGameActivityMotionEvent(motionEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
android_app_clear_motion_events(inputBuffer);
|
||||
}
|
||||
|
||||
if (controllerEvent) {
|
||||
cookGameControllerEvent(_gameControllerIndex);
|
||||
}
|
||||
}
|
||||
|
||||
struct ButtonState {
|
||||
uint32_t buttonsDown;
|
||||
uint32_t &prevState;
|
||||
#define DEF_ATTR(name, code) \
|
||||
bool name##Pressed() const { return buttonsDown & PADDLEBOAT_BUTTON_##code; } \
|
||||
bool name##Rel() const { return !name##Pressed() && (prevState & PADDLEBOAT_BUTTON_##code); } \
|
||||
bool name() const { return name##Pressed() || name##Rel(); }
|
||||
|
||||
DEF_ATTR(dpadLeft, DPAD_LEFT)
|
||||
DEF_ATTR(dpadRight, DPAD_RIGHT)
|
||||
DEF_ATTR(dpadUp, DPAD_UP)
|
||||
DEF_ATTR(dpadDown, DPAD_DOWN)
|
||||
|
||||
DEF_ATTR(l2, L2)
|
||||
DEF_ATTR(r2, R2)
|
||||
|
||||
#undef DEF_ATTR
|
||||
};
|
||||
|
||||
bool cookGameControllerEvent(const int32_t gameControllerIndex) {
|
||||
static std::vector<uint32_t> prevStates{4};
|
||||
bool addedControllerEvent = false;
|
||||
cc::ControllerInfo info;
|
||||
if (gameControllerIndex >= 0) {
|
||||
if (gameControllerIndex >= prevStates.size()) {
|
||||
prevStates.resize(gameControllerIndex * 2 + 1);
|
||||
}
|
||||
uint32_t &prevButtonsDown = prevStates[gameControllerIndex];
|
||||
|
||||
info.napdId = gameControllerIndex;
|
||||
Paddleboat_Controller_Data controllerData;
|
||||
if (Paddleboat_getControllerData(gameControllerIndex, &controllerData) ==
|
||||
PADDLEBOAT_NO_ERROR) {
|
||||
addedControllerEvent = true;
|
||||
// Generate events from buttons
|
||||
for (auto inputAction : PADDLEBOAT_ACTIONS) {
|
||||
if (controllerData.buttonsDown & inputAction.buttonMask) {
|
||||
reportKeyState(inputAction.actionCode, true);
|
||||
} else if (prevButtonsDown & inputAction.buttonMask) {
|
||||
reportKeyState(inputAction.actionCode, false);
|
||||
}
|
||||
}
|
||||
for (auto remap : PADDLEBOAT_MAPKEY) {
|
||||
auto code = remap.actionCode;
|
||||
if (controllerData.buttonsDown & remap.buttonMask) {
|
||||
if (code == StickKeyCode::UNDEFINE) {
|
||||
CC_LOG_ERROR("key \"%s\" is unhandled", remap.name);
|
||||
}
|
||||
cc::ControllerInfo::ButtonInfo buttonInfo{code, true};
|
||||
info.buttonInfos.emplace_back(buttonInfo);
|
||||
} else if (prevButtonsDown & remap.buttonMask) {
|
||||
cc::ControllerInfo::ButtonInfo buttonInfo{code, false};
|
||||
buttonInfo.key = code;
|
||||
buttonInfo.isPress = false;
|
||||
info.buttonInfos.emplace_back(buttonInfo);
|
||||
}
|
||||
}
|
||||
const ButtonState bts{controllerData.buttonsDown, prevButtonsDown};
|
||||
if (bts.dpadLeft() || bts.dpadRight()) {
|
||||
float dLeft = bts.dpadLeftRel() ? 0.0F : (bts.dpadLeftPressed() ? -1.0F : 0.0F);
|
||||
float dRight = bts.dpadRightRel() ? 0.0F : (bts.dpadRightPressed() ? 1.0F : 0.0F);
|
||||
const ControllerInfo::AxisInfo axisInfo(StickAxisCode::X, dLeft + dRight);
|
||||
info.axisInfos.emplace_back(axisInfo);
|
||||
}
|
||||
if (bts.dpadUp() || bts.dpadDown()) {
|
||||
float dUp = bts.dpadUpRel() ? 0.0F : (bts.dpadUp() ? 1.0F : 0.0F);
|
||||
float dDown = bts.dpadDownRel() ? 0.0F : (bts.dpadDown() ? -1.0F : 0.0F);
|
||||
const ControllerInfo::AxisInfo axisInfo(StickAxisCode::Y, dUp + dDown);
|
||||
info.axisInfos.emplace_back(axisInfo);
|
||||
}
|
||||
if (bts.l2()) {
|
||||
const ControllerInfo::AxisInfo axisInfo(StickAxisCode::L2, bts.l2Rel() ? 0.0F : (bts.l2Pressed() ? controllerData.triggerL2 : 0.0F));
|
||||
info.axisInfos.emplace_back(axisInfo);
|
||||
}
|
||||
if (bts.r2()) {
|
||||
const ControllerInfo::AxisInfo axisInfo(StickAxisCode::R2, bts.r2Rel() ? 0.0F : (bts.r2Pressed() ? controllerData.triggerR2 : 0.0F));
|
||||
info.axisInfos.emplace_back(axisInfo);
|
||||
}
|
||||
|
||||
auto lx = controllerData.leftStick.stickX;
|
||||
auto ly = -controllerData.leftStick.stickY;
|
||||
auto rx = controllerData.rightStick.stickX;
|
||||
auto ry = -controllerData.rightStick.stickY;
|
||||
|
||||
info.axisInfos.emplace_back(StickAxisCode::LEFT_STICK_X, lx);
|
||||
info.axisInfos.emplace_back(StickAxisCode::LEFT_STICK_Y, ly);
|
||||
info.axisInfos.emplace_back(StickAxisCode::RIGHT_STICK_X, rx);
|
||||
info.axisInfos.emplace_back(StickAxisCode::RIGHT_STICK_Y, ry);
|
||||
|
||||
ControllerEvent controllerEvent;
|
||||
controllerEvent.type = ControllerEvent::Type::GAMEPAD;
|
||||
controllerEvent.controllerInfos.emplace_back(std::make_unique<ControllerInfo>(std::move(info)));
|
||||
events::Controller::broadcast(controllerEvent);
|
||||
|
||||
// Update our prev variable so we can det
|
||||
// ect delta changes from down to up
|
||||
prevButtonsDown = controllerData.buttonsDown;
|
||||
}
|
||||
}
|
||||
return addedControllerEvent;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
bool cookGameActivityMouseEvent(GameActivityMotionEvent *motionEvent) {
|
||||
cc::MouseEvent mouseEvent;
|
||||
if (motionEvent->pointerCount > 0) {
|
||||
mouseEvent.windowId = ISystemWindow::mainWindowId; // must be main window here
|
||||
|
||||
int action = motionEvent->action;
|
||||
int actionMasked = action & AMOTION_EVENT_ACTION_MASK;
|
||||
int button = motionEvent->buttonState;
|
||||
|
||||
setMousePosition(mouseEvent, motionEvent);
|
||||
switch (button) {
|
||||
case AMOTION_EVENT_BUTTON_PRIMARY:
|
||||
mouseEvent.button = 0;
|
||||
break;
|
||||
case AMOTION_EVENT_BUTTON_SECONDARY:
|
||||
mouseEvent.button = 2;
|
||||
break;
|
||||
case AMOTION_EVENT_BUTTON_TERTIARY:
|
||||
mouseEvent.button = 1;
|
||||
break;
|
||||
case AMOTION_EVENT_BUTTON_BACK:
|
||||
mouseEvent.button = 3;
|
||||
break;
|
||||
case AMOTION_EVENT_BUTTON_FORWARD:
|
||||
mouseEvent.button = 4;
|
||||
break;
|
||||
default:
|
||||
mouseEvent.button = 0;
|
||||
}
|
||||
|
||||
if (actionMasked == AMOTION_EVENT_ACTION_DOWN ||
|
||||
actionMasked == AMOTION_EVENT_ACTION_POINTER_DOWN) {
|
||||
mouseEvent.type = cc::MouseEvent::Type::DOWN;
|
||||
} else if (actionMasked == AMOTION_EVENT_ACTION_UP ||
|
||||
actionMasked == AMOTION_EVENT_ACTION_POINTER_UP) {
|
||||
mouseEvent.type = cc::MouseEvent::Type::UP;
|
||||
} else if (actionMasked == AMOTION_EVENT_ACTION_SCROLL) {
|
||||
mouseEvent.type = cc::MouseEvent::Type::WHEEL;
|
||||
// TODO(): wheel delta
|
||||
} else if (actionMasked == AMOTION_EVENT_ACTION_MOVE) {
|
||||
mouseEvent.type = cc::MouseEvent::Type::MOVE;
|
||||
} else if (actionMasked == AMOTION_EVENT_ACTION_HOVER_MOVE) {
|
||||
mouseEvent.type = cc::MouseEvent::Type::MOVE;
|
||||
} else if (actionMasked == AMOTION_EVENT_ACTION_HOVER_ENTER) {
|
||||
return false;
|
||||
} else if (actionMasked == AMOTION_EVENT_ACTION_HOVER_EXIT) {
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
events::Mouse::broadcast(mouseEvent);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
bool cookGameActivityMotionEvent(GameActivityMotionEvent *motionEvent) {
|
||||
cc::TouchEvent touchEvent;
|
||||
if (motionEvent->pointerCount > 0) {
|
||||
touchEvent.windowId = ISystemWindow::mainWindowId; // must be main window here
|
||||
|
||||
int action = motionEvent->action;
|
||||
int actionMasked = action & AMOTION_EVENT_ACTION_MASK;
|
||||
int eventChangedIndex = -1;
|
||||
|
||||
bool isMouseEvent = motionEvent->pointerCount > 0 && (motionEvent->pointers[0].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || motionEvent->pointers[0].toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE);
|
||||
|
||||
if (actionMasked == AMOTION_EVENT_ACTION_DOWN ||
|
||||
actionMasked == AMOTION_EVENT_ACTION_POINTER_DOWN) {
|
||||
if (actionMasked == AMOTION_EVENT_ACTION_POINTER_DOWN) {
|
||||
eventChangedIndex = ((action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
|
||||
>> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
|
||||
} else {
|
||||
eventChangedIndex = 0;
|
||||
}
|
||||
touchEvent.type = cc::TouchEvent::Type::BEGAN;
|
||||
} else if (actionMasked == AMOTION_EVENT_ACTION_UP ||
|
||||
actionMasked == AMOTION_EVENT_ACTION_POINTER_UP) {
|
||||
touchEvent.type = cc::TouchEvent::Type::ENDED;
|
||||
if (actionMasked == AMOTION_EVENT_ACTION_POINTER_UP) {
|
||||
eventChangedIndex = ((action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
|
||||
>> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
|
||||
} else {
|
||||
eventChangedIndex = 0;
|
||||
}
|
||||
} else if (actionMasked == AMOTION_EVENT_ACTION_CANCEL) {
|
||||
touchEvent.type = cc::TouchEvent::Type::CANCELLED;
|
||||
} else if (actionMasked == AMOTION_EVENT_ACTION_MOVE) {
|
||||
touchEvent.type = cc::TouchEvent::Type::MOVED;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool touchHandled = true;
|
||||
if (eventChangedIndex >= 0) {
|
||||
touchHandled = addTouchEvent(touchEvent, eventChangedIndex, motionEvent);
|
||||
} else {
|
||||
for (int i = 0; i < motionEvent->pointerCount; i++) {
|
||||
addTouchEvent(touchEvent, i, motionEvent);
|
||||
}
|
||||
}
|
||||
events::Touch::broadcast(touchEvent);
|
||||
touchEvent.touches.clear();
|
||||
return touchHandled;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
bool cookGameActivityKeyEvent(GameActivityKeyEvent *keyEvent) {
|
||||
if (_gameControllerIndex >= 0) {
|
||||
for (const auto &action : INPUT_KEY_ACTIONS) {
|
||||
if (action.buttonMask != keyEvent->keyCode) {
|
||||
continue;
|
||||
}
|
||||
keyboardEvent.action = 0 == keyEvent->action ? cc::KeyboardEvent::Action::PRESS
|
||||
: cc::KeyboardEvent::Action::RELEASE;
|
||||
keyboardEvent.key = action.actionCode;
|
||||
events::Keyboard::broadcast(keyboardEvent);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
keyboardEvent.action = 0 == keyEvent->action ? cc::KeyboardEvent::Action::PRESS
|
||||
: cc::KeyboardEvent::Action::RELEASE;
|
||||
keyboardEvent.key = keyEvent->keyCode;
|
||||
auto keyCodeItr = androidKeyCodes.find(keyEvent->keyCode);
|
||||
if (keyCodeItr == androidKeyCodes.end()) {
|
||||
keyboardEvent.code.clear();
|
||||
} else {
|
||||
keyboardEvent.code = keyCodeItr->second;
|
||||
}
|
||||
events::Keyboard::broadcast(keyboardEvent);
|
||||
return true;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
void reportKeyState(int keyCode, bool state) {
|
||||
bool wentDown = !keyState[keyCode] && state;
|
||||
bool wentUp = keyState[keyCode] && !state;
|
||||
keyState[keyCode] = state;
|
||||
|
||||
if (wentUp) {
|
||||
keyboardEvent.key = keyCode;
|
||||
keyboardEvent.action = cc::KeyboardEvent::Action::RELEASE;
|
||||
events::Keyboard::broadcast(keyboardEvent);
|
||||
} else if (wentDown) {
|
||||
keyboardEvent.key = keyCode;
|
||||
keyboardEvent.action = cc::KeyboardEvent::Action::PRESS;
|
||||
events::Keyboard::broadcast(keyboardEvent);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t getActiveGameControllerIndex() const {
|
||||
return _gameControllerIndex;
|
||||
}
|
||||
|
||||
void setActiveGameControllerIndex(const int32_t controllerIndex) {
|
||||
_gameControllerIndex = controllerIndex;
|
||||
}
|
||||
|
||||
void handleAppCommand(int32_t cmd) {
|
||||
switch (cmd) {
|
||||
case APP_CMD_SAVE_STATE:
|
||||
// The system has asked us to save our current state.
|
||||
CC_LOG_DEBUG("AndroidPlatform: APP_CMD_SAVE_STATE");
|
||||
break;
|
||||
case APP_CMD_INIT_WINDOW: {
|
||||
_hasWindow = true;
|
||||
ANativeWindow *nativeWindow = _androidPlatform->_app->window;
|
||||
|
||||
// We have a window!
|
||||
CC_LOG_DEBUG("AndroidPlatform: APP_CMD_INIT_WINDOW");
|
||||
if (!_launched) {
|
||||
_launched = true;
|
||||
|
||||
ISystemWindowInfo info;
|
||||
info.width = ANativeWindow_getWidth(nativeWindow);
|
||||
info.height = ANativeWindow_getHeight(nativeWindow);
|
||||
info.externalHandle = nativeWindow;
|
||||
_androidPlatform->getInterface<SystemWindowManager>()->createWindow(info);
|
||||
|
||||
if (cocos_main(0, nullptr) != 0) {
|
||||
CC_LOG_ERROR("AndroidPlatform: Launch game failed!");
|
||||
} else {
|
||||
IXRInterface *xr = CC_GET_XR_INTERFACE();
|
||||
if (xr) {
|
||||
xr->onRenderResume();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
IXRInterface *xr = CC_GET_XR_INTERFACE();
|
||||
if (xr) {
|
||||
xr->onRenderResume();
|
||||
}
|
||||
|
||||
auto *windowMgr = _androidPlatform->getInterface<SystemWindowManager>();
|
||||
auto *window = static_cast<cc::SystemWindow *>(windowMgr->getWindow(ISystemWindow::mainWindowId));
|
||||
window->setWindowHandle(nativeWindow);
|
||||
events::WindowRecreated::broadcast(ISystemWindow::mainWindowId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case APP_CMD_TERM_WINDOW: {
|
||||
_hasWindow = false;
|
||||
// The window is going away -- kill the surface
|
||||
CC_LOG_DEBUG("AndroidPlatform: APP_CMD_TERM_WINDOW");
|
||||
IXRInterface *xr = CC_GET_XR_INTERFACE();
|
||||
if (xr) {
|
||||
xr->onRenderPause();
|
||||
}
|
||||
// NOLINTNEXTLINE
|
||||
events::WindowDestroy::broadcast(ISystemWindow::mainWindowId);
|
||||
break;
|
||||
}
|
||||
case APP_CMD_GAINED_FOCUS:
|
||||
_isActive = true;
|
||||
CC_LOG_INFO("AndroidPlatform: APP_CMD_GAINED_FOCUS");
|
||||
break;
|
||||
case APP_CMD_LOST_FOCUS:
|
||||
_isActive = false;
|
||||
CC_LOG_INFO("AndroidPlatform: APP_CMD_LOST_FOCUS");
|
||||
break;
|
||||
case APP_CMD_PAUSE:
|
||||
_isActive = false;
|
||||
CC_LOG_INFO("AndroidPlatform: APP_CMD_PAUSE");
|
||||
break;
|
||||
case APP_CMD_RESUME: {
|
||||
_isActive = true;
|
||||
CC_LOG_INFO("AndroidPlatform: APP_CMD_RESUME");
|
||||
break;
|
||||
}
|
||||
case APP_CMD_DESTROY: {
|
||||
CC_LOG_INFO("AndroidPlatform: APP_CMD_DESTROY");
|
||||
IXRInterface *xr = CC_GET_XR_INTERFACE();
|
||||
if (xr) {
|
||||
xr->onRenderDestroy();
|
||||
}
|
||||
WindowEvent ev;
|
||||
ev.type = WindowEvent::Type::CLOSE;
|
||||
events::WindowEvent::broadcast(ev);
|
||||
break;
|
||||
}
|
||||
case APP_CMD_STOP: {
|
||||
CC_LOG_INFO("AndroidPlatform: APP_CMD_STOP");
|
||||
_isVisible = false;
|
||||
Paddleboat_onStop(_jniEnv);
|
||||
WindowEvent ev;
|
||||
ev.type = WindowEvent::Type::HIDDEN;
|
||||
events::WindowEvent::broadcast(ev);
|
||||
break;
|
||||
}
|
||||
case APP_CMD_START: {
|
||||
CC_LOG_INFO("AndroidPlatform: APP_CMD_START");
|
||||
_isVisible = true;
|
||||
Paddleboat_onStart(_jniEnv);
|
||||
WindowEvent ev;
|
||||
ev.type = WindowEvent::Type::SHOW;
|
||||
events::WindowEvent::broadcast(ev);
|
||||
break;
|
||||
}
|
||||
case APP_CMD_WINDOW_RESIZED: {
|
||||
CC_LOG_INFO("AndroidPlatform: APP_CMD_WINDOW_RESIZED");
|
||||
cc::WindowEvent ev;
|
||||
ev.type = cc::WindowEvent::Type::SIZE_CHANGED;
|
||||
ev.width = ANativeWindow_getWidth(_androidPlatform->_app->window);
|
||||
ev.height = ANativeWindow_getHeight(_androidPlatform->_app->window);
|
||||
ev.windowId = ISystemWindow::mainWindowId;
|
||||
events::WindowEvent::broadcast(ev);
|
||||
break;
|
||||
}
|
||||
case APP_CMD_CONFIG_CHANGED:
|
||||
CC_LOG_INFO("AndroidPlatform: APP_CMD_CONFIG_CHANGED");
|
||||
// Window was resized or some other configuration changed.
|
||||
// Note: we don't handle this event because we check the surface dimensions
|
||||
// every frame, so that's how we know it was resized. If you are NOT doing that,
|
||||
// then you need to handle this event!
|
||||
break;
|
||||
case APP_CMD_LOW_MEMORY: {
|
||||
// system told us we have low memory. So if we are not visible, let's
|
||||
// cooperate by deallocating all of our graphic resources.
|
||||
CC_LOG_INFO("AndroidPlatform: APP_CMD_LOW_MEMORY");
|
||||
events::LowMemory::broadcast();
|
||||
break;
|
||||
}
|
||||
case APP_CMD_CONTENT_RECT_CHANGED:
|
||||
CC_LOG_DEBUG("AndroidPlatform: APP_CMD_CONTENT_RECT_CHANGED");
|
||||
break;
|
||||
case APP_CMD_WINDOW_REDRAW_NEEDED:
|
||||
CC_LOG_INFO("AndroidPlatform: APP_CMD_WINDOW_REDRAW_NEEDED");
|
||||
break;
|
||||
case APP_CMD_WINDOW_INSETS_CHANGED:
|
||||
CC_LOG_DEBUG("AndroidPlatform: APP_CMD_WINDOW_INSETS_CHANGED");
|
||||
// ARect insets;
|
||||
// // Log all the insets types
|
||||
// for (int type = 0; type < GAMECOMMON_INSETS_TYPE_COUNT; ++type) {
|
||||
// GameActivity_getWindowInsets(_app->activity, (GameCommonInsetsType)type, &insets);
|
||||
// }
|
||||
break;
|
||||
default:
|
||||
CC_LOG_INFO("AndroidPlatform: (unknown command).");
|
||||
break;
|
||||
}
|
||||
if (_eventCallback) {
|
||||
_eventCallback(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
using AppEventCallback = std::function<void(int32_t)>;
|
||||
void registerAppEventCallback(AppEventCallback callback) {
|
||||
_eventCallback = std::move(callback);
|
||||
}
|
||||
|
||||
inline bool isAnimating() const {
|
||||
return _isVisible && _hasWindow;
|
||||
}
|
||||
|
||||
inline bool isActive() const {
|
||||
return _isActive;
|
||||
}
|
||||
|
||||
private:
|
||||
static bool addTouchEvent(cc::TouchEvent &touchEvent, int index, GameActivityMotionEvent *motionEvent) {
|
||||
if (index < 0 || index >= motionEvent->pointerCount) {
|
||||
return false;
|
||||
}
|
||||
int id = motionEvent->pointers[index].id;
|
||||
float x = GameActivityPointerAxes_getX(&motionEvent->pointers[index]);
|
||||
float y = GameActivityPointerAxes_getY(&motionEvent->pointers[index]);
|
||||
touchEvent.touches.emplace_back(x, y, id);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void setMousePosition(cc::MouseEvent &mouseEvent, GameActivityMotionEvent *motionEvent) {
|
||||
if (motionEvent->pointerCount == 0) {
|
||||
ABORT_IF(false);
|
||||
}
|
||||
mouseEvent.x = GameActivityPointerAxes_getX(&motionEvent->pointers[0]);
|
||||
mouseEvent.y = GameActivityPointerAxes_getY(&motionEvent->pointers[0]);
|
||||
}
|
||||
|
||||
AppEventCallback _eventCallback{nullptr};
|
||||
AndroidPlatform *_androidPlatform{nullptr};
|
||||
JNIEnv *_jniEnv{nullptr}; // JNI environment
|
||||
uint64_t _activeAxisIds{0};
|
||||
int32_t _gameControllerIndex{-1}; // Most recently connected game controller index
|
||||
bool _launched{false};
|
||||
bool _isVisible{false};
|
||||
bool _hasWindow{false};
|
||||
bool _isActive{false};
|
||||
};
|
||||
|
||||
static void handleCmdProxy(struct android_app *app, int32_t cmd) {
|
||||
auto *proxy = static_cast<GameInputProxy *>(app->userData);
|
||||
proxy->handleAppCommand(cmd);
|
||||
}
|
||||
|
||||
void gameControllerStatusCallback(const int32_t controllerIndex,
|
||||
const Paddleboat_ControllerStatus status,
|
||||
void *userData) {
|
||||
auto *inputProxy = static_cast<GameInputProxy *>(userData);
|
||||
if (inputProxy) {
|
||||
// Always make the most recently connected controller the active one
|
||||
if (status == PADDLEBOAT_CONTROLLER_JUST_CONNECTED) {
|
||||
inputProxy->setActiveGameControllerIndex(controllerIndex);
|
||||
} else if (status == PADDLEBOAT_CONTROLLER_JUST_DISCONNECTED) {
|
||||
// We only care if the controller that disconnected was the one
|
||||
// we are currently using
|
||||
if (controllerIndex == inputProxy->getActiveGameControllerIndex()) {
|
||||
// Default to no fallback controller, loop and look for another connected
|
||||
// one
|
||||
int32_t newControllerIndex = -1;
|
||||
|
||||
for (int32_t i = 0; i < PADDLEBOAT_MAX_CONTROLLERS; ++i) {
|
||||
if (i != controllerIndex &&
|
||||
Paddleboat_getControllerStatus(i) == PADDLEBOAT_CONTROLLER_ACTIVE) {
|
||||
newControllerIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
inputProxy->setActiveGameControllerIndex(newControllerIndex);
|
||||
}
|
||||
}
|
||||
ControllerChangeEvent event;
|
||||
for (int32_t i = 0; i < PADDLEBOAT_MAX_CONTROLLERS; ++i) {
|
||||
if (Paddleboat_getControllerStatus(i) == PADDLEBOAT_CONTROLLER_ACTIVE) {
|
||||
event.controllerIds.emplace_back(i);
|
||||
}
|
||||
}
|
||||
events::ControllerChange::broadcast(event);
|
||||
}
|
||||
}
|
||||
|
||||
AndroidPlatform::~AndroidPlatform() = default;
|
||||
|
||||
int AndroidPlatform::init() {
|
||||
#if CC_USE_XR
|
||||
registerInterface(std::make_shared<XRInterface>());
|
||||
#endif
|
||||
IXRInterface *xr = CC_GET_XR_INTERFACE();
|
||||
if (xr) {
|
||||
JniHelper::getEnv();
|
||||
xr->initialize(JniHelper::getJavaVM(), getActivity());
|
||||
}
|
||||
cc::FileUtilsAndroid::setAssetManager(_app->activity->assetManager);
|
||||
_inputProxy = ccnew GameInputProxy(this);
|
||||
_inputProxy->registerAppEventCallback([this](int32_t cmd) {
|
||||
IXRInterface *xr = CC_GET_XR_INTERFACE();
|
||||
if (xr) {
|
||||
xr->handleAppCommand(cmd);
|
||||
}
|
||||
|
||||
if (APP_CMD_START == cmd || APP_CMD_INIT_WINDOW == cmd) {
|
||||
if (_inputProxy->isAnimating()) {
|
||||
_isLowFrequencyLoopEnabled = false;
|
||||
_loopTimeOut = 0;
|
||||
}
|
||||
} else if (APP_CMD_STOP == cmd) {
|
||||
_lowFrequencyTimer.reset();
|
||||
_loopTimeOut = LOW_FREQUENCY_TIME_INTERVAL;
|
||||
_isLowFrequencyLoopEnabled = true;
|
||||
if (xr && !xr->getXRConfig(xr::XRConfigKey::INSTANCE_CREATED).getBool()) {
|
||||
// xr will sleep, -1 we will block forever waiting for events.
|
||||
_loopTimeOut = -1;
|
||||
_isLowFrequencyLoopEnabled = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
_app->userData = _inputProxy;
|
||||
_app->onAppCmd = handleCmdProxy;
|
||||
|
||||
registerInterface(std::make_shared<Accelerometer>());
|
||||
registerInterface(std::make_shared<Battery>());
|
||||
registerInterface(std::make_shared<Network>());
|
||||
registerInterface(std::make_shared<Screen>());
|
||||
registerInterface(std::make_shared<System>());
|
||||
registerInterface(std::make_shared<SystemWindowManager>());
|
||||
registerInterface(std::make_shared<Vibrator>());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AndroidPlatform::onDestroy() {
|
||||
UniversalPlatform::onDestroy();
|
||||
unregisterAllInterfaces();
|
||||
JniHelper::onDestroy();
|
||||
CC_SAFE_DELETE(_inputProxy)
|
||||
}
|
||||
|
||||
cc::ISystemWindow *AndroidPlatform::createNativeWindow(uint32_t windowId, void *externalHandle) {
|
||||
return ccnew SystemWindow(windowId, externalHandle);
|
||||
}
|
||||
|
||||
int AndroidPlatform::getSdkVersion() const {
|
||||
return AConfiguration_getSdkVersion(_app->config);
|
||||
}
|
||||
|
||||
int32_t AndroidPlatform::run(int /*argc*/, const char ** /*argv*/) {
|
||||
loop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AndroidPlatform::exit() {
|
||||
_app->destroyRequested = 1;
|
||||
}
|
||||
|
||||
int32_t AndroidPlatform::loop() {
|
||||
IXRInterface *xr = CC_GET_XR_INTERFACE();
|
||||
while (true) {
|
||||
int events;
|
||||
struct android_poll_source *source;
|
||||
|
||||
// suspend thread while _loopTimeOut set to -1
|
||||
while ((ALooper_pollAll(_loopTimeOut, nullptr, &events,
|
||||
reinterpret_cast<void **>(&source))) >= 0) {
|
||||
// process event
|
||||
if (source != nullptr) {
|
||||
source->process(_app, source);
|
||||
}
|
||||
|
||||
// Exit the game loop when the Activity is destroyed
|
||||
if (_app->destroyRequested) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Exit the game loop when the Activity is destroyed
|
||||
if (_app->destroyRequested) {
|
||||
break;
|
||||
}
|
||||
if (xr && !xr->platformLoopStart()) continue;
|
||||
_inputProxy->handleInput();
|
||||
if (_inputProxy->isAnimating() && (xr ? xr->getXRConfig(xr::XRConfigKey::SESSION_RUNNING).getBool() : true)) {
|
||||
runTask();
|
||||
if (_inputProxy->isActive()) {
|
||||
flushTasksOnGameThreadAtForegroundJNI();
|
||||
}
|
||||
}
|
||||
flushTasksOnGameThreadJNI();
|
||||
|
||||
#if CC_ENABLE_SUSPEND_GAME_THREAD
|
||||
if (_isLowFrequencyLoopEnabled) {
|
||||
// Suspend a game thread after it has been running in the background for a specified amount of time
|
||||
if (_lowFrequencyTimer.getSeconds() > LOW_FREQUENCY_EXPIRED_DURATION_SECONDS) {
|
||||
_isLowFrequencyLoopEnabled = false;
|
||||
_loopTimeOut = -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (xr) xr->platformLoopEnd();
|
||||
}
|
||||
onDestroy();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AndroidPlatform::pollEvent() {
|
||||
//
|
||||
}
|
||||
|
||||
void *AndroidPlatform::getActivity() { // Dangerous
|
||||
return _app->activity->javaGameActivity;
|
||||
}
|
||||
|
||||
void *AndroidPlatform::getEnv() {
|
||||
return JniHelper::getEnv();
|
||||
}
|
||||
|
||||
} // namespace cc
|
||||
72
cocos/platform/android/AndroidPlatform.h
Normal file
72
cocos/platform/android/AndroidPlatform.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "base/Timer.h"
|
||||
#include "platform/UniversalPlatform.h"
|
||||
|
||||
struct android_app;
|
||||
|
||||
namespace cc {
|
||||
class GameInputProxy;
|
||||
|
||||
class CC_DLL AndroidPlatform : public UniversalPlatform {
|
||||
public:
|
||||
AndroidPlatform() = default;
|
||||
|
||||
~AndroidPlatform() override;
|
||||
|
||||
int init() override;
|
||||
|
||||
void pollEvent() override;
|
||||
|
||||
int32_t run(int argc, const char **argv) override;
|
||||
|
||||
int getSdkVersion() const override;
|
||||
|
||||
int32_t loop() override;
|
||||
void exit() override;
|
||||
|
||||
void *getActivity();
|
||||
|
||||
static void *getEnv();
|
||||
|
||||
void onDestroy() override;
|
||||
|
||||
inline void setAndroidApp(android_app *app) {
|
||||
_app = app;
|
||||
}
|
||||
|
||||
ISystemWindow *createNativeWindow(uint32_t windowId, void *externalHandle) override;
|
||||
|
||||
private:
|
||||
bool _isLowFrequencyLoopEnabled{false};
|
||||
utils::Timer _lowFrequencyTimer;
|
||||
int _loopTimeOut{-1};
|
||||
GameInputProxy *_inputProxy{nullptr};
|
||||
android_app *_app{nullptr};
|
||||
friend class GameInputProxy;
|
||||
};
|
||||
} // namespace cc
|
||||
236
cocos/platform/android/FileUtils-android.cpp
Normal file
236
cocos/platform/android/FileUtils-android.cpp
Normal file
@@ -0,0 +1,236 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "platform/android/FileUtils-android.h"
|
||||
#include <android/log.h>
|
||||
#include <sys/stat.h>
|
||||
#include <cstdlib>
|
||||
#include "android/asset_manager.h"
|
||||
#include "android/asset_manager_jni.h"
|
||||
#include "base/Log.h"
|
||||
#include "base/ZipUtils.h"
|
||||
#include "base/memory/Memory.h"
|
||||
#include "platform/java/jni/JniHelper.h"
|
||||
#include "platform/java/jni/JniImp.h"
|
||||
|
||||
#define LOG_TAG "FileUtils-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 "com/cocos/lib/CocosHelper"
|
||||
#endif
|
||||
|
||||
namespace cc {
|
||||
|
||||
AAssetManager *FileUtilsAndroid::assetmanager = nullptr;
|
||||
ZipFile *FileUtilsAndroid::obbfile = nullptr;
|
||||
|
||||
FileUtils *createFileUtils() {
|
||||
return ccnew FileUtilsAndroid();
|
||||
}
|
||||
|
||||
void FileUtilsAndroid::setAssetManager(AAssetManager *a) {
|
||||
if (nullptr == a) {
|
||||
LOGD("setAssetManager : received unexpected nullptr parameter");
|
||||
return;
|
||||
}
|
||||
|
||||
cc::FileUtilsAndroid::assetmanager = a;
|
||||
}
|
||||
|
||||
FileUtilsAndroid::FileUtilsAndroid() {
|
||||
init();
|
||||
}
|
||||
|
||||
FileUtilsAndroid::~FileUtilsAndroid() {
|
||||
CC_SAFE_DELETE(obbfile);
|
||||
}
|
||||
|
||||
bool FileUtilsAndroid::init() {
|
||||
_defaultResRootPath = ASSETS_FOLDER_NAME;
|
||||
|
||||
ccstd::string assetsPath(getObbFilePathJNI());
|
||||
if (assetsPath.find("/obb/") != ccstd::string::npos) {
|
||||
obbfile = ccnew ZipFile(assetsPath);
|
||||
}
|
||||
|
||||
return FileUtils::init();
|
||||
}
|
||||
|
||||
bool FileUtilsAndroid::isFileExistInternal(const ccstd::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 {
|
||||
// CC_LOG_DEBUG("[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 ccstd::string &testDirPath) const {
|
||||
if (testDirPath.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ccstd::string dirPath = testDirPath;
|
||||
if (dirPath[dirPath.length() - 1] == '/') {
|
||||
dirPath[dirPath.length() - 1] = '\0';
|
||||
}
|
||||
|
||||
// find absolute path in flash memory
|
||||
if (dirPath[0] == '/') {
|
||||
CC_LOG_DEBUG("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
|
||||
CC_LOG_DEBUG("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 ccstd::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.
|
||||
return strPath[0] == '/' || strPath.find(ASSETS_FOLDER_NAME) == 0;
|
||||
}
|
||||
|
||||
FileUtils::Status FileUtilsAndroid::getContents(const ccstd::string &filename, ResizableBuffer *buffer) {
|
||||
if (filename.empty()) {
|
||||
return FileUtils::Status::NOT_EXISTS;
|
||||
}
|
||||
|
||||
ccstd::string fullPath = fullPathForFilename(filename);
|
||||
if (fullPath.empty()) {
|
||||
return FileUtils::Status::NOT_EXISTS;
|
||||
}
|
||||
|
||||
if (fullPath[0] == '/') {
|
||||
return FileUtils::getContents(fullPath, buffer);
|
||||
}
|
||||
|
||||
ccstd::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::NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
AAsset *asset = AAssetManager_open(assetmanager, relativePath.data(), AASSET_MODE_UNKNOWN);
|
||||
if (nullptr == asset) {
|
||||
LOGD("asset (%s) is nullptr", filename.c_str());
|
||||
return FileUtils::Status::OPEN_FAILED;
|
||||
}
|
||||
|
||||
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::READ_FAILED;
|
||||
}
|
||||
|
||||
return FileUtils::Status::OK;
|
||||
}
|
||||
|
||||
ccstd::string FileUtilsAndroid::getWritablePath() const {
|
||||
if (!_writablePath.empty()) {
|
||||
return _writablePath;
|
||||
}
|
||||
// Fix for Nexus 10 (Android 4.2 multi-user environment)
|
||||
// the path is retrieved through Java Context.getCacheDir() method
|
||||
ccstd::string dir;
|
||||
ccstd::string tmp = JniHelper::callStaticStringMethod(JCLS_HELPER, "getWritablePath");
|
||||
|
||||
if (tmp.length() > 0) {
|
||||
dir.append(tmp).append("/");
|
||||
return dir;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace cc
|
||||
74
cocos/platform/android/FileUtils-android.h
Normal file
74
cocos/platform/android/FileUtils-android.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "android/asset_manager.h"
|
||||
#include "base/Macros.h"
|
||||
#include "base/std/container/string.h"
|
||||
#include "jni.h"
|
||||
#include "platform/FileUtils.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
class ZipFile;
|
||||
|
||||
/**
|
||||
* @addtogroup platform
|
||||
* @{
|
||||
*/
|
||||
|
||||
//! @brief Helper class to handle file operations
|
||||
class CC_DLL FileUtilsAndroid : public FileUtils {
|
||||
friend class FileUtils;
|
||||
|
||||
public:
|
||||
FileUtilsAndroid();
|
||||
~FileUtilsAndroid() override;
|
||||
|
||||
static void setAssetManager(AAssetManager *a);
|
||||
static AAssetManager *getAssetManager() { return assetmanager; }
|
||||
static ZipFile *getObbFile() { return obbfile; }
|
||||
|
||||
/* override functions */
|
||||
bool init() override;
|
||||
FileUtils::Status getContents(const ccstd::string &filename, ResizableBuffer *buffer) override;
|
||||
|
||||
ccstd::string getWritablePath() const override;
|
||||
bool isAbsolutePath(const ccstd::string &strPath) const override;
|
||||
|
||||
private:
|
||||
bool isFileExistInternal(const ccstd::string &strFilePath) const override;
|
||||
bool isDirectoryExistInternal(const ccstd::string &dirPath) const override;
|
||||
|
||||
static AAssetManager *assetmanager;
|
||||
static ZipFile *obbfile;
|
||||
};
|
||||
|
||||
// end of platform group
|
||||
/// @}
|
||||
|
||||
} // namespace cc
|
||||
272
cocos/platform/android/adpf_manager.cpp
Normal file
272
cocos/platform/android/adpf_manager.cpp
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* Copyright 2022 The Android Open Source Project
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
#include "adpf_manager.h"
|
||||
#include "platform/BasePlatform.h"
|
||||
|
||||
#if CC_PLATFORM == CC_PLATFORM_ANDROID && __ANDROID_API__ >= 30
|
||||
|
||||
#include <unistd.h>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include "java/jni/JniHelper.h"
|
||||
|
||||
#define ALOGI(...)
|
||||
#define ALOGE(...)
|
||||
|
||||
// Invoke the method periodically (once a frame) to monitor
|
||||
// the device's thermal throttling status.
|
||||
void ADPFManager::Monitor() {
|
||||
auto currentClock = std::chrono::high_resolution_clock::now();
|
||||
auto past = currentClock - last_clock_;
|
||||
auto pastMS = std::chrono::duration_cast<std::chrono::milliseconds>(past).count();
|
||||
|
||||
// if (current_clock - last_clock_ >= kThermalHeadroomUpdateThreshold) {
|
||||
if (past > kThermalHeadroomUpdateThreshold) {
|
||||
// Update thermal headroom.
|
||||
// CC_LOG_INFO(" Monitor past %d ms", static_cast<int>(pastMS));
|
||||
UpdateThermalStatusHeadRoom();
|
||||
last_clock_ = currentClock;
|
||||
}
|
||||
}
|
||||
|
||||
float ADPFManager::GetThermalStatusNormalized() const {
|
||||
if (thermal_manager_ == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
auto level = AThermal_getCurrentThermalStatus(thermal_manager_);
|
||||
auto levelValue = (static_cast<int>(level) - static_cast<int>(ATHERMAL_STATUS_NONE)) * 1.0f /
|
||||
static_cast<int>(ATHERMAL_STATUS_SHUTDOWN);
|
||||
return levelValue;
|
||||
}
|
||||
|
||||
// Invoke the API first to set the android_app instance.
|
||||
void ADPFManager::Initialize() {
|
||||
// Initialize PowerManager reference.
|
||||
InitializePowerManager();
|
||||
|
||||
// Initialize PowerHintManager reference.
|
||||
InitializePerformanceHintManager();
|
||||
|
||||
beforeTick.bind([&]() {
|
||||
this->BeginPerfHintSession();
|
||||
this->Monitor();
|
||||
});
|
||||
|
||||
afterTick.bind([&]() {
|
||||
auto fps = cc::BasePlatform::getPlatform()->getFps();
|
||||
auto frameDurationNS = 1000000000LL / fps;
|
||||
this->EndPerfHintSession(frameDurationNS);
|
||||
});
|
||||
|
||||
if (thermal_manager_) {
|
||||
auto ret = AThermal_registerThermalStatusListener(
|
||||
thermal_manager_, +[](void *data, AThermalStatus status) {
|
||||
ADPFManager::getInstance().SetThermalStatus(status);
|
||||
CC_LOG_INFO("Thermal Status :%d", static_cast<int>(status));
|
||||
},
|
||||
nullptr);
|
||||
ALOGI("Thermal Status callback registerred to:%d", ret);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize JNI calls for the powermanager.
|
||||
bool ADPFManager::InitializePowerManager() {
|
||||
#if __ANDROID_API__ >= 31
|
||||
if (android_get_device_api_level() >= 31) {
|
||||
// Initialize the powermanager using NDK API.
|
||||
thermal_manager_ = AThermal_acquireManager();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
JNIEnv *env = cc::JniHelper::getEnv();
|
||||
auto *javaGameActivity = cc::JniHelper::getActivity();
|
||||
|
||||
// Retrieve class information
|
||||
jclass context = env->FindClass("android/content/Context");
|
||||
|
||||
// Get the value of a constant
|
||||
jfieldID fid =
|
||||
env->GetStaticFieldID(context, "POWER_SERVICE", "Ljava/lang/String;");
|
||||
jobject str_svc = env->GetStaticObjectField(context, fid);
|
||||
|
||||
// Get the method 'getSystemService' and call it
|
||||
jmethodID mid_getss = env->GetMethodID(
|
||||
context, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
|
||||
jobject obj_power_service = env->CallObjectMethod(javaGameActivity, mid_getss, str_svc);
|
||||
|
||||
// Add global reference to the power service object.
|
||||
obj_power_service_ = env->NewGlobalRef(obj_power_service);
|
||||
|
||||
jclass cls_power_service = env->GetObjectClass(obj_power_service_);
|
||||
get_thermal_headroom_ =
|
||||
env->GetMethodID(cls_power_service, "getThermalHeadroom", "(I)F");
|
||||
|
||||
// Free references
|
||||
env->DeleteLocalRef(cls_power_service);
|
||||
env->DeleteLocalRef(obj_power_service);
|
||||
env->DeleteLocalRef(str_svc);
|
||||
env->DeleteLocalRef(context);
|
||||
|
||||
if (get_thermal_headroom_ == 0) {
|
||||
// The API is not supported in the platform version.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Retrieve current thermal headroom using JNI call.
|
||||
float ADPFManager::UpdateThermalStatusHeadRoom() {
|
||||
#if __ANDROID_API__ >= 31
|
||||
if (android_get_device_api_level() >= 31) {
|
||||
// Use NDK API to retrieve thermal status headroom.
|
||||
auto seconds = kThermalHeadroomUpdateThreshold.count();
|
||||
thermal_headroom_ = AThermal_getThermalHeadroom(
|
||||
thermal_manager_, seconds);
|
||||
if (!std::isnan(thermal_headroom_)) {
|
||||
thermal_headroom_valid_ = thermal_headroom_;
|
||||
}
|
||||
return thermal_headroom_;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (get_thermal_headroom_ == 0) {
|
||||
return 0.f;
|
||||
}
|
||||
JNIEnv *env = cc::JniHelper::getEnv();
|
||||
|
||||
// Get thermal headroom!
|
||||
thermal_headroom_ =
|
||||
env->CallFloatMethod(obj_power_service_, get_thermal_headroom_,
|
||||
kThermalHeadroomUpdateThreshold);
|
||||
ALOGE("Current thermal Headroom %f", thermal_headroom_);
|
||||
return thermal_headroom_;
|
||||
}
|
||||
|
||||
// Initialize JNI calls for the PowerHintManager.
|
||||
bool ADPFManager::InitializePerformanceHintManager() {
|
||||
JNIEnv *env = cc::JniHelper::getEnv();
|
||||
auto *javaGameActivity = cc::JniHelper::getActivity();
|
||||
|
||||
// Retrieve class information
|
||||
jclass context = env->FindClass("android/content/Context");
|
||||
|
||||
// Get the value of a constant
|
||||
jfieldID fid = env->GetStaticFieldID(context, "PERFORMANCE_HINT_SERVICE",
|
||||
"Ljava/lang/String;");
|
||||
jobject str_svc = env->GetStaticObjectField(context, fid);
|
||||
|
||||
// Get the method 'getSystemService' and call it
|
||||
jmethodID mid_getss = env->GetMethodID(
|
||||
context, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
|
||||
jobject obj_perfhint_service = env->CallObjectMethod(
|
||||
javaGameActivity, mid_getss, str_svc);
|
||||
|
||||
// Add global reference to the power service object.
|
||||
obj_perfhint_service_ = env->NewGlobalRef(obj_perfhint_service);
|
||||
|
||||
// Retrieve methods IDs for the APIs.
|
||||
jclass cls_perfhint_service = env->GetObjectClass(obj_perfhint_service_);
|
||||
jmethodID mid_createhintsession =
|
||||
env->GetMethodID(cls_perfhint_service, "createHintSession",
|
||||
"([IJ)Landroid/os/PerformanceHintManager$Session;");
|
||||
jmethodID mid_preferedupdaterate = env->GetMethodID(
|
||||
cls_perfhint_service, "getPreferredUpdateRateNanos", "()J");
|
||||
|
||||
// Create int array which contain current tid.
|
||||
jintArray array = env->NewIntArray(1);
|
||||
int32_t tid = getpid();
|
||||
env->SetIntArrayRegion(array, 0, 1, &tid);
|
||||
const jlong DEFAULT_TARGET_NS = 16666666;
|
||||
|
||||
// Create Hint session for the thread.
|
||||
jobject obj_hintsession = env->CallObjectMethod(
|
||||
obj_perfhint_service_, mid_createhintsession, array, DEFAULT_TARGET_NS);
|
||||
if (obj_hintsession == nullptr) {
|
||||
ALOGI("Failed to create a perf hint session.");
|
||||
} else {
|
||||
obj_perfhint_session_ = env->NewGlobalRef(obj_hintsession);
|
||||
preferred_update_rate_ =
|
||||
env->CallLongMethod(obj_perfhint_service_, mid_preferedupdaterate);
|
||||
|
||||
// Retrieve mid of Session APIs.
|
||||
jclass cls_perfhint_session = env->GetObjectClass(obj_perfhint_session_);
|
||||
report_actual_work_duration_ = env->GetMethodID(
|
||||
cls_perfhint_session, "reportActualWorkDuration", "(J)V");
|
||||
update_target_work_duration_ = env->GetMethodID(
|
||||
cls_perfhint_session, "updateTargetWorkDuration", "(J)V");
|
||||
}
|
||||
|
||||
// Free local references
|
||||
env->DeleteLocalRef(obj_hintsession);
|
||||
env->DeleteLocalRef(array);
|
||||
env->DeleteLocalRef(cls_perfhint_service);
|
||||
env->DeleteLocalRef(obj_perfhint_service);
|
||||
env->DeleteLocalRef(str_svc);
|
||||
env->DeleteLocalRef(context);
|
||||
|
||||
if (report_actual_work_duration_ == 0 || update_target_work_duration_ == 0) {
|
||||
// The API is not supported in the platform version.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
thermalStateChangeListener ADPFManager::thermalListener = NULL;
|
||||
|
||||
void ADPFManager::SetThermalStatus(int32_t i) {
|
||||
int32_t prev_status_ = thermal_status_;
|
||||
int32_t current_status_ = i;
|
||||
thermal_status_ = i;
|
||||
if (thermalListener != NULL) {
|
||||
thermalListener(prev_status_, current_status_);
|
||||
}
|
||||
}
|
||||
|
||||
void ADPFManager::SetThermalListener(thermalStateChangeListener listener) {
|
||||
thermalListener = listener;
|
||||
}
|
||||
|
||||
// Indicates the start and end of the performance intensive task.
|
||||
// The methods call performance hint API to tell the performance
|
||||
// hint to the system.
|
||||
void ADPFManager::BeginPerfHintSession() { perfhintsession_start_ = std::chrono::high_resolution_clock::now(); }
|
||||
|
||||
void ADPFManager::EndPerfHintSession(jlong target_duration_ns) {
|
||||
auto current_clock = std::chrono::high_resolution_clock::now();
|
||||
auto duration = current_clock - perfhintsession_start_;
|
||||
frame_time_ns_ = std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count();
|
||||
if (obj_perfhint_session_) {
|
||||
jlong duration_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
duration * 100000000)
|
||||
.count();
|
||||
auto *env = cc::JniHelper::getEnv();
|
||||
|
||||
// Report and update the target work duration using JNI calls.
|
||||
env->CallVoidMethod(obj_perfhint_session_, report_actual_work_duration_,
|
||||
duration_ns);
|
||||
env->CallVoidMethod(obj_perfhint_session_, update_target_work_duration_,
|
||||
target_duration_ns);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
161
cocos/platform/android/adpf_manager.h
Normal file
161
cocos/platform/android/adpf_manager.h
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright 2022 The Android Open Source Project
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
#ifndef ADPF_MANAGER_H_
|
||||
#define ADPF_MANAGER_H_
|
||||
|
||||
#if CC_PLATFORM == CC_PLATFORM_ANDROID && __ANDROID_API__ >= 30
|
||||
#include <android/api-level.h>
|
||||
#include <android/log.h>
|
||||
#include <android/thermal.h>
|
||||
#include <jni.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include "3d/models/SkinningModel.h"
|
||||
#include "engine/EngineEvents.h"
|
||||
#include "platform/java/jni/JniHelper.h"
|
||||
|
||||
// Forward declarations of functions that need to be in C decl.
|
||||
|
||||
typedef void (*thermalStateChangeListener)(int32_t, int32_t);
|
||||
|
||||
/*
|
||||
* ADPFManager class anages the ADPF APIs.
|
||||
*/
|
||||
class ADPFManager {
|
||||
public:
|
||||
// Singleton function.
|
||||
static ADPFManager &getInstance() {
|
||||
static ADPFManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
// Dtor.
|
||||
~ADPFManager() {
|
||||
// Remove global reference.
|
||||
auto env = cc::JniHelper::getEnv();
|
||||
if (env != nullptr) {
|
||||
if (obj_power_service_ != nullptr) {
|
||||
env->DeleteGlobalRef(obj_power_service_);
|
||||
}
|
||||
if (obj_perfhint_service_ != nullptr) {
|
||||
env->DeleteGlobalRef(obj_perfhint_service_);
|
||||
}
|
||||
if (obj_perfhint_session_ != nullptr) {
|
||||
env->DeleteGlobalRef(obj_perfhint_session_);
|
||||
}
|
||||
if (thermal_manager_ != nullptr) {
|
||||
AThermal_releaseManager(thermal_manager_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete copy constructor since the class is used as a singleton.
|
||||
ADPFManager(ADPFManager const &) = delete;
|
||||
|
||||
void operator=(ADPFManager const &) = delete;
|
||||
|
||||
// Invoke the method periodically (once a frame) to monitor
|
||||
// the device's thermal throttling status.
|
||||
void Monitor();
|
||||
|
||||
// Invoke the API first to set the android_app instance.
|
||||
|
||||
// Method to set thermal status. Need to be public since the method
|
||||
// is called from C native listener.
|
||||
void SetThermalStatus(int32_t i);
|
||||
|
||||
// Get current thermal status and headroom.
|
||||
int32_t GetThermalStatus() { return thermal_status_; }
|
||||
float GetThermalStatusNormalized() const;
|
||||
|
||||
float GetFrameTimeMS() const { return frame_time_ns_ / 1000000.0F; }
|
||||
|
||||
float GetThermalHeadroom() { return thermal_headroom_; }
|
||||
|
||||
void SetThermalListener(thermalStateChangeListener listener);
|
||||
|
||||
// Indicates the start and end of the performance intensive task.
|
||||
// The methods call performance hint API to tell the performance
|
||||
// hint to the system.
|
||||
void BeginPerfHintSession();
|
||||
|
||||
void EndPerfHintSession(jlong target_duration_ns);
|
||||
|
||||
// Method to retrieve thermal manager. The API is used to register/unregister
|
||||
// callbacks from C API.
|
||||
AThermalManager *GetThermalManager() { return thermal_manager_; }
|
||||
|
||||
void Initialize();
|
||||
|
||||
private:
|
||||
// Update thermal headroom each sec.
|
||||
static constexpr auto kThermalHeadroomUpdateThreshold = std::chrono::seconds(1);
|
||||
|
||||
// Function pointer from the game, will be invoked when we receive state changed event from Thermal API
|
||||
static thermalStateChangeListener thermalListener;
|
||||
|
||||
// Ctor. It's private since the class is designed as a singleton.
|
||||
ADPFManager()
|
||||
: thermal_manager_(nullptr),
|
||||
thermal_status_(0),
|
||||
thermal_headroom_(0.f),
|
||||
obj_power_service_(nullptr),
|
||||
get_thermal_headroom_(0),
|
||||
obj_perfhint_service_(nullptr),
|
||||
obj_perfhint_session_(nullptr),
|
||||
report_actual_work_duration_(0),
|
||||
update_target_work_duration_(0),
|
||||
preferred_update_rate_(0) {
|
||||
last_clock_ = std::chrono::high_resolution_clock::now();
|
||||
perfhintsession_start_ = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
// Functions to initialize ADPF API's calls.
|
||||
bool InitializePowerManager();
|
||||
|
||||
float UpdateThermalStatusHeadRoom();
|
||||
|
||||
bool InitializePerformanceHintManager();
|
||||
|
||||
AThermalManager *thermal_manager_ = nullptr;
|
||||
int32_t thermal_status_;
|
||||
float thermal_headroom_ = 0;
|
||||
float thermal_headroom_valid_ = 0;
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> last_clock_;
|
||||
jobject obj_power_service_;
|
||||
jmethodID get_thermal_headroom_;
|
||||
|
||||
jobject obj_perfhint_service_;
|
||||
jobject obj_perfhint_session_;
|
||||
jmethodID report_actual_work_duration_;
|
||||
jmethodID update_target_work_duration_;
|
||||
jlong preferred_update_rate_;
|
||||
|
||||
cc::events::BeforeTick::Listener beforeTick;
|
||||
cc::events::AfterTick::Listener afterTick;
|
||||
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> perfhintsession_start_;
|
||||
int64_t frame_time_ns_{0};
|
||||
};
|
||||
|
||||
#define CC_SUPPORT_ADPF 1 // NOLINT
|
||||
#else
|
||||
#define CC_SUPPORT_ADPF 0 // NOLINT
|
||||
#endif // ADPF_MANAGER_H_
|
||||
|
||||
#endif
|
||||
9
cocos/platform/android/java/.classpath
Normal file
9
cocos/platform/android/java/.classpath
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="output" path="bin/classes"/>
|
||||
</classpath>
|
||||
33
cocos/platform/android/java/.project
Normal file
33
cocos/platform/android/java/.project
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>libcocos2dx</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
9
cocos/platform/android/java/AndroidManifest.xml
Normal file
9
cocos/platform/android/java/AndroidManifest.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.cocos.lib"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
|
||||
<uses-sdk android:minSdkVersion="9"/>
|
||||
|
||||
</manifest>
|
||||
83
cocos/platform/android/java/build.xml
Normal file
83
cocos/platform/android/java/build.xml
Normal file
@@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="cocos2dxandroid" default="help">
|
||||
|
||||
<!-- The local.properties file is created and updated by the 'android' tool.
|
||||
It contains the path to the SDK. It should *NOT* be checked into
|
||||
Version Control Systems. -->
|
||||
<property file="local.properties" />
|
||||
|
||||
<!-- The ant.properties file can be created by you. It is only edited by the
|
||||
'android' tool to add properties to it.
|
||||
This is the place to change some Ant specific build properties.
|
||||
Here are some properties you may want to change/update:
|
||||
|
||||
source.dir
|
||||
The name of the source directory. Default is 'src'.
|
||||
out.dir
|
||||
The name of the output directory. Default is 'bin'.
|
||||
|
||||
For other overridable properties, look at the beginning of the rules
|
||||
files in the SDK, at tools/ant/build.xml
|
||||
|
||||
Properties related to the SDK location or the project target should
|
||||
be updated using the 'android' tool with the 'update' action.
|
||||
|
||||
This file is an integral part of the build system for your
|
||||
application and should be checked into Version Control Systems.
|
||||
|
||||
-->
|
||||
<property file="ant.properties" />
|
||||
|
||||
<!-- The project.properties file is created and updated by the 'android'
|
||||
tool, as well as ADT.
|
||||
|
||||
This contains project specific properties such as project target, and library
|
||||
dependencies. Lower level build properties are stored in ant.properties
|
||||
(or in .classpath for Eclipse projects).
|
||||
|
||||
This file is an integral part of the build system for your
|
||||
application and should be checked into Version Control Systems. -->
|
||||
<loadproperties srcFile="project.properties" />
|
||||
|
||||
<!-- quick check on sdk.dir -->
|
||||
<fail
|
||||
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through an env var"
|
||||
unless="sdk.dir"
|
||||
/>
|
||||
|
||||
<!--
|
||||
Import per project custom build rules if present at the root of the project.
|
||||
This is the place to put custom intermediary targets such as:
|
||||
-pre-build
|
||||
-pre-compile
|
||||
-post-compile (This is typically used for code obfuscation.
|
||||
Compiled code location: ${out.classes.absolute.dir}
|
||||
If this is not done in place, override ${out.dex.input.absolute.dir})
|
||||
-post-package
|
||||
-post-build
|
||||
-pre-clean
|
||||
-->
|
||||
<import file="custom_rules.xml" optional="true" />
|
||||
|
||||
<!-- Import the actual build file.
|
||||
|
||||
To customize existing targets, there are two options:
|
||||
- Customize only one target:
|
||||
- copy/paste the target into this file, *before* the
|
||||
<import> task.
|
||||
- customize it to your needs.
|
||||
- Customize the whole content of build.xml
|
||||
- copy/paste the content of the rules files (minus the top node)
|
||||
into this file, replacing the <import> task.
|
||||
- customize to your needs.
|
||||
|
||||
***********************
|
||||
****** IMPORTANT ******
|
||||
***********************
|
||||
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
|
||||
in order to avoid having your file be overridden by tools such as "android update project"
|
||||
-->
|
||||
<!-- version-tag: 1 -->
|
||||
<import file="${sdk.dir}/tools/ant/build.xml" />
|
||||
|
||||
</project>
|
||||
Binary file not shown.
BIN
cocos/platform/android/java/libs/game-sdk.jar
Normal file
BIN
cocos/platform/android/java/libs/game-sdk.jar
Normal file
Binary file not shown.
BIN
cocos/platform/android/java/libs/okhttp-3.12.14.jar
Normal file
BIN
cocos/platform/android/java/libs/okhttp-3.12.14.jar
Normal file
Binary file not shown.
BIN
cocos/platform/android/java/libs/okio-1.15.0.jar
Normal file
BIN
cocos/platform/android/java/libs/okio-1.15.0.jar
Normal file
Binary file not shown.
13
cocos/platform/android/java/proguard-project.txt
Normal file
13
cocos/platform/android/java/proguard-project.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
# To enable ProGuard in your project, edit project.properties
|
||||
# to define the proguard.config property as described in that file.
|
||||
#
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in ${sdk.dir}/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the ProGuard
|
||||
# include property in project.properties.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
15
cocos/platform/android/java/project.properties
Executable file
15
cocos/platform/android/java/project.properties
Executable file
@@ -0,0 +1,15 @@
|
||||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must be checked in Version Control Systems.
|
||||
#
|
||||
# To customize properties used by the Ant build system edit
|
||||
# "ant.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
#
|
||||
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||
|
||||
android.library=true
|
||||
# Project target.
|
||||
target=android-10
|
||||
@@ -0,0 +1,529 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2018 Xiamen Yaji Software Co., Ltd.
|
||||
*
|
||||
* http://www.cocos.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
package com.cocos.lib;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Build;
|
||||
import android.text.TextPaint;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class CanvasRenderingContext2DImpl {
|
||||
|
||||
private static final String TAG = "CanvasContext2D";
|
||||
|
||||
private static final int TEXT_ALIGN_LEFT = 0;
|
||||
private static final int TEXT_ALIGN_CENTER = 1;
|
||||
private static final int TEXT_ALIGN_RIGHT = 2;
|
||||
|
||||
private static final int TEXT_BASELINE_TOP = 0;
|
||||
private static final int TEXT_BASELINE_MIDDLE = 1;
|
||||
private static final int TEXT_BASELINE_BOTTOM = 2;
|
||||
private static final int TEXT_BASELINE_ALPHABETIC = 3;
|
||||
|
||||
private static WeakReference<Context> sContext;
|
||||
private TextPaint mTextPaint;
|
||||
private Paint mLinePaint;
|
||||
private Path mLinePath;
|
||||
private Canvas mCanvas = new Canvas();
|
||||
private Bitmap mBitmap;
|
||||
private int mTextAlign = TEXT_ALIGN_LEFT;
|
||||
private int mTextBaseline = TEXT_BASELINE_BOTTOM;
|
||||
private int mFillStyleR = 0;
|
||||
private int mFillStyleG = 0;
|
||||
private int mFillStyleB = 0;
|
||||
private int mFillStyleA = 255;
|
||||
|
||||
private int mStrokeStyleR = 0;
|
||||
private int mStrokeStyleG = 0;
|
||||
private int mStrokeStyleB = 0;
|
||||
private int mStrokeStyleA = 255;
|
||||
|
||||
private String mFontName = "Arial";
|
||||
private float mFontSize = 40.0f;
|
||||
private float mLineWidth = 0.0f;
|
||||
private float mShadowBlur = 0.0f;
|
||||
private float mShadowOffsetX = 0.0f;
|
||||
private float mShadowOffsetY = 0.0f;
|
||||
private int mShadowColorA = 0;
|
||||
private int mShadowColorB = 0;
|
||||
private int mShadowColorG = 0;
|
||||
private int mShadowColorR = 0;
|
||||
|
||||
private static float _sApproximatingOblique = -0.25f;//please check paint api documentation
|
||||
private boolean mIsBoldFont = false;
|
||||
private boolean mIsItalicFont = false;
|
||||
private boolean mIsObliqueFont = false;
|
||||
private boolean mIsSmallCapsFontVariant = false;
|
||||
private String mLineCap = "butt";
|
||||
private String mLineJoin = "miter";
|
||||
|
||||
private class Size {
|
||||
Size(float w, float h) {
|
||||
this.width = w;
|
||||
this.height = h;
|
||||
}
|
||||
|
||||
Size() {
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
}
|
||||
public float width;
|
||||
public float height;
|
||||
}
|
||||
|
||||
private class Point {
|
||||
Point(float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
Point() {
|
||||
this.x = this.y = 0.0f;
|
||||
}
|
||||
|
||||
Point(Point pt) {
|
||||
this.x = pt.x;
|
||||
this.y = pt.y;
|
||||
}
|
||||
|
||||
void set(float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public float x;
|
||||
public float y;
|
||||
}
|
||||
|
||||
static void init(Context context) {
|
||||
sContext = new WeakReference<>(context);
|
||||
}
|
||||
|
||||
static void destroy() {
|
||||
sContext = null;
|
||||
}
|
||||
|
||||
private static HashMap<String, Typeface> sTypefaceCache = new HashMap<>();
|
||||
|
||||
// url is a full path started with '@assets/'
|
||||
private static void loadTypeface(String familyName, String url) {
|
||||
if (!sTypefaceCache.containsKey(familyName)) {
|
||||
try {
|
||||
Typeface typeface = null;
|
||||
if (url.startsWith("/")) {
|
||||
typeface = Typeface.createFromFile(url);
|
||||
} else if (sContext.get() != null) {
|
||||
final String prefix = "@assets/";
|
||||
if (url.startsWith(prefix)) {
|
||||
url = url.substring(prefix.length());
|
||||
}
|
||||
typeface = Typeface.createFromAsset(sContext.get().getAssets(), url);
|
||||
}
|
||||
|
||||
if (typeface != null) {
|
||||
sTypefaceCache.put(familyName, typeface);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// REFINE:: native should clear font cache before exiting game.
|
||||
private static void clearTypefaceCache() {
|
||||
sTypefaceCache.clear();
|
||||
}
|
||||
|
||||
private static TextPaint newPaint(String fontName, int fontSize, boolean enableBold, boolean enableItalic, boolean obliqueFont, boolean smallCapsFontVariant) {
|
||||
TextPaint paint = new TextPaint();
|
||||
paint.setTextSize(fontSize);
|
||||
paint.setAntiAlias(true);
|
||||
paint.setSubpixelText(true);
|
||||
|
||||
int style = Typeface.NORMAL;
|
||||
if (enableBold && enableItalic) {
|
||||
paint.setFakeBoldText(true);
|
||||
style = Typeface.BOLD_ITALIC;
|
||||
} else if (enableBold) {
|
||||
paint.setFakeBoldText(true);
|
||||
style = Typeface.BOLD;
|
||||
} else if (enableItalic) {
|
||||
style = Typeface.ITALIC;
|
||||
}
|
||||
|
||||
Typeface typeFace = null;
|
||||
if (sTypefaceCache.containsKey(fontName)) {
|
||||
typeFace = sTypefaceCache.get(fontName);
|
||||
typeFace = Typeface.create(typeFace, style);
|
||||
} else {
|
||||
typeFace = Typeface.create(fontName, style);
|
||||
}
|
||||
paint.setTypeface(typeFace);
|
||||
if(obliqueFont) {
|
||||
paint.setTextSkewX(_sApproximatingOblique);
|
||||
}
|
||||
if(smallCapsFontVariant && Build.VERSION.SDK_INT >= 21) {
|
||||
CocosReflectionHelper.<Void>invokeInstanceMethod(paint,
|
||||
"setFontFeatureSettings",
|
||||
new Class[]{String.class},
|
||||
new Object[]{"smcp"});
|
||||
}
|
||||
return paint;
|
||||
}
|
||||
|
||||
private CanvasRenderingContext2DImpl() {
|
||||
// Log.d(TAG, "constructor");
|
||||
}
|
||||
|
||||
private void recreateBuffer(float w, float h) {
|
||||
// Log.d(TAG, "recreateBuffer:" + w + ", " + h);
|
||||
if (mBitmap != null) {
|
||||
mBitmap.recycle();
|
||||
}
|
||||
mBitmap = Bitmap.createBitmap((int)Math.ceil(w), (int)Math.ceil(h), Bitmap.Config.ARGB_8888);
|
||||
// FIXME: in MIX 2S, its API level is 28, but can not find invokeInstanceMethod. It seems
|
||||
// devices may not obey the specification, so comment the codes.
|
||||
// if (Build.VERSION.SDK_INT >= 19) {
|
||||
// CocosReflectionHelper.<Void>invokeInstanceMethod(mBitmap,
|
||||
// "setPremultiplied",
|
||||
// new Class[]{Boolean.class},
|
||||
// new Object[]{Boolean.FALSE});
|
||||
// }
|
||||
mCanvas.setBitmap(mBitmap);
|
||||
}
|
||||
|
||||
private void beginPath() {
|
||||
if (mLinePath == null) {
|
||||
mLinePath = new Path();
|
||||
}
|
||||
mLinePath.reset();
|
||||
}
|
||||
|
||||
private void closePath() {
|
||||
mLinePath.close();
|
||||
}
|
||||
|
||||
private void moveTo(float x, float y) {
|
||||
mLinePath.moveTo(x, y);
|
||||
}
|
||||
|
||||
private void lineTo(float x, float y) {
|
||||
mLinePath.lineTo(x, y);
|
||||
}
|
||||
|
||||
private void stroke() {
|
||||
if (mLinePaint == null) {
|
||||
mLinePaint = new Paint();
|
||||
mLinePaint.setAntiAlias(true);
|
||||
}
|
||||
|
||||
if(mLinePath == null) {
|
||||
mLinePath = new Path();
|
||||
}
|
||||
|
||||
mLinePaint.setARGB(mStrokeStyleA, mStrokeStyleR, mStrokeStyleG, mStrokeStyleB);
|
||||
mLinePaint.setStyle(Paint.Style.STROKE);
|
||||
mLinePaint.setStrokeWidth(mLineWidth);
|
||||
this.setStrokeCap(mLinePaint);
|
||||
this.setStrokeJoin(mLinePaint);
|
||||
mCanvas.drawPath(mLinePath, mLinePaint);
|
||||
}
|
||||
|
||||
private void setStrokeCap(Paint paint) {
|
||||
switch (mLineCap) {
|
||||
case "butt":
|
||||
paint.setStrokeCap(Paint.Cap.BUTT);
|
||||
break;
|
||||
case "round":
|
||||
paint.setStrokeCap(Paint.Cap.ROUND);
|
||||
break;
|
||||
case "square":
|
||||
paint.setStrokeCap(Paint.Cap.SQUARE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void setStrokeJoin(Paint paint) {
|
||||
switch (mLineJoin) {
|
||||
case "bevel":
|
||||
paint.setStrokeJoin(Paint.Join.BEVEL);
|
||||
break;
|
||||
case "round":
|
||||
paint.setStrokeJoin(Paint.Join.ROUND);
|
||||
break;
|
||||
case "miter":
|
||||
paint.setStrokeJoin(Paint.Join.MITER);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void fill() {
|
||||
if (mLinePaint == null) {
|
||||
mLinePaint = new Paint();
|
||||
}
|
||||
|
||||
if(mLinePath == null) {
|
||||
mLinePath = new Path();
|
||||
}
|
||||
|
||||
mLinePaint.setARGB(mFillStyleA, mFillStyleR, mFillStyleG, mFillStyleB);
|
||||
mLinePaint.setStyle(Paint.Style.FILL);
|
||||
mCanvas.drawPath(mLinePath, mLinePaint);
|
||||
// workaround: draw a hairline to cover the border
|
||||
mLinePaint.setStrokeWidth(0);
|
||||
this.setStrokeCap(mLinePaint);
|
||||
this.setStrokeJoin(mLinePaint);
|
||||
mLinePaint.setStyle(Paint.Style.STROKE);
|
||||
mCanvas.drawPath(mLinePath, mLinePaint);
|
||||
mLinePaint.setStrokeWidth(mLineWidth);
|
||||
}
|
||||
|
||||
private void setLineCap(String lineCap) {
|
||||
mLineCap = lineCap;
|
||||
}
|
||||
|
||||
private void setLineJoin(String lineJoin) {
|
||||
mLineJoin = lineJoin;
|
||||
}
|
||||
|
||||
private void setShadowBlur(float blur) {
|
||||
mShadowBlur = blur * 0.5f;
|
||||
}
|
||||
|
||||
private void setShadowColor(int r, int g, int b, int a) {
|
||||
mShadowColorR = r;
|
||||
mShadowColorG = g;
|
||||
mShadowColorB = b;
|
||||
mShadowColorA = a;
|
||||
}
|
||||
|
||||
private void setShadowOffsetX(float offsetX) {
|
||||
mShadowOffsetX = offsetX;
|
||||
}
|
||||
|
||||
private void setShadowOffsetY(float offsetY) {
|
||||
mShadowOffsetY = offsetY;
|
||||
}
|
||||
|
||||
private void saveContext() {
|
||||
mCanvas.save();
|
||||
}
|
||||
|
||||
private void restoreContext() {
|
||||
// If there is no saved state, this method should do nothing.
|
||||
if (mCanvas.getSaveCount() > 1){
|
||||
mCanvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
private void rect(float x, float y, float w, float h) {
|
||||
// Log.d(TAG, "this: " + this + ", rect: " + x + ", " + y + ", " + w + ", " + h);
|
||||
beginPath();
|
||||
moveTo(x, y);
|
||||
lineTo(x, y + h);
|
||||
lineTo(x + w, y + h);
|
||||
lineTo(x + w, y);
|
||||
closePath();
|
||||
}
|
||||
|
||||
private void clearRect(float x, float y, float w, float h) {
|
||||
// Log.d(TAG, "this: " + this + ", clearRect: " + x + ", " + y + ", " + w + ", " + h);
|
||||
int clearSize = (int)(w * h);
|
||||
int[] clearColor = new int[clearSize];
|
||||
for (int i = 0; i < clearSize; ++i) {
|
||||
clearColor[i] = Color.TRANSPARENT;
|
||||
}
|
||||
mBitmap.setPixels(clearColor, 0, (int) w, (int) x, (int) y, (int) w, (int) h);
|
||||
}
|
||||
|
||||
private void createTextPaintIfNeeded() {
|
||||
if (mTextPaint == null) {
|
||||
mTextPaint = newPaint(mFontName, (int) mFontSize, mIsBoldFont, mIsItalicFont, mIsObliqueFont, mIsSmallCapsFontVariant);
|
||||
}
|
||||
}
|
||||
|
||||
private void fillRect(float x, float y, float w, float h) {
|
||||
// Log.d(TAG, "fillRect: " + x + ", " + y + ", " + ", " + w + ", " + h);
|
||||
int pixelValue = (mFillStyleA & 0xff) << 24 | (mFillStyleR & 0xff) << 16 | (mFillStyleG & 0xff) << 8 | (mFillStyleB & 0xff);
|
||||
int fillSize = (int)(w * h);
|
||||
int[] fillColors = new int[fillSize];
|
||||
for (int i = 0; i < fillSize; ++i) {
|
||||
fillColors[i] = pixelValue;
|
||||
}
|
||||
mBitmap.setPixels(fillColors, 0, (int) w, (int)x, (int)y, (int)w, (int)h);
|
||||
}
|
||||
|
||||
private void scaleX(TextPaint textPaint, String text, float maxWidth) {
|
||||
if(maxWidth < Float.MIN_VALUE) return;
|
||||
float measureWidth = this.measureText(text);
|
||||
if((measureWidth - maxWidth) < Float.MIN_VALUE) return;
|
||||
float scaleX = maxWidth/measureWidth;
|
||||
textPaint.setTextScaleX(scaleX);
|
||||
}
|
||||
|
||||
private void fillText(String text, float x, float y, float maxWidth) {
|
||||
// Log.d(TAG, "this: " + this + ", fillText: " + text + ", " + x + ", " + y + ", " + ", " + maxWidth);
|
||||
createTextPaintIfNeeded();
|
||||
configShadow(mTextPaint);
|
||||
mTextPaint.setARGB(mFillStyleA, mFillStyleR, mFillStyleG, mFillStyleB);
|
||||
mTextPaint.setStyle(Paint.Style.FILL);
|
||||
scaleX(mTextPaint, text, maxWidth);
|
||||
Point pt = convertDrawPoint(new Point(x, y), text);
|
||||
mCanvas.drawText(text, pt.x, pt.y, mTextPaint);
|
||||
}
|
||||
|
||||
private void strokeText(String text, float x, float y, float maxWidth) {
|
||||
// Log.d(TAG, "strokeText: " + text + ", " + x + ", " + y + ", " + ", " + maxWidth);
|
||||
createTextPaintIfNeeded();
|
||||
configShadow(mTextPaint);
|
||||
mTextPaint.setARGB(mStrokeStyleA, mStrokeStyleR, mStrokeStyleG, mStrokeStyleB);
|
||||
mTextPaint.setStyle(Paint.Style.STROKE);
|
||||
mTextPaint.setStrokeWidth(mLineWidth);
|
||||
scaleX(mTextPaint, text, maxWidth);
|
||||
Point pt = convertDrawPoint(new Point(x, y), text);
|
||||
mCanvas.drawText(text, pt.x, pt.y, mTextPaint);
|
||||
}
|
||||
|
||||
private void configShadow(Paint paint) {
|
||||
if ((Math.abs(mShadowOffsetX) > Float.MIN_VALUE || Math.abs(mShadowOffsetY) > Float.MIN_VALUE)) {
|
||||
if (mShadowBlur < 0) {
|
||||
return;
|
||||
}
|
||||
if (mShadowBlur < Float.MIN_VALUE) {
|
||||
mShadowBlur = 0.001f;//If shadowBlur is 0, the shadow effect is not consistent with the web.
|
||||
}
|
||||
paint.setShadowLayer(mShadowBlur, mShadowOffsetX, mShadowOffsetY,
|
||||
Color.argb(mShadowColorA, mShadowColorR, mShadowColorG, mShadowColorB));
|
||||
}
|
||||
}
|
||||
|
||||
private float measureText(String text) {
|
||||
createTextPaintIfNeeded();
|
||||
float ret = mTextPaint.measureText(text);
|
||||
// Log.d(TAG, "measureText: " + text + ", return: " + ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void updateFont(String fontName, float fontSize, boolean bold, boolean italic, boolean oblique, boolean smallCaps) {
|
||||
// Log.d(TAG, "updateFont: " + fontName + ", " + fontSize);
|
||||
mFontName = fontName;
|
||||
mFontSize = fontSize;
|
||||
mIsBoldFont = bold;
|
||||
mIsItalicFont = italic;
|
||||
mIsObliqueFont = oblique;
|
||||
mIsSmallCapsFontVariant = smallCaps;
|
||||
mTextPaint = null; // Reset paint to re-create paint object in createTextPaintIfNeeded
|
||||
}
|
||||
|
||||
private void setTextAlign(int align) {
|
||||
// Log.d(TAG, "setTextAlign: " + align);
|
||||
mTextAlign = align;
|
||||
}
|
||||
|
||||
private void setTextBaseline(int baseline) {
|
||||
// Log.d(TAG, "setTextBaseline: " + baseline);
|
||||
mTextBaseline = baseline;
|
||||
}
|
||||
|
||||
private void setFillStyle(int r, int g, int b, int a) {
|
||||
// Log.d(TAG, "setFillStyle: " + r + ", " + g + ", " + b + ", " + a);
|
||||
mFillStyleR = r;
|
||||
mFillStyleG = g;
|
||||
mFillStyleB = b;
|
||||
mFillStyleA = a;
|
||||
}
|
||||
|
||||
private void setStrokeStyle(int r, int g, int b, int a) {
|
||||
// Log.d(TAG, "setStrokeStyle: " + r + ", " + g + ", " + b + ", " + a);
|
||||
mStrokeStyleR = r;
|
||||
mStrokeStyleG = g;
|
||||
mStrokeStyleB = b;
|
||||
mStrokeStyleA = a;
|
||||
}
|
||||
|
||||
private void setLineWidth(float lineWidth) {
|
||||
mLineWidth = lineWidth;
|
||||
}
|
||||
|
||||
private void _fillImageData(int[] imageData, float imageWidth, float imageHeight, float offsetX, float offsetY) {
|
||||
Log.d(TAG, "_fillImageData: ");
|
||||
int fillSize = (int) (imageWidth * imageHeight);
|
||||
int[] fillColors = new int[fillSize];
|
||||
int r, g, b, a;
|
||||
for (int i = 0; i < fillSize; ++i) {
|
||||
// imageData Pixel (RGBA) -> fillColors int (ARGB)
|
||||
fillColors[i] = Integer.rotateRight(imageData[i], 8);
|
||||
}
|
||||
mBitmap.setPixels(fillColors, 0, (int) imageWidth, (int) offsetX, (int) offsetY, (int) imageWidth, (int) imageHeight);
|
||||
}
|
||||
|
||||
private Point convertDrawPoint(final Point point, String text) {
|
||||
// The parameter 'point' is located at left-bottom position.
|
||||
// Need to adjust 'point' according 'text align' & 'text base line'.
|
||||
Point ret = new Point(point);
|
||||
createTextPaintIfNeeded();
|
||||
Paint.FontMetrics fm = mTextPaint.getFontMetrics();
|
||||
float width = measureText(text);
|
||||
|
||||
if (mTextAlign == TEXT_ALIGN_CENTER)
|
||||
{
|
||||
ret.x -= width / 2;
|
||||
}
|
||||
else if (mTextAlign == TEXT_ALIGN_RIGHT)
|
||||
{
|
||||
ret.x -= width;
|
||||
}
|
||||
|
||||
// Canvas.drawText accepts the y parameter as the baseline position, not the most bottom
|
||||
if (mTextBaseline == TEXT_BASELINE_TOP)
|
||||
{
|
||||
ret.y += -fm.ascent;
|
||||
}
|
||||
else if (mTextBaseline == TEXT_BASELINE_MIDDLE)
|
||||
{
|
||||
ret.y += (fm.descent - fm.ascent) / 2 - fm.descent;
|
||||
}
|
||||
else if (mTextBaseline == TEXT_BASELINE_BOTTOM)
|
||||
{
|
||||
ret.y += -fm.descent;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private Bitmap getBitmap() {
|
||||
return mBitmap;
|
||||
}
|
||||
}
|
||||
222
cocos/platform/android/java/src/com/cocos/lib/CocosActivity.java
Normal file
222
cocos/platform/android/java/src/com/cocos/lib/CocosActivity.java
Normal file
@@ -0,0 +1,222 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2018-2022 Xiamen Yaji Software Co., Ltd.
|
||||
*
|
||||
* http://www.cocos.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
package com.cocos.lib;
|
||||
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.google.androidgamesdk.GameActivity;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CocosActivity extends GameActivity {
|
||||
private static final String TAG = "CocosActivity";
|
||||
private CocosWebViewHelper mWebViewHelper = null;
|
||||
private CocosVideoHelper mVideoHelper = null;
|
||||
|
||||
private CocosSensorHandler mSensorHandler;
|
||||
private List<CocosSurfaceView> mSurfaceViewArray;
|
||||
private FrameLayout mRootLayout;
|
||||
|
||||
|
||||
|
||||
private native void onCreateNative();
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
onLoadNativeLibraries();
|
||||
onCreateNative();
|
||||
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// GlobalObject.init should be initialized at first.
|
||||
GlobalObject.init(this, this);
|
||||
|
||||
CocosHelper.registerBatteryLevelReceiver(this);
|
||||
CocosHelper.init();
|
||||
CocosAudioFocusManager.registerAudioFocusListener(this);
|
||||
CanvasRenderingContext2DImpl.init(this);
|
||||
this.setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
||||
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
|
||||
initView();
|
||||
|
||||
|
||||
mSensorHandler = new CocosSensorHandler(this);
|
||||
|
||||
setImmersiveMode();
|
||||
|
||||
Utils.hideVirtualButton();
|
||||
|
||||
mSurfaceView.setOnTouchListener((v, event) -> processMotionEvent(event));
|
||||
}
|
||||
|
||||
private void setImmersiveMode() {
|
||||
WindowManager.LayoutParams lp = getWindow().getAttributes();
|
||||
try {
|
||||
Field field = lp.getClass().getField("layoutInDisplayCutoutMode");
|
||||
//Field constValue = lp.getClass().getDeclaredField("LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER");
|
||||
Field constValue = lp.getClass().getDeclaredField("LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES");
|
||||
field.setInt(lp, constValue.getInt(null));
|
||||
|
||||
// https://developer.android.com/training/system-ui/immersive
|
||||
int flag = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
|
||||
|
||||
flag |= View.class.getDeclaredField("SYSTEM_UI_FLAG_IMMERSIVE_STICKY").getInt(null);
|
||||
View view = getWindow().getDecorView();
|
||||
view.setSystemUiVisibility(flag);
|
||||
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
protected void initView() {
|
||||
mRootLayout = findViewById(contentViewId);
|
||||
if (mWebViewHelper == null) {
|
||||
mWebViewHelper = new CocosWebViewHelper(mRootLayout);
|
||||
}
|
||||
|
||||
if (mVideoHelper == null) {
|
||||
mVideoHelper = new CocosVideoHelper(this, mRootLayout);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public SurfaceView getSurfaceView() {
|
||||
return this.mSurfaceView;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
CocosHelper.unregisterBatteryLevelReceiver(this);
|
||||
CocosAudioFocusManager.unregisterAudioFocusListener(this);
|
||||
CanvasRenderingContext2DImpl.destroy();
|
||||
GlobalObject.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
mSensorHandler.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
mSensorHandler.onResume();
|
||||
Utils.hideVirtualButton();
|
||||
if (CocosAudioFocusManager.isAudioFocusLoss()) {
|
||||
CocosAudioFocusManager.registerAudioFocusListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
mSurfaceView.setVisibility(View.INVISIBLE);
|
||||
if (null != mSurfaceViewArray) {
|
||||
for (CocosSurfaceView surfaceView : mSurfaceViewArray) {
|
||||
surfaceView.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
mSurfaceView.setVisibility(View.VISIBLE);
|
||||
if (null != mSurfaceViewArray) {
|
||||
for (CocosSurfaceView surfaceView : mSurfaceViewArray) {
|
||||
surfaceView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
super.onWindowFocusChanged(hasFocus);
|
||||
if (hasFocus && CocosAudioFocusManager.isAudioFocusLoss()) {
|
||||
CocosAudioFocusManager.registerAudioFocusListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
// invoke from native code
|
||||
@SuppressWarnings({"UnusedDeclaration"})
|
||||
private void createSurface(int x, int y, int width, int height, int windowId) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CocosSurfaceView view = new CocosSurfaceView(CocosActivity.this, windowId);
|
||||
view.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(width, height);
|
||||
params.leftMargin = x;
|
||||
params.topMargin = y;
|
||||
//mSubsurfaceView.setBackgroundColor(Color.BLUE);
|
||||
mRootLayout.addView(view, params);
|
||||
if (null == mSurfaceViewArray) {
|
||||
mSurfaceViewArray = new ArrayList<>();
|
||||
}
|
||||
mSurfaceViewArray.add(view);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onLoadNativeLibraries() {
|
||||
try {
|
||||
ApplicationInfo ai = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
|
||||
|
||||
Bundle bundle = ai.metaData;
|
||||
String libName = bundle.getString("android.app.lib_name");
|
||||
if (TextUtils.isEmpty(libName)) {
|
||||
Log.e(TAG, "can not find library, please config android.app.lib_name at AndroidManifest.xml");
|
||||
}
|
||||
assert libName != null;
|
||||
System.loadLibrary(libName);
|
||||
getIntent().putExtra(GameActivity.META_DATA_LIB_NAME, libName);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2018-2022 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated engine source code (the "Software"), a limited,
|
||||
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
||||
to use Cocos Creator solely to develop games on your target platforms. You shall
|
||||
not use Cocos Creator software for developing other software or tools that's
|
||||
used for developing games. You are not granted to publish, distribute,
|
||||
sublicense, and/or sell copies of Cocos Creator.
|
||||
|
||||
The software or tools in this License Agreement are licensed, not sold.
|
||||
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
package com.cocos.lib;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.AudioFocusRequest;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
class CocosAudioFocusManager {
|
||||
|
||||
private static final String _TAG = "CocosAudioFocusManager";
|
||||
private static boolean isAudioFocusLost = true;
|
||||
|
||||
private final static AudioManager.OnAudioFocusChangeListener sAfChangeListener = focusChange -> {
|
||||
Log.d(_TAG, "onAudioFocusChange: " + focusChange + ", thread: " + Thread.currentThread().getName());
|
||||
|
||||
if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
|
||||
// Permanent loss of audio focus
|
||||
// Pause playback immediately
|
||||
Log.d(_TAG, "Pause music by AUDIOFOCUS_LOSS");
|
||||
isAudioFocusLost = true;
|
||||
CocosHelper.runOnGameThreadAtForeground(() -> nativeSetAudioVolumeFactor(0));
|
||||
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
|
||||
// Pause playback
|
||||
Log.d(_TAG, "Pause music by AUDIOFOCUS_LOSS_TRANSILENT");
|
||||
isAudioFocusLost = true;
|
||||
CocosHelper.runOnGameThreadAtForeground(() -> nativeSetAudioVolumeFactor(0));
|
||||
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
|
||||
// Lower the volume, keep playing
|
||||
Log.d(_TAG, "Lower the volume, keep playing by AUDIOFOCUS_LOSS_TRANSILENT_CAN_DUCK");
|
||||
isAudioFocusLost = false;
|
||||
CocosHelper.runOnGameThreadAtForeground(() -> nativeSetAudioVolumeFactor(0.1f));
|
||||
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
|
||||
// Your app has been granted audio focus again
|
||||
// Raise volume to normal, restart playback if necessary
|
||||
Log.d(_TAG, "Resume music by AUDIOFOCUS_GAIN");
|
||||
isAudioFocusLost = false;
|
||||
CocosHelper.runOnGameThreadAtForeground(() -> nativeSetAudioVolumeFactor(1.0f));
|
||||
}
|
||||
};
|
||||
|
||||
static void registerAudioFocusListener(Context context) {
|
||||
AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
assert am != null;
|
||||
int result;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
AudioAttributes playbackAttributes = new AudioAttributes.Builder()
|
||||
.setUsage(AudioAttributes.USAGE_GAME)
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
|
||||
.build();
|
||||
|
||||
// set the playback attributes for the focus requester
|
||||
AudioFocusRequest focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK)
|
||||
.setAudioAttributes(playbackAttributes)
|
||||
.setWillPauseWhenDucked(true)
|
||||
.setOnAudioFocusChangeListener(sAfChangeListener)
|
||||
.build();
|
||||
|
||||
result = am.requestAudioFocus(focusRequest);
|
||||
} else {
|
||||
// Request audio focus for playback
|
||||
result = am.requestAudioFocus(sAfChangeListener,
|
||||
// Use the music stream.
|
||||
AudioManager.STREAM_MUSIC,
|
||||
// Request permanent focus.
|
||||
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
|
||||
}
|
||||
|
||||
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
|
||||
Log.d(_TAG, "requestAudioFocus succeed");
|
||||
isAudioFocusLost = false;
|
||||
CocosHelper.runOnGameThreadAtForeground(() -> nativeSetAudioVolumeFactor(1.0f));
|
||||
return;
|
||||
}
|
||||
|
||||
Log.e(_TAG, "requestAudioFocus failed!");
|
||||
}
|
||||
|
||||
static void unregisterAudioFocusListener(Context context) {
|
||||
AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
assert am != null;
|
||||
int result = am.abandonAudioFocus(sAfChangeListener);
|
||||
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
|
||||
Log.d(_TAG, "abandonAudioFocus succeed!");
|
||||
} else {
|
||||
Log.e(_TAG, "abandonAudioFocus failed!");
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isAudioFocusLoss() {
|
||||
return isAudioFocusLost;
|
||||
}
|
||||
|
||||
private static native void nativeSetAudioVolumeFactor(float focus);
|
||||
}
|
||||
@@ -0,0 +1,406 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated engine source code (the "Software"), a limited,
|
||||
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
||||
to use Cocos Creator solely to develop games on your target platforms. You shall
|
||||
not use Cocos Creator software for developing other software or tools that's
|
||||
used for developing games. You are not granted to publish, distribute,
|
||||
sublicense, and/or sell copies of Cocos Creator.
|
||||
|
||||
The software or tools in this License Agreement are licensed, not sold.
|
||||
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
package com.cocos.lib;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
|
||||
import org.cocos2dx.okhttp3.Call;
|
||||
import org.cocos2dx.okhttp3.Callback;
|
||||
import org.cocos2dx.okhttp3.Dispatcher;
|
||||
import org.cocos2dx.okhttp3.OkHttpClient;
|
||||
import org.cocos2dx.okhttp3.Protocol;
|
||||
import org.cocos2dx.okhttp3.Request;
|
||||
import org.cocos2dx.okhttp3.Response;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
// Rename package okhttp3 to org.cocos2dx.okhttp3
|
||||
// Github repo: https://github.com/PatriceJiang/okhttp/tree/cocos2dx-rename-3.12.x
|
||||
// and https://github.com/PatriceJiang/okio/tree/cocos2dx-rename-1.15.0
|
||||
|
||||
public class CocosDownloader {
|
||||
|
||||
private int _id;
|
||||
private OkHttpClient _httpClient = null;
|
||||
private static Dispatcher dispatcher = null;
|
||||
|
||||
private String _tempFileNameSuffix;
|
||||
private int _countOfMaxProcessingTasks;
|
||||
private ConcurrentHashMap<Integer,Call> _taskMap = new ConcurrentHashMap<>();
|
||||
private Queue<Runnable> _taskQueue = new LinkedList<>();
|
||||
private int _runningTaskCount = 0;
|
||||
|
||||
private void onProgress(final int id, final long downloadBytes, final long downloadNow, final long downloadTotal) {
|
||||
CocosHelper.runOnGameThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
nativeOnProgress(_id, id, downloadBytes, downloadNow, downloadTotal);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onFinish(final int id, final int errCode, final String errStr, final byte[] data) {
|
||||
Call task =_taskMap.get(id);
|
||||
if (null == task) return;
|
||||
_taskMap.remove(id);
|
||||
_runningTaskCount -= 1;
|
||||
CocosHelper.runOnGameThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
nativeOnFinish(_id, id, errCode, errStr, data);
|
||||
}
|
||||
});
|
||||
runNextTaskIfExists();
|
||||
}
|
||||
|
||||
public static CocosDownloader createDownloader(int id, int timeoutInSeconds, String tempFileSuffix, int maxProcessingTasks) {
|
||||
CocosDownloader downloader = new CocosDownloader();
|
||||
downloader._id = id;
|
||||
|
||||
if(dispatcher == null) {
|
||||
dispatcher = new Dispatcher();
|
||||
}
|
||||
|
||||
if (timeoutInSeconds > 0) {
|
||||
downloader._httpClient = new OkHttpClient().newBuilder()
|
||||
.dispatcher(dispatcher)
|
||||
.followRedirects(true)
|
||||
.followSslRedirects(true)
|
||||
.connectTimeout(timeoutInSeconds, TimeUnit.SECONDS)
|
||||
.protocols(Collections.singletonList(Protocol.HTTP_1_1))
|
||||
.build();
|
||||
} else {
|
||||
downloader._httpClient = new OkHttpClient().newBuilder()
|
||||
.dispatcher(dispatcher)
|
||||
.followRedirects(true)
|
||||
.followSslRedirects(true)
|
||||
.protocols(Collections.singletonList(Protocol.HTTP_1_1))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
downloader._tempFileNameSuffix = tempFileSuffix;
|
||||
downloader._countOfMaxProcessingTasks = maxProcessingTasks;
|
||||
return downloader;
|
||||
}
|
||||
|
||||
public static void createTask(final CocosDownloader downloader, int id_, String url_, String path_, String []header_) {
|
||||
final int id = id_;
|
||||
final String url = url_;
|
||||
final String path = path_;
|
||||
final String pathWithUrlHash = path + url.hashCode();
|
||||
final String tempFilePath = pathWithUrlHash + downloader._tempFileNameSuffix;
|
||||
final String[] header = header_;
|
||||
|
||||
Runnable taskRunnable = new Runnable() {
|
||||
String domain = null;
|
||||
String host = null;
|
||||
File tempFile = null;
|
||||
File finalFile = null;
|
||||
long downloadStart = 0;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Call task = null;
|
||||
|
||||
do {
|
||||
if (path.length() > 0) {
|
||||
try {
|
||||
URI uri = new URI(url);
|
||||
domain = uri.getHost();
|
||||
} catch (URISyntaxException e) {
|
||||
e.printStackTrace();
|
||||
break;
|
||||
} catch (NullPointerException e) {
|
||||
e.printStackTrace();
|
||||
break;
|
||||
}
|
||||
|
||||
// file task
|
||||
tempFile = new File(tempFilePath);
|
||||
if (tempFile.isDirectory()) break;
|
||||
|
||||
File parent = tempFile.getParentFile();
|
||||
if (parent == null) {
|
||||
String errStr = "Invalid path " + path + " : The current path is inaccessible.";
|
||||
Log.e("CocosDownloader", errStr);
|
||||
CocosHelper.runOnGameThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
downloader.nativeOnFinish(downloader._id, id, 0, errStr, null);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
if (!parent.isDirectory() && !parent.mkdirs()) break;
|
||||
|
||||
finalFile = new File(path);
|
||||
if (finalFile.isDirectory()) break;
|
||||
long fileLen = tempFile.length();
|
||||
|
||||
host = domain.startsWith("www.") ? domain.substring(4) : domain;
|
||||
if (fileLen > 0) {
|
||||
SharedPreferences sharedPreferences = GlobalObject.getContext().getSharedPreferences("breakpointDownloadSupport", Context.MODE_PRIVATE);
|
||||
if (sharedPreferences.contains(host) && sharedPreferences.getBoolean(host, false)) {
|
||||
downloadStart = fileLen;
|
||||
} else {
|
||||
// Remove previous downloaded context
|
||||
try {
|
||||
PrintWriter writer = new PrintWriter(tempFile);
|
||||
writer.print("");
|
||||
writer.close();
|
||||
}
|
||||
// Not found then nothing to do
|
||||
catch (FileNotFoundException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final Request.Builder builder = new Request.Builder().url(url);
|
||||
for (int i = 0; i < header.length / 2; i++) {
|
||||
builder.addHeader(header[i * 2], header[(i * 2) + 1]);
|
||||
}
|
||||
if (downloadStart > 0) {
|
||||
builder.addHeader("RANGE", "bytes=" + downloadStart + "-");
|
||||
}
|
||||
|
||||
final Request request = builder.build();
|
||||
task = downloader._httpClient.newCall(request);
|
||||
if (null == task) {
|
||||
final String errStr = "Can't create DownloadTask for " + url;
|
||||
CocosHelper.runOnGameThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
downloader.nativeOnFinish(downloader._id, id, 0, errStr, null);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
downloader._taskMap.put(id, task);
|
||||
}
|
||||
task.enqueue(new Callback() {
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e) {
|
||||
downloader.onFinish(id, 0, e.toString(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) throws IOException {
|
||||
InputStream is = null;
|
||||
byte[] buf = new byte[4096];
|
||||
FileOutputStream fos = null;
|
||||
|
||||
try {
|
||||
|
||||
if(!(response.code() >= 200 && response.code() <= 206)) {
|
||||
// it is encourage to delete the tmp file when requested range not satisfiable.
|
||||
if (response.code() == 416) {
|
||||
File file = new File(tempFilePath);
|
||||
if (file.exists() && file.isFile()) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
downloader.onFinish(id, -2, response.message(), null);
|
||||
return;
|
||||
}
|
||||
|
||||
// save breakpointDownloadSupport Data to SharedPreferences storage
|
||||
Context context = GlobalObject.getContext();
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences("breakpointDownloadSupport", Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
long total = response.body().contentLength() + downloadStart;
|
||||
if (path.length() > 0 && !sharedPreferences.contains(host)) {
|
||||
if (total > 0) {
|
||||
editor.putBoolean(host, true);
|
||||
} else {
|
||||
editor.putBoolean(host, false);
|
||||
}
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
long current = downloadStart;
|
||||
is = response.body().byteStream();
|
||||
|
||||
if (path.length() > 0) {
|
||||
if (downloadStart > 0) {
|
||||
fos = new FileOutputStream(tempFile, true);
|
||||
} else {
|
||||
fos = new FileOutputStream(tempFile, false);
|
||||
}
|
||||
|
||||
int len;
|
||||
while ((len = is.read(buf)) != -1) {
|
||||
current += len;
|
||||
fos.write(buf, 0, len);
|
||||
downloader.onProgress(id, len, current, total);
|
||||
}
|
||||
fos.flush();
|
||||
|
||||
String errStr = null;
|
||||
do {
|
||||
// rename temp file to final file, if final file exist, remove it
|
||||
if (finalFile.exists()) {
|
||||
if (finalFile.isDirectory()) {
|
||||
break;
|
||||
}
|
||||
if (!finalFile.delete()) {
|
||||
errStr = "Can't remove old file:" + finalFile.getAbsolutePath();
|
||||
break;
|
||||
}
|
||||
}
|
||||
tempFile.renameTo(finalFile);
|
||||
} while (false);
|
||||
|
||||
if (errStr == null) {
|
||||
downloader.onFinish(id, 0, null, null);
|
||||
downloader.runNextTaskIfExists();
|
||||
} else {
|
||||
downloader.onFinish(id, 0, errStr, null);
|
||||
}
|
||||
if (sharedPreferences.contains(host)) {
|
||||
editor.remove(host);
|
||||
editor.commit();
|
||||
}
|
||||
} else {
|
||||
// non-file
|
||||
ByteArrayOutputStream buffer;
|
||||
if(total > 0) {
|
||||
buffer = new ByteArrayOutputStream((int) total);
|
||||
} else {
|
||||
buffer = new ByteArrayOutputStream(4096);
|
||||
}
|
||||
|
||||
int len;
|
||||
while ((len = is.read(buf)) != -1) {
|
||||
current += len;
|
||||
buffer.write(buf, 0, len);
|
||||
downloader.onProgress(id, len, current, total);
|
||||
}
|
||||
downloader.onFinish(id, 0, null, buffer.toByteArray());
|
||||
downloader.runNextTaskIfExists();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
downloader.onFinish(id, 0, e.toString(), null);
|
||||
} finally {
|
||||
try {
|
||||
if (is != null) {
|
||||
is.close();
|
||||
}
|
||||
if (fos != null) {
|
||||
fos.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e("CocosDownloader", e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} while (false);
|
||||
}
|
||||
};
|
||||
downloader.enqueueTask(taskRunnable);
|
||||
}
|
||||
|
||||
public static void abort(final CocosDownloader downloader, final int id) {
|
||||
GlobalObject.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Iterator iter = downloader._taskMap.entrySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
Map.Entry entry = (Map.Entry) iter.next();
|
||||
Object key = entry.getKey();
|
||||
Call task = (Call) entry.getValue();
|
||||
if (null != task && Integer.parseInt(key.toString()) == id) {
|
||||
task.cancel();
|
||||
downloader._taskMap.remove(id);
|
||||
downloader.runNextTaskIfExists();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void cancelAllRequests(final CocosDownloader downloader) {
|
||||
GlobalObject.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (Object o : downloader._taskMap.entrySet()) {
|
||||
Map.Entry entry = (Map.Entry) o;
|
||||
Call task = (Call) entry.getValue();
|
||||
if (null != task) {
|
||||
task.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void enqueueTask(Runnable taskRunnable) {
|
||||
synchronized (_taskQueue) {
|
||||
if (_runningTaskCount < _countOfMaxProcessingTasks) {
|
||||
GlobalObject.runOnUiThread(taskRunnable);
|
||||
_runningTaskCount++;
|
||||
} else {
|
||||
_taskQueue.add(taskRunnable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void runNextTaskIfExists() {
|
||||
synchronized (_taskQueue) {
|
||||
while (_runningTaskCount < _countOfMaxProcessingTasks &&
|
||||
CocosDownloader.this._taskQueue.size() > 0) {
|
||||
|
||||
Runnable taskRunnable = CocosDownloader.this._taskQueue.poll();
|
||||
GlobalObject.runOnUiThread(taskRunnable);
|
||||
_runningTaskCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
native void nativeOnProgress(int id, int taskId, long dl, long dlnow, long dltotal);
|
||||
native void nativeOnFinish(int id, int taskId, int errCode, String errStr, final byte[] data);
|
||||
}
|
||||
@@ -0,0 +1,486 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
package com.cocos.lib;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.ShapeDrawable;
|
||||
import android.graphics.drawable.StateListDrawable;
|
||||
import android.graphics.drawable.shapes.RoundRectShape;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.InputFilter;
|
||||
import android.text.InputType;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class CocosEditBoxActivity extends Activity {
|
||||
|
||||
// a color of dark green, was used for confirm button background
|
||||
private static final int DARK_GREEN = Color.parseColor("#1fa014");
|
||||
private static final int DARK_GREEN_PRESS = Color.parseColor("#008e26");
|
||||
|
||||
private static CocosEditBoxActivity sThis = null;
|
||||
private Cocos2dxEditText mEditText = null;
|
||||
private Button mButton = null;
|
||||
private String mButtonTitle = null;
|
||||
private boolean mConfirmHold = true;
|
||||
private int mEditTextID = 1;
|
||||
private int mButtonLayoutID = 2;
|
||||
|
||||
/***************************************************************************************
|
||||
Inner class.
|
||||
**************************************************************************************/
|
||||
class Cocos2dxEditText extends EditText {
|
||||
private final String TAG = "Cocos2dxEditBox";
|
||||
private boolean mIsMultiLine = false;
|
||||
private TextWatcher mTextWatcher = null;
|
||||
private boolean keyboardVisible = false;
|
||||
private int mScreenHeight;
|
||||
private boolean mCheckKeyboardShowNormally = false;
|
||||
|
||||
public Cocos2dxEditText(Activity context){
|
||||
super(context);
|
||||
this.setTextColor(Color.BLACK);
|
||||
mScreenHeight = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).
|
||||
getDefaultDisplay().getHeight();
|
||||
|
||||
mTextWatcher = new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
// Pass text to c++.
|
||||
CocosEditBoxActivity.this.onKeyboardInput(s.toString());
|
||||
}
|
||||
};
|
||||
registKeyboardVisible();
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
Public functions.
|
||||
**************************************************************************************/
|
||||
|
||||
public void show(String defaultValue, int maxLength, boolean isMultiline, boolean confirmHold, String confirmType, String inputType) {
|
||||
mIsMultiLine = isMultiline;
|
||||
this.setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength) });
|
||||
this.setText(defaultValue);
|
||||
if (this.getText().length() >= defaultValue.length()) {
|
||||
this.setSelection(defaultValue.length());
|
||||
} else {
|
||||
this.setSelection(this.getText().length());
|
||||
}
|
||||
this.setConfirmType(confirmType);
|
||||
this.setInputType(inputType, mIsMultiLine);
|
||||
this.setVisibility(View.VISIBLE);
|
||||
|
||||
// Open soft keyboard manually. Should request focus to open soft keyboard.
|
||||
this.requestFocus();
|
||||
|
||||
this.addListeners();
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
mEditText.setVisibility(View.INVISIBLE);
|
||||
this.removeListeners();
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
Private functions.
|
||||
**************************************************************************************/
|
||||
|
||||
private void setConfirmType(final String confirmType) {
|
||||
if (confirmType.contentEquals("done")) {
|
||||
this.setImeOptions(EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
|
||||
mButtonTitle = getResources().getString(R.string.done);
|
||||
} else if (confirmType.contentEquals("next")) {
|
||||
this.setImeOptions(EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
|
||||
mButtonTitle = getResources().getString(R.string.next);
|
||||
} else if (confirmType.contentEquals("search")) {
|
||||
this.setImeOptions(EditorInfo.IME_ACTION_SEARCH | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
|
||||
mButtonTitle = getResources().getString(R.string.search);
|
||||
} else if (confirmType.contentEquals("go")) {
|
||||
this.setImeOptions(EditorInfo.IME_ACTION_GO | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
|
||||
mButtonTitle = getResources().getString(R.string.go);
|
||||
} else if (confirmType.contentEquals("send")) {
|
||||
this.setImeOptions(EditorInfo.IME_ACTION_SEND | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
|
||||
mButtonTitle = getResources().getString(R.string.send);
|
||||
} else{
|
||||
mButtonTitle = null;
|
||||
Log.e(TAG, "unknown confirm type " + confirmType);
|
||||
}
|
||||
}
|
||||
|
||||
private void setInputType(final String inputType, boolean isMultiLine){
|
||||
mCheckKeyboardShowNormally = false;
|
||||
if (inputType.contentEquals("text")) {
|
||||
if (isMultiLine)
|
||||
this.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
|
||||
else
|
||||
this.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
}
|
||||
else if (inputType.contentEquals("email"))
|
||||
this.setInputType(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
|
||||
else if (inputType.contentEquals("number"))
|
||||
this.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL | InputType.TYPE_NUMBER_FLAG_SIGNED);
|
||||
else if (inputType.contentEquals("phone"))
|
||||
this.setInputType(InputType.TYPE_CLASS_PHONE);
|
||||
else if (inputType.contentEquals("password")) {
|
||||
if (Build.BRAND.equalsIgnoreCase("oppo")) {
|
||||
mCheckKeyboardShowNormally = true;
|
||||
}
|
||||
this.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
}
|
||||
else
|
||||
Log.e(TAG, "unknown input type " + inputType);
|
||||
}
|
||||
|
||||
private void addListeners() {
|
||||
|
||||
this.setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if (! mIsMultiLine) {
|
||||
CocosEditBoxActivity.this.hide();
|
||||
}
|
||||
|
||||
return false; // pass on to other listeners.
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this.addTextChangedListener(mTextWatcher);
|
||||
}
|
||||
|
||||
private void removeListeners() {
|
||||
this.setOnEditorActionListener(null);
|
||||
this.removeTextChangedListener(mTextWatcher);
|
||||
}
|
||||
|
||||
private boolean isSystemAdjustUIWhenPopKeyboard(int bottom) {
|
||||
int bottomOffset = 0;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
bottomOffset = getWindow().getDecorView().getRootWindowInsets().getSystemWindowInsetBottom();
|
||||
}
|
||||
// view will be scrolled to the target position by system,
|
||||
if (Math.abs(bottom - bottomOffset) < 10) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void registKeyboardVisible() {
|
||||
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
Rect r = new Rect();
|
||||
getWindowVisibleDisplayFrame(r);
|
||||
int heightDiff = getRootView().getHeight() - (r.bottom - r.top);
|
||||
// if more than a quarter of the screen, its probably a keyboard
|
||||
if (heightDiff > mScreenHeight/4) {
|
||||
if (!keyboardVisible) {
|
||||
keyboardVisible = true;
|
||||
}
|
||||
if (!isSystemAdjustUIWhenPopKeyboard(heightDiff)) {
|
||||
getRootView().scrollTo(0, heightDiff);
|
||||
}
|
||||
} else {
|
||||
getRootView().scrollTo(0, 0);
|
||||
if (mCheckKeyboardShowNormally && !keyboardVisible) {
|
||||
Toast.makeText(CocosEditBoxActivity.this, R.string.tip_disable_safe_input_type, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
if (keyboardVisible) {
|
||||
keyboardVisible = false;
|
||||
CocosEditBoxActivity.this.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
|
||||
CocosEditBoxActivity.sThis = this;
|
||||
|
||||
ViewGroup.LayoutParams frameLayoutParams =
|
||||
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
RelativeLayout frameLayout = new RelativeLayout(this);
|
||||
frameLayout.setLayoutParams(frameLayoutParams);
|
||||
frameLayout.setClickable(true);
|
||||
frameLayout.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
CocosEditBoxActivity.this.hide();
|
||||
}
|
||||
});
|
||||
setContentView(frameLayout);
|
||||
|
||||
this.addItems(frameLayout);
|
||||
|
||||
Intent intent = getIntent();
|
||||
Bundle extras = null;
|
||||
if (null != intent) {
|
||||
extras = intent.getExtras();
|
||||
}
|
||||
if(extras == null){
|
||||
show("",
|
||||
10,
|
||||
false,
|
||||
false,
|
||||
"done",
|
||||
"text"
|
||||
);
|
||||
} else {
|
||||
show(extras.getString("defaultValue"),
|
||||
extras.getInt("maxLength"),
|
||||
extras.getBoolean("isMultiline"),
|
||||
extras.getBoolean("confirmHold"),
|
||||
extras.getString("confirmType"),
|
||||
extras.getString("inputType"));
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
Public functions.
|
||||
**************************************************************************************/
|
||||
|
||||
/***************************************************************************************
|
||||
Private functions.
|
||||
**************************************************************************************/
|
||||
private void addItems(RelativeLayout layout) {
|
||||
RelativeLayout myLayout = new RelativeLayout(this);
|
||||
myLayout.setBackgroundColor(Color.argb(255, 244, 244, 244));
|
||||
|
||||
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
|
||||
layout.addView(myLayout, layoutParams);
|
||||
|
||||
this.addEditText(myLayout);
|
||||
this.addButton(myLayout);
|
||||
}
|
||||
|
||||
private int dpToPixel(int dp) {
|
||||
final float scale = getResources().getDisplayMetrics().density;
|
||||
int px = (int) (dp * scale + 0.5f);
|
||||
return px;
|
||||
}
|
||||
private void addEditText(RelativeLayout layout) {
|
||||
mEditText = new Cocos2dxEditText(this);
|
||||
mEditText.setVisibility(View.INVISIBLE);
|
||||
mEditText.setGravity(Gravity.CENTER_VERTICAL);
|
||||
mEditText.setBackground(getRoundRectShape(18, Color.WHITE, Color.WHITE));
|
||||
mEditText.setId(mEditTextID);
|
||||
int bottomPadding = dpToPixel(4);
|
||||
int leftPadding = dpToPixel(3);
|
||||
mEditText.setPadding(leftPadding,bottomPadding,leftPadding,bottomPadding);
|
||||
|
||||
RelativeLayout.LayoutParams editParams = new RelativeLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
|
||||
editParams.addRule(RelativeLayout.CENTER_VERTICAL);
|
||||
editParams.addRule(RelativeLayout.LEFT_OF, mButtonLayoutID);
|
||||
int bottomMargin = dpToPixel(5);
|
||||
int leftMargin = dpToPixel(4);
|
||||
editParams.setMargins(leftMargin, bottomMargin, bottomMargin, bottomMargin);
|
||||
layout.addView(mEditText, editParams);
|
||||
}
|
||||
|
||||
private void addButton(RelativeLayout layout) {
|
||||
mButton = new Button(this);
|
||||
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
mButton.setTextColor(Color.WHITE);
|
||||
mButton.setTextSize(16);
|
||||
mButton.setBackground(getRoundRectShape(18, DARK_GREEN, DARK_GREEN_PRESS));
|
||||
int paddingLeft = dpToPixel(5);
|
||||
mButton.setPadding(paddingLeft,0,paddingLeft,0);
|
||||
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
|
||||
layoutParams.addRule(RelativeLayout.ALIGN_TOP, mEditTextID);
|
||||
layoutParams.addRule(RelativeLayout.ALIGN_BOTTOM, mEditTextID);
|
||||
layoutParams.rightMargin = dpToPixel(4);
|
||||
layout.addView(mButton, layoutParams);
|
||||
mButton.setGravity(Gravity.CENTER);
|
||||
mButton.setId(mButtonLayoutID);
|
||||
|
||||
mButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
CocosEditBoxActivity.this.onKeyboardConfirm(mEditText.getText().toString());
|
||||
|
||||
if (!CocosEditBoxActivity.this.mConfirmHold)
|
||||
CocosEditBoxActivity.this.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Drawable getRoundRectShape(int radius, int normalColor, int pressColor) {
|
||||
float[] outerRadii = new float[]{radius, radius, radius, radius, radius, radius, radius, radius};
|
||||
RoundRectShape roundRectShape = new RoundRectShape(outerRadii, null, null);
|
||||
ShapeDrawable shapeDrawableNormal = new ShapeDrawable();
|
||||
shapeDrawableNormal.setShape(roundRectShape);
|
||||
shapeDrawableNormal.getPaint().setStyle(Paint.Style.FILL);
|
||||
shapeDrawableNormal.getPaint().setColor(normalColor);
|
||||
ShapeDrawable shapeDrawablePress = new ShapeDrawable();
|
||||
shapeDrawablePress.setShape(roundRectShape);
|
||||
shapeDrawablePress.getPaint().setStyle(Paint.Style.FILL);
|
||||
shapeDrawablePress.getPaint().setColor(pressColor);
|
||||
StateListDrawable drawable = new StateListDrawable();
|
||||
drawable.addState(new int[]{android.R.attr.state_pressed}, shapeDrawablePress);
|
||||
drawable.addState(new int[]{}, shapeDrawableNormal);
|
||||
return drawable;
|
||||
}
|
||||
|
||||
|
||||
private void hide() {
|
||||
Utils.hideVirtualButton();
|
||||
this.closeKeyboard();
|
||||
finish();
|
||||
}
|
||||
|
||||
public void show(String defaultValue, int maxLength, boolean isMultiline, boolean confirmHold, String confirmType, String inputType) {
|
||||
mConfirmHold = confirmHold;
|
||||
mEditText.show(defaultValue, maxLength, isMultiline, confirmHold, confirmType, inputType);
|
||||
mButton.setText(mButtonTitle);
|
||||
if (TextUtils.isEmpty(mButtonTitle)) {
|
||||
mButton.setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
mButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
this.openKeyboard();
|
||||
}
|
||||
|
||||
private void closeKeyboard() {
|
||||
InputMethodManager imm = (InputMethodManager) getSystemService(this.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
|
||||
|
||||
this.onKeyboardComplete(mEditText.getText().toString());
|
||||
}
|
||||
|
||||
private void openKeyboard() {
|
||||
InputMethodManager imm = (InputMethodManager) getSystemService(this.INPUT_METHOD_SERVICE);
|
||||
imm.showSoftInput(mEditText, InputMethodManager.SHOW_IMPLICIT);
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
Functions invoked by CPP.
|
||||
**************************************************************************************/
|
||||
|
||||
private static void showNative(String defaultValue, int maxLength, boolean isMultiline, boolean confirmHold, String confirmType, String inputType) {
|
||||
|
||||
GlobalObject.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Intent i = new Intent(GlobalObject.getActivity(), CocosEditBoxActivity.class);
|
||||
i.putExtra("defaultValue", defaultValue);
|
||||
i.putExtra("maxLength", maxLength);
|
||||
i.putExtra("isMultiline", isMultiline);
|
||||
i.putExtra("confirmHold", confirmHold);
|
||||
i.putExtra("confirmType", confirmType);
|
||||
i.putExtra("inputType", inputType);
|
||||
GlobalObject.getActivity().startActivity(i);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void hideNative() {
|
||||
if (null != CocosEditBoxActivity.sThis) {
|
||||
GlobalObject.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CocosEditBoxActivity.sThis.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
Native functions invoked by UI.
|
||||
**************************************************************************************/
|
||||
private void onKeyboardInput(String text) {
|
||||
CocosHelper.runOnGameThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CocosEditBoxActivity.onKeyboardInputNative(text);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onKeyboardComplete(String text) {
|
||||
CocosHelper.runOnGameThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CocosEditBoxActivity.onKeyboardCompleteNative(text);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onKeyboardConfirm(String text) {
|
||||
CocosHelper.runOnGameThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CocosEditBoxActivity.onKeyboardConfirmNative(text);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static native void onKeyboardInputNative(String text);
|
||||
private static native void onKeyboardCompleteNative(String text);
|
||||
private static native void onKeyboardConfirmNative(String text);
|
||||
}
|
||||
105
cocos/platform/android/java/src/com/cocos/lib/CocosHandler.java
Normal file
105
cocos/platform/android/java/src/com/cocos/lib/CocosHandler.java
Normal file
@@ -0,0 +1,105 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2013 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
package com.cocos.lib;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.app.Activity;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class CocosHandler extends Handler {
|
||||
// ===========================================================
|
||||
// Constants
|
||||
// ===========================================================
|
||||
public final static int HANDLER_SHOW_DIALOG = 1;
|
||||
|
||||
// ===========================================================
|
||||
// Fields
|
||||
// ===========================================================
|
||||
private WeakReference<Activity> mActivity;
|
||||
|
||||
// ===========================================================
|
||||
// Constructors
|
||||
// ===========================================================
|
||||
public CocosHandler(Activity activity) {
|
||||
this.mActivity = new WeakReference<Activity>(activity);
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Getter & Setter
|
||||
// ===========================================================
|
||||
|
||||
// ===========================================================
|
||||
// Methods for/from SuperClass/Interfaces
|
||||
// ===========================================================
|
||||
|
||||
// ===========================================================
|
||||
// Methods
|
||||
// ===========================================================
|
||||
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case CocosHandler.HANDLER_SHOW_DIALOG:
|
||||
showDialog(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void showDialog(Message msg) {
|
||||
Activity theActivity = this.mActivity.get();
|
||||
DialogMessage dialogMessage = (DialogMessage)msg.obj;
|
||||
new AlertDialog.Builder(theActivity)
|
||||
.setTitle(dialogMessage.title)
|
||||
.setMessage(dialogMessage.message)
|
||||
.setPositiveButton("Ok",
|
||||
new DialogInterface.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
// REFINE: Auto-generated method stub
|
||||
|
||||
}
|
||||
}).create().show();
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Inner and Anonymous Classes
|
||||
// ===========================================================
|
||||
|
||||
public static class DialogMessage {
|
||||
public String title;
|
||||
public String message;
|
||||
|
||||
public DialogMessage(String title, String message) {
|
||||
this.title = title;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
435
cocos/platform/android/java/src/com/cocos/lib/CocosHelper.java
Normal file
435
cocos/platform/android/java/src/com/cocos/lib/CocosHelper.java
Normal file
@@ -0,0 +1,435 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
package com.cocos.lib;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.BatteryManager;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.os.LocaleList;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.Vibrator;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.Surface;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.vending.expansion.zipfile.APKExpansionSupport;
|
||||
import com.android.vending.expansion.zipfile.ZipResourceFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
|
||||
public class CocosHelper {
|
||||
// ===========================================================
|
||||
// Constants
|
||||
// ===========================================================
|
||||
private static final String TAG = CocosHelper.class.getSimpleName();
|
||||
|
||||
// ===========================================================
|
||||
// Fields
|
||||
// ===========================================================
|
||||
|
||||
private static Vibrator sVibrateService;
|
||||
private static BatteryReceiver sBatteryReceiver = new BatteryReceiver();
|
||||
|
||||
public static final int NETWORK_TYPE_NONE = 0;
|
||||
public static final int NETWORK_TYPE_LAN = 1;
|
||||
public static final int NETWORK_TYPE_WWAN = 2;
|
||||
|
||||
// The absolute path to the OBB if it exists.
|
||||
private static String sObbFilePath = "";
|
||||
|
||||
// The OBB file
|
||||
private static ZipResourceFile sOBBFile = null;
|
||||
|
||||
static class LockedTaskQ {
|
||||
private final Object readMtx = new Object();
|
||||
private Queue<Runnable> sTaskQ = new LinkedList<>();
|
||||
public void addTask(Runnable runnable) {
|
||||
synchronized (readMtx) {
|
||||
sTaskQ.add(runnable);
|
||||
}
|
||||
}
|
||||
public void runTasks(){
|
||||
Queue<Runnable> tmp;
|
||||
synchronized (readMtx) {
|
||||
tmp = sTaskQ;
|
||||
sTaskQ = new LinkedList<>();
|
||||
}
|
||||
for(Runnable runnable : tmp){
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static LockedTaskQ sTaskQOnGameThread = new LockedTaskQ();
|
||||
private static LockedTaskQ sForegroundTaskQOnGameThread = new LockedTaskQ();
|
||||
/**
|
||||
* Battery receiver to getting battery level.
|
||||
*/
|
||||
static class BatteryReceiver extends BroadcastReceiver {
|
||||
public float sBatteryLevel = 0.0f;
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
setBatteryLevelByIntent(intent);
|
||||
}
|
||||
|
||||
public void setBatteryLevelByIntent(Intent intent) {
|
||||
if (null != intent) {
|
||||
int current = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
|
||||
int total = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 1);
|
||||
float level = current * 1.0f / total;
|
||||
// clamp to 0~1
|
||||
sBatteryLevel = Math.min(Math.max(level, 0.0f), 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void registerBatteryLevelReceiver(Context context) {
|
||||
Intent intent = context.registerReceiver(sBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
||||
sBatteryReceiver.setBatteryLevelByIntent(intent);
|
||||
}
|
||||
|
||||
static void unregisterBatteryLevelReceiver(Context context) {
|
||||
context.unregisterReceiver(sBatteryReceiver);
|
||||
}
|
||||
|
||||
//Run on game thread forever, no matter foreground or background
|
||||
public static void runOnGameThread(final Runnable runnable) {
|
||||
sTaskQOnGameThread.addTask(runnable);
|
||||
}
|
||||
|
||||
static void flushTasksOnGameThread() {
|
||||
sTaskQOnGameThread.runTasks();
|
||||
}
|
||||
public static void runOnGameThreadAtForeground(final Runnable runnable) {
|
||||
sForegroundTaskQOnGameThread.addTask(runnable);
|
||||
}
|
||||
|
||||
static void flushTasksOnGameThreadAtForeground() {
|
||||
sForegroundTaskQOnGameThread.runTasks();
|
||||
}
|
||||
|
||||
public static int getNetworkType() {
|
||||
int status = NETWORK_TYPE_NONE;
|
||||
NetworkInfo networkInfo;
|
||||
try {
|
||||
ConnectivityManager connMgr = (ConnectivityManager) GlobalObject.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
networkInfo = connMgr.getActiveNetworkInfo();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return status;
|
||||
}
|
||||
if (networkInfo == null) {
|
||||
return status;
|
||||
}
|
||||
int nType = networkInfo.getType();
|
||||
if (nType == ConnectivityManager.TYPE_MOBILE) {
|
||||
status = NETWORK_TYPE_WWAN;
|
||||
} else if (nType == ConnectivityManager.TYPE_WIFI) {
|
||||
status = NETWORK_TYPE_LAN;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Constructors
|
||||
// ===========================================================
|
||||
|
||||
private static boolean sInited = false;
|
||||
|
||||
public static void init() {
|
||||
if (!sInited) {
|
||||
CocosHelper.sVibrateService = (Vibrator) GlobalObject.getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
||||
CocosHelper.initObbFilePath();
|
||||
CocosHelper.initializeOBBFile();
|
||||
|
||||
sInited = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static float getBatteryLevel() {
|
||||
return sBatteryReceiver.sBatteryLevel;
|
||||
}
|
||||
|
||||
public static String getObbFilePath() {
|
||||
return CocosHelper.sObbFilePath;
|
||||
}
|
||||
|
||||
public static String getWritablePath() {
|
||||
return GlobalObject.getContext().getFilesDir().getAbsolutePath();
|
||||
}
|
||||
|
||||
public static String getCurrentLanguage() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
return LocaleList.getDefault().get(0).getLanguage();
|
||||
} else {
|
||||
return Locale.getDefault().getLanguage();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getCurrentLanguageCode() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
return LocaleList.getDefault().get(0).toString();
|
||||
} else {
|
||||
return Locale.getDefault().toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getDeviceModel() {
|
||||
return Build.MODEL;
|
||||
}
|
||||
|
||||
public static String getSystemVersion() {
|
||||
return Build.VERSION.RELEASE;
|
||||
}
|
||||
|
||||
public static void vibrate(float duration) {
|
||||
try {
|
||||
if (sVibrateService != null && sVibrateService.hasVibrator()) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= 26) {
|
||||
Class<?> vibrationEffectClass = Class.forName("android.os.VibrationEffect");
|
||||
if (vibrationEffectClass != null) {
|
||||
final int DEFAULT_AMPLITUDE = CocosReflectionHelper.<Integer>getConstantValue(vibrationEffectClass,
|
||||
"DEFAULT_AMPLITUDE");
|
||||
//VibrationEffect.createOneShot(long milliseconds, int amplitude)
|
||||
final Method method = vibrationEffectClass.getMethod("createOneShot",
|
||||
new Class[]{Long.TYPE, Integer.TYPE});
|
||||
Class<?> type = method.getReturnType();
|
||||
|
||||
Object effect = method.invoke(vibrationEffectClass,
|
||||
new Object[]{(long) (duration * 1000), DEFAULT_AMPLITUDE});
|
||||
//sVibrateService.vibrate(VibrationEffect effect);
|
||||
CocosReflectionHelper.invokeInstanceMethod(sVibrateService, "vibrate",
|
||||
new Class[]{type}, new Object[]{(effect)});
|
||||
}
|
||||
} else {
|
||||
sVibrateService.vibrate((long) (duration * 1000));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean openURL(String url) {
|
||||
if (GlobalObject.getActivity() == null) {
|
||||
Log.e(TAG, "activity is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean ret = false;
|
||||
try {
|
||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
i.setData(Uri.parse(url));
|
||||
GlobalObject.getActivity().startActivity(i);
|
||||
ret = true;
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static void copyTextToClipboard(final String text) {
|
||||
GlobalObject.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ClipboardManager myClipboard = (ClipboardManager) GlobalObject.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
ClipData myClip = ClipData.newPlainText("text", text);
|
||||
myClipboard.setPrimaryClip(myClip);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static long[] getObbAssetFileDescriptor(final String path) {
|
||||
long[] array = new long[3];
|
||||
if (CocosHelper.sOBBFile != null) {
|
||||
AssetFileDescriptor descriptor = CocosHelper.sOBBFile.getAssetFileDescriptor(path);
|
||||
if (descriptor != null) {
|
||||
try {
|
||||
ParcelFileDescriptor parcel = descriptor.getParcelFileDescriptor();
|
||||
Method method = parcel.getClass().getMethod("getFd", new Class[]{});
|
||||
array[0] = (Integer) method.invoke(parcel);
|
||||
array[1] = descriptor.getStartOffset();
|
||||
array[2] = descriptor.getLength();
|
||||
} catch (NoSuchMethodException e) {
|
||||
Log.e(CocosHelper.TAG, "Accessing file descriptor directly from the OBB is only supported from Android 3.1 (API level 12) and above.");
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.e(CocosHelper.TAG, e.toString());
|
||||
} catch (InvocationTargetException e) {
|
||||
Log.e(CocosHelper.TAG, e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
public static int getDeviceRotation() {
|
||||
try {
|
||||
Display display = ((WindowManager) GlobalObject.getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
|
||||
return display.getRotation();
|
||||
} catch (NullPointerException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return Surface.ROTATION_0;
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Private functions.
|
||||
// ===========================================================
|
||||
|
||||
// Initialize asset path:
|
||||
// - absolute path to the OBB if it exists,
|
||||
// - else empty string.
|
||||
private static void initObbFilePath() {
|
||||
int versionCode = 1;
|
||||
final ApplicationInfo applicationInfo = GlobalObject.getContext().getApplicationInfo();
|
||||
try {
|
||||
versionCode = GlobalObject.getContext().getPackageManager().getPackageInfo(applicationInfo.packageName, 0).versionCode;
|
||||
} catch (NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
String pathToOBB = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/obb/" + applicationInfo.packageName + "/main." + versionCode + "." + applicationInfo.packageName + ".obb";
|
||||
File obbFile = new File(pathToOBB);
|
||||
if (obbFile.exists())
|
||||
CocosHelper.sObbFilePath = pathToOBB;
|
||||
}
|
||||
|
||||
private static void initializeOBBFile() {
|
||||
int versionCode = 1;
|
||||
final ApplicationInfo applicationInfo = GlobalObject.getContext().getApplicationInfo();
|
||||
try {
|
||||
versionCode = GlobalObject.getContext().getPackageManager().getPackageInfo(applicationInfo.packageName, 0).versionCode;
|
||||
} catch (NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
try {
|
||||
CocosHelper.sOBBFile = APKExpansionSupport.getAPKExpansionZipFile(GlobalObject.getContext(), versionCode, 0);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static float[] getSafeArea() {
|
||||
if (GlobalObject.getActivity() == null) {
|
||||
Log.e(TAG, "activity is null");
|
||||
return new float[]{0, 0, 0, 0};
|
||||
}
|
||||
if (android.os.Build.VERSION.SDK_INT >= 28) {
|
||||
do {
|
||||
Object windowInsectObj = GlobalObject.getActivity().getWindow().getDecorView().getRootWindowInsets();
|
||||
|
||||
if (windowInsectObj == null) break;
|
||||
|
||||
Class<?> windowInsets = WindowInsets.class;
|
||||
try {
|
||||
Method wiGetDisplayCutout = windowInsets.getMethod("getDisplayCutout");
|
||||
Object cutout = wiGetDisplayCutout.invoke(windowInsectObj);
|
||||
|
||||
if (cutout == null) break;
|
||||
|
||||
Class<?> displayCutout = cutout.getClass();
|
||||
Method dcGetLeft = displayCutout.getMethod("getSafeInsetLeft");
|
||||
Method dcGetRight = displayCutout.getMethod("getSafeInsetRight");
|
||||
Method dcGetBottom = displayCutout.getMethod("getSafeInsetBottom");
|
||||
Method dcGetTop = displayCutout.getMethod("getSafeInsetTop");
|
||||
|
||||
if (dcGetLeft != null && dcGetRight != null && dcGetBottom != null && dcGetTop != null) {
|
||||
int left = (Integer) dcGetLeft.invoke(cutout);
|
||||
int right = (Integer) dcGetRight.invoke(cutout);
|
||||
int top = (Integer) dcGetTop.invoke(cutout);
|
||||
int bottom = (Integer) dcGetBottom.invoke(cutout);
|
||||
return new float[]{top, left, bottom, right};
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} while (false);
|
||||
}
|
||||
return new float[]{0, 0, 0, 0};
|
||||
}
|
||||
public static void finishActivity() {
|
||||
if (GlobalObject.getActivity() == null) {
|
||||
Log.e(TAG, "activity is null");
|
||||
return;
|
||||
}
|
||||
GlobalObject.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GlobalObject.getActivity().finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
public static void setKeepScreenOn(boolean keepScreenOn) {
|
||||
if (GlobalObject.getActivity() == null) {
|
||||
Log.e(TAG, "activity is null");
|
||||
return;
|
||||
}
|
||||
GlobalObject.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (keepScreenOn) {
|
||||
GlobalObject.getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
} else {
|
||||
GlobalObject.getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
public static boolean supportHPE() {
|
||||
PackageManager pm = GlobalObject.getContext().getPackageManager();
|
||||
return pm.hasSystemFeature("com.google.android.play.feature.HPE_EXPERIENCE");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,414 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2014 cocos2d-x.org
|
||||
Copyright (c) 2014-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
package com.cocos.lib;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.URL;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
|
||||
public class CocosHttpURLConnection
|
||||
{
|
||||
private static String TAG = "CocosHttpURLConnection";
|
||||
private static final String POST_METHOD = "POST" ;
|
||||
private static final String PUT_METHOD = "PUT" ;
|
||||
private static final String PATCH_METHOD = "PATCH" ;
|
||||
|
||||
static HttpURLConnection createHttpURLConnection(String linkURL) {
|
||||
URL url;
|
||||
HttpURLConnection urlConnection;
|
||||
try {
|
||||
url = new URL(linkURL);
|
||||
urlConnection = (HttpURLConnection) url.openConnection();
|
||||
//Accept-Encoding
|
||||
urlConnection.setRequestProperty("Accept-Encoding", "identity");
|
||||
urlConnection.setDoInput(true);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "createHttpURLConnection:" + e.toString());
|
||||
return null;
|
||||
}
|
||||
|
||||
return urlConnection;
|
||||
}
|
||||
|
||||
static void setReadAndConnectTimeout(HttpURLConnection urlConnection, int readMiliseconds, int connectMiliseconds) {
|
||||
urlConnection.setReadTimeout(readMiliseconds);
|
||||
urlConnection.setConnectTimeout(connectMiliseconds);
|
||||
}
|
||||
|
||||
static void setRequestMethod(HttpURLConnection urlConnection, String method){
|
||||
try {
|
||||
urlConnection.setRequestMethod(method);
|
||||
if(method.equalsIgnoreCase(POST_METHOD) || method.equalsIgnoreCase(PUT_METHOD) || method.equalsIgnoreCase(PATCH_METHOD)) {
|
||||
urlConnection.setDoOutput(true);
|
||||
}
|
||||
} catch (ProtocolException e) {
|
||||
Log.e(TAG, "setRequestMethod:" + e.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void setVerifySSL(HttpURLConnection urlConnection, String sslFilename) {
|
||||
if(!(urlConnection instanceof HttpsURLConnection))
|
||||
return;
|
||||
|
||||
|
||||
HttpsURLConnection httpsURLConnection = (HttpsURLConnection)urlConnection;
|
||||
|
||||
try {
|
||||
InputStream caInput = null;
|
||||
if (sslFilename.startsWith("/")) {
|
||||
caInput = new BufferedInputStream(new FileInputStream(sslFilename));
|
||||
}else {
|
||||
String assetString = "assets/";
|
||||
String assetsfilenameString = sslFilename.substring(assetString.length());
|
||||
caInput = new BufferedInputStream(GlobalObject.getContext().getAssets().open(assetsfilenameString));
|
||||
}
|
||||
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
Certificate ca;
|
||||
ca = cf.generateCertificate(caInput);
|
||||
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
|
||||
caInput.close();
|
||||
|
||||
// Create a KeyStore containing our trusted CAs
|
||||
String keyStoreType = KeyStore.getDefaultType();
|
||||
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
|
||||
keyStore.load(null, null);
|
||||
keyStore.setCertificateEntry("ca", ca);
|
||||
|
||||
// Create a TrustManager that trusts the CAs in our KeyStore
|
||||
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
|
||||
tmf.init(keyStore);
|
||||
|
||||
// Create an SSLContext that uses our TrustManager
|
||||
SSLContext context = SSLContext.getInstance("TLS");
|
||||
context.init(null, tmf.getTrustManagers(), null);
|
||||
|
||||
httpsURLConnection.setSSLSocketFactory(context.getSocketFactory());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "setVerifySSL:" + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
//Add header
|
||||
static void addRequestHeader(HttpURLConnection urlConnection, String key, String value) {
|
||||
urlConnection.setRequestProperty(key, value);
|
||||
}
|
||||
|
||||
static int connect(HttpURLConnection http) {
|
||||
int suc = 0;
|
||||
|
||||
try {
|
||||
http.connect();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "connect" + e.toString());
|
||||
suc = 1;
|
||||
}
|
||||
|
||||
return suc;
|
||||
}
|
||||
|
||||
static void disconnect(HttpURLConnection http) {
|
||||
http.disconnect();
|
||||
}
|
||||
|
||||
static void sendRequest(HttpURLConnection http, byte[] byteArray) {
|
||||
try {
|
||||
OutputStream out = http.getOutputStream();
|
||||
if(null != byteArray) {
|
||||
out.write(byteArray);
|
||||
out.flush();
|
||||
}
|
||||
out.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "sendRequest:" + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
static String getResponseHeaders(HttpURLConnection http) {
|
||||
Map<String, List<String>> headers = http.getHeaderFields();
|
||||
if (null == headers) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String header = "";
|
||||
|
||||
for (Entry<String, List<String>> entry: headers.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
if (null == key) {
|
||||
header += listToString(entry.getValue(), ",") + "\n";
|
||||
} else {
|
||||
header += key + ":" + listToString(entry.getValue(), ",") + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
static String getResponseHeaderByIdx(HttpURLConnection http, int idx) {
|
||||
Map<String, List<String>> headers = http.getHeaderFields();
|
||||
if (null == headers) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String header = null;
|
||||
|
||||
int counter = 0;
|
||||
for (Entry<String, List<String>> entry: headers.entrySet()) {
|
||||
if (counter == idx) {
|
||||
String key = entry.getKey();
|
||||
if (null == key) {
|
||||
header = listToString(entry.getValue(), ",") + "\n";
|
||||
} else {
|
||||
header = key + ":" + listToString(entry.getValue(), ",") + "\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
static String getResponseHeaderByKey(HttpURLConnection http, String key) {
|
||||
if (null == key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, List<String>> headers = http.getHeaderFields();
|
||||
if (null == headers) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String header = null;
|
||||
|
||||
for (Entry<String, List<String>> entry: headers.entrySet()) {
|
||||
if (key.equalsIgnoreCase(entry.getKey())) {
|
||||
if ("set-cookie".equalsIgnoreCase(key)) {
|
||||
header = combinCookies(entry.getValue(), http.getURL().getHost());
|
||||
} else {
|
||||
header = listToString(entry.getValue(), ",");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
static int getResponseHeaderByKeyInt(HttpURLConnection http, String key) {
|
||||
String value = http.getHeaderField(key);
|
||||
|
||||
if (null == value) {
|
||||
return 0;
|
||||
} else {
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
}
|
||||
|
||||
static byte[] getResponseContent(HttpURLConnection http) {
|
||||
InputStream in;
|
||||
try {
|
||||
in = http.getInputStream();
|
||||
String contentEncoding = http.getContentEncoding();
|
||||
if (contentEncoding != null) {
|
||||
if(contentEncoding.equalsIgnoreCase("gzip")){
|
||||
in = new GZIPInputStream(http.getInputStream()); //reads 2 bytes to determine GZIP stream!
|
||||
}
|
||||
else if(contentEncoding.equalsIgnoreCase("deflate")){
|
||||
in = new InflaterInputStream(http.getInputStream());
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
in = http.getErrorStream();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "1 getResponseContent: " + e.toString());
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] buffer = new byte[1024];
|
||||
int size = 0;
|
||||
ByteArrayOutputStream bytestream = new ByteArrayOutputStream();
|
||||
while((size = in.read(buffer, 0 , 1024)) != -1)
|
||||
{
|
||||
bytestream.write(buffer, 0, size);
|
||||
}
|
||||
byte retbuffer[] = bytestream.toByteArray();
|
||||
bytestream.close();
|
||||
return retbuffer;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "2 getResponseContent:" + e.toString());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static int getResponseCode(HttpURLConnection http) {
|
||||
int code = 0;
|
||||
try {
|
||||
code = http.getResponseCode();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "getResponseCode:" + e.toString());
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
static String getResponseMessage(HttpURLConnection http) {
|
||||
String msg;
|
||||
try {
|
||||
msg = http.getResponseMessage();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
msg = e.toString();
|
||||
Log.e(TAG, "getResponseMessage: " + msg);
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
public static String listToString(List<String> list, String strInterVal) {
|
||||
if (list == null) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder result = new StringBuilder();
|
||||
boolean flag = false;
|
||||
for (String str : list) {
|
||||
if (flag) {
|
||||
result.append(strInterVal);
|
||||
}
|
||||
if (null == str) {
|
||||
str = "";
|
||||
}
|
||||
result.append(str);
|
||||
flag = true;
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public static String combinCookies(List<String> list, String hostDomain) {
|
||||
StringBuilder sbCookies = new StringBuilder();
|
||||
String domain = hostDomain;
|
||||
String tailmatch = "FALSE";
|
||||
String path = "/";
|
||||
String secure = "FALSE";
|
||||
String key = null;
|
||||
String value = null;
|
||||
String expires = null;
|
||||
for (String str : list) {
|
||||
String[] parts = str.split(";");
|
||||
for (String part : parts) {
|
||||
int firstIndex = part.indexOf("=");
|
||||
if (-1 == firstIndex)
|
||||
continue;
|
||||
|
||||
String[] item = {part.substring(0, firstIndex), part.substring(firstIndex + 1)};
|
||||
if ("expires".equalsIgnoreCase(item[0].trim())) {
|
||||
expires = str2Seconds(item[1].trim());
|
||||
} else if("path".equalsIgnoreCase(item[0].trim())) {
|
||||
path = item[1];
|
||||
} else if("secure".equalsIgnoreCase(item[0].trim())) {
|
||||
secure = item[1];
|
||||
} else if("domain".equalsIgnoreCase(item[0].trim())) {
|
||||
domain = item[1];
|
||||
} else if("version".equalsIgnoreCase(item[0].trim()) || "max-age".equalsIgnoreCase(item[0].trim())) {
|
||||
//do nothing
|
||||
} else {
|
||||
key = item[0];
|
||||
value = item[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (null == domain) {
|
||||
domain = "none";
|
||||
}
|
||||
|
||||
sbCookies.append(domain);
|
||||
sbCookies.append('\t');
|
||||
sbCookies.append(tailmatch); //access
|
||||
sbCookies.append('\t');
|
||||
sbCookies.append(path); //path
|
||||
sbCookies.append('\t');
|
||||
sbCookies.append(secure); //secure
|
||||
sbCookies.append('\t');
|
||||
sbCookies.append(expires); //expires
|
||||
sbCookies.append("\t");
|
||||
sbCookies.append(key); //key
|
||||
sbCookies.append("\t");
|
||||
sbCookies.append(value); //value
|
||||
sbCookies.append('\n');
|
||||
}
|
||||
|
||||
return sbCookies.toString();
|
||||
}
|
||||
|
||||
private static String str2Seconds(String strTime) {
|
||||
Calendar c = Calendar.getInstance();
|
||||
long milliseconds = 0;
|
||||
|
||||
try {
|
||||
c.setTime(new SimpleDateFormat("EEE, dd-MMM-yy hh:mm:ss zzz", Locale.US).parse(strTime));
|
||||
milliseconds = c.getTimeInMillis() / 1000;
|
||||
} catch (ParseException e) {
|
||||
Log.e(TAG, "str2Seconds: " + e.toString());
|
||||
}
|
||||
|
||||
return Long.toString(milliseconds);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
* Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.cocos.lib;
|
||||
|
||||
public class CocosJavascriptJavaBridge {
|
||||
public static native int evalString(String value);
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2013 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
package com.cocos.lib;
|
||||
|
||||
import android.view.KeyEvent;
|
||||
|
||||
public class CocosKeyCodeHandler {
|
||||
private CocosActivity mAct;
|
||||
|
||||
public native void handleKeyDown(final int keyCode);
|
||||
|
||||
public native void handleKeyUp(final int keyCode);
|
||||
|
||||
public CocosKeyCodeHandler(CocosActivity act) {
|
||||
mAct = act;
|
||||
}
|
||||
|
||||
public boolean onKeyDown(final int keyCode, final KeyEvent event) {
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
// CocosVideoHelper.mVideoHandler.sendEmptyMessage(CocosVideoHelper.KeyEventBack);
|
||||
case KeyEvent.KEYCODE_MENU:
|
||||
case KeyEvent.KEYCODE_DPAD_LEFT:
|
||||
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
||||
case KeyEvent.KEYCODE_DPAD_UP:
|
||||
case KeyEvent.KEYCODE_DPAD_DOWN:
|
||||
case KeyEvent.KEYCODE_ENTER:
|
||||
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
|
||||
case KeyEvent.KEYCODE_DPAD_CENTER:
|
||||
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
handleKeyDown(keyCode);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onKeyUp(final int keyCode, KeyEvent event) {
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
case KeyEvent.KEYCODE_MENU:
|
||||
case KeyEvent.KEYCODE_DPAD_LEFT:
|
||||
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
||||
case KeyEvent.KEYCODE_DPAD_UP:
|
||||
case KeyEvent.KEYCODE_DPAD_DOWN:
|
||||
case KeyEvent.KEYCODE_ENTER:
|
||||
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
|
||||
case KeyEvent.KEYCODE_DPAD_CENTER:
|
||||
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
handleKeyUp(keyCode);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
package com.cocos.lib;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.util.Log;
|
||||
|
||||
public class CocosLocalStorage {
|
||||
|
||||
private static final String TAG = "CocosLocalStorage";
|
||||
|
||||
private static String DATABASE_NAME = "jsb.sqlite";
|
||||
private static String TABLE_NAME = "data";
|
||||
private static final int DATABASE_VERSION = 1;
|
||||
|
||||
private static DBOpenHelper mDatabaseOpenHelper = null;
|
||||
private static SQLiteDatabase mDatabase = null;
|
||||
|
||||
public static boolean init(String dbName, String tableName) {
|
||||
if (GlobalObject.getContext() != null) {
|
||||
DATABASE_NAME = dbName;
|
||||
TABLE_NAME = tableName;
|
||||
mDatabaseOpenHelper = new DBOpenHelper(GlobalObject.getContext());
|
||||
mDatabase = mDatabaseOpenHelper.getWritableDatabase();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void destroy() {
|
||||
if (mDatabase != null) {
|
||||
mDatabase.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void setItem(String key, String value) {
|
||||
try {
|
||||
String sql = "replace into "+TABLE_NAME+"(key,value)values(?,?)";
|
||||
mDatabase.execSQL(sql, new Object[] { key, value });
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getItem(String key) {
|
||||
String ret = null;
|
||||
try {
|
||||
String sql = "select value from "+TABLE_NAME+" where key=?";
|
||||
Cursor c = mDatabase.rawQuery(sql, new String[]{key});
|
||||
while (c.moveToNext()) {
|
||||
// only return the first value
|
||||
if (ret != null)
|
||||
{
|
||||
Log.e(TAG, "The key contains more than one value.");
|
||||
break;
|
||||
}
|
||||
ret = c.getString(c.getColumnIndex("value"));
|
||||
}
|
||||
c.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static void removeItem(String key) {
|
||||
try {
|
||||
String sql = "delete from "+TABLE_NAME+" where key=?";
|
||||
mDatabase.execSQL(sql, new Object[] {key});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
try {
|
||||
String sql = "delete from "+TABLE_NAME;
|
||||
mDatabase.execSQL(sql);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static String getKey(int nIndex) {
|
||||
String ret = null;
|
||||
try {
|
||||
int nCount = 0;
|
||||
String sql = "select key from "+TABLE_NAME + " order by rowid asc";
|
||||
Cursor c = mDatabase.rawQuery(sql, null);
|
||||
if(nIndex < 0 || nIndex >= c.getCount()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
while (c.moveToNext()) {
|
||||
if(nCount == nIndex) {
|
||||
ret = c.getString(c.getColumnIndex("key"));
|
||||
break;
|
||||
}
|
||||
nCount++;
|
||||
}
|
||||
c.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static int getLength() {
|
||||
int res = 0;
|
||||
try {
|
||||
String sql = "select count(*) as nums from "+TABLE_NAME;
|
||||
Cursor c = mDatabase.rawQuery(sql, null);
|
||||
if (c.moveToNext()){
|
||||
res = c.getInt(c.getColumnIndex("nums"));
|
||||
}
|
||||
c.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* This creates/opens the database.
|
||||
*/
|
||||
private static class DBOpenHelper extends SQLiteOpenHelper {
|
||||
|
||||
DBOpenHelper(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL("CREATE TABLE IF NOT EXISTS "+TABLE_NAME+"(key TEXT PRIMARY KEY,value TEXT);");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
|
||||
+ newVersion + ", which will destroy all old data");
|
||||
//db.execSQL("DROP TABLE IF EXISTS " + VIRTUAL_TABLE);
|
||||
//onCreate(db);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2020 Xiamen Yaji Software Co., Ltd.
|
||||
*
|
||||
* http://www.cocos.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
package com.cocos.lib;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.OrientationEventListener;
|
||||
|
||||
public class CocosOrientationHelper extends OrientationEventListener {
|
||||
|
||||
private int mCurrentOrientation;
|
||||
|
||||
public CocosOrientationHelper(Context context) {
|
||||
super(context);
|
||||
mCurrentOrientation = CocosHelper.getDeviceRotation();
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
this.disable();
|
||||
}
|
||||
|
||||
public void onResume() {
|
||||
this.enable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOrientationChanged(int orientation) {
|
||||
int curOrientation = CocosHelper.getDeviceRotation();
|
||||
if (curOrientation != mCurrentOrientation) {
|
||||
mCurrentOrientation = CocosHelper.getDeviceRotation();
|
||||
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
nativeOnOrientationChanged(mCurrentOrientation);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static native void nativeOnOrientationChanged(int rotation);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016 cocos2d-x.org
|
||||
Copyright (c) 2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
package com.cocos.lib;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class CocosReflectionHelper {
|
||||
public static <T> T getConstantValue(final Class aClass, final String constantName) {
|
||||
try {
|
||||
return (T)aClass.getDeclaredField(constantName).get(null);
|
||||
} catch (NoSuchFieldException e) {
|
||||
Log.e("error", "can not find " + constantName + " in " + aClass.getName());
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
Log.e("error", constantName + " is not accessable");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
Log.e("error", "arguments error when get " + constantName);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Log.e("error", "can not get constant" + constantName);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T> T invokeInstanceMethod(final Object instance, final String methodName,
|
||||
final Class[] parameterTypes, final Object[] parameters) {
|
||||
|
||||
final Class aClass = instance.getClass();
|
||||
try {
|
||||
final Method method = aClass.getMethod(methodName, parameterTypes);
|
||||
return (T)method.invoke(instance, parameters);
|
||||
} catch (NoSuchMethodException e) {
|
||||
Log.e("error", "can not find " + methodName + " in " + aClass.getName());
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
Log.e("error", methodName + " is not accessible");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
Log.e("error", "arguments are error when invoking " + methodName);
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
Log.e("error", "an exception was thrown by the invoked method when invoking " + methodName);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2013 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
package com.cocos.lib;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
|
||||
public class CocosSensorHandler implements SensorEventListener {
|
||||
// ===========================================================
|
||||
// Constants
|
||||
// ===========================================================
|
||||
|
||||
private static final String TAG = "CocosSensorHandler";
|
||||
private static CocosSensorHandler mSensorHandler;
|
||||
private static boolean mEnableSensor = false;
|
||||
|
||||
private final Context mContext;
|
||||
private SensorManager mSensorManager;
|
||||
private Sensor mAcceleration;
|
||||
private Sensor mAccelerationIncludingGravity;
|
||||
private Sensor mGyroscope;
|
||||
private int mSamplingPeriodUs = SensorManager.SENSOR_DELAY_GAME;
|
||||
|
||||
private static float[] sDeviceMotionValues = new float[9];
|
||||
|
||||
// ===========================================================
|
||||
// Constructors
|
||||
// ===========================================================
|
||||
|
||||
public CocosSensorHandler(final Context context) {
|
||||
mContext = context;
|
||||
mSensorHandler = this;
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Getter & Setter
|
||||
// ===========================================================
|
||||
public void enable() {
|
||||
if (mEnableSensor) {
|
||||
if (null == mSensorManager) {
|
||||
mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
|
||||
mAcceleration = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
|
||||
mAccelerationIncludingGravity = mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
|
||||
mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
|
||||
}
|
||||
|
||||
mSensorManager.registerListener(this, mAcceleration, mSamplingPeriodUs);
|
||||
mSensorManager.registerListener(this, mAccelerationIncludingGravity, mSamplingPeriodUs);
|
||||
mSensorManager.registerListener(this, mGyroscope, mSamplingPeriodUs);
|
||||
}
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
if (mEnableSensor && null != mSensorManager) {
|
||||
this.mSensorManager.unregisterListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void setInterval(float interval) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= 11) {
|
||||
mSamplingPeriodUs = (int) (interval * 1000000);
|
||||
}
|
||||
disable();
|
||||
enable();
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Methods for/from SuperClass/Interfaces
|
||||
// ===========================================================
|
||||
@Override
|
||||
public void onSensorChanged(final SensorEvent sensorEvent) {
|
||||
int type = sensorEvent.sensor.getType();
|
||||
if (type == Sensor.TYPE_ACCELEROMETER) {
|
||||
sDeviceMotionValues[0] = sensorEvent.values[0];
|
||||
sDeviceMotionValues[1] = sensorEvent.values[1];
|
||||
// Issue https://github.com/cocos-creator/2d-tasks/issues/2532
|
||||
// use negative event.acceleration.z to match iOS value
|
||||
sDeviceMotionValues[2] = -sensorEvent.values[2];
|
||||
} else if (type == Sensor.TYPE_LINEAR_ACCELERATION) {
|
||||
sDeviceMotionValues[3] = sensorEvent.values[0];
|
||||
sDeviceMotionValues[4] = sensorEvent.values[1];
|
||||
sDeviceMotionValues[5] = sensorEvent.values[2];
|
||||
} else if (type == Sensor.TYPE_GYROSCOPE) {
|
||||
// The unit is rad/s, need to be converted to deg/s
|
||||
sDeviceMotionValues[6] = (float) Math.toDegrees(sensorEvent.values[0]);
|
||||
sDeviceMotionValues[7] = (float) Math.toDegrees(sensorEvent.values[1]);
|
||||
sDeviceMotionValues[8] = (float) Math.toDegrees(sensorEvent.values[2]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(final Sensor sensor, final int accuracy) {
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
disable();
|
||||
}
|
||||
|
||||
public void onResume() {
|
||||
enable();
|
||||
}
|
||||
|
||||
public static void setAccelerometerInterval(float interval) {
|
||||
mSensorHandler.setInterval(interval);
|
||||
}
|
||||
|
||||
public static void setAccelerometerEnabled(boolean enabled) {
|
||||
mEnableSensor = enabled;
|
||||
if (enabled) {
|
||||
mSensorHandler.enable();
|
||||
} else {
|
||||
mSensorHandler.disable();
|
||||
}
|
||||
}
|
||||
|
||||
public static float[] getDeviceMotionValue() {
|
||||
return sDeviceMotionValues;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2020 Xiamen Yaji Software Co., Ltd.
|
||||
*
|
||||
* http://www.cocos.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
package com.cocos.lib;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
|
||||
public class CocosSurfaceView extends SurfaceView implements android.view.SurfaceHolder.Callback2 {
|
||||
private CocosTouchHandler mTouchHandler;
|
||||
private long mNativeHandle;
|
||||
private int mWindowId;
|
||||
|
||||
public CocosSurfaceView(Context context, int windowId) {
|
||||
super(context);
|
||||
mWindowId = windowId;
|
||||
mNativeHandle = constructNative(windowId);
|
||||
mTouchHandler = new CocosTouchHandler(mWindowId);
|
||||
getHolder().addCallback(this);
|
||||
}
|
||||
|
||||
private native long constructNative(int windowId);
|
||||
private native void destructNative(long handle);
|
||||
private native void onSizeChangedNative(int windowId, int width, final int height);
|
||||
private native void onSurfaceRedrawNeededNative(long handle);
|
||||
private native void onSurfaceCreatedNative(long handle, Surface surface);
|
||||
private native void onSurfaceChangedNative(long handle, Surface surface, int format, int width, int height);
|
||||
private native void onSurfaceDestroyedNative(long handle);
|
||||
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
onSizeChangedNative(mWindowId, w, h);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
return mTouchHandler.onTouchEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceRedrawNeeded(SurfaceHolder holder) {
|
||||
onSurfaceRedrawNeededNative(mNativeHandle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
onSurfaceCreatedNative(mNativeHandle, holder.getSurface());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||
onSurfaceChangedNative(mNativeHandle, holder.getSurface(), format, width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
onSurfaceDestroyedNative(mNativeHandle);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
super.finalize();
|
||||
destructNative(mNativeHandle);
|
||||
mNativeHandle = 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2013 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
package com.cocos.lib;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceView;
|
||||
|
||||
public class CocosTouchHandler {
|
||||
public final static String TAG = "CocosTouchHandler";
|
||||
private boolean mStopHandleTouchAndKeyEvents = false;
|
||||
private int mWindowId;
|
||||
|
||||
public CocosTouchHandler(int windowId) {
|
||||
mWindowId = windowId;
|
||||
}
|
||||
|
||||
boolean onTouchEvent(MotionEvent pMotionEvent) {
|
||||
// these data are used in ACTION_MOVE and ACTION_CANCEL
|
||||
final int pointerNumber = pMotionEvent.getPointerCount();
|
||||
final int[] ids = new int[pointerNumber];
|
||||
final float[] xs = new float[pointerNumber];
|
||||
final float[] ys = new float[pointerNumber];
|
||||
|
||||
for (int i = 0; i < pointerNumber; i++) {
|
||||
ids[i] = pMotionEvent.getPointerId(i);
|
||||
xs[i] = pMotionEvent.getX(i);
|
||||
ys[i] = pMotionEvent.getY(i);
|
||||
}
|
||||
|
||||
switch (pMotionEvent.getAction() & MotionEvent.ACTION_MASK) {
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
if (mStopHandleTouchAndKeyEvents) {
|
||||
// Cocos2dxEditBox.complete();
|
||||
return true;
|
||||
}
|
||||
|
||||
final int indexPointerDown = pMotionEvent.getAction() >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
|
||||
final int idPointerDown = pMotionEvent.getPointerId(indexPointerDown);
|
||||
final float xPointerDown = pMotionEvent.getX(indexPointerDown);
|
||||
final float yPointerDown = pMotionEvent.getY(indexPointerDown);
|
||||
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
handleActionDown(mWindowId, idPointerDown, xPointerDown, yPointerDown);
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
if (mStopHandleTouchAndKeyEvents) {
|
||||
// Cocos2dxEditBox.complete();
|
||||
return true;
|
||||
}
|
||||
|
||||
// there are only one finger on the screen
|
||||
final int idDown = pMotionEvent.getPointerId(0);
|
||||
final float xDown = xs[0];
|
||||
final float yDown = ys[0];
|
||||
|
||||
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
handleActionDown(mWindowId, idDown, xDown, yDown);
|
||||
}
|
||||
});
|
||||
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
handleActionMove(mWindowId, ids, xs, ys);
|
||||
}
|
||||
});
|
||||
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
final int indexPointUp = pMotionEvent.getAction() >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
|
||||
final int idPointerUp = pMotionEvent.getPointerId(indexPointUp);
|
||||
final float xPointerUp = pMotionEvent.getX(indexPointUp);
|
||||
final float yPointerUp = pMotionEvent.getY(indexPointUp);
|
||||
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
handleActionUp(mWindowId, idPointerUp, xPointerUp, yPointerUp);
|
||||
}
|
||||
});
|
||||
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
// there are only one finger on the screen
|
||||
final int idUp = pMotionEvent.getPointerId(0);
|
||||
final float xUp = xs[0];
|
||||
final float yUp = ys[0];
|
||||
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
handleActionUp(mWindowId, idUp, xUp, yUp);
|
||||
}
|
||||
});
|
||||
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
handleActionCancel(mWindowId, ids, xs, ys);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
// CocosTouchHandler.dumpMotionEvent(pMotionEvent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setStopHandleTouchAndKeyEvents(boolean value) {
|
||||
mStopHandleTouchAndKeyEvents = value;
|
||||
}
|
||||
|
||||
private static void dumpMotionEvent(final MotionEvent event) {
|
||||
final String names[] = {"DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE", "POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?"};
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
final int action = event.getAction();
|
||||
final int actionCode = action & MotionEvent.ACTION_MASK;
|
||||
sb.append("event ACTION_").append(names[actionCode]);
|
||||
if (actionCode == MotionEvent.ACTION_POINTER_DOWN || actionCode == MotionEvent.ACTION_POINTER_UP) {
|
||||
sb.append("(pid ").append(action >> MotionEvent.ACTION_POINTER_INDEX_SHIFT);
|
||||
sb.append(")");
|
||||
}
|
||||
sb.append("[");
|
||||
for (int i = 0; i < event.getPointerCount(); i++) {
|
||||
sb.append("#").append(i);
|
||||
sb.append("(pid ").append(event.getPointerId(i));
|
||||
sb.append(")=").append((int) event.getX(i));
|
||||
sb.append(",").append((int) event.getY(i));
|
||||
if (i + 1 < event.getPointerCount()) {
|
||||
sb.append(";");
|
||||
}
|
||||
}
|
||||
sb.append("]");
|
||||
Log.d(TAG, sb.toString());
|
||||
}
|
||||
|
||||
private native void handleActionDown(int windowId, final int id, final float x, final float y);
|
||||
|
||||
private native void handleActionMove(int windowId, final int[] ids, final float[] xPointerList, final float[] yPointerList);
|
||||
|
||||
private native void handleActionUp(int windowId, final int id, final float x, final float y);
|
||||
|
||||
private native void handleActionCancel(int windowId, final int[] ids, final float[] xPointerList, final float[] yPointerList);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,509 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2014-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2020 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
package com.cocos.lib;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.app.Activity;
|
||||
|
||||
import com.cocos.lib.CocosVideoView.OnVideoEventListener;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
public class CocosVideoHelper {
|
||||
|
||||
private FrameLayout mLayout = null;
|
||||
private Activity mActivity = null;
|
||||
private static SparseArray<CocosVideoView> sVideoViews = null;
|
||||
static VideoHandler mVideoHandler = null;
|
||||
private static Handler sHandler = null;
|
||||
|
||||
CocosVideoHelper(Activity activity, FrameLayout layout)
|
||||
{
|
||||
mActivity = activity;
|
||||
mLayout = layout;
|
||||
|
||||
mVideoHandler = new VideoHandler(this);
|
||||
sVideoViews = new SparseArray<CocosVideoView>();
|
||||
sHandler = new Handler(Looper.myLooper());
|
||||
}
|
||||
|
||||
private static int videoTag = 0;
|
||||
private final static int VideoTaskCreate = 0;
|
||||
private final static int VideoTaskRemove = 1;
|
||||
private final static int VideoTaskSetSource = 2;
|
||||
private final static int VideoTaskSetRect = 3;
|
||||
private final static int VideoTaskStart = 4;
|
||||
private final static int VideoTaskPause = 5;
|
||||
private final static int VideoTaskResume = 6;
|
||||
private final static int VideoTaskStop = 7;
|
||||
private final static int VideoTaskSeek = 8;
|
||||
private final static int VideoTaskSetVisible = 9;
|
||||
private final static int VideoTaskRestart = 10;
|
||||
private final static int VideoTaskKeepRatio = 11;
|
||||
private final static int VideoTaskFullScreen = 12;
|
||||
private final static int VideoTaskSetVolume = 13;
|
||||
private final static int VideoTaskSetPlaybackRate = 14;
|
||||
private final static int VideoTaskSetMute = 15;
|
||||
private final static int VideoTaskSetLoop = 16;
|
||||
|
||||
|
||||
final static int KeyEventBack = 1000;
|
||||
|
||||
static class VideoHandler extends Handler{
|
||||
WeakReference<CocosVideoHelper> mReference;
|
||||
|
||||
VideoHandler(CocosVideoHelper helper){
|
||||
mReference = new WeakReference<CocosVideoHelper>(helper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
CocosVideoHelper helper = mReference.get();
|
||||
switch (msg.what) {
|
||||
case VideoTaskCreate: {
|
||||
helper._createVideoView(msg.arg1);
|
||||
break;
|
||||
}
|
||||
case VideoTaskRemove: {
|
||||
helper._removeVideoView(msg.arg1);
|
||||
break;
|
||||
}
|
||||
case VideoTaskSetSource: {
|
||||
helper._setVideoURL(msg.arg1, msg.arg2, (String)msg.obj);
|
||||
break;
|
||||
}
|
||||
case VideoTaskStart: {
|
||||
helper._startVideo(msg.arg1);
|
||||
break;
|
||||
}
|
||||
case VideoTaskSetRect: {
|
||||
Rect rect = (Rect)msg.obj;
|
||||
helper._setVideoRect(msg.arg1, rect.left, rect.top, rect.right, rect.bottom);
|
||||
break;
|
||||
}
|
||||
case VideoTaskFullScreen:{
|
||||
if (msg.arg2 == 1) {
|
||||
helper._setFullScreenEnabled(msg.arg1, true);
|
||||
} else {
|
||||
helper._setFullScreenEnabled(msg.arg1, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case VideoTaskPause: {
|
||||
helper._pauseVideo(msg.arg1);
|
||||
break;
|
||||
}
|
||||
|
||||
case VideoTaskStop: {
|
||||
helper._stopVideo(msg.arg1);
|
||||
break;
|
||||
}
|
||||
case VideoTaskSeek: {
|
||||
helper._seekVideoTo(msg.arg1, msg.arg2);
|
||||
break;
|
||||
}
|
||||
case VideoTaskSetVisible: {
|
||||
if (msg.arg2 == 1) {
|
||||
helper._setVideoVisible(msg.arg1, true);
|
||||
} else {
|
||||
helper._setVideoVisible(msg.arg1, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case VideoTaskKeepRatio: {
|
||||
if (msg.arg2 == 1) {
|
||||
helper._setVideoKeepRatio(msg.arg1, true);
|
||||
} else {
|
||||
helper._setVideoKeepRatio(msg.arg1, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case KeyEventBack: {
|
||||
helper.onBackKeyEvent();
|
||||
break;
|
||||
}
|
||||
case VideoTaskSetVolume: {
|
||||
float volume = (float) msg.arg2 / 10;
|
||||
helper._setVolume(msg.arg1, volume);
|
||||
break;
|
||||
}
|
||||
case VideoTaskSetPlaybackRate: {
|
||||
float rate = (float) msg.arg2 / 10;
|
||||
helper._setPlaybackRate(msg.arg1, rate);
|
||||
break;
|
||||
}
|
||||
case VideoTaskSetMute: {
|
||||
if (msg.arg2 == 1) {
|
||||
helper._setMute(msg.arg1, true);
|
||||
} else {
|
||||
helper._setMute(msg.arg1, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case VideoTaskSetLoop: {
|
||||
if (msg.arg2 == 1) {
|
||||
helper._setLoop(msg.arg1, true);
|
||||
} else {
|
||||
helper._setLoop(msg.arg1, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
super.handleMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static native void nativeExecuteVideoCallback(int index,int event);
|
||||
|
||||
OnVideoEventListener videoEventListener = new OnVideoEventListener() {
|
||||
|
||||
@Override
|
||||
public void onVideoEvent(int tag,int event) {
|
||||
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
nativeExecuteVideoCallback(tag, event);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
public static int createVideoWidget() {
|
||||
Message msg = new Message();
|
||||
msg.what = VideoTaskCreate;
|
||||
msg.arg1 = videoTag;
|
||||
mVideoHandler.sendMessage(msg);
|
||||
|
||||
return videoTag++;
|
||||
}
|
||||
|
||||
private void _createVideoView(int index) {
|
||||
CocosVideoView videoView = new CocosVideoView(mActivity,index);
|
||||
sVideoViews.put(index, videoView);
|
||||
FrameLayout.LayoutParams lParams = new FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.WRAP_CONTENT,
|
||||
FrameLayout.LayoutParams.WRAP_CONTENT);
|
||||
mLayout.addView(videoView, lParams);
|
||||
|
||||
videoView.setZOrderOnTop(true);
|
||||
videoView.setVideoViewEventListener(videoEventListener);
|
||||
}
|
||||
|
||||
public static void removeVideoWidget(int index){
|
||||
Message msg = new Message();
|
||||
msg.what = VideoTaskRemove;
|
||||
msg.arg1 = index;
|
||||
mVideoHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
private void _removeVideoView(int index) {
|
||||
CocosVideoView view = sVideoViews.get(index);
|
||||
if (view != null) {
|
||||
view.stopPlayback();
|
||||
sVideoViews.remove(index);
|
||||
mLayout.removeView(view);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setVideoUrl(int index, int videoSource, String videoUrl) {
|
||||
Message msg = new Message();
|
||||
msg.what = VideoTaskSetSource;
|
||||
msg.arg1 = index;
|
||||
msg.arg2 = videoSource;
|
||||
msg.obj = videoUrl;
|
||||
mVideoHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
private void _setVideoURL(int index, int videoSource, String videoUrl) {
|
||||
CocosVideoView videoView = sVideoViews.get(index);
|
||||
if (videoView != null) {
|
||||
switch (videoSource) {
|
||||
case 0:
|
||||
videoView.setVideoFileName(videoUrl);
|
||||
break;
|
||||
case 1:
|
||||
videoView.setVideoURL(videoUrl);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void setVideoRect(int index, int left, int top, int maxWidth, int maxHeight) {
|
||||
Message msg = new Message();
|
||||
msg.what = VideoTaskSetRect;
|
||||
msg.arg1 = index;
|
||||
msg.obj = new Rect(left, top, maxWidth, maxHeight);
|
||||
mVideoHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
private void _setVideoRect(int index, int left, int top, int maxWidth, int maxHeight) {
|
||||
CocosVideoView videoView = sVideoViews.get(index);
|
||||
if (videoView != null) {
|
||||
videoView.setVideoRect(left, top, maxWidth, maxHeight);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setFullScreenEnabled(int index, boolean enabled) {
|
||||
Message msg = new Message();
|
||||
msg.what = VideoTaskFullScreen;
|
||||
msg.arg1 = index;
|
||||
if (enabled) {
|
||||
msg.arg2 = 1;
|
||||
} else {
|
||||
msg.arg2 = 0;
|
||||
}
|
||||
mVideoHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
private void _setFullScreenEnabled(int index, boolean enabled) {
|
||||
CocosVideoView videoView = sVideoViews.get(index);
|
||||
if (videoView != null) {
|
||||
videoView.setFullScreenEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
private void onBackKeyEvent() {
|
||||
int viewCount = sVideoViews.size();
|
||||
for (int i = 0; i < viewCount; i++) {
|
||||
int key = sVideoViews.keyAt(i);
|
||||
CocosVideoView videoView = sVideoViews.get(key);
|
||||
if (videoView != null) {
|
||||
videoView.setFullScreenEnabled(false);
|
||||
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
nativeExecuteVideoCallback(key, KeyEventBack);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void startVideo(int index) {
|
||||
Message msg = new Message();
|
||||
msg.what = VideoTaskStart;
|
||||
msg.arg1 = index;
|
||||
mVideoHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
private void _startVideo(int index) {
|
||||
CocosVideoView videoView = sVideoViews.get(index);
|
||||
if (videoView != null) {
|
||||
videoView.start();
|
||||
}
|
||||
}
|
||||
|
||||
public static void pauseVideo(int index) {
|
||||
Message msg = new Message();
|
||||
msg.what = VideoTaskPause;
|
||||
msg.arg1 = index;
|
||||
mVideoHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
private void _pauseVideo(int index) {
|
||||
CocosVideoView videoView = sVideoViews.get(index);
|
||||
if (videoView != null) {
|
||||
videoView.pause();
|
||||
}
|
||||
}
|
||||
|
||||
public static void stopVideo(int index) {
|
||||
Message msg = new Message();
|
||||
msg.what = VideoTaskStop;
|
||||
msg.arg1 = index;
|
||||
mVideoHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
private void _stopVideo(int index) {
|
||||
CocosVideoView videoView = sVideoViews.get(index);
|
||||
if (videoView != null) {
|
||||
videoView.stop();
|
||||
}
|
||||
}
|
||||
|
||||
public static void seekVideoTo(int index,int msec) {
|
||||
Message msg = new Message();
|
||||
msg.what = VideoTaskSeek;
|
||||
msg.arg1 = index;
|
||||
msg.arg2 = msec;
|
||||
mVideoHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
private void _seekVideoTo(int index,int msec) {
|
||||
CocosVideoView videoView = sVideoViews.get(index);
|
||||
if (videoView != null) {
|
||||
videoView.seekTo(msec);
|
||||
}
|
||||
}
|
||||
|
||||
public static float getCurrentTime(final int index) {
|
||||
CocosVideoView video = sVideoViews.get(index);
|
||||
float currentPosition = -1;
|
||||
if (video != null) {
|
||||
currentPosition = video.getCurrentPosition() / 1000.0f;
|
||||
}
|
||||
return currentPosition;
|
||||
}
|
||||
|
||||
public static float getDuration(final int index) {
|
||||
CocosVideoView video = sVideoViews.get(index);
|
||||
float duration = -1;
|
||||
if (video != null) {
|
||||
duration = video.getDuration() / 1000.0f;
|
||||
}
|
||||
if (duration <= 0) {
|
||||
Log.w("CocosVideoHelper", "Video player's duration is not ready to get now!");
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
|
||||
public static void setVideoVisible(int index, boolean visible) {
|
||||
Message msg = new Message();
|
||||
msg.what = VideoTaskSetVisible;
|
||||
msg.arg1 = index;
|
||||
if (visible) {
|
||||
msg.arg2 = 1;
|
||||
} else {
|
||||
msg.arg2 = 0;
|
||||
}
|
||||
|
||||
mVideoHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
private void _setVideoVisible(int index, boolean visible) {
|
||||
CocosVideoView videoView = sVideoViews.get(index);
|
||||
if (videoView != null) {
|
||||
if (visible) {
|
||||
videoView.fixSize();
|
||||
videoView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
videoView.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void setVideoKeepRatioEnabled(int index, boolean enable) {
|
||||
Message msg = new Message();
|
||||
msg.what = VideoTaskKeepRatio;
|
||||
msg.arg1 = index;
|
||||
if (enable) {
|
||||
msg.arg2 = 1;
|
||||
} else {
|
||||
msg.arg2 = 0;
|
||||
}
|
||||
mVideoHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
public static void setPlaybackRate(final int index, final float value) {
|
||||
Message msg = new Message();
|
||||
msg.what = VideoTaskSetPlaybackRate;
|
||||
msg.arg1 = index;
|
||||
msg.arg2 = (int) (value * 10);
|
||||
mVideoHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
private void _setPlaybackRate(final int index, final float value) {
|
||||
CocosVideoView videoView = sVideoViews.get(index);
|
||||
if (videoView != null) {
|
||||
videoView.playbackRate(value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setMute(int index, boolean enable) {
|
||||
Message msg = new Message();
|
||||
msg.what = VideoTaskSetMute;
|
||||
msg.arg1 = index;
|
||||
if (enable) {
|
||||
msg.arg2 = 1;
|
||||
} else {
|
||||
msg.arg2 = 0;
|
||||
}
|
||||
mVideoHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
private void _setMute(int index, boolean enable) {
|
||||
CocosVideoView videoView = sVideoViews.get(index);
|
||||
if (videoView != null) {
|
||||
videoView.setMute(enable);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setLoop(int index, boolean enable) {
|
||||
Message msg = new Message();
|
||||
msg.what = VideoTaskSetLoop;
|
||||
msg.arg1 = index;
|
||||
if (enable) {
|
||||
msg.arg2 = 1;
|
||||
} else {
|
||||
msg.arg2 = 0;
|
||||
}
|
||||
mVideoHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
private void _setLoop(int index, boolean enable) {
|
||||
CocosVideoView videoView = sVideoViews.get(index);
|
||||
if (videoView != null) {
|
||||
videoView.setLoop(enable);
|
||||
}
|
||||
}
|
||||
|
||||
private void _setVideoKeepRatio(int index, boolean enable) {
|
||||
CocosVideoView videoView = sVideoViews.get(index);
|
||||
if (videoView != null) {
|
||||
videoView.setKeepRatio(enable);
|
||||
}
|
||||
}
|
||||
|
||||
private void _setVolume(final int index, final float volume) {
|
||||
CocosVideoView videoView = sVideoViews.get(index);
|
||||
if (videoView != null) {
|
||||
videoView.setVolume(volume);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setVolume(final int index, final float volume) {
|
||||
Message msg = new Message();
|
||||
msg.what = VideoTaskSetVolume;
|
||||
msg.arg1 = index;
|
||||
msg.arg2 = (int) (volume * 10);
|
||||
mVideoHandler.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,637 @@
|
||||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
* Copyright (c) 2014-2016 Chukong Technologies Inc.
|
||||
* Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.cocos.lib;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Point;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.media.PlaybackParams;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
public class CocosVideoView extends SurfaceView {
|
||||
|
||||
// ===========================================================
|
||||
// Internal classes and interfaces.
|
||||
// ===========================================================
|
||||
|
||||
public interface OnVideoEventListener {
|
||||
void onVideoEvent(int tag,int event);
|
||||
}
|
||||
|
||||
private enum State {
|
||||
IDLE,
|
||||
ERROR,
|
||||
INITIALIZED,
|
||||
PREPARING,
|
||||
PREPARED,
|
||||
STARTED,
|
||||
PAUSED,
|
||||
STOPPED,
|
||||
PLAYBACK_COMPLETED,
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Constants
|
||||
// ===========================================================
|
||||
private static final String AssetResourceRoot = "@assets/";
|
||||
|
||||
// ===========================================================
|
||||
// Fields
|
||||
// ===========================================================
|
||||
|
||||
private String TAG = "CocosVideoView";
|
||||
|
||||
private Uri mVideoUri;
|
||||
private int mDuration;
|
||||
private int mPosition;
|
||||
|
||||
private State mCurrentState = State.IDLE;
|
||||
|
||||
// All the stuff we need for playing and showing a video
|
||||
private SurfaceHolder mSurfaceHolder = null;
|
||||
private MediaPlayer mMediaPlayer = null;
|
||||
private int mVideoWidth = 0;
|
||||
private int mVideoHeight = 0;
|
||||
|
||||
private OnVideoEventListener mOnVideoEventListener;
|
||||
|
||||
// recording the seek position while preparing
|
||||
private int mSeekWhenPrepared = 0;
|
||||
|
||||
protected Activity mActivity = null;
|
||||
|
||||
protected int mViewLeft = 0;
|
||||
protected int mViewTop = 0;
|
||||
protected int mViewWidth = 0;
|
||||
protected int mViewHeight = 0;
|
||||
|
||||
protected int mVisibleLeft = 0;
|
||||
protected int mVisibleTop = 0;
|
||||
protected int mVisibleWidth = 0;
|
||||
protected int mVisibleHeight = 0;
|
||||
|
||||
protected boolean mFullScreenEnabled = false;
|
||||
|
||||
private boolean mIsAssetResource = false;
|
||||
private String mVideoFilePath = null;
|
||||
|
||||
private int mViewTag = 0;
|
||||
private boolean mKeepRatio = false;
|
||||
private boolean mMetaUpdated = false;
|
||||
|
||||
// MediaPlayer will be released when surface view is destroyed, so should record the position,
|
||||
// and use it to play after MedialPlayer is created again.
|
||||
private int mPositionBeforeRelease = 0;
|
||||
// also need to record play state when surface is destroyed.
|
||||
private State mStateBeforeRelease = State.IDLE;
|
||||
|
||||
// ===========================================================
|
||||
// Constructors
|
||||
// ===========================================================
|
||||
|
||||
public CocosVideoView(Activity activity, int tag) {
|
||||
super(activity);
|
||||
|
||||
mViewTag = tag;
|
||||
mActivity = activity;
|
||||
initVideoView();
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Getter & Setter
|
||||
// ===========================================================
|
||||
|
||||
public void setVideoRect(int left, int top, int maxWidth, int maxHeight) {
|
||||
if (mViewLeft == left && mViewTop == top && mViewWidth == maxWidth && mViewHeight == maxHeight)
|
||||
return;
|
||||
|
||||
mViewLeft = left;
|
||||
mViewTop = top;
|
||||
mViewWidth = maxWidth;
|
||||
mViewHeight = maxHeight;
|
||||
|
||||
fixSize(mViewLeft, mViewTop, mViewWidth, mViewHeight);
|
||||
}
|
||||
|
||||
public void setFullScreenEnabled(boolean enabled) {
|
||||
if (mFullScreenEnabled != enabled) {
|
||||
mFullScreenEnabled = enabled;
|
||||
fixSize();
|
||||
}
|
||||
}
|
||||
|
||||
public void setVolume (float volume) {
|
||||
if (mMediaPlayer != null) {
|
||||
mMediaPlayer.setVolume(volume, volume);
|
||||
}
|
||||
}
|
||||
|
||||
public void setKeepRatio(boolean enabled) {
|
||||
mKeepRatio = enabled;
|
||||
fixSize();
|
||||
}
|
||||
|
||||
public void playbackRate(float value) {
|
||||
if (mMediaPlayer != null) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
||||
PlaybackParams params = new PlaybackParams();
|
||||
params.setSpeed(value);
|
||||
mMediaPlayer.setPlaybackParams(params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setMute(boolean enabled) {
|
||||
if (mMediaPlayer != null) {
|
||||
if (enabled) {
|
||||
mMediaPlayer.setVolume(0f, 0f);
|
||||
} else {
|
||||
mMediaPlayer.setVolume(1f, 1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setLoop(boolean enabled) {
|
||||
if (mMediaPlayer != null) {
|
||||
mMediaPlayer.setLooping(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setVideoURL(String url) {
|
||||
mIsAssetResource = false;
|
||||
setVideoURI(Uri.parse(url), null);
|
||||
}
|
||||
|
||||
public void setVideoFileName(String path) {
|
||||
if (path.startsWith(AssetResourceRoot)) {
|
||||
path = path.substring(AssetResourceRoot.length());
|
||||
}
|
||||
|
||||
if (path.startsWith("/")) {
|
||||
mIsAssetResource = false;
|
||||
setVideoURI(Uri.parse(path),null);
|
||||
}
|
||||
else {
|
||||
|
||||
mVideoFilePath = path;
|
||||
mIsAssetResource = true;
|
||||
setVideoURI(Uri.parse(path), null);
|
||||
}
|
||||
}
|
||||
|
||||
public int getCurrentPosition() {
|
||||
if (! (mCurrentState == State.IDLE ||
|
||||
mCurrentState == State.ERROR ||
|
||||
mCurrentState == State.INITIALIZED ||
|
||||
mCurrentState == State.STOPPED ||
|
||||
mMediaPlayer == null) ) {
|
||||
mPosition = mMediaPlayer.getCurrentPosition();
|
||||
}
|
||||
return mPosition;
|
||||
}
|
||||
|
||||
public int getDuration() {
|
||||
if (! (mCurrentState == State.IDLE ||
|
||||
mCurrentState == State.ERROR ||
|
||||
mCurrentState == State.INITIALIZED ||
|
||||
mCurrentState == State.STOPPED ||
|
||||
mMediaPlayer == null) ) {
|
||||
mDuration = mMediaPlayer.getDuration();
|
||||
}
|
||||
|
||||
return mDuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be invoked when some video event triggered.
|
||||
*
|
||||
* @param l The callback that will be run
|
||||
*/
|
||||
public void setVideoViewEventListener(OnVideoEventListener l)
|
||||
{
|
||||
mOnVideoEventListener = l;
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Overrides
|
||||
// ===========================================================
|
||||
|
||||
@Override
|
||||
public void setVisibility(int visibility) {
|
||||
super.setVisibility(visibility);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
setMeasuredDimension(mVisibleWidth, mVisibleHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
|
||||
this.sendEvent(EVENT_CLICKED);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Public functions
|
||||
// ===========================================================
|
||||
|
||||
public void stop() {
|
||||
if (!(mCurrentState == State.IDLE || mCurrentState == State.INITIALIZED || mCurrentState == State.ERROR || mCurrentState == State.STOPPED)
|
||||
&& mMediaPlayer != null) {
|
||||
mCurrentState = State.STOPPED;
|
||||
mMediaPlayer.stop();
|
||||
this.sendEvent(EVENT_STOPPED);
|
||||
|
||||
// after the video is stop, it shall prepare to be playable again
|
||||
try {
|
||||
mMediaPlayer.reset(); // reset to avoid some problems on start.
|
||||
loadDataSource();
|
||||
mMediaPlayer.prepare();
|
||||
this.showFirstFrame();
|
||||
} catch (Exception ex) {}
|
||||
}
|
||||
}
|
||||
|
||||
public void stopPlayback() {
|
||||
this.release();
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if ((mCurrentState == State.PREPARED ||
|
||||
mCurrentState == State.PAUSED ||
|
||||
mCurrentState == State.PLAYBACK_COMPLETED) &&
|
||||
mMediaPlayer != null) {
|
||||
|
||||
mCurrentState = State.STARTED;
|
||||
mMediaPlayer.start();
|
||||
this.sendEvent(EVENT_PLAYING);
|
||||
}
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
if ((mCurrentState == State.STARTED || mCurrentState == State.PLAYBACK_COMPLETED) &&
|
||||
mMediaPlayer != null) {
|
||||
mCurrentState = State.PAUSED;
|
||||
mMediaPlayer.pause();
|
||||
this.sendEvent(EVENT_PAUSED);
|
||||
}
|
||||
}
|
||||
|
||||
public void seekTo(int ms) {
|
||||
if (mCurrentState == State.IDLE || mCurrentState == State.INITIALIZED ||
|
||||
mCurrentState == State.STOPPED || mCurrentState == State.ERROR ||
|
||||
mMediaPlayer == null) {
|
||||
return;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
try {
|
||||
Method seekTo = mMediaPlayer.getClass().getMethod("seekTo", long.class, int.class);
|
||||
// The mode argument added in API level 26, 3 = MediaPlayer.SEEK_CLOSEST
|
||||
// https://developer.android.com/reference/android/media/MediaPlayer#seekTo(int)
|
||||
seekTo.invoke(mMediaPlayer, ms, 3);
|
||||
} catch (Exception e) {
|
||||
mMediaPlayer.seekTo(ms);
|
||||
}
|
||||
}
|
||||
else {
|
||||
mMediaPlayer.seekTo(ms);
|
||||
}
|
||||
}
|
||||
|
||||
public void fixSize() {
|
||||
if (mFullScreenEnabled) {
|
||||
Point screenSize = new Point();
|
||||
mActivity.getWindowManager().getDefaultDisplay().getSize(screenSize);
|
||||
fixSize(0, 0, screenSize.x, screenSize.y);
|
||||
} else {
|
||||
fixSize(mViewLeft, mViewTop, mViewWidth, mViewHeight);
|
||||
}
|
||||
}
|
||||
|
||||
public void fixSize(int left, int top, int width, int height) {
|
||||
if (mVideoWidth == 0 || mVideoHeight == 0) {
|
||||
mVisibleLeft = left;
|
||||
mVisibleTop = top;
|
||||
mVisibleWidth = width;
|
||||
mVisibleHeight = height;
|
||||
}
|
||||
else if (width != 0 && height != 0) {
|
||||
if (mKeepRatio && !mFullScreenEnabled) {
|
||||
if ( mVideoWidth * height > width * mVideoHeight ) {
|
||||
mVisibleWidth = width;
|
||||
mVisibleHeight = width * mVideoHeight / mVideoWidth;
|
||||
} else if ( mVideoWidth * height < width * mVideoHeight ) {
|
||||
mVisibleWidth = height * mVideoWidth / mVideoHeight;
|
||||
mVisibleHeight = height;
|
||||
}
|
||||
mVisibleLeft = left + (width - mVisibleWidth) / 2;
|
||||
mVisibleTop = top + (height - mVisibleHeight) / 2;
|
||||
} else {
|
||||
mVisibleLeft = left;
|
||||
mVisibleTop = top;
|
||||
mVisibleWidth = width;
|
||||
mVisibleHeight = height;
|
||||
}
|
||||
}
|
||||
else {
|
||||
mVisibleLeft = left;
|
||||
mVisibleTop = top;
|
||||
mVisibleWidth = mVideoWidth;
|
||||
mVisibleHeight = mVideoHeight;
|
||||
}
|
||||
|
||||
getHolder().setFixedSize(mVisibleWidth, mVisibleHeight);
|
||||
|
||||
FrameLayout.LayoutParams lParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
|
||||
FrameLayout.LayoutParams.MATCH_PARENT);
|
||||
lParams.leftMargin = mVisibleLeft;
|
||||
lParams.topMargin = mVisibleTop;
|
||||
setLayoutParams(lParams);
|
||||
}
|
||||
|
||||
public int resolveAdjustedSize(int desiredSize, int measureSpec) {
|
||||
int result = desiredSize;
|
||||
int specMode = MeasureSpec.getMode(measureSpec);
|
||||
int specSize = MeasureSpec.getSize(measureSpec);
|
||||
|
||||
switch (specMode) {
|
||||
case MeasureSpec.UNSPECIFIED:
|
||||
/* Parent says we can be as big as we want. Just don't be larger
|
||||
* than max size imposed on ourselves.
|
||||
*/
|
||||
result = desiredSize;
|
||||
break;
|
||||
|
||||
case MeasureSpec.AT_MOST:
|
||||
/* Parent says we can be as big as we want, up to specSize.
|
||||
* Don't be larger than specSize, and don't be larger than
|
||||
* the max size imposed on ourselves.
|
||||
*/
|
||||
result = Math.min(desiredSize, specSize);
|
||||
break;
|
||||
|
||||
case MeasureSpec.EXACTLY:
|
||||
// No choice. Do what we are told.
|
||||
result = specSize;
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Private functions
|
||||
// ===========================================================
|
||||
|
||||
private void initVideoView() {
|
||||
mVideoWidth = 0;
|
||||
mVideoHeight = 0;
|
||||
getHolder().addCallback(mSHCallback);
|
||||
//Fix issue#11516:Can't play video on Android 2.3.x
|
||||
getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
mCurrentState = State.IDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
private void setVideoURI(Uri uri, Map<String, String> headers) {
|
||||
mVideoUri = uri;
|
||||
mVideoWidth = 0;
|
||||
mVideoHeight = 0;
|
||||
}
|
||||
|
||||
private void loadDataSource() throws IOException {
|
||||
if (mIsAssetResource) {
|
||||
AssetFileDescriptor afd = mActivity.getAssets().openFd(mVideoFilePath);
|
||||
mMediaPlayer.setDataSource(afd.getFileDescriptor(),afd.getStartOffset(),afd.getLength());
|
||||
} else {
|
||||
mMediaPlayer.setDataSource(mVideoUri.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void openVideo() {
|
||||
if (mSurfaceHolder == null) {
|
||||
// not ready for playback just yet, will try again later
|
||||
return;
|
||||
}
|
||||
if (mIsAssetResource) {
|
||||
if(mVideoFilePath == null)
|
||||
return;
|
||||
} else if(mVideoUri == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.pausePlaybackService();
|
||||
|
||||
try {
|
||||
mMediaPlayer = new MediaPlayer();
|
||||
mMediaPlayer.setOnPreparedListener(mPreparedListener);
|
||||
mMediaPlayer.setOnCompletionListener(mCompletionListener);
|
||||
mMediaPlayer.setOnErrorListener(mErrorListener);
|
||||
mMediaPlayer.setDisplay(mSurfaceHolder);
|
||||
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
||||
mMediaPlayer.setScreenOnWhilePlaying(true);
|
||||
|
||||
loadDataSource();
|
||||
mCurrentState = State.INITIALIZED;
|
||||
|
||||
// Use Prepare() instead of PrepareAsync to make things easy.
|
||||
mMediaPlayer.prepare();
|
||||
this.showFirstFrame();
|
||||
|
||||
// mMediaPlayer.prepareAsync();
|
||||
} catch (IOException ex) {
|
||||
Log.w(TAG, "Unable to open content: " + mVideoUri, ex);
|
||||
mCurrentState = State.ERROR;
|
||||
mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
|
||||
return;
|
||||
} catch (IllegalArgumentException ex) {
|
||||
Log.w(TAG, "Unable to open content: " + mVideoUri, ex);
|
||||
mCurrentState = State.ERROR;
|
||||
mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {
|
||||
public void onPrepared(MediaPlayer mp) {
|
||||
mVideoWidth = mp.getVideoWidth();
|
||||
mVideoHeight = mp.getVideoHeight();
|
||||
|
||||
if (mVideoWidth != 0 && mVideoHeight != 0) {
|
||||
fixSize();
|
||||
}
|
||||
|
||||
if(!mMetaUpdated) {
|
||||
CocosVideoView.this.sendEvent(EVENT_META_LOADED);
|
||||
CocosVideoView.this.sendEvent(EVENT_READY_TO_PLAY);
|
||||
mMetaUpdated = true;
|
||||
}
|
||||
|
||||
mCurrentState = State.PREPARED;
|
||||
|
||||
if (mStateBeforeRelease == State.STARTED) {
|
||||
CocosVideoView.this.start();
|
||||
}
|
||||
|
||||
if (mPositionBeforeRelease > 0) {
|
||||
CocosVideoView.this.seekTo(mPositionBeforeRelease);
|
||||
}
|
||||
|
||||
mStateBeforeRelease = State.IDLE;
|
||||
mPositionBeforeRelease = 0;
|
||||
}
|
||||
};
|
||||
|
||||
private MediaPlayer.OnCompletionListener mCompletionListener =
|
||||
new MediaPlayer.OnCompletionListener() {
|
||||
public void onCompletion(MediaPlayer mp) {
|
||||
mCurrentState = State.PLAYBACK_COMPLETED;
|
||||
CocosVideoView.this.sendEvent(EVENT_COMPLETED);
|
||||
//make it playable again.
|
||||
CocosVideoView.this.showFirstFrame();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private static final int EVENT_PLAYING = 0;
|
||||
private static final int EVENT_PAUSED = 1;
|
||||
private static final int EVENT_STOPPED = 2;
|
||||
private static final int EVENT_COMPLETED = 3;
|
||||
private static final int EVENT_META_LOADED = 4;
|
||||
private static final int EVENT_CLICKED = 5;
|
||||
private static final int EVENT_READY_TO_PLAY = 6;
|
||||
|
||||
private MediaPlayer.OnErrorListener mErrorListener =
|
||||
new MediaPlayer.OnErrorListener() {
|
||||
public boolean onError(MediaPlayer mp, int framework_err, int impl_err) {
|
||||
Log.d(TAG, "Error: " + framework_err + "," + impl_err);
|
||||
mCurrentState = State.ERROR;
|
||||
|
||||
/* Otherwise, pop up an error dialog so the user knows that
|
||||
* something bad has happened. Only try and pop up the dialog
|
||||
* if we're attached to a window. When we're going away and no
|
||||
* longer have a window, don't bother showing the user an error.
|
||||
*/
|
||||
if (getWindowToken() != null) {
|
||||
Resources r = mActivity.getResources();
|
||||
int messageId;
|
||||
|
||||
if (framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
|
||||
// messageId = com.android.internal.R.string.VideoView_error_text_invalid_progressive_playback;
|
||||
messageId = r.getIdentifier("VideoView_error_text_invalid_progressive_playback", "string", "android");
|
||||
} else {
|
||||
// messageId = com.android.internal.R.string.VideoView_error_text_unknown;
|
||||
messageId = r.getIdentifier("VideoView_error_text_unknown", "string", "android");
|
||||
}
|
||||
|
||||
int titleId = r.getIdentifier("VideoView_error_title", "string", "android");
|
||||
int buttonStringId = r.getIdentifier("VideoView_error_button", "string", "android");
|
||||
|
||||
new AlertDialog.Builder(mActivity)
|
||||
.setTitle(r.getString(titleId))
|
||||
.setMessage(messageId)
|
||||
.setPositiveButton(r.getString(buttonStringId),
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int whichButton) {
|
||||
/* If we get here, there is no onError listener, so
|
||||
* at least inform them that the video is over.
|
||||
*/
|
||||
CocosVideoView.this.sendEvent(EVENT_COMPLETED);
|
||||
}
|
||||
})
|
||||
.setCancelable(false)
|
||||
.show();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback()
|
||||
{
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
|
||||
}
|
||||
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
mSurfaceHolder = holder;
|
||||
CocosVideoView.this.openVideo();
|
||||
}
|
||||
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
// after we return from this we can't use the surface any more
|
||||
mSurfaceHolder = null;
|
||||
mPositionBeforeRelease = getCurrentPosition();
|
||||
mStateBeforeRelease = mCurrentState;
|
||||
CocosVideoView.this.release();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* release the media player in any state
|
||||
*/
|
||||
private void release() {
|
||||
if (mMediaPlayer != null) {
|
||||
mMediaPlayer.release();
|
||||
mMediaPlayer = null;
|
||||
mCurrentState = State.IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
private void showFirstFrame() {
|
||||
mMediaPlayer.seekTo(1);
|
||||
}
|
||||
|
||||
private void sendEvent(int event) {
|
||||
if (this.mOnVideoEventListener != null) {
|
||||
this.mOnVideoEventListener.onVideoEvent(this.mViewTag, event);
|
||||
}
|
||||
}
|
||||
|
||||
// Tell the music playback service to pause
|
||||
// REFINE: these constants need to be published somewhere in the framework.
|
||||
private void pausePlaybackService() {
|
||||
Intent i = new Intent("com.android.music.musicservicecommand");
|
||||
i.putExtra("command", "pause");
|
||||
mActivity.sendBroadcast(i);
|
||||
}
|
||||
}
|
||||
173
cocos/platform/android/java/src/com/cocos/lib/CocosWebView.java
Executable file
173
cocos/platform/android/java/src/com/cocos/lib/CocosWebView.java
Executable file
@@ -0,0 +1,173 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated engine source code (the "Software"), a limited,
|
||||
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
||||
to use Cocos Creator solely to develop games on your target platforms. You shall
|
||||
not use Cocos Creator software for developing other software or tools that's
|
||||
used for developing games. You are not granted to publish, distribute,
|
||||
sublicense, and/or sell copies of Cocos Creator.
|
||||
|
||||
The software or tools in this License Agreement are licensed, not sold.
|
||||
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
package com.cocos.lib;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
class ShouldStartLoadingWorker implements Runnable {
|
||||
private CountDownLatch mLatch;
|
||||
private boolean[] mResult;
|
||||
private final int mViewTag;
|
||||
private final String mUrlString;
|
||||
|
||||
ShouldStartLoadingWorker(CountDownLatch latch, boolean[] result, int viewTag, String urlString) {
|
||||
this.mLatch = latch;
|
||||
this.mResult = result;
|
||||
this.mViewTag = viewTag;
|
||||
this.mUrlString = urlString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
this.mResult[0] = CocosWebViewHelper._shouldStartLoading(mViewTag, mUrlString);
|
||||
this.mLatch.countDown(); // notify that result is ready
|
||||
}
|
||||
}
|
||||
|
||||
public class CocosWebView extends WebView {
|
||||
private static final String TAG = CocosWebViewHelper.class.getSimpleName();
|
||||
|
||||
private int mViewTag;
|
||||
private String mJSScheme;
|
||||
|
||||
public CocosWebView(Context context) {
|
||||
this(context, -1);
|
||||
}
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
public CocosWebView(Context context, int viewTag) {
|
||||
super(context);
|
||||
this.mViewTag = viewTag;
|
||||
this.mJSScheme = "";
|
||||
|
||||
this.setFocusable(true);
|
||||
this.setFocusableInTouchMode(true);
|
||||
|
||||
this.getSettings().setSupportZoom(false);
|
||||
|
||||
this.getSettings().setDomStorageEnabled(true);
|
||||
this.getSettings().setJavaScriptEnabled(true);
|
||||
|
||||
// `searchBoxJavaBridge_` has big security risk. http://jvn.jp/en/jp/JVN53768697
|
||||
try {
|
||||
Method method = this.getClass().getMethod("removeJavascriptInterface", new Class[]{String.class});
|
||||
method.invoke(this, "searchBoxJavaBridge_");
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, "This API level do not support `removeJavascriptInterface`");
|
||||
}
|
||||
|
||||
this.setWebViewClient(new Cocos2dxWebViewClient());
|
||||
this.setWebChromeClient(new WebChromeClient());
|
||||
}
|
||||
|
||||
public void setJavascriptInterfaceScheme(String scheme) {
|
||||
this.mJSScheme = scheme != null ? scheme : "";
|
||||
}
|
||||
|
||||
public void setScalesPageToFit(boolean scalesPageToFit) {
|
||||
this.getSettings().setSupportZoom(scalesPageToFit);
|
||||
}
|
||||
|
||||
class Cocos2dxWebViewClient extends WebViewClient {
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, final String urlString) {
|
||||
try {
|
||||
URI uri = URI.create(urlString);
|
||||
if (uri != null && uri.getScheme().equals(mJSScheme)) {
|
||||
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CocosWebViewHelper._onJsCallback(mViewTag, urlString);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG, "Failed to create URI from url");
|
||||
}
|
||||
|
||||
boolean[] result = new boolean[] { true };
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
// run worker on cocos thread
|
||||
GlobalObject.runOnUiThread(new ShouldStartLoadingWorker(latch, result, mViewTag, urlString));
|
||||
|
||||
// wait for result from cocos thread
|
||||
try {
|
||||
latch.await();
|
||||
} catch (InterruptedException ex) {
|
||||
Log.d(TAG, "'shouldOverrideUrlLoading' failed");
|
||||
}
|
||||
|
||||
return result[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageFinished(WebView view, final String url) {
|
||||
super.onPageFinished(view, url);
|
||||
|
||||
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CocosWebViewHelper._didFinishLoading(mViewTag, url);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedError(WebView view, int errorCode, String description, final String failingUrl) {
|
||||
super.onReceivedError(view, errorCode, description, failingUrl);
|
||||
|
||||
CocosHelper.runOnGameThreadAtForeground(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CocosWebViewHelper._didFailLoading(mViewTag, failingUrl);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void setWebViewRect(int left, int top, int maxWidth, int maxHeight) {
|
||||
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||
FrameLayout.LayoutParams.MATCH_PARENT);
|
||||
layoutParams.leftMargin = left;
|
||||
layoutParams.topMargin = top;
|
||||
layoutParams.width = maxWidth;
|
||||
layoutParams.height = maxHeight;
|
||||
this.setLayoutParams(layoutParams);
|
||||
}
|
||||
}
|
||||
310
cocos/platform/android/java/src/com/cocos/lib/CocosWebViewHelper.java
Executable file
310
cocos/platform/android/java/src/com/cocos/lib/CocosWebViewHelper.java
Executable file
@@ -0,0 +1,310 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2011 cocos2d-x.org
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
package com.cocos.lib;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
import android.webkit.WebView;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.PopupWindow;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
|
||||
public class CocosWebViewHelper {
|
||||
private static final String TAG = CocosWebViewHelper.class.getSimpleName();
|
||||
private static Handler sHandler;
|
||||
private static PopupWindow sPopUp;
|
||||
private static FrameLayout sLayout;
|
||||
|
||||
private static SparseArray<CocosWebView> webViews;
|
||||
private static int viewTag = 0;
|
||||
|
||||
public CocosWebViewHelper(FrameLayout layout) {
|
||||
|
||||
CocosWebViewHelper.sLayout = layout;
|
||||
CocosWebViewHelper.sHandler = new Handler(Looper.myLooper());
|
||||
|
||||
CocosWebViewHelper.webViews = new SparseArray<CocosWebView>();
|
||||
}
|
||||
|
||||
private static native boolean shouldStartLoading(int index, String message);
|
||||
private static native void didFinishLoading(int index, String message);
|
||||
private static native void didFailLoading(int index, String message);
|
||||
private static native void onJsCallback(int index, String message);
|
||||
|
||||
public static boolean _shouldStartLoading(int index, String message) { return !shouldStartLoading(index, message); }
|
||||
public static void _didFinishLoading(int index, String message) { didFinishLoading(index, message); }
|
||||
public static void _didFailLoading(int index, String message) { didFailLoading(index, message); }
|
||||
public static void _onJsCallback(int index, String message) { onJsCallback(index, message); }
|
||||
|
||||
public static int createWebView() {
|
||||
final int index = viewTag;
|
||||
GlobalObject.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CocosWebView webView = new CocosWebView(GlobalObject.getContext(), index);
|
||||
FrameLayout.LayoutParams lParams = new FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.WRAP_CONTENT,
|
||||
FrameLayout.LayoutParams.WRAP_CONTENT);
|
||||
sLayout.addView(webView, lParams);
|
||||
|
||||
webViews.put(index, webView);
|
||||
}
|
||||
});
|
||||
return viewTag++;
|
||||
}
|
||||
|
||||
public static void removeWebView(final int index) {
|
||||
GlobalObject.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CocosWebView webView = webViews.get(index);
|
||||
if (webView != null) {
|
||||
webViews.remove(index);
|
||||
sLayout.removeView(webView);
|
||||
webView.destroy();
|
||||
webView = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void setVisible(final int index, final boolean visible) {
|
||||
GlobalObject.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CocosWebView webView = webViews.get(index);
|
||||
if (webView != null) {
|
||||
webView.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void setWebViewRect(final int index, final int left, final int top, final int maxWidth, final int maxHeight) {
|
||||
GlobalObject.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CocosWebView webView = webViews.get(index);
|
||||
if (webView != null) {
|
||||
webView.setWebViewRect(left, top, maxWidth, maxHeight);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void setBackgroundTransparent(final int index, final boolean isTransparent) {
|
||||
GlobalObject.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CocosWebView webView = webViews.get(index);
|
||||
if (webView != null) {
|
||||
webView.setBackgroundColor(isTransparent ? Color.TRANSPARENT : Color.WHITE);
|
||||
webView.setLayerType(WebView.LAYER_TYPE_SOFTWARE, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void setJavascriptInterfaceScheme(final int index, final String scheme) {
|
||||
GlobalObject.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CocosWebView webView = webViews.get(index);
|
||||
if (webView != null) {
|
||||
webView.setJavascriptInterfaceScheme(scheme);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void loadData(final int index, final String data, final String mimeType, final String encoding, final String baseURL) {
|
||||
GlobalObject.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CocosWebView webView = webViews.get(index);
|
||||
if (webView != null) {
|
||||
webView.loadDataWithBaseURL(baseURL, data, mimeType, encoding, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void loadHTMLString(final int index, final String data, final String baseUrl) {
|
||||
GlobalObject.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CocosWebView webView = webViews.get(index);
|
||||
if (webView != null) {
|
||||
webView.loadDataWithBaseURL(baseUrl, data, null, null, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void loadUrl(final int index, final String url) {
|
||||
GlobalObject.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CocosWebView webView = webViews.get(index);
|
||||
if (webView != null) {
|
||||
webView.loadUrl(url);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void loadFile(final int index, final String filePath) {
|
||||
GlobalObject.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CocosWebView webView = webViews.get(index);
|
||||
if (webView != null) {
|
||||
webView.loadUrl(filePath);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void stopLoading(final int index) {
|
||||
GlobalObject.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CocosWebView webView = webViews.get(index);
|
||||
if (webView != null) {
|
||||
webView.stopLoading();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public static void reload(final int index) {
|
||||
GlobalObject.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CocosWebView webView = webViews.get(index);
|
||||
if (webView != null) {
|
||||
webView.reload();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static <T> T callInMainThread(Callable<T> call) throws ExecutionException, InterruptedException {
|
||||
FutureTask<T> task = new FutureTask<T>(call);
|
||||
sHandler.post(task);
|
||||
return task.get();
|
||||
}
|
||||
|
||||
public static boolean canGoBack(final int index) {
|
||||
Callable<Boolean> callable = new Callable<Boolean>() {
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
CocosWebView webView = webViews.get(index);
|
||||
return webView != null && webView.canGoBack();
|
||||
}
|
||||
};
|
||||
try {
|
||||
return callInMainThread(callable);
|
||||
} catch (ExecutionException e) {
|
||||
return false;
|
||||
} catch (InterruptedException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean canGoForward(final int index) {
|
||||
Callable<Boolean> callable = new Callable<Boolean>() {
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
CocosWebView webView = webViews.get(index);
|
||||
return webView != null && webView.canGoForward();
|
||||
}
|
||||
};
|
||||
try {
|
||||
return callInMainThread(callable);
|
||||
} catch (ExecutionException e) {
|
||||
return false;
|
||||
} catch (InterruptedException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void goBack(final int index) {
|
||||
GlobalObject.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CocosWebView webView = webViews.get(index);
|
||||
if (webView != null) {
|
||||
webView.goBack();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void goForward(final int index) {
|
||||
GlobalObject.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CocosWebView webView = webViews.get(index);
|
||||
if (webView != null) {
|
||||
webView.goForward();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void evaluateJS(final int index, final String js) {
|
||||
GlobalObject.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CocosWebView webView = webViews.get(index);
|
||||
if (webView != null) {
|
||||
webView.loadUrl("javascript:" + js);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void setScalesPageToFit(final int index, final boolean scalesPageToFit) {
|
||||
GlobalObject.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CocosWebView webView = webViews.get(index);
|
||||
if (webView != null) {
|
||||
webView.setScalesPageToFit(scalesPageToFit);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2020 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
package com.cocos.lib;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
public class GlobalObject {
|
||||
private static Context sContext = null;
|
||||
private static Activity sActivity = null;
|
||||
private static Handler sHandler = null;
|
||||
private static Thread sUiThread = null;
|
||||
|
||||
// Should be invoked in UI thread. The parameter `context` and `activity` could be the same value.
|
||||
public static void init(Context context, Activity activity) {
|
||||
sContext = context;
|
||||
sActivity = activity;
|
||||
sHandler = new Handler(Looper.getMainLooper());
|
||||
sUiThread = Thread.currentThread();
|
||||
if (sUiThread != Looper.getMainLooper().getThread()) {
|
||||
throw new RuntimeException("GlobalObject.init should be invoked in UI thread");
|
||||
}
|
||||
}
|
||||
|
||||
public static void destroy() {
|
||||
sContext = null;
|
||||
sActivity = null;
|
||||
if (sHandler != null) {
|
||||
sHandler.removeCallbacksAndMessages(null);
|
||||
}
|
||||
sHandler = null;
|
||||
}
|
||||
|
||||
public static Activity getActivity() {
|
||||
return sActivity;
|
||||
}
|
||||
|
||||
public static Context getContext() {
|
||||
return sContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the specified action on the UI thread. If the current thread is the UI
|
||||
* thread, then the action is executed immediately. If the current thread is
|
||||
* not the UI thread, the action is posted to the event queue of the UI thread.
|
||||
* This method keeps the same logic as which in Activity.runOnUiThread.
|
||||
* https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-13.0.0_r37/core/java/android/app/Activity.java#7364
|
||||
* @param action the action to run on the UI thread
|
||||
*/
|
||||
public static void runOnUiThread(Runnable action) {
|
||||
if (Thread.currentThread() != sUiThread) {
|
||||
sHandler.post(action);
|
||||
} else {
|
||||
action.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
61
cocos/platform/android/java/src/com/cocos/lib/JsbBridge.java
Normal file
61
cocos/platform/android/java/src/com/cocos/lib/JsbBridge.java
Normal file
@@ -0,0 +1,61 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2018-2021 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated engine source code (the "Software"), a limited,
|
||||
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
||||
to use Cocos Creator solely to develop games on your target platforms. You shall
|
||||
not use Cocos Creator software for developing other software or tools that's
|
||||
used for developing games. You are not granted to publish, distribute,
|
||||
sublicense, and/or sell copies of Cocos Creator.
|
||||
|
||||
The software or tools in this License Agreement are licensed, not sold.
|
||||
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
package com.cocos.lib;
|
||||
|
||||
public class JsbBridge {
|
||||
public interface ICallback{
|
||||
/**
|
||||
* Applies this callback to the given argument.
|
||||
*
|
||||
* @param arg0 as input
|
||||
* @param arg1 as input
|
||||
*/
|
||||
void onScript(String arg0, String arg1);
|
||||
}
|
||||
private static ICallback callback;
|
||||
|
||||
private static void callByScript(String arg0, String arg1){
|
||||
if(JsbBridge.callback != null)
|
||||
callback.onScript(arg0, arg1);
|
||||
}
|
||||
|
||||
/**Add a callback which you would like to apply
|
||||
* @param f ICallback, the method which will be actually applied. multiple calls will override
|
||||
* */
|
||||
public static void setCallback(ICallback f){
|
||||
JsbBridge.callback = f;
|
||||
}
|
||||
/**
|
||||
* Java dispatch Js event, use native c++ code
|
||||
* @param arg0 input values
|
||||
*/
|
||||
private static native void nativeSendToScript(String arg0, String arg1);
|
||||
public static void sendToScript(String arg0, String arg1){
|
||||
nativeSendToScript(arg0, arg1);
|
||||
}
|
||||
public static void sendToScript(String arg0){
|
||||
nativeSendToScript(arg0, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2018-2021 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated engine source code (the "Software"), a limited,
|
||||
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
||||
to use Cocos Creator solely to develop games on your target platforms. You shall
|
||||
not use Cocos Creator software for developing other software or tools that's
|
||||
used for developing games. You are not granted to publish, distribute,
|
||||
sublicense, and/or sell copies of Cocos Creator.
|
||||
|
||||
The software or tools in this License Agreement are licensed, not sold.
|
||||
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
package com.cocos.lib;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class JsbBridgeWrapper {
|
||||
//Interface for listener, should be implemented and dispatched
|
||||
public interface OnScriptEventListener {
|
||||
void onScriptEvent(String arg);
|
||||
}
|
||||
/**
|
||||
* Get the instance of JsbBridgetWrapper
|
||||
*/
|
||||
public static JsbBridgeWrapper getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new JsbBridgeWrapper();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
/**
|
||||
* Add a listener to specified event, if the event does not exist, the wrapper will create one. Concurrent listener will be ignored
|
||||
*/
|
||||
public void addScriptEventListener(String eventName, OnScriptEventListener listener) {
|
||||
if (eventMap.get(eventName) == null) {
|
||||
eventMap.put(eventName, new ArrayList<OnScriptEventListener>());
|
||||
}
|
||||
eventMap.get(eventName).add(listener);
|
||||
}
|
||||
/**
|
||||
* Remove listener for specified event, concurrent event will be deleted. Return false only if the event does not exist
|
||||
*/
|
||||
public boolean removeScriptEventListener(String eventName, OnScriptEventListener listener) {
|
||||
ArrayList<OnScriptEventListener> arr = eventMap.get(eventName);
|
||||
if (arr == null) {
|
||||
return false;
|
||||
}
|
||||
arr.remove(listener);
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Remove all listener for event specified.
|
||||
*/
|
||||
public void removeAllListenersForEvent(String eventName) {
|
||||
this.eventMap.remove(eventName);
|
||||
}
|
||||
/**
|
||||
* Remove all event registered. Use it carefully!
|
||||
*/
|
||||
public void removeAllListeners() {
|
||||
this.eventMap.clear();
|
||||
}
|
||||
/**
|
||||
* Dispatch the event with argument, the event should be registered in javascript, or other script language in future.
|
||||
*/
|
||||
public void dispatchEventToScript(String eventName, String arg) {
|
||||
JsbBridge.sendToScript(eventName, arg);
|
||||
}
|
||||
/**
|
||||
* Dispatch the event which is registered in javascript, or other script language in future.
|
||||
*/
|
||||
public void dispatchEventToScript(String eventName) {
|
||||
JsbBridge.sendToScript(eventName);
|
||||
}
|
||||
|
||||
private JsbBridgeWrapper() {
|
||||
JsbBridge.setCallback(new JsbBridge.ICallback() {
|
||||
@Override
|
||||
public void onScript(String arg0, String arg1) {
|
||||
triggerEvents(arg0, arg1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private final HashMap<String, ArrayList<OnScriptEventListener>> eventMap = new HashMap<>();
|
||||
private static JsbBridgeWrapper instance;
|
||||
|
||||
private void triggerEvents(String eventName, String arg) {
|
||||
ArrayList<OnScriptEventListener> arr = eventMap.get(eventName);
|
||||
if (arr == null)
|
||||
return;
|
||||
for (OnScriptEventListener m : arr) {
|
||||
m.onScriptEvent(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
57
cocos/platform/android/java/src/com/cocos/lib/Utils.java
Normal file
57
cocos/platform/android/java/src/com/cocos/lib/Utils.java
Normal file
@@ -0,0 +1,57 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
package com.cocos.lib;
|
||||
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
|
||||
public class Utils {
|
||||
|
||||
public static void hideVirtualButton() {
|
||||
if (Build.VERSION.SDK_INT >= 19 &&
|
||||
null != GlobalObject.getActivity()) {
|
||||
// use reflection to remove dependence of API level
|
||||
|
||||
Class viewClass = View.class;
|
||||
final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = CocosReflectionHelper.<Integer>getConstantValue(viewClass, "SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION");
|
||||
final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = CocosReflectionHelper.<Integer>getConstantValue(viewClass, "SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN");
|
||||
final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = CocosReflectionHelper.<Integer>getConstantValue(viewClass, "SYSTEM_UI_FLAG_HIDE_NAVIGATION");
|
||||
final int SYSTEM_UI_FLAG_FULLSCREEN = CocosReflectionHelper.<Integer>getConstantValue(viewClass, "SYSTEM_UI_FLAG_FULLSCREEN");
|
||||
final int SYSTEM_UI_FLAG_IMMERSIVE_STICKY = CocosReflectionHelper.<Integer>getConstantValue(viewClass, "SYSTEM_UI_FLAG_IMMERSIVE_STICKY");
|
||||
final int SYSTEM_UI_FLAG_LAYOUT_STABLE = CocosReflectionHelper.<Integer>getConstantValue(viewClass, "SYSTEM_UI_FLAG_LAYOUT_STABLE");
|
||||
|
||||
// getWindow().getDecorView().setSystemUiVisibility();
|
||||
final Object[] parameters = new Object[]{SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
|
||||
| SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
|
||||
| SYSTEM_UI_FLAG_IMMERSIVE_STICKY};
|
||||
CocosReflectionHelper.<Void>invokeInstanceMethod(GlobalObject.getActivity().getWindow().getDecorView(),
|
||||
"setSystemUiVisibility",
|
||||
new Class[]{Integer.TYPE},
|
||||
parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.cocos.lib.websocket;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
/**
|
||||
* An SSL socket factory that forwards all calls to a delegate. Override {@link
|
||||
* #configureSocket} to customize a created socket before it is returned.
|
||||
*/
|
||||
class CocosDelegatingSSLSocketFactory extends SSLSocketFactory {
|
||||
protected final SSLSocketFactory delegate;
|
||||
|
||||
CocosDelegatingSSLSocketFactory(SSLSocketFactory delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getDefaultCipherSuites() {
|
||||
return delegate.getDefaultCipherSuites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedCipherSuites() {
|
||||
return delegate.getSupportedCipherSuites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(Socket socket, String host, int port,
|
||||
boolean autoClose) throws IOException {
|
||||
return configureSocket(
|
||||
(SSLSocket)delegate.createSocket(socket, host, port, autoClose));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port) throws IOException {
|
||||
return configureSocket((SSLSocket)delegate.createSocket(host, port));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port, InetAddress localHost,
|
||||
int localPort) throws IOException {
|
||||
return configureSocket(
|
||||
(SSLSocket)delegate.createSocket(host, port, localHost, localPort));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress host, int port) throws IOException {
|
||||
return configureSocket((SSLSocket)delegate.createSocket(host, port));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress address, int port,
|
||||
InetAddress localAddress, int localPort)
|
||||
throws IOException {
|
||||
return configureSocket((SSLSocket)delegate.createSocket(
|
||||
address, port, localAddress, localPort));
|
||||
}
|
||||
|
||||
protected SSLSocket configureSocket(SSLSocket socket) throws IOException {
|
||||
return socket;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.cocos.lib.websocket;
|
||||
|
||||
import org.cocos2dx.okhttp3.Interceptor;
|
||||
import org.cocos2dx.okhttp3.MediaType;
|
||||
import org.cocos2dx.okhttp3.Request;
|
||||
import org.cocos2dx.okhttp3.RequestBody;
|
||||
import org.cocos2dx.okhttp3.Response;
|
||||
import org.cocos2dx.okio.BufferedSink;
|
||||
import org.cocos2dx.okio.GzipSink;
|
||||
import org.cocos2dx.okio.Okio;
|
||||
import java.io.IOException;
|
||||
|
||||
public class CocosGzipRequestInterceptor implements Interceptor {
|
||||
@Override
|
||||
public Response intercept(Chain chain) throws IOException {
|
||||
Request originalRequest = chain.request();
|
||||
if (originalRequest.body() == null ||
|
||||
originalRequest.header("Content-Encoding") != null) {
|
||||
return chain.proceed(originalRequest);
|
||||
}
|
||||
|
||||
Request compressedRequest =
|
||||
originalRequest.newBuilder()
|
||||
.header("Content-Encoding", "gzip")
|
||||
.method(originalRequest.method(), gzip(originalRequest.body()))
|
||||
.build();
|
||||
return chain.proceed(compressedRequest);
|
||||
}
|
||||
|
||||
private RequestBody gzip(final RequestBody body) {
|
||||
return new RequestBody() {
|
||||
@Override
|
||||
public MediaType contentType() {
|
||||
return body.contentType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long contentLength() {
|
||||
return -1; // 无法提前知道压缩后的数据大小
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(BufferedSink sink) throws IOException {
|
||||
BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
|
||||
body.writeTo(gzipSink);
|
||||
gzipSink.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
package com.cocos.lib.websocket;
|
||||
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import com.cocos.lib.GlobalObject;
|
||||
|
||||
import org.cocos2dx.okhttp3.CipherSuite;
|
||||
import org.cocos2dx.okhttp3.Dispatcher;
|
||||
import org.cocos2dx.okhttp3.OkHttpClient;
|
||||
import org.cocos2dx.okhttp3.Protocol;
|
||||
import org.cocos2dx.okhttp3.Request;
|
||||
import org.cocos2dx.okhttp3.Response;
|
||||
import org.cocos2dx.okhttp3.WebSocketListener;
|
||||
import org.cocos2dx.okio.ByteString;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class CocosWebSocket extends WebSocketListener {
|
||||
private final static String _TAG = "cocos-websocket";
|
||||
|
||||
static {
|
||||
NativeInit();
|
||||
}
|
||||
|
||||
private static class _WebSocketContext {
|
||||
long identifier;
|
||||
long handlerPtr;
|
||||
}
|
||||
private static Dispatcher dispatcher = null;
|
||||
|
||||
private final long _timeout;
|
||||
private final boolean _perMessageDeflate;
|
||||
private final boolean _tcpNoDelay;
|
||||
private final String[] _header;
|
||||
private final _WebSocketContext _wsContext =
|
||||
new _WebSocketContext();
|
||||
|
||||
private org.cocos2dx.okhttp3.WebSocket _webSocket;
|
||||
private OkHttpClient _client;
|
||||
|
||||
CocosWebSocket(long ptr, long handler, String[] header, boolean tcpNoDelay,
|
||||
boolean perMessageDeflate, long timeout) {
|
||||
_wsContext.identifier = ptr;
|
||||
_wsContext.handlerPtr = handler;
|
||||
_header = header;
|
||||
_tcpNoDelay = tcpNoDelay;
|
||||
_perMessageDeflate = perMessageDeflate;
|
||||
_timeout = timeout;
|
||||
}
|
||||
|
||||
private void _removeHandler() {
|
||||
synchronized (_wsContext) {
|
||||
_wsContext.identifier = 0;
|
||||
_wsContext.handlerPtr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void _send(final byte[] msg) {
|
||||
// Log.d(_TAG, "try sending binary msg");
|
||||
if (null == _webSocket) {
|
||||
Log.e(_TAG, "WebSocket hasn't connected yet");
|
||||
return;
|
||||
}
|
||||
|
||||
ByteString byteString = ByteString.of(msg);
|
||||
_webSocket.send(byteString);
|
||||
}
|
||||
|
||||
private void _send(final String msg) {
|
||||
// Log.d(_TAG, "try sending string msg: " + msg);
|
||||
if (null == _webSocket) {
|
||||
Log.e(_TAG, "WebSocket hasn't connected yet");
|
||||
return;
|
||||
}
|
||||
|
||||
_webSocket.send(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the VM's default SSL socket factory, using {@code trustManager} for
|
||||
* trusted root certificates.
|
||||
*/
|
||||
private SSLSocketFactory
|
||||
defaultSslSocketFactory(X509TrustManager trustManager)
|
||||
throws NoSuchAlgorithmException, KeyManagementException {
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
SecureRandom random;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
random = SecureRandom.getInstanceStrong();
|
||||
} else {
|
||||
random = SecureRandom.getInstance("SHA1PRNG");
|
||||
}
|
||||
sslContext.init(null, new TrustManager[] {trustManager}, random);
|
||||
return sslContext.getSocketFactory();
|
||||
}
|
||||
|
||||
private String[] javaNames(List<CipherSuite> cipherSuites) {
|
||||
if (cipherSuites == null) {
|
||||
return new String[0];
|
||||
} else {
|
||||
String[] result = new String[cipherSuites.size()];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = cipherSuites.get(i).javaName();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private void _connect(final String url, final String protocols,
|
||||
final String caFilePath) {
|
||||
Log.d(_TAG, "connect ws url: '" + url + "' ,protocols: '" + protocols + "' ,ca_: '" + caFilePath + "'");
|
||||
Request.Builder requestBuilder = new Request.Builder().url(url);
|
||||
URI uriObj = null;
|
||||
try {
|
||||
requestBuilder = requestBuilder.url(url.trim());
|
||||
uriObj = URI.create(url);
|
||||
} catch (NullPointerException | IllegalArgumentException e) {
|
||||
synchronized (_wsContext) {
|
||||
nativeOnError("invalid url", _wsContext.identifier,
|
||||
_wsContext.handlerPtr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!protocols.isEmpty()) {
|
||||
requestBuilder.addHeader("Sec-WebSocket-Protocol", protocols);
|
||||
}
|
||||
if (_header != null) {
|
||||
for (int index = 0; index < _header.length; index += 2) {
|
||||
requestBuilder.header(_header[index], _header[index + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
String originProtocol =uriObj.getScheme().toLowerCase();
|
||||
String uriScheme = (originProtocol.equals("wss") || originProtocol.equals("https"))? "https" : "http";
|
||||
requestBuilder.addHeader("Origin", uriScheme + "://" + uriObj.getHost() + (uriObj.getPort() < 0 ? "" : ":" + uriObj.getPort()));
|
||||
|
||||
Request request = requestBuilder.build();
|
||||
|
||||
if(dispatcher == null) {
|
||||
dispatcher = new Dispatcher();
|
||||
}
|
||||
|
||||
OkHttpClient.Builder builder =
|
||||
new OkHttpClient.Builder()
|
||||
.dispatcher(dispatcher)
|
||||
.protocols(Collections.singletonList(Protocol.HTTP_1_1))
|
||||
.readTimeout(_timeout, TimeUnit.MILLISECONDS)
|
||||
.writeTimeout(_timeout, TimeUnit.MILLISECONDS)
|
||||
.connectTimeout(_timeout, TimeUnit.MILLISECONDS);
|
||||
|
||||
if (_perMessageDeflate) {
|
||||
// 开启压缩扩展, 开启 Gzip 压缩
|
||||
builder.addInterceptor(new CocosGzipRequestInterceptor());
|
||||
}
|
||||
KeyStore keyStore = null;
|
||||
if (url.toLowerCase().startsWith("wss://") && !caFilePath.isEmpty()) {
|
||||
try {
|
||||
InputStream caInput = null;
|
||||
|
||||
if (caFilePath.startsWith("assets/")) {
|
||||
caInput = GlobalObject.getContext().getResources().getAssets().open(caFilePath);
|
||||
} else {
|
||||
caInput = new FileInputStream(caFilePath);
|
||||
}
|
||||
if (caFilePath.toLowerCase().endsWith(".pem")) {
|
||||
keyStore = CocosWebSocketUtils.GetPEMKeyStore(caInput);
|
||||
} else {
|
||||
keyStore = CocosWebSocketUtils.GetCERKeyStore(caInput);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
String errMsg = e.getMessage();
|
||||
if (errMsg == null) {
|
||||
errMsg = "unknown error";
|
||||
}
|
||||
synchronized (_wsContext) {
|
||||
nativeOnError(errMsg, _wsContext.identifier, _wsContext.handlerPtr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
builder.hostnameVerifier(new HostnameVerifier() {
|
||||
@Override
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
Log.d(_TAG, "ca hostname: " + hostname);
|
||||
HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
|
||||
return hv.verify(hostname, session);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (url.toLowerCase().startsWith("wss://") || _tcpNoDelay) {
|
||||
try {
|
||||
X509TrustManager trustManager =
|
||||
CocosWebSocketUtils.GetTrustManager(keyStore);
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
SecureRandom random;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
random = SecureRandom.getInstanceStrong();
|
||||
} else {
|
||||
random = SecureRandom.getInstance("SHA1PRNG");
|
||||
}
|
||||
sslContext.init(null, new TrustManager[] {trustManager}, random);
|
||||
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
|
||||
SSLSocketFactory customSslSocketFactory =
|
||||
new CocosDelegatingSSLSocketFactory(sslSocketFactory) {
|
||||
@Override
|
||||
protected SSLSocket configureSocket(SSLSocket socket)
|
||||
throws IOException {
|
||||
socket.setTcpNoDelay(_tcpNoDelay);
|
||||
// TLSv1.2 is disabled default below API20----
|
||||
// https://developer.android.com/reference/javax/net/ssl/SSLSocket
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT_WATCH) {
|
||||
socket.setEnabledProtocols(new String[] {"TLSv1.2"});
|
||||
}
|
||||
return socket;
|
||||
}
|
||||
};
|
||||
builder.sslSocketFactory(customSslSocketFactory, trustManager);
|
||||
} catch (GeneralSecurityException e) {
|
||||
e.printStackTrace();
|
||||
String errMsg = e.getMessage();
|
||||
if (errMsg == null) {
|
||||
errMsg = "unknown error";
|
||||
}
|
||||
synchronized (_wsContext) {
|
||||
nativeOnError(errMsg, _wsContext.identifier, _wsContext.handlerPtr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
_client = builder.build();
|
||||
_webSocket = _client.newWebSocket(request, this);
|
||||
}
|
||||
|
||||
private void _close(final int code, final String reason) {
|
||||
_webSocket.close(code, reason);
|
||||
// _client.dispatcher().executorService().shutdown();
|
||||
}
|
||||
|
||||
private long _getBufferedAmountID() {
|
||||
return _webSocket.queueSize();
|
||||
}
|
||||
|
||||
private void output(final String content) {
|
||||
Log.w(_TAG, content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(org.cocos2dx.okhttp3.WebSocket _webSocket, Response response) {
|
||||
output("WebSocket onOpen _client: " + _client);
|
||||
synchronized (_wsContext) {
|
||||
nativeOnOpen(response.protocol().toString(),
|
||||
response.headers().toString(), _wsContext.identifier,
|
||||
_wsContext.handlerPtr);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(org.cocos2dx.okhttp3.WebSocket _webSocket, String text) {
|
||||
// output("Receiving string msg: " + text);
|
||||
synchronized (_wsContext) {
|
||||
nativeOnStringMessage(text, _wsContext.identifier, _wsContext.handlerPtr);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(org.cocos2dx.okhttp3.WebSocket _webSocket, ByteString bytes) {
|
||||
// output("Receiving binary msg");
|
||||
synchronized (_wsContext) {
|
||||
nativeOnBinaryMessage(bytes.toByteArray(), _wsContext.identifier,
|
||||
_wsContext.handlerPtr);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClosing(org.cocos2dx.okhttp3.WebSocket _webSocket, int code,
|
||||
String reason) {
|
||||
output("Closing : " + code + " / " + reason);
|
||||
if (_webSocket != null) {
|
||||
_webSocket.close(code, reason);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(org.cocos2dx.okhttp3.WebSocket _webSocket, Throwable t,
|
||||
Response response) {
|
||||
String msg = "";
|
||||
if (t != null) {
|
||||
msg = t.getMessage() == null ? t.getClass().getSimpleName() : t.getMessage();
|
||||
}
|
||||
output("onFailure Error : " + msg);
|
||||
synchronized (_wsContext) {
|
||||
nativeOnError(msg, _wsContext.identifier, _wsContext.handlerPtr);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClosed(org.cocos2dx.okhttp3.WebSocket _webSocket, int code,
|
||||
String reason) {
|
||||
output("onClosed : " + code + " / " + reason);
|
||||
synchronized (_wsContext) {
|
||||
nativeOnClosed(code, reason, _wsContext.identifier,
|
||||
_wsContext.handlerPtr);
|
||||
}
|
||||
}
|
||||
|
||||
private static native void NativeInit();
|
||||
|
||||
private native void nativeOnStringMessage(final String msg, long identifier,
|
||||
long handler);
|
||||
|
||||
private native void nativeOnBinaryMessage(final byte[] msg, long identifier,
|
||||
long handler);
|
||||
|
||||
private native void nativeOnOpen(final String protocol,
|
||||
final String headerString, long identifier,
|
||||
long handler);
|
||||
|
||||
private native void nativeOnClosed(final int code, final String reason,
|
||||
long identifier, long handler);
|
||||
|
||||
private native void nativeOnError(final String msg, long identifier,
|
||||
long handler);
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.cocos.lib.websocket;
|
||||
|
||||
import android.util.Base64;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.util.Arrays;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
class CocosWebSocketUtils {
|
||||
static X509TrustManager GetTrustManager(KeyStore keyStore)
|
||||
throws GeneralSecurityException {
|
||||
String algorithm = TrustManagerFactory.getDefaultAlgorithm();
|
||||
TrustManagerFactory trustManagerFactory =
|
||||
TrustManagerFactory.getInstance(algorithm);
|
||||
trustManagerFactory.init(keyStore);
|
||||
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
|
||||
if (trustManagers.length != 1 ||
|
||||
!(trustManagers[0] instanceof X509TrustManager)) {
|
||||
String prefixErrorMessage = "Unexpected default trust managers:";
|
||||
throw new IllegalStateException(prefixErrorMessage +
|
||||
Arrays.toString(trustManagers));
|
||||
}
|
||||
return (X509TrustManager)trustManagers[0];
|
||||
}
|
||||
|
||||
static KeyStore GetCERKeyStore(InputStream inputStream)
|
||||
throws CertificateException, KeyStoreException, IOException,
|
||||
NoSuchAlgorithmException {
|
||||
CertificateFactory factory = CertificateFactory.getInstance("X.509");
|
||||
Certificate certificate = factory.generateCertificate(inputStream);
|
||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
keyStore.load(null);
|
||||
keyStore.setCertificateEntry("0", certificate);
|
||||
return keyStore;
|
||||
}
|
||||
|
||||
static KeyStore GetPEMKeyStore(InputStream inputStream) throws Exception {
|
||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
keyStore.load(null);
|
||||
int index = 0;
|
||||
|
||||
BufferedReader bufferedReader =
|
||||
new BufferedReader(new InputStreamReader(inputStream));
|
||||
String carBegin;
|
||||
while ((carBegin = bufferedReader.readLine()) != null) {
|
||||
if (carBegin.contains("BEGIN CERTIFICATE")) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
while ((carBegin = bufferedReader.readLine()) != null) {
|
||||
if (carBegin.contains("END CERTIFICATE")) {
|
||||
String hexString = stringBuilder.toString();
|
||||
byte[] bytes = Base64.decode(hexString, Base64.DEFAULT);
|
||||
Certificate certificate = _GenerateCertificateFromDER(bytes);
|
||||
keyStore.setCertificateEntry(Integer.toString(index++),
|
||||
certificate);
|
||||
break;
|
||||
} else {
|
||||
stringBuilder.append(carBegin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bufferedReader.close();
|
||||
if (index == 0) {
|
||||
throw new IllegalArgumentException("No CERTIFICATE found");
|
||||
}
|
||||
return keyStore;
|
||||
}
|
||||
|
||||
private static Certificate _GenerateCertificateFromDER(byte[] certBytes)
|
||||
throws CertificateException {
|
||||
CertificateFactory factory = CertificateFactory.getInstance("X.509");
|
||||
return factory.generateCertificate(new ByteArrayInputStream(certBytes));
|
||||
}
|
||||
}
|
||||
45
cocos/platform/android/jni/JniCocosEntry.cpp
Normal file
45
cocos/platform/android/jni/JniCocosEntry.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "game-activity/native_app_glue/android_native_app_glue.h"
|
||||
#include "platform/android/AndroidPlatform.h"
|
||||
#include "platform/java/jni/JniHelper.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
void android_main(struct android_app *app) {
|
||||
auto *platform = cc::BasePlatform::getPlatform();
|
||||
auto *androidPlatform = static_cast<cc::AndroidPlatform *>(platform);
|
||||
androidPlatform->setAndroidApp(app);
|
||||
androidPlatform->init();
|
||||
androidPlatform->run(0, nullptr);
|
||||
}
|
||||
|
||||
//NOLINTNEXTLINE
|
||||
JNIEXPORT void JNICALL Java_com_cocos_lib_CocosActivity_onCreateNative(JNIEnv *env, jobject activity) {
|
||||
cc::JniHelper::init(env, activity);
|
||||
}
|
||||
}
|
||||
263
cocos/platform/android/jni/JniCocosSurfaceView.cpp
Normal file
263
cocos/platform/android/jni/JniCocosSurfaceView.cpp
Normal file
@@ -0,0 +1,263 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include <android/keycodes.h>
|
||||
#include <android/log.h>
|
||||
#include <android/native_window.h>
|
||||
#include <android/native_window_jni.h>
|
||||
#include <jni.h>
|
||||
#include "application/ApplicationManager.h"
|
||||
#include "base/memory/Memory.h"
|
||||
#include "engine/EngineEvents.h"
|
||||
#include "platform/android/AndroidPlatform.h"
|
||||
#include "platform/java/modules/SystemWindow.h"
|
||||
#include "platform/java/modules/SystemWindowManager.h"
|
||||
|
||||
namespace {
|
||||
struct cc::TouchEvent touchEvent;
|
||||
|
||||
class NativeWindowCache {
|
||||
public:
|
||||
explicit NativeWindowCache(int windowId) : _windowId{windowId} {
|
||||
}
|
||||
|
||||
~NativeWindowCache() {
|
||||
setSurface(nullptr);
|
||||
}
|
||||
|
||||
void setSurface(jobject surface) {
|
||||
if (_nativeWindow != nullptr) {
|
||||
ANativeWindow_release(_nativeWindow);
|
||||
}
|
||||
if (surface != nullptr) {
|
||||
_nativeWindow = ANativeWindow_fromSurface(env, surface);
|
||||
} else {
|
||||
_nativeWindow = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ANativeWindow *getNativeWindow() {
|
||||
return _nativeWindow;
|
||||
}
|
||||
|
||||
int getWindowId() const {
|
||||
return _windowId;
|
||||
}
|
||||
|
||||
JNIEnv *env;
|
||||
|
||||
private:
|
||||
int _windowId{0};
|
||||
ANativeWindow *_nativeWindow{nullptr};
|
||||
};
|
||||
} // namespace
|
||||
|
||||
//#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "JniCocosSurfaceView JNI", __VA_ARGS__)
|
||||
|
||||
extern "C" {
|
||||
|
||||
JNIEXPORT jlong Java_com_cocos_lib_CocosSurfaceView_constructNative(JNIEnv *env, jobject /*thiz*/, jint windowId) { // NOLINT JNI function name
|
||||
auto *cache = ccnew NativeWindowCache(windowId);
|
||||
cache->env = env;
|
||||
return reinterpret_cast<jlong>(cache);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_cocos_lib_CocosSurfaceView_destructNative(JNIEnv * /*env*/, jobject /*thiz*/, jlong handle) { // NOLINT JNI function name
|
||||
auto *windowCache = reinterpret_cast<NativeWindowCache *>(handle);
|
||||
CC_SAFE_DELETE(windowCache);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_cocos_lib_CocosSurfaceView_onSizeChangedNative(JNIEnv * /*env*/, jobject /*thiz*/, jint windowId, jint width, jint height) { // NOLINT JNI function name
|
||||
auto func = [width, height, windowId]() -> void {
|
||||
cc::events::Resize::broadcast(width, height, windowId);
|
||||
};
|
||||
CC_CURRENT_ENGINE()->getScheduler()->performFunctionInCocosThread(func);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_cocos_lib_CocosSurfaceView_onSurfaceRedrawNeededNative(JNIEnv * /*env*/, jobject /*thiz*/, jlong handle) { // NOLINT JNI function name
|
||||
//
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_cocos_lib_CocosSurfaceView_onSurfaceCreatedNative(JNIEnv *env, jobject /*thiz*/, jlong handle, jobject surface) { // NOLINT JNI function name
|
||||
CC_UNUSED_PARAM(env);
|
||||
auto *windowCache = reinterpret_cast<NativeWindowCache *>(handle);
|
||||
ANativeWindow *oldNativeWindow = windowCache->getNativeWindow();
|
||||
windowCache->setSurface(surface);
|
||||
ANativeWindow *nativeWindow = windowCache->getNativeWindow();
|
||||
|
||||
auto *platform = static_cast<cc::AndroidPlatform *>(cc::BasePlatform::getPlatform());
|
||||
auto *windowMgr = platform->getInterface<cc::SystemWindowManager>();
|
||||
auto *iSysWindow = windowMgr->getWindow(windowCache->getWindowId());
|
||||
auto *sysWindow = static_cast<cc::SystemWindow *>(iSysWindow);
|
||||
sysWindow->setWindowHandle(nativeWindow);
|
||||
if (oldNativeWindow) {
|
||||
auto func = [sysWindow]() -> void {
|
||||
cc::events::WindowRecreated::broadcast(sysWindow->getWindowId());
|
||||
};
|
||||
CC_CURRENT_ENGINE()->getScheduler()->performFunctionInCocosThread(func);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_cocos_lib_CocosSurfaceView_onSurfaceChangedNative(JNIEnv *env, // NOLINT JNI function name
|
||||
jobject /*thiz*/,
|
||||
jlong handle,
|
||||
jobject surface,
|
||||
jint format,
|
||||
jint width,
|
||||
jint height) {
|
||||
CC_UNUSED_PARAM(env);
|
||||
CC_UNUSED_PARAM(format);
|
||||
CC_UNUSED_PARAM(width);
|
||||
CC_UNUSED_PARAM(height);
|
||||
if (handle != 0) {
|
||||
auto *windowCache = (NativeWindowCache *)handle;
|
||||
ANativeWindow *oldNativeWindow = windowCache->getNativeWindow();
|
||||
// Fix for window being destroyed behind the scenes on older Android
|
||||
// versions.
|
||||
if (oldNativeWindow != nullptr) {
|
||||
ANativeWindow_acquire(oldNativeWindow);
|
||||
}
|
||||
|
||||
windowCache->setSurface(surface);
|
||||
ANativeWindow *newNativeWindow = windowCache->getNativeWindow();
|
||||
if (oldNativeWindow != newNativeWindow) {
|
||||
auto *iSysWindow = CC_GET_PLATFORM_INTERFACE(cc::SystemWindowManager)->getWindow(windowCache->getWindowId());
|
||||
auto *sysWindow = static_cast<cc::SystemWindow *>(iSysWindow);
|
||||
sysWindow->setWindowHandle(newNativeWindow);
|
||||
|
||||
auto func = [sysWindow]() -> void {
|
||||
cc::events::WindowRecreated::broadcast(sysWindow->getWindowId());
|
||||
};
|
||||
CC_CURRENT_ENGINE()->getScheduler()->performFunctionInCocosThread(func);
|
||||
}
|
||||
// Release the window we acquired earlier.
|
||||
if (oldNativeWindow != nullptr) {
|
||||
ANativeWindow_release(oldNativeWindow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_cocos_lib_CocosSurfaceView_onSurfaceDestroyedNative(JNIEnv *env, jobject /*thiz*/, jlong handle) { // NOLINT JNI function name
|
||||
auto *windowCache = (NativeWindowCache *)handle;
|
||||
ANativeWindow *nativeWindow = windowCache->getNativeWindow();
|
||||
|
||||
// todo: destroy gfx surface
|
||||
auto func = [nativeWindow]() -> void {
|
||||
auto *platform = static_cast<cc::AndroidPlatform *>(cc::BasePlatform::getPlatform());
|
||||
auto *windowMgr = platform->getInterface<cc::SystemWindowManager>();
|
||||
cc::ISystemWindow *window = windowMgr->getWindowFromANativeWindow(nativeWindow);
|
||||
|
||||
cc::events::WindowDestroy::broadcast(window->getWindowId());
|
||||
};
|
||||
CC_CURRENT_ENGINE()->getScheduler()->performFunctionInCocosThread(func);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_cocos_lib_CocosTouchHandler_handleActionDown(JNIEnv *env, // NOLINT JNI function name
|
||||
jobject obj,
|
||||
jint windowId,
|
||||
jint id,
|
||||
jfloat x,
|
||||
jfloat y) {
|
||||
CC_UNUSED_PARAM(env);
|
||||
CC_UNUSED_PARAM(obj);
|
||||
|
||||
touchEvent.windowId = windowId;
|
||||
touchEvent.type = cc::TouchEvent::Type::BEGAN;
|
||||
touchEvent.touches.emplace_back(x, y, id);
|
||||
cc::events::Touch::broadcast(touchEvent);
|
||||
touchEvent.touches.clear();
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
JNIEXPORT void JNICALL Java_com_cocos_lib_CocosTouchHandler_handleActionUp(JNIEnv *env, // NOLINT JNI function name
|
||||
jobject obj,
|
||||
jint windowId,
|
||||
jint id,
|
||||
jfloat x,
|
||||
jfloat y) {
|
||||
CC_UNUSED_PARAM(env);
|
||||
CC_UNUSED_PARAM(obj);
|
||||
|
||||
touchEvent.windowId = windowId;
|
||||
touchEvent.type = cc::TouchEvent::Type::ENDED;
|
||||
touchEvent.touches.emplace_back(x, y, id);
|
||||
cc::events::Touch::broadcast(touchEvent);
|
||||
touchEvent.touches.clear();
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
JNIEXPORT void JNICALL Java_com_cocos_lib_CocosTouchHandler_handleActionMove(JNIEnv *env, // NOLINT JNI function name
|
||||
jobject obj,
|
||||
jint windowId,
|
||||
jintArray ids,
|
||||
jfloatArray xs,
|
||||
jfloatArray ys) {
|
||||
CC_UNUSED_PARAM(obj);
|
||||
|
||||
touchEvent.windowId = windowId;
|
||||
touchEvent.type = cc::TouchEvent::Type::MOVED;
|
||||
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);
|
||||
for (int i = 0; i < size; i++) {
|
||||
touchEvent.touches.emplace_back(x[i], y[i], id[i]);
|
||||
}
|
||||
|
||||
cc::events::Touch::broadcast(touchEvent);
|
||||
touchEvent.touches.clear();
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
JNIEXPORT void JNICALL Java_com_cocos_lib_CocosTouchHandler_handleActionCancel(JNIEnv *env, // NOLINT JNI function name
|
||||
jobject obj,
|
||||
jint windowId,
|
||||
jintArray ids,
|
||||
jfloatArray xs,
|
||||
jfloatArray ys) {
|
||||
CC_UNUSED_PARAM(obj);
|
||||
|
||||
touchEvent.windowId = windowId;
|
||||
touchEvent.type = cc::TouchEvent::Type::CANCELLED;
|
||||
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);
|
||||
for (int i = 0; i < size; i++) {
|
||||
touchEvent.touches.emplace_back(x[i], y[i], id[i]);
|
||||
}
|
||||
cc::events::Touch::broadcast(touchEvent);
|
||||
touchEvent.touches.clear();
|
||||
}
|
||||
}
|
||||
4
cocos/platform/android/libcocos2dx/AndroidManifest.xml
Normal file
4
cocos/platform/android/libcocos2dx/AndroidManifest.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
</manifest>
|
||||
40
cocos/platform/android/libcocos2dx/build.gradle
Normal file
40
cocos/platform/android/libcocos2dx/build.gradle
Normal file
@@ -0,0 +1,40 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion PROP_COMPILE_SDK_VERSION.toInteger()
|
||||
namespace 'com.cocos.lib'
|
||||
defaultConfig {
|
||||
minSdkVersion PROP_MIN_SDK_VERSION
|
||||
targetSdkVersion PROP_TARGET_SDK_VERSION
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
consumerProguardFiles 'proguard-rules.pro'
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
buildConfig = true
|
||||
}
|
||||
|
||||
sourceSets.main {
|
||||
aidl.srcDir "../java/src"
|
||||
java.srcDir "../java/src"
|
||||
manifest.srcFile "AndroidManifest.xml"
|
||||
}
|
||||
|
||||
buildDir = new File(rootProject.buildDir, project.name)
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api fileTree(include: ['*.jar'], dir: '../java/libs')
|
||||
}
|
||||
17
cocos/platform/android/libcocos2dx/proguard-rules.pro
vendored
Normal file
17
cocos/platform/android/libcocos2dx/proguard-rules.pro
vendored
Normal 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:
|
||||
-keep public class com.google.** { *; }
|
||||
-keep public class androidx.** { *; }
|
||||
-keep class com.cocos.lib.CocosActivity {
|
||||
public <methods>;
|
||||
protected <methods>;
|
||||
private void createSurface(...);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?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>
|
||||
<string name="tip_disable_safe_input_type" translatable="false">请到系统设置关闭安全键盘</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?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>
|
||||
<string name="tip_disable_safe_input_type" translatable="false">Please go to the system settings to turn off the security keyboard!</string>
|
||||
</resources>
|
||||
4
cocos/platform/android/libcocosxr/AndroidManifest.xml
Normal file
4
cocos/platform/android/libcocosxr/AndroidManifest.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.cocos.lib.xr">
|
||||
|
||||
</manifest>
|
||||
37
cocos/platform/android/libcocosxr/build.gradle
Normal file
37
cocos/platform/android/libcocosxr/build.gradle
Normal file
@@ -0,0 +1,37 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion PROP_COMPILE_SDK_VERSION.toInteger()
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion PROP_MIN_SDK_VERSION
|
||||
targetSdkVersion PROP_TARGET_SDK_VERSION
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
|
||||
sourceSets.main {
|
||||
java.srcDirs "src"
|
||||
res.srcDirs 'res'
|
||||
jniLibs.srcDirs 'libs'
|
||||
manifest.srcFile "AndroidManifest.xml"
|
||||
}
|
||||
|
||||
buildDir = new File(rootProject.buildDir, project.name)
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar','*.aar'])
|
||||
implementation project(':libcocos')
|
||||
}
|
||||
21
cocos/platform/android/libcocosxr/proguard-rules.pro
vendored
Normal file
21
cocos/platform/android/libcocosxr/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -0,0 +1,212 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
|
||||
*
|
||||
* http://www.cocos.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
package com.cocos.lib.xr;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class CocosXRApi {
|
||||
private static final String TAG = "CocosXRApi";
|
||||
private final static String ACTION_ADB_CMD = "com.cocosxr.adb.cmd";
|
||||
private enum ActivityLifecycleType {
|
||||
UnKnown,
|
||||
Created,
|
||||
Started,
|
||||
Resumed,
|
||||
Paused,
|
||||
Stopped,
|
||||
SaveInstanceState,
|
||||
Destroyed
|
||||
}
|
||||
|
||||
private final static CocosXRApi instance = new CocosXRApi();
|
||||
|
||||
/**
|
||||
* adb shell am broadcast -a com.cocosxr.adb.cmd --es CMD_KEY LOG --ei CMD_VALUE 1
|
||||
* adb shell am broadcast -a com.cocosxr.adb.cmd --es CMD_KEY LOG --es CMD_VALUE abc
|
||||
*/
|
||||
private class CocosXRActionReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (ACTION_ADB_CMD.equals(intent.getAction())) {
|
||||
// adb cmd
|
||||
if (intent.getExtras() == null) {
|
||||
Log.w(TAG, "[CocosXRActionReceiver] intent.getExtras() == null");
|
||||
return;
|
||||
}
|
||||
Object cmdKey = intent.getExtras().get("CMD_KEY");
|
||||
String key = cmdKey == null ? "" : cmdKey.toString();
|
||||
Object cmdValue = intent.getExtras().get("CMD_VALUE");
|
||||
String valueStr = null;
|
||||
if (cmdValue instanceof Integer) {
|
||||
valueStr = String.valueOf(intent.getIntExtra("CMD_VALUE", Integer.MIN_VALUE));
|
||||
} else if (cmdValue instanceof String) {
|
||||
valueStr = intent.getStringExtra("CMD_VALUE");
|
||||
}
|
||||
|
||||
try {
|
||||
onAdbCmd(key, valueStr);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CocosXRApi() {
|
||||
}
|
||||
|
||||
public static CocosXRApi getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private Application application;
|
||||
private WeakReference<Activity> activityWeakReference;
|
||||
private Context applicationContext;
|
||||
private Application.ActivityLifecycleCallbacks activityLifecycleCallbacks;
|
||||
private CocosXRActionReceiver actionReceiver;
|
||||
private CocosXRWebViewManager webViewManager;
|
||||
|
||||
public void onCreate(Activity activity) {
|
||||
activityWeakReference = new WeakReference<>(activity);
|
||||
application = activity.getApplication();
|
||||
applicationContext = activity.getApplicationContext();
|
||||
webViewManager = new CocosXRWebViewManager();
|
||||
if (activityLifecycleCallbacks == null) {
|
||||
activityLifecycleCallbacks = new Application.ActivityLifecycleCallbacks() {
|
||||
@Override
|
||||
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
|
||||
try {
|
||||
onActivityLifecycleCallback(ActivityLifecycleType.Created.ordinal(), activity.getLocalClassName());
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStarted(Activity activity) {
|
||||
webViewManager.onCreate(activity);
|
||||
try {
|
||||
onActivityLifecycleCallback(ActivityLifecycleType.Started.ordinal(), activity.getLocalClassName());
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResumed(Activity activity) {
|
||||
try {
|
||||
onActivityLifecycleCallback(ActivityLifecycleType.Resumed.ordinal(), activity.getLocalClassName());
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
webViewManager.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPaused(Activity activity) {
|
||||
try {
|
||||
onActivityLifecycleCallback(ActivityLifecycleType.Paused.ordinal(), activity.getLocalClassName());
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
webViewManager.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStopped(Activity activity) {
|
||||
try {
|
||||
onActivityLifecycleCallback(ActivityLifecycleType.Stopped.ordinal(), activity.getLocalClassName());
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
|
||||
try {
|
||||
onActivityLifecycleCallback(ActivityLifecycleType.SaveInstanceState.ordinal(), activity.getLocalClassName());
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed(Activity activity) {
|
||||
try {
|
||||
onActivityLifecycleCallback(ActivityLifecycleType.Destroyed.ordinal(), activity.getLocalClassName());
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
webViewManager.onDestroy();
|
||||
}
|
||||
};
|
||||
}
|
||||
application.registerActivityLifecycleCallbacks(activityLifecycleCallbacks);
|
||||
|
||||
if(actionReceiver != null) {
|
||||
applicationContext.unregisterReceiver(actionReceiver);
|
||||
actionReceiver =null;
|
||||
}
|
||||
|
||||
actionReceiver = new CocosXRActionReceiver();
|
||||
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(ACTION_ADB_CMD);
|
||||
applicationContext.registerReceiver(actionReceiver, intentFilter);
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
if (application != null && activityLifecycleCallbacks != null) {
|
||||
application.unregisterActivityLifecycleCallbacks(activityLifecycleCallbacks);
|
||||
activityLifecycleCallbacks = null;
|
||||
}
|
||||
|
||||
if(applicationContext != null && actionReceiver != null) {
|
||||
applicationContext.unregisterReceiver(actionReceiver);
|
||||
actionReceiver = null;
|
||||
}
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
public Activity getActivity() {
|
||||
return activityWeakReference.get();
|
||||
}
|
||||
|
||||
// native
|
||||
private native void onActivityLifecycleCallback(int id, String activityClassName);
|
||||
private native void onAdbCmd(String key, String value);
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
|
||||
*
|
||||
* http://www.cocos.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
package com.cocos.lib.xr;
|
||||
|
||||
import android.opengl.GLES11Ext;
|
||||
import android.opengl.GLES30;
|
||||
import android.util.Log;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
public class CocosXRGLHelper {
|
||||
private static final String TAG = "CocosXRGLHelper";
|
||||
|
||||
public static int loadShader(int shaderType, String source) {
|
||||
int shader = GLES30.glCreateShader(shaderType);
|
||||
if (shader != 0) {
|
||||
GLES30.glShaderSource(shader, source);
|
||||
GLES30.glCompileShader(shader);
|
||||
int[] compiled = new int[1];
|
||||
GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compiled, 0);
|
||||
if (compiled[0] == 0) {
|
||||
Log.e(TAG, "Could not compile shader " + shaderType + ":");
|
||||
Log.e(TAG, GLES30.glGetShaderInfoLog(shader));
|
||||
GLES30.glDeleteShader(shader);
|
||||
shader = 0;
|
||||
}
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
public static int createProgram(String vertexSource, String fragmentSource) {
|
||||
int vertexShader = loadShader(GLES30.GL_VERTEX_SHADER, vertexSource);
|
||||
if (vertexShader == 0) {
|
||||
return 0;
|
||||
}
|
||||
checkGLError("vertex shader");
|
||||
|
||||
int pixelShader = loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentSource);
|
||||
if (pixelShader == 0) {
|
||||
return 0;
|
||||
}
|
||||
checkGLError("fragment shader");
|
||||
int program = GLES30.glCreateProgram();
|
||||
if (program != 0) {
|
||||
GLES30.glAttachShader(program, vertexShader);
|
||||
checkGLError("glAttachShader vertexShader");
|
||||
GLES30.glAttachShader(program, pixelShader);
|
||||
checkGLError("glAttachShader pixelShader");
|
||||
GLES30.glLinkProgram(program);
|
||||
GLES30.glDetachShader(program, vertexShader);
|
||||
GLES30.glDetachShader(program, pixelShader);
|
||||
int[] linkStatus = new int[1];
|
||||
GLES30.glGetProgramiv(program, GLES30.GL_LINK_STATUS, linkStatus, 0);
|
||||
if (linkStatus[0] != GLES30.GL_TRUE) {
|
||||
Log.e(TAG, "Could not link program: ");
|
||||
Log.e(TAG, GLES30.glGetProgramInfoLog(program));
|
||||
GLES30.glDeleteProgram(program);
|
||||
program = 0;
|
||||
}
|
||||
}
|
||||
return program;
|
||||
}
|
||||
|
||||
private static final int SIZE_OF_FLOAT = 4;
|
||||
private static final int SIZE_OF_SHORT = 2;
|
||||
public static FloatBuffer createFloatBuffer(float[] array) {
|
||||
ByteBuffer bb = ByteBuffer.allocateDirect(array.length * SIZE_OF_FLOAT);
|
||||
bb.order(ByteOrder.nativeOrder());
|
||||
FloatBuffer fb = bb.asFloatBuffer();
|
||||
fb.put(array);
|
||||
fb.position(0);
|
||||
return fb;
|
||||
}
|
||||
|
||||
public static ShortBuffer createShortBuffer(short[] array) {
|
||||
ByteBuffer bb = ByteBuffer.allocateDirect(array.length * SIZE_OF_SHORT);
|
||||
bb.order(ByteOrder.nativeOrder());
|
||||
ShortBuffer fb = bb.asShortBuffer();
|
||||
fb.put(array);
|
||||
fb.position(0);
|
||||
return fb;
|
||||
}
|
||||
|
||||
public static void checkGLError(String operation) {
|
||||
int errerCode = GLES30.glGetError();
|
||||
if (errerCode != GLES30.GL_NO_ERROR) {
|
||||
String msg = operation + ":error" + errerCode;
|
||||
Log.e(TAG, msg);
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static int createOESTexture() {
|
||||
int[] oesTex = new int[1];
|
||||
GLES30.glGenTextures(1, oesTex, 0);
|
||||
GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, oesTex[0]);
|
||||
GLES30.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR);
|
||||
GLES30.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
|
||||
GLES30.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE);
|
||||
GLES30.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE);
|
||||
GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
|
||||
return oesTex[0];
|
||||
}
|
||||
|
||||
public static class GLQuadScreen {
|
||||
final String quadMeshVertexShader_EXT =
|
||||
" #version 310 es\n in vec4 vertexPosition; \n "
|
||||
+ "in vec2 vertexTexCoord; \n "
|
||||
+ "out vec2 texCoord; \n "
|
||||
+ "uniform mat4 textureMatrix;\n"
|
||||
+ "void main() \n "
|
||||
+ "{"
|
||||
+ " gl_Position = vertexPosition; \n "
|
||||
+ " vec4 temp = vec4(vertexTexCoord.x, vertexTexCoord.y, 0, 1); \n"
|
||||
+ " texCoord = (textureMatrix * temp).xy; \n "
|
||||
+ "}";
|
||||
|
||||
final String quadFragmentShader_EXT =
|
||||
"#version 310 es\n #extension GL_OES_EGL_image_external_essl3 : require \n"
|
||||
+ "precision mediump float; \n"
|
||||
+ "in vec2 texCoord; \n"
|
||||
+ "uniform samplerExternalOES texSampler2D; \n"
|
||||
+ "out vec4 frag_color;\n"
|
||||
+ "void main() \n"
|
||||
+ "{ \n"
|
||||
+ " frag_color = texture(texSampler2D, texCoord); \n"
|
||||
+ "}";
|
||||
float[] orthoQuadVertices = {
|
||||
-1.0f, -1.0f, 0.0f, 1.0f,
|
||||
1.0f, -1.0f, 0.0f, 1.0f,
|
||||
1.0f, 1.0f, 0.0f, 1.0f,
|
||||
-1.0f, 1.0f, 0.0f, 1.0f};
|
||||
|
||||
float[] orthoQuadTexCoords_EXT = {
|
||||
0.0f, 1.0f,
|
||||
1.0f, 1.0f,
|
||||
1.0f, 0.0f,
|
||||
0.0f, 0.0f
|
||||
};
|
||||
|
||||
short[] orthoQuadIndices = {0, 1, 2, 2, 3, 0};
|
||||
|
||||
ShortBuffer indexBuffer;
|
||||
FloatBuffer vetexBuffer;
|
||||
FloatBuffer textureCoordBuffer;
|
||||
|
||||
int program = -1;
|
||||
int vertexHandle = 0;
|
||||
int textureCoordHandle = 0;
|
||||
int textureMatrixHandle = 0;
|
||||
|
||||
public GLQuadScreen() {
|
||||
}
|
||||
|
||||
public void initShader() {
|
||||
if(program == -1) {
|
||||
vetexBuffer = createFloatBuffer(orthoQuadVertices);
|
||||
textureCoordBuffer = createFloatBuffer(orthoQuadTexCoords_EXT);
|
||||
indexBuffer = createShortBuffer(orthoQuadIndices);
|
||||
program = createProgram(quadMeshVertexShader_EXT, quadFragmentShader_EXT);
|
||||
GLES30.glUseProgram(program);
|
||||
vertexHandle = GLES30.glGetAttribLocation(program, "vertexPosition");
|
||||
textureCoordHandle = GLES30.glGetAttribLocation(program, "vertexTexCoord");
|
||||
textureMatrixHandle = GLES30.glGetUniformLocation(program, "textureMatrix");
|
||||
GLES30.glUseProgram(0);
|
||||
}
|
||||
Log.d(TAG, "GLQuadScreen Shader Info:" + program + "," + vertexHandle + "," + textureCoordHandle);
|
||||
}
|
||||
|
||||
public void release() {
|
||||
GLES30.glDeleteProgram(program);
|
||||
program = 0;
|
||||
}
|
||||
|
||||
public void draw(int oesTextureId, float[] videoTransformMatrix) {
|
||||
if(program == -1) {
|
||||
initShader();
|
||||
return;
|
||||
}
|
||||
|
||||
GLES30.glUseProgram(program);
|
||||
GLES30.glVertexAttribPointer(vertexHandle, 4, GLES30.GL_FLOAT, false, 0, vetexBuffer);
|
||||
GLES30.glVertexAttribPointer(textureCoordHandle, 2, GLES30.GL_FLOAT, false, 0, textureCoordBuffer);
|
||||
|
||||
GLES30.glEnableVertexAttribArray(vertexHandle);
|
||||
GLES30.glEnableVertexAttribArray(textureCoordHandle);
|
||||
|
||||
GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
|
||||
GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, oesTextureId);
|
||||
|
||||
GLES30.glUniformMatrix4fv(textureMatrixHandle, 1, false, videoTransformMatrix, 0);
|
||||
|
||||
GLES30.glDrawElements(GLES30.GL_TRIANGLES, indexBuffer.capacity(), GLES30.GL_UNSIGNED_SHORT, indexBuffer);
|
||||
|
||||
GLES30.glDisableVertexAttribArray(vertexHandle);
|
||||
GLES30.glDisableVertexAttribArray(textureCoordHandle);
|
||||
GLES30.glUseProgram(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,484 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
|
||||
*
|
||||
* http://www.cocos.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
package com.cocos.lib.xr;
|
||||
|
||||
import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.opengl.EGL14;
|
||||
import android.opengl.EGLConfig;
|
||||
import android.opengl.EGLContext;
|
||||
import android.opengl.EGLDisplay;
|
||||
import android.opengl.EGLSurface;
|
||||
import android.opengl.GLES30;
|
||||
import android.util.Log;
|
||||
import com.cocos.lib.JsbBridgeWrapper;
|
||||
import com.cocos.lib.xr.permission.CocosXRPermissionHelper;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class CocosXRVideoManager {
|
||||
private static final String TAG = "CocosXRVideoManager";
|
||||
private final static CocosXRVideoManager instance = new CocosXRVideoManager();
|
||||
|
||||
class VideoEventData {
|
||||
public String headTag;
|
||||
public int eventId;
|
||||
public String videoPlayerHandleKey;
|
||||
public int videoSourceType;
|
||||
public String videoSourceUrl;
|
||||
public int videoWidth;
|
||||
public int videoHeight;
|
||||
public int videoTextureId;
|
||||
public boolean isLoop;
|
||||
public int seekToMsec;
|
||||
public String eventName;
|
||||
public float volume;
|
||||
public float playbackSpeed = 1.0f;
|
||||
|
||||
public VideoEventData(String data) {
|
||||
String[] dataArray = data.split("&");
|
||||
if (dataArray[0].equals(XR_VIDEO_EVENT_TAG)) {
|
||||
this.eventId = Integer.parseInt(dataArray[1]);
|
||||
this.eventName = dataArray[2];
|
||||
this.videoPlayerHandleKey = dataArray[3];
|
||||
switch (this.eventId) {
|
||||
case VIDEO_EVENT_PREPARE: {
|
||||
this.videoSourceType = Integer.parseInt(dataArray[4]);
|
||||
this.videoSourceUrl = dataArray[5];
|
||||
this.isLoop = Integer.parseInt(dataArray[6]) == 1;
|
||||
this.volume = Float.parseFloat(dataArray[7]);
|
||||
this.playbackSpeed = Float.parseFloat(dataArray[8]);
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDEO_EVENT_SEEK_TO: {
|
||||
this.seekToMsec = Integer.parseInt(dataArray[4]);
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDEO_EVENT_SET_LOOP: {
|
||||
this.isLoop = Integer.parseInt(dataArray[4]) == 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDEO_EVENT_SET_VOLUME: {
|
||||
this.volume = Float.parseFloat(dataArray[4]);
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDEO_EVENT_SET_TEXTURE_INFO: {
|
||||
this.videoWidth = Integer.parseInt(dataArray[4]);
|
||||
this.videoHeight = Integer.parseInt(dataArray[5]);
|
||||
this.videoTextureId = Integer.parseInt(dataArray[6]);
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDEO_EVENT_SET_SPEED: {
|
||||
this.playbackSpeed = Float.parseFloat(dataArray[4]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CocosXRVideoManager() {
|
||||
}
|
||||
|
||||
public static CocosXRVideoManager getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
final int MAX_COUNT = 3;
|
||||
public static final int VIDEO_SOURCE_TYPE_LOCAL = 1;
|
||||
public static final int VIDEO_SOURCE_TYPE_REMOTE = 2;
|
||||
|
||||
public static final int VIDEO_EVENT_INVALID = 1;
|
||||
//xr-event&id&handle&
|
||||
public static final int VIDEO_EVENT_PREPARE = 2;
|
||||
public static final int VIDEO_EVENT_PLAY = 3;
|
||||
public static final int VIDEO_EVENT_PAUSE = 4;
|
||||
public static final int VIDEO_EVENT_STOP = 5;
|
||||
public static final int VIDEO_EVENT_RESET = 6;
|
||||
public static final int VIDEO_EVENT_DESTROY = 7;
|
||||
|
||||
public static final int VIDEO_EVENT_GET_POSITION = 30;
|
||||
public static final int VIDEO_EVENT_GET_DURATION = 31;
|
||||
public static final int VIDEO_EVENT_GET_IS_PALYING = 32;
|
||||
public static final int VIDEO_EVENT_GET_IS_LOOPING = 33;
|
||||
|
||||
public static final int VIDEO_EVENT_SET_LOOP = 50;
|
||||
public static final int VIDEO_EVENT_SEEK_TO = 51;
|
||||
public static final int VIDEO_EVENT_SET_VOLUME = 52;
|
||||
public static final int VIDEO_EVENT_SET_TEXTURE_INFO = 53;
|
||||
public static final int VIDEO_EVENT_SET_SPEED = 54;
|
||||
|
||||
public static final int VIDEO_EVENT_MEDIA_PLAYER_PREPARED = 100;
|
||||
public static final int VIDEO_EVENT_MEDIA_PLAYER_PLAY_COMPLETE = 101;
|
||||
public static final int VIDEO_EVENT_MEDIA_PLAYER_SEEK_COMPLETE = 102;
|
||||
public static final int VIDEO_EVENT_MEDIA_PLAYER_ERROR = 103;
|
||||
public static final int VIDEO_EVENT_MEDIA_PLAYER_VIDEO_SIZE = 104;
|
||||
public static final int VIDEO_EVENT_MEDIA_PLAYER_ON_INFO = 105;
|
||||
|
||||
private WeakReference<Activity> activityWeakReference;
|
||||
final String XR_VIDEO_PLAYER_EVENT_NAME = "xr-video-player:";
|
||||
//xr-event&handle&id&url&512&512&1
|
||||
final String XR_VIDEO_EVENT_TAG = "xr-event";
|
||||
|
||||
HashMap<String, CocosXRVideoPlayer> xrVideoPlayerHashMap = new HashMap<>();
|
||||
HashMap<String, ArrayList<String>> cachedScriptEventHashMap = new HashMap<>();
|
||||
CocosXRVideoGLThread videoGLThread = null;
|
||||
boolean isPaused = false;
|
||||
public void onCreate(Activity activity) {
|
||||
activityWeakReference = new WeakReference<>(activity);
|
||||
for (int i = 0; i < MAX_COUNT; i++) {
|
||||
JsbBridgeWrapper.getInstance().addScriptEventListener(XR_VIDEO_PLAYER_EVENT_NAME + i, eventData -> {
|
||||
if(isPaused) {
|
||||
return;
|
||||
}
|
||||
processVideoEvent(eventData);
|
||||
});
|
||||
}
|
||||
JsbBridgeWrapper.getInstance().addScriptEventListener(CocosXRPermissionHelper.XR_PERMISSION_EVENT_NAME, CocosXRPermissionHelper::onScriptEvent);
|
||||
|
||||
CocosXRApi.getInstance().onCreate(activity);
|
||||
}
|
||||
|
||||
public void onResume() {
|
||||
Log.d(TAG, "onResume");
|
||||
isPaused = false;
|
||||
if(videoGLThread != null) {
|
||||
videoGLThread.onResume();
|
||||
}
|
||||
Set<Map.Entry<String, ArrayList<String>>> entrySets = cachedScriptEventHashMap.entrySet();
|
||||
for (Map.Entry<String, ArrayList<String>> entrySet : entrySets) {
|
||||
if (entrySet.getKey() != null && entrySet.getValue() != null) {
|
||||
for (String data : entrySet.getValue()) {
|
||||
Log.d(TAG, "onResume.dispatchEventToScript:" + entrySet.getKey() + ":" + data);
|
||||
JsbBridgeWrapper.getInstance().dispatchEventToScript(entrySet.getKey(), data);
|
||||
}
|
||||
}
|
||||
}
|
||||
cachedScriptEventHashMap.clear();
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
Log.d(TAG, "onPause");
|
||||
isPaused = true;
|
||||
if(videoGLThread != null) {
|
||||
videoGLThread.onPause();
|
||||
}
|
||||
|
||||
Set<Map.Entry<String, CocosXRVideoPlayer>> entrySets = xrVideoPlayerHashMap.entrySet();
|
||||
for (Map.Entry<String, CocosXRVideoPlayer> entrySet : entrySets) {
|
||||
if(entrySet.getValue() != null) {
|
||||
entrySet.getValue().pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void sendVideoEvent(VideoEventData videoEventData, String... eventData) {
|
||||
sendVideoEvent(videoEventData.eventId, videoEventData.eventName, videoEventData.videoPlayerHandleKey, eventData);
|
||||
}
|
||||
|
||||
public void sendVideoEvent(int eventId, String eventName, String videoPlayerHandleKey, String... eventData) {
|
||||
StringBuilder data = new StringBuilder("xr-event&" + eventId + "&" + eventName + '&' + videoPlayerHandleKey);
|
||||
if (eventData.length > 0) {
|
||||
for (String evtData : eventData) {
|
||||
data.append("&").append(evtData);
|
||||
}
|
||||
}
|
||||
|
||||
if(videoGLThread == null) return;
|
||||
if(isPaused) {
|
||||
Log.e(TAG, "sendVideoEvent failed, because is paused !!! [" + data + "]");
|
||||
if (!cachedScriptEventHashMap.containsKey(eventName)) {
|
||||
cachedScriptEventHashMap.put(eventName, new ArrayList<>());
|
||||
}
|
||||
Objects.requireNonNull(cachedScriptEventHashMap.get(eventName)).add(data.toString());
|
||||
return;
|
||||
}
|
||||
JsbBridgeWrapper.getInstance().dispatchEventToScript(eventName, data.toString());
|
||||
}
|
||||
|
||||
private void processVideoEvent(String eventData) {
|
||||
VideoEventData videoEventData = new VideoEventData(eventData);
|
||||
if (videoEventData.eventId == VIDEO_EVENT_PREPARE) {
|
||||
CocosXRVideoPlayer videoPlayer = xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey);
|
||||
if (videoPlayer == null) {
|
||||
videoPlayer = new CocosXRVideoPlayer(activityWeakReference, videoEventData.videoPlayerHandleKey, videoEventData.eventName);
|
||||
videoPlayer.prepare(videoEventData);
|
||||
xrVideoPlayerHashMap.put(videoEventData.videoPlayerHandleKey, videoPlayer);
|
||||
} else {
|
||||
videoPlayer.prepare(videoEventData);
|
||||
}
|
||||
|
||||
if (videoGLThread == null) {
|
||||
videoGLThread = new CocosXRVideoGLThread();
|
||||
videoGLThread.start();
|
||||
}
|
||||
} else if (videoEventData.eventId == VIDEO_EVENT_PLAY) {
|
||||
Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).play();
|
||||
} else if (videoEventData.eventId == VIDEO_EVENT_PAUSE) {
|
||||
Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).pause();
|
||||
} else if (videoEventData.eventId == VIDEO_EVENT_STOP) {
|
||||
Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).stop();
|
||||
} else if (videoEventData.eventId == VIDEO_EVENT_RESET) {
|
||||
Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).reset();
|
||||
} else if (videoEventData.eventId == VIDEO_EVENT_DESTROY) {
|
||||
Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).release();
|
||||
videoGLThread.queueEvent(() -> {
|
||||
Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).onGLDestroy();
|
||||
xrVideoPlayerHashMap.remove(videoEventData.videoPlayerHandleKey);
|
||||
});
|
||||
} else if (videoEventData.eventId == VIDEO_EVENT_GET_POSITION) {
|
||||
int position = Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).getCurrentPosition();
|
||||
sendVideoEvent(videoEventData, String.valueOf(position));
|
||||
} else if (videoEventData.eventId == VIDEO_EVENT_GET_DURATION) {
|
||||
int duration = Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).getDuration();
|
||||
sendVideoEvent(videoEventData, String.valueOf(duration));
|
||||
} else if (videoEventData.eventId == VIDEO_EVENT_GET_IS_PALYING) {
|
||||
boolean isPlaying = Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).isPlaying();
|
||||
sendVideoEvent(videoEventData, String.valueOf(isPlaying));
|
||||
} else if (videoEventData.eventId == VIDEO_EVENT_GET_IS_LOOPING) {
|
||||
boolean isLooping = Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).isLooping();
|
||||
sendVideoEvent(videoEventData, String.valueOf(isLooping));
|
||||
} else if (videoEventData.eventId == VIDEO_EVENT_SET_LOOP) {
|
||||
Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).setLooping(videoEventData.isLoop);
|
||||
} else if (videoEventData.eventId == VIDEO_EVENT_SEEK_TO) {
|
||||
Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).seekTo(videoEventData.seekToMsec);
|
||||
} else if(videoEventData.eventId == VIDEO_EVENT_SET_VOLUME) {
|
||||
Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).setVolume(videoEventData.volume);
|
||||
} else if(videoEventData.eventId == VIDEO_EVENT_SET_TEXTURE_INFO) {
|
||||
Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).setTextureInfo(videoEventData.videoWidth, videoEventData.videoHeight, videoEventData.videoTextureId);
|
||||
} else if(videoEventData.eventId == VIDEO_EVENT_SET_SPEED) {
|
||||
Objects.requireNonNull(xrVideoPlayerHashMap.get(videoEventData.videoPlayerHandleKey)).setPlaybackSpeed(videoEventData.playbackSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
Log.d(TAG, "onDestroy:" + xrVideoPlayerHashMap.size());
|
||||
CocosXRApi.getInstance().onDestroy();
|
||||
if(videoGLThread != null) {
|
||||
videoGLThread.onDestroy();
|
||||
videoGLThread = null;
|
||||
}
|
||||
for (int i = 0; i < MAX_COUNT; i++) {
|
||||
JsbBridgeWrapper.getInstance().removeAllListenersForEvent(XR_VIDEO_PLAYER_EVENT_NAME + i);
|
||||
}
|
||||
JsbBridgeWrapper.getInstance().removeAllListenersForEvent(CocosXRPermissionHelper.XR_PERMISSION_EVENT_NAME);
|
||||
Set<Map.Entry<String, CocosXRVideoPlayer>> entrySets = xrVideoPlayerHashMap.entrySet();
|
||||
for (Map.Entry<String, CocosXRVideoPlayer> entrySet : entrySets) {
|
||||
entrySet.getValue().release();
|
||||
}
|
||||
xrVideoPlayerHashMap.clear();
|
||||
cachedScriptEventHashMap.clear();
|
||||
}
|
||||
|
||||
class CocosXRVideoGLThread extends Thread {
|
||||
private final ReentrantLock lockObj = new ReentrantLock(true);
|
||||
private final Condition pauseCondition = lockObj.newCondition();
|
||||
private boolean requestPaused = false;
|
||||
private boolean requestExited = false;
|
||||
long lastTickTime = System.nanoTime();
|
||||
private final ArrayList<Runnable> mEventQueue = new ArrayList<>();
|
||||
|
||||
EGLContext eglContext;
|
||||
EGLDisplay eglDisplay;
|
||||
EGLSurface pBufferSurface;
|
||||
EGLContext parentContext;
|
||||
int renderTargetFboId;
|
||||
|
||||
CocosXRVideoGLThread() {
|
||||
parentContext = EGL14.eglGetCurrentContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
init();
|
||||
while (true) {
|
||||
lockObj.lock();
|
||||
try {
|
||||
if (requestExited) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (requestPaused) {
|
||||
pauseCondition.await();
|
||||
}
|
||||
|
||||
if (requestExited) {
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
lockObj.unlock();
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
if (!mEventQueue.isEmpty()) {
|
||||
Runnable event = mEventQueue.remove(0);
|
||||
if (event != null) {
|
||||
event.run();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tick();
|
||||
}
|
||||
|
||||
exit();
|
||||
}
|
||||
|
||||
void init() {
|
||||
eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
|
||||
int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL14.EGL_NONE};
|
||||
int[] attrList = new int[] {EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT,
|
||||
EGL14.EGL_RENDERABLE_TYPE, 0x00000040,
|
||||
EGL14.EGL_RED_SIZE, 8,
|
||||
EGL14.EGL_GREEN_SIZE, 8,
|
||||
EGL14.EGL_BLUE_SIZE, 8,
|
||||
EGL14.EGL_DEPTH_SIZE, 8,
|
||||
EGL14.EGL_SAMPLE_BUFFERS, 1,
|
||||
EGL14.EGL_SAMPLES, 1,
|
||||
EGL14.EGL_STENCIL_SIZE, 0,
|
||||
EGL14.EGL_NONE};
|
||||
EGLConfig[] configOut = new EGLConfig[1];
|
||||
int[] configNumOut = new int[1];
|
||||
EGL14.eglChooseConfig(eglDisplay, attrList, 0, configOut, 0, 1,
|
||||
configNumOut, 0);
|
||||
eglContext = EGL14.eglCreateContext(eglDisplay, configOut[0], parentContext, attrib_list, 0);
|
||||
|
||||
int[] sur_attrib_list = {EGL14.EGL_WIDTH, 1, EGL14.EGL_HEIGHT, 1, EGL14.EGL_NONE};
|
||||
pBufferSurface = EGL14.eglCreatePbufferSurface(eglDisplay, configOut[0], sur_attrib_list, 0);
|
||||
|
||||
EGL14.eglMakeCurrent(eglDisplay, pBufferSurface, pBufferSurface, eglContext);
|
||||
GLES30.glDisable(GLES30.GL_DEPTH_TEST);
|
||||
GLES30.glDisable(GLES30.GL_BLEND);
|
||||
GLES30.glDisable(GLES30.GL_CULL_FACE);
|
||||
|
||||
int[] tmpFboId = new int[1];
|
||||
GLES30.glGenFramebuffers(1, tmpFboId, 0);
|
||||
renderTargetFboId = tmpFboId[0];
|
||||
CocosXRGLHelper.checkGLError("fbo");
|
||||
|
||||
lastTickTime = System.nanoTime();
|
||||
|
||||
Set<Map.Entry<String, CocosXRVideoPlayer>> entrySets = xrVideoPlayerHashMap.entrySet();
|
||||
for (Map.Entry<String, CocosXRVideoPlayer> entrySet : entrySets) {
|
||||
entrySet.getValue().onGLReady();
|
||||
}
|
||||
Log.d(TAG, "CocosXRVideoGLThread init");
|
||||
}
|
||||
|
||||
void tick() {
|
||||
// draw
|
||||
lastTickTime = System.nanoTime();
|
||||
Set<Map.Entry<String, CocosXRVideoPlayer>> entrySets = xrVideoPlayerHashMap.entrySet();
|
||||
for (Map.Entry<String, CocosXRVideoPlayer> entrySet : entrySets) {
|
||||
entrySet.getValue().onBeforeGLDrawFrame();
|
||||
if(entrySet.getValue().isStopped() || entrySet.getValue().getVideoTextureWidth() == 0 || entrySet.getValue().getVideoTextureHeight() == 0) {
|
||||
continue;
|
||||
}
|
||||
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, renderTargetFboId);
|
||||
GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, entrySet.getValue().getTargetTextureId(), 0);
|
||||
int offsetX = (entrySet.getValue().getVideoTextureWidth() - entrySet.getValue().getVideoSourceWidth()) / 2;
|
||||
int offsetY = (entrySet.getValue().getVideoTextureHeight() - entrySet.getValue().getVideoSourceHeight()) / 2;
|
||||
GLES30.glViewport(offsetX, offsetY, entrySet.getValue().getVideoSourceWidth(), entrySet.getValue().getVideoSourceHeight());
|
||||
GLES30.glScissor(0, 0, entrySet.getValue().getVideoTextureWidth(), entrySet.getValue().getVideoTextureHeight());
|
||||
entrySet.getValue().onGLDrawFrame();
|
||||
}
|
||||
EGL14.eglSwapBuffers(eglDisplay, pBufferSurface);
|
||||
|
||||
if (System.nanoTime() - lastTickTime < 16666666) {
|
||||
try {
|
||||
long sleepTimeNS = 16666666 - (System.nanoTime() - lastTickTime);
|
||||
Thread.sleep(sleepTimeNS / 1000000);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void exit() {
|
||||
Set<Map.Entry<String, CocosXRVideoPlayer>> entrySets = xrVideoPlayerHashMap.entrySet();
|
||||
for (Map.Entry<String, CocosXRVideoPlayer> entrySet : entrySets) {
|
||||
entrySet.getValue().onGLDestroy();
|
||||
}
|
||||
GLES30.glDeleteFramebuffers(1, new int[] {renderTargetFboId}, 0);
|
||||
EGL14.eglMakeCurrent(eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
|
||||
EGL14.eglDestroySurface(eglDisplay, pBufferSurface);
|
||||
EGL14.eglDestroyContext(eglDisplay, eglContext);
|
||||
Log.d(TAG, "CocosXRVideoGLThread exit");
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
lockObj.lock();
|
||||
requestPaused = true;
|
||||
lockObj.unlock();
|
||||
Log.d(TAG, "CocosXRVideoGLThread onPause");
|
||||
}
|
||||
|
||||
public void onResume() {
|
||||
lockObj.lock();
|
||||
requestPaused = false;
|
||||
pauseCondition.signalAll();
|
||||
lockObj.unlock();
|
||||
Log.d(TAG, "CocosXRVideoGLThread onResume");
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
lockObj.lock();
|
||||
requestExited = true;
|
||||
pauseCondition.signalAll();
|
||||
lockObj.unlock();
|
||||
|
||||
try {
|
||||
videoGLThread.join();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, e.getLocalizedMessage());
|
||||
}
|
||||
Log.d(TAG, "CocosXRVideoGLThread onDestroy");
|
||||
}
|
||||
|
||||
public void queueEvent(Runnable r) {
|
||||
synchronized (this) {
|
||||
mEventQueue.add(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
|
||||
*
|
||||
* http://www.cocos.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
package com.cocos.lib.xr;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.Surface;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class CocosXRVideoPlayer {
|
||||
enum MediaPlayerState {
|
||||
IDLE,
|
||||
INITIALIZED,
|
||||
READY_PREPARE,
|
||||
PREPARING,
|
||||
PREPARED,
|
||||
STARTED,
|
||||
STOPPED,
|
||||
PAUSED,
|
||||
END,
|
||||
ERROR,
|
||||
COMPLETED
|
||||
}
|
||||
|
||||
private static final String TAG = "CocosXRVideoPlayer";
|
||||
private String uniqueKey;
|
||||
private String eventName;
|
||||
private CocosXRVideoTexture videoTexture;
|
||||
private String videoSourceUrl;
|
||||
private int videoSourceType;
|
||||
private int videoTextureId = 0;
|
||||
private boolean isGLInitialized = false;
|
||||
private int videoSourceSizeWidth = 0;
|
||||
private int videoSourceSizeHeight = 0;
|
||||
private int videoTextureWidth = 0;
|
||||
private int videoTextureHeight = 0;
|
||||
private MediaPlayerState mediaPlayerState = MediaPlayerState.IDLE;
|
||||
|
||||
CocosXRGLHelper.GLQuadScreen quadScreen;
|
||||
MediaPlayer mediaPlayer;
|
||||
WeakReference<Activity> atyWeakReference;
|
||||
|
||||
public CocosXRVideoPlayer(WeakReference<Activity> activityWeakReference, String key, String eventName) {
|
||||
this.atyWeakReference = activityWeakReference;
|
||||
this.uniqueKey = key;
|
||||
this.eventName = eventName;
|
||||
this.quadScreen = new CocosXRGLHelper.GLQuadScreen();
|
||||
this.mediaPlayer = new MediaPlayer();
|
||||
this.mediaPlayer.setOnErrorListener((mp, what, extra) -> {
|
||||
mediaPlayerState = MediaPlayerState.ERROR;
|
||||
Log.e(TAG, "onError " + what + "," + extra + "." + mp.toString());
|
||||
CocosXRVideoManager.getInstance().sendVideoEvent(CocosXRVideoManager.VIDEO_EVENT_MEDIA_PLAYER_ERROR, eventName, uniqueKey);
|
||||
return false;
|
||||
});
|
||||
this.mediaPlayer.setOnInfoListener((mp, what, extra) -> {
|
||||
// Log.d(TAG, "onInfo " + what + "," + extra + "." + mp.toString());
|
||||
CocosXRVideoManager.getInstance().sendVideoEvent(CocosXRVideoManager.VIDEO_EVENT_MEDIA_PLAYER_ON_INFO, eventName, uniqueKey, String.valueOf(what));
|
||||
return false;
|
||||
});
|
||||
this.mediaPlayer.setOnPreparedListener(mp -> {
|
||||
mediaPlayerState = MediaPlayerState.PREPARED;
|
||||
CocosXRVideoManager.getInstance().sendVideoEvent(CocosXRVideoManager.VIDEO_EVENT_MEDIA_PLAYER_PREPARED, eventName, uniqueKey);
|
||||
CocosXRVideoManager.getInstance().sendVideoEvent(CocosXRVideoManager.VIDEO_EVENT_GET_DURATION, eventName, uniqueKey, String.valueOf(mp.getDuration()));
|
||||
Log.d(TAG, "onPrepared." + mp+ ", getDuration." + mp.getDuration() + "," + mp.getVideoWidth() + "X" + mp.getVideoHeight() + "," + mp.isPlaying());
|
||||
});
|
||||
this.mediaPlayer.setOnCompletionListener(mp -> {
|
||||
Log.d(TAG, "onCompletion." + mp.toString());
|
||||
mediaPlayerState = MediaPlayerState.COMPLETED;
|
||||
CocosXRVideoManager.getInstance().sendVideoEvent(CocosXRVideoManager.VIDEO_EVENT_MEDIA_PLAYER_PLAY_COMPLETE, eventName, uniqueKey);
|
||||
});
|
||||
this.mediaPlayer.setOnSeekCompleteListener(mp -> {
|
||||
// Log.d(TAG, "onSeekComplete." + mp.toString());
|
||||
CocosXRVideoManager.getInstance().sendVideoEvent(CocosXRVideoManager.VIDEO_EVENT_MEDIA_PLAYER_SEEK_COMPLETE, eventName, uniqueKey);
|
||||
});
|
||||
this.mediaPlayer.setOnVideoSizeChangedListener((mp, width, height) -> {
|
||||
Log.d(TAG, "onVideoSizeChanged " + width + "x" + height + "." + mp.toString() + ", isPlaying." + mp.isPlaying());
|
||||
if(videoSourceSizeWidth != width || videoSourceSizeHeight != height) {
|
||||
videoSourceSizeWidth = width;
|
||||
videoSourceSizeHeight = height;
|
||||
CocosXRVideoManager.getInstance().sendVideoEvent(CocosXRVideoManager.VIDEO_EVENT_GET_IS_PALYING, eventName, uniqueKey, String.valueOf(mp.isPlaying() ? 1 : 0));
|
||||
CocosXRVideoManager.getInstance().sendVideoEvent(CocosXRVideoManager.VIDEO_EVENT_MEDIA_PLAYER_VIDEO_SIZE, eventName, uniqueKey, width + "&" + height);
|
||||
}
|
||||
});
|
||||
this.mediaPlayer.setAudioAttributes(new AudioAttributes.Builder().setLegacyStreamType(AudioManager.STREAM_MUSIC).build());
|
||||
mediaPlayerState = MediaPlayerState.INITIALIZED;
|
||||
Log.d(TAG, "constructor() " + key + "|" + eventName + "|" + quadScreen.toString());
|
||||
}
|
||||
|
||||
public void runOnUIThread(Runnable runnable) {
|
||||
if (atyWeakReference != null && atyWeakReference.get() != null) {
|
||||
atyWeakReference.get().runOnUiThread(runnable);
|
||||
} else {
|
||||
Log.e(TAG, "runOnUIThread failed, activity not exist !!!");
|
||||
}
|
||||
}
|
||||
|
||||
public void setTextureInfo(int textureWidth, int textureHeight, int videoTextureId) {
|
||||
this.videoTextureId = videoTextureId;
|
||||
this.videoTextureWidth = textureWidth;
|
||||
this.videoTextureHeight = textureHeight;
|
||||
Log.d(TAG, "setTextureInfo." + textureWidth + "x" + textureHeight + ":" + videoTextureId);
|
||||
}
|
||||
|
||||
public void prepare(CocosXRVideoManager.VideoEventData data) {
|
||||
if (this.videoTexture == null) {
|
||||
this.videoTexture = new CocosXRVideoTexture();
|
||||
}
|
||||
this.videoSourceType = data.videoSourceType;
|
||||
this.videoSourceUrl = data.videoSourceUrl;
|
||||
if(TextUtils.isEmpty(this.videoSourceUrl)) {
|
||||
Log.w(TAG, "prepare failed, because video source is empty !!!");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (data.videoSourceType == CocosXRVideoManager.VIDEO_SOURCE_TYPE_LOCAL) {
|
||||
AssetFileDescriptor afd = atyWeakReference.get().getResources().getAssets().openFd(data.videoSourceUrl);
|
||||
mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
|
||||
} else {
|
||||
mediaPlayer.setDataSource(data.videoSourceUrl);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, e.getLocalizedMessage());
|
||||
}
|
||||
|
||||
mediaPlayer.setLooping(data.isLoop);
|
||||
mediaPlayer.setVolume(data.volume, data.volume);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
try {
|
||||
mediaPlayer.setPlaybackParams(mediaPlayer.getPlaybackParams().setSpeed(data.playbackSpeed));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, "prepare:" + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
mediaPlayerState = MediaPlayerState.READY_PREPARE;
|
||||
if (isGLInitialized) {
|
||||
runOnUIThread(() -> {
|
||||
if(mediaPlayerState == MediaPlayerState.READY_PREPARE) {
|
||||
mediaPlayerState = MediaPlayerState.PREPARING;
|
||||
mediaPlayer.prepareAsync();
|
||||
}
|
||||
});
|
||||
}
|
||||
Log.d(TAG, "prepare");
|
||||
}
|
||||
|
||||
public int getTargetTextureId() {
|
||||
return videoTextureId;
|
||||
}
|
||||
|
||||
public int getVideoTextureWidth() {
|
||||
return videoTextureWidth;
|
||||
}
|
||||
|
||||
public int getVideoTextureHeight() {
|
||||
return videoTextureHeight;
|
||||
}
|
||||
|
||||
public int getVideoSourceWidth() {
|
||||
return videoSourceSizeWidth;
|
||||
}
|
||||
|
||||
public int getVideoSourceHeight() {
|
||||
return videoSourceSizeHeight;
|
||||
}
|
||||
|
||||
public String getVideoSourceUrl() {
|
||||
return videoSourceUrl;
|
||||
}
|
||||
|
||||
public int getVideoSourceType() {
|
||||
return videoSourceType;
|
||||
}
|
||||
|
||||
public void onGLReady() {
|
||||
Log.d(TAG, "onGLReady." + this.hashCode());
|
||||
videoTexture.createSurfaceTexture();
|
||||
quadScreen.initShader();
|
||||
Surface surface = new Surface(videoTexture.getSurfaceTexture());
|
||||
mediaPlayer.setSurface(surface);
|
||||
surface.release();
|
||||
isGLInitialized = true;
|
||||
if(mediaPlayerState == MediaPlayerState.READY_PREPARE) {
|
||||
runOnUIThread(() -> {
|
||||
if(mediaPlayerState == MediaPlayerState.READY_PREPARE) {
|
||||
mediaPlayerState = MediaPlayerState.PREPARING;
|
||||
mediaPlayer.prepareAsync();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void onBeforeGLDrawFrame() {
|
||||
if (!isGLInitialized) {
|
||||
onGLReady();
|
||||
}
|
||||
}
|
||||
|
||||
public void onGLDrawFrame() {
|
||||
if (videoTextureId == 0 || videoTextureWidth == 0 || videoTextureHeight == 0) {
|
||||
return;
|
||||
}
|
||||
videoTexture.updateTexture();
|
||||
quadScreen.draw(videoTexture.getOESTextureId(), videoTexture.getVideoMatrix());
|
||||
}
|
||||
|
||||
public void onGLDestroy() {
|
||||
if (videoTexture != null) {
|
||||
videoTexture.release();
|
||||
videoTexture = null;
|
||||
}
|
||||
|
||||
if (quadScreen != null) {
|
||||
quadScreen.release();
|
||||
quadScreen = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void play() {
|
||||
runOnUIThread(() -> {
|
||||
Log.d(TAG, "- start");
|
||||
mediaPlayer.start();
|
||||
mediaPlayerState = MediaPlayerState.STARTED;
|
||||
CocosXRVideoManager.getInstance().sendVideoEvent(CocosXRVideoManager.VIDEO_EVENT_GET_IS_PALYING, eventName, uniqueKey, String.valueOf(mediaPlayer.isPlaying() ? 1 : 0));
|
||||
});
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
if(!mediaPlayer.isPlaying()) return;
|
||||
runOnUIThread(() -> {
|
||||
Log.d(TAG, "- pause");
|
||||
mediaPlayer.pause();
|
||||
mediaPlayerState = MediaPlayerState.PAUSED;
|
||||
CocosXRVideoManager.getInstance().sendVideoEvent(CocosXRVideoManager.VIDEO_EVENT_GET_IS_PALYING, eventName, uniqueKey, String.valueOf(mediaPlayer.isPlaying() ? 1 : 0));
|
||||
});
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
runOnUIThread(() -> {
|
||||
Log.d(TAG, "- stop");
|
||||
mediaPlayer.stop();
|
||||
mediaPlayerState = MediaPlayerState.STOPPED;
|
||||
});
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
runOnUIThread(() -> {
|
||||
Log.d(TAG, "- reset");
|
||||
mediaPlayer.reset();
|
||||
mediaPlayerState = MediaPlayerState.IDLE;
|
||||
});
|
||||
}
|
||||
|
||||
public void release() {
|
||||
runOnUIThread(() -> {
|
||||
if (mediaPlayer != null) {
|
||||
try {
|
||||
if (mediaPlayer.isPlaying()) {
|
||||
mediaPlayer.pause();
|
||||
}
|
||||
mediaPlayer.stop();
|
||||
mediaPlayer.release();
|
||||
mediaPlayer = null;
|
||||
mediaPlayerState = MediaPlayerState.END;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, e.getLocalizedMessage());
|
||||
}
|
||||
Log.d(TAG, "- release");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean isPlaying() {
|
||||
return mediaPlayer != null && mediaPlayer.isPlaying();
|
||||
}
|
||||
|
||||
public boolean isStopped() {
|
||||
return mediaPlayerState == MediaPlayerState.STOPPED;
|
||||
}
|
||||
|
||||
public boolean isLooping() {
|
||||
return mediaPlayer.isLooping();
|
||||
}
|
||||
|
||||
public void setLooping(boolean looping) {
|
||||
runOnUIThread(() -> {
|
||||
Log.d(TAG, "- setLooping." + looping);
|
||||
mediaPlayer.setLooping(looping);
|
||||
});
|
||||
}
|
||||
|
||||
public void setVolume(float volume) {
|
||||
runOnUIThread(() -> mediaPlayer.setVolume(volume, volume));
|
||||
}
|
||||
|
||||
public int getDuration() {
|
||||
return mediaPlayer.getDuration();
|
||||
}
|
||||
|
||||
public int getCurrentPosition() {
|
||||
return mediaPlayer.getCurrentPosition();
|
||||
}
|
||||
|
||||
public void seekTo(int mSec) {
|
||||
runOnUIThread(() -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
mediaPlayer.seekTo(mSec, MediaPlayer.SEEK_CLOSEST);
|
||||
} else {
|
||||
mediaPlayer.seekTo(mSec);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public float getPlaybackSpeed() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
return mediaPlayer.getPlaybackParams().getSpeed();
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
public void setPlaybackSpeed(float speed) {
|
||||
runOnUIThread(() -> {
|
||||
Log.d(TAG, "- setPlaybackSpeed." + speed);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
try {
|
||||
mediaPlayer.setPlaybackParams(mediaPlayer.getPlaybackParams().setSpeed(Math.max(speed, 0.1f)));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
|
||||
*
|
||||
* http://www.cocos.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
package com.cocos.lib.xr;
|
||||
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.opengl.GLES30;
|
||||
import android.opengl.Matrix;
|
||||
|
||||
public class CocosXRVideoTexture implements SurfaceTexture.OnFrameAvailableListener {
|
||||
SurfaceTexture surfaceTexture;
|
||||
private boolean surfaceNeedsUpdate = false;
|
||||
private long videoTimestampNs = -1;
|
||||
private final float[] videoSTMatrix = new float[16];
|
||||
private long lastFrameAvailableTime = 0;
|
||||
|
||||
@Override
|
||||
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
|
||||
surfaceNeedsUpdate = true;
|
||||
lastFrameAvailableTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private int videoOESTextureId;
|
||||
|
||||
public CocosXRVideoTexture() {
|
||||
Matrix.setIdentityM(videoSTMatrix, 0);
|
||||
}
|
||||
|
||||
public SurfaceTexture createSurfaceTexture() {
|
||||
videoOESTextureId = CocosXRGLHelper.createOESTexture();
|
||||
surfaceTexture = new SurfaceTexture(videoOESTextureId);
|
||||
surfaceTexture.setOnFrameAvailableListener(this);
|
||||
return surfaceTexture;
|
||||
}
|
||||
|
||||
public SurfaceTexture getSurfaceTexture() {
|
||||
return surfaceTexture;
|
||||
}
|
||||
|
||||
public int getOESTextureId() {
|
||||
return videoOESTextureId;
|
||||
}
|
||||
|
||||
public float[] getVideoMatrix() {
|
||||
return videoSTMatrix;
|
||||
}
|
||||
|
||||
public long getVideoTimestampNs() {
|
||||
return videoTimestampNs;
|
||||
}
|
||||
|
||||
public synchronized boolean updateTexture() {
|
||||
if (!surfaceNeedsUpdate && System.currentTimeMillis() - lastFrameAvailableTime > 30) {
|
||||
surfaceNeedsUpdate = true;
|
||||
lastFrameAvailableTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
if (surfaceNeedsUpdate) {
|
||||
surfaceTexture.updateTexImage();
|
||||
surfaceTexture.getTransformMatrix(videoSTMatrix);
|
||||
videoTimestampNs = surfaceTexture.getTimestamp();
|
||||
surfaceNeedsUpdate = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isFrameAvailable() {
|
||||
return surfaceNeedsUpdate;
|
||||
}
|
||||
|
||||
public void release() {
|
||||
if (surfaceTexture != null) {
|
||||
surfaceTexture.release();
|
||||
}
|
||||
if (videoOESTextureId != 0) {
|
||||
GLES30.glDeleteTextures(1, new int[] {videoOESTextureId}, 0);
|
||||
videoOESTextureId = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,476 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
|
||||
*
|
||||
* http://www.cocos.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
package com.cocos.lib.xr;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.net.http.SslError;
|
||||
import android.opengl.GLES30;
|
||||
import android.os.Build;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.InputDevice;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
import android.webkit.ClientCertRequest;
|
||||
import android.webkit.CookieManager;
|
||||
import android.webkit.HttpAuthHandler;
|
||||
import android.webkit.PermissionRequest;
|
||||
import android.webkit.SafeBrowsingResponse;
|
||||
import android.webkit.SslErrorHandler;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebResourceError;
|
||||
import android.webkit.WebResourceRequest;
|
||||
import android.webkit.WebResourceResponse;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class CocosXRWebViewContainer extends FrameLayout {
|
||||
private static final String TAG = "CocosXRWebViewContainer";
|
||||
WebView webView;
|
||||
private long lastDrawTime = 0;
|
||||
private CocosXRVideoTexture videoTexture;
|
||||
private Surface webViewSurface;
|
||||
private CocosXRGLHelper.GLQuadScreen quadScreen;
|
||||
private boolean isGLInitialized = false;
|
||||
private int renderTargetFboId;
|
||||
private int videoTextureId = 0;
|
||||
private int videoTextureWidth = 0;
|
||||
private int videoTextureHeight = 0;
|
||||
|
||||
private boolean isKeyDown = false;
|
||||
private long keyDownTime = 0;
|
||||
|
||||
public CocosXRWebViewContainer(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public CocosXRWebViewContainer(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public CocosXRWebViewContainer(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
init();
|
||||
}
|
||||
|
||||
void init() {
|
||||
webView = new WebView(getContext());
|
||||
webView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT, Gravity.NO_GRAVITY));
|
||||
webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
|
||||
|
||||
webView.getSettings().setSupportZoom(false);
|
||||
webView.getSettings().setBuiltInZoomControls(true);
|
||||
webView.getSettings().setTextZoom(100);
|
||||
|
||||
webView.getSettings().setDisplayZoomControls(false);
|
||||
webView.getSettings().setLoadWithOverviewMode(true);
|
||||
webView.getSettings().setUseWideViewPort(true);
|
||||
webView.getSettings().setMediaPlaybackRequiresUserGesture(false);
|
||||
|
||||
webView.getSettings().setJavaScriptEnabled(true);
|
||||
webView.getSettings().setDomStorageEnabled(true);
|
||||
webView.getSettings().setDefaultTextEncodingName("UTF-8");
|
||||
webView.getSettings().setAllowFileAccess(true);
|
||||
webView.getSettings().setAllowContentAccess(true);
|
||||
webView.getSettings().setAllowFileAccessFromFileURLs(true);
|
||||
webView.getSettings().setDatabaseEnabled(true);
|
||||
webView.getSettings().setAppCacheEnabled(true);
|
||||
webView.getSettings().setLoadsImagesAutomatically(true);
|
||||
webView.getSettings().setSupportMultipleWindows(false);
|
||||
webView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);
|
||||
webView.getSettings().setBlockNetworkImage(false);
|
||||
webView.getSettings().setGeolocationEnabled(true);
|
||||
webView.getSettings().setPluginState(WebSettings.PluginState.ON);
|
||||
webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
webView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
|
||||
} else {
|
||||
webView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
webView.getSettings().setSafeBrowsingEnabled(false);
|
||||
}
|
||||
CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);
|
||||
webView.setWebViewClient(new WebViewClient() {
|
||||
|
||||
@Override
|
||||
public void onSafeBrowsingHit(WebView view, WebResourceRequest request, int threatType, SafeBrowsingResponse callback) {
|
||||
Log.d(TAG, "onSafeBrowsingHit:" + threatType + "," + request.getUrl().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
|
||||
return super.shouldInterceptRequest(view, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
|
||||
Log.d(TAG, "onReceivedSslError:" + error.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) {
|
||||
Log.d(TAG, "onReceivedClientCertRequest:" + request.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
|
||||
Log.d(TAG, "onReceivedHttpAuthRequest:" + host + ":" + realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedLoginRequest(WebView view, String realm, String account, String args) {
|
||||
Log.d(TAG, "onReceivedLoginRequest:" + account + ":" + realm + ":" + args);
|
||||
}
|
||||
|
||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
||||
Log.e(TAG, "shouldOverrideUrlLoading failed:" + url);
|
||||
view.reload();
|
||||
return false;
|
||||
}
|
||||
|
||||
view.loadUrl(url);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
Log.e(TAG, "onReceivedError:" + error.getDescription() + "," + error.getErrorCode() + "," + view.getTitle());
|
||||
}
|
||||
super.onReceivedError(view, request, error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
|
||||
Log.e(TAG, "onReceivedHttpError:" + errorResponse.toString());
|
||||
super.onReceivedHttpError(view, request, errorResponse);
|
||||
}
|
||||
});
|
||||
webView.setWebChromeClient(new WebChromeClient() {
|
||||
|
||||
@Override
|
||||
public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
|
||||
Log.d(TAG, "onCreateWindow:" + resultMsg.toString());
|
||||
return super.onCreateWindow(view, isDialog, isUserGesture, resultMsg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCloseWindow(WebView window) {
|
||||
Log.d(TAG, "onCreateWindowonCloseWindow");
|
||||
super.onCloseWindow(window);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedTitle(WebView view, String title) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(WebView view, int newProgress) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShowCustomView(View view, CustomViewCallback callback) {
|
||||
Log.d(TAG, "onShowCustomView");
|
||||
if(callback != null) {
|
||||
view.setVisibility(View.GONE);
|
||||
callback.onCustomViewHidden();
|
||||
}
|
||||
/*ViewParent viewParent = webView.getParent();
|
||||
if (viewParent instanceof ViewGroup) {
|
||||
ViewGroup viewGroup = (ViewGroup)viewParent;
|
||||
if (viewGroup.getChildCount() > 1) {
|
||||
callback.onCustomViewHidden();
|
||||
return;
|
||||
}
|
||||
viewGroup.getChildAt(0).setVisibility(View.GONE);
|
||||
view.setBackgroundColor(0);
|
||||
viewGroup.addView(view);
|
||||
view.setVisibility(View.VISIBLE);
|
||||
}*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHideCustomView() {
|
||||
Log.d(TAG, "onHideCustomView");
|
||||
/*ViewParent viewParent = webView.getParent();
|
||||
if (viewParent instanceof ViewGroup) {
|
||||
ViewGroup viewGroup = (ViewGroup)viewParent;
|
||||
while (viewGroup.getChildCount() > 1)
|
||||
viewGroup.removeViewAt(1);
|
||||
viewGroup.getChildAt(0).setVisibility(View.VISIBLE);
|
||||
}*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPermissionRequest(PermissionRequest request) {
|
||||
String[] resources = request.getResources();
|
||||
ArrayList<String> permissions = new ArrayList<>();
|
||||
for (String resource : resources) {
|
||||
Log.d(TAG, "onPermissionRequest.resource:" + resource);
|
||||
if (resource.equals("android.webkit.resource.AUDIO_CAPTURE")) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (getContext().checkSelfPermission("android.permission.RECORD_AUDIO") != PackageManager.PERMISSION_GRANTED) {
|
||||
permissions.add(resource);
|
||||
}
|
||||
}
|
||||
} else if (resource.equals("android.webkit.resource.PROTECTED_MEDIA_ID")) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (getContext().checkSelfPermission("android.permission.PROTECTED_MEDIA_ID") != PackageManager.PERMISSION_GRANTED) {
|
||||
permissions.add(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(permissions.size() > 0) {
|
||||
String[] names = new String[permissions.size()];
|
||||
permissions.toArray(names);
|
||||
Log.d(TAG, "acquirePermissions:" + Arrays.toString(names));
|
||||
request.grant(names);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
String userAgentString = webView.getSettings().getUserAgentString();
|
||||
Log.d(TAG, "ua:" + userAgentString);
|
||||
|
||||
|
||||
this.quadScreen = new CocosXRGLHelper.GLQuadScreen();
|
||||
videoTexture = new CocosXRVideoTexture();
|
||||
webView.clearFocus();
|
||||
webView.setFocusableInTouchMode(false);
|
||||
addView(webView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
lastDrawTime = System.currentTimeMillis();
|
||||
if (webViewSurface == null && canvas != null) {
|
||||
super.draw(canvas);
|
||||
return;
|
||||
} else if(webViewSurface == null) {
|
||||
return;
|
||||
}
|
||||
//returns canvas attached to gl texture to draw on
|
||||
Canvas glAttachedCanvas;
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
||||
glAttachedCanvas = webViewSurface.lockHardwareCanvas();
|
||||
} else {
|
||||
glAttachedCanvas = webViewSurface.lockCanvas(null);
|
||||
}
|
||||
if (glAttachedCanvas != null) {
|
||||
glAttachedCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
|
||||
glAttachedCanvas.scale(1.0F, 1.0F);
|
||||
glAttachedCanvas.translate(-getScrollX(), -getScrollY());
|
||||
//draw the view to provided canvas
|
||||
super.draw(glAttachedCanvas);
|
||||
}
|
||||
webViewSurface.unlockCanvasAndPost(glAttachedCanvas);
|
||||
if (videoTexture != null) {
|
||||
videoTexture.onFrameAvailable(null);
|
||||
}
|
||||
}
|
||||
|
||||
public void onDrawCheck() {
|
||||
if (webViewSurface != null && System.currentTimeMillis() - lastDrawTime > 16) {
|
||||
post(() -> draw(null));
|
||||
}
|
||||
}
|
||||
|
||||
CocosXRVideoTexture getVideoTexture() {
|
||||
return videoTexture;
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
Log.d(TAG, "destroy." + hashCode());
|
||||
if(webView != null) {
|
||||
webView.removeAllViews();
|
||||
webView.destroy();
|
||||
webView = null;
|
||||
}
|
||||
|
||||
if (webViewSurface != null) {
|
||||
webViewSurface.release();
|
||||
webViewSurface = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setTextureInfo(int textureWidth, int textureHeight, int videoTextureId) {
|
||||
this.videoTextureId = videoTextureId;
|
||||
this.videoTextureWidth = textureWidth;
|
||||
this.videoTextureHeight = textureHeight;
|
||||
}
|
||||
|
||||
public int getTargetTextureId() {
|
||||
return videoTextureId;
|
||||
}
|
||||
|
||||
public int getVideoTextureWidth() {
|
||||
return videoTextureWidth;
|
||||
}
|
||||
|
||||
public int getVideoTextureHeight() {
|
||||
return videoTextureHeight;
|
||||
}
|
||||
|
||||
public void onGLReady() {
|
||||
videoTexture.createSurfaceTexture();
|
||||
videoTexture.getSurfaceTexture().setDefaultBufferSize(this.videoTextureWidth, this.videoTextureHeight);
|
||||
quadScreen.initShader();
|
||||
webViewSurface = new Surface(videoTexture.getSurfaceTexture());
|
||||
isGLInitialized = true;
|
||||
|
||||
int[] tmpFboId = new int[1];
|
||||
GLES30.glGenFramebuffers(1, tmpFboId, 0);
|
||||
renderTargetFboId = tmpFboId[0];
|
||||
CocosXRGLHelper.checkGLError("fbo");
|
||||
Log.d(TAG, "onGLReady." + this.hashCode() + ",oes." + videoTexture.getOESTextureId() + ",fbo." + tmpFboId[0] +
|
||||
"," + this.videoTextureWidth + "x" + this.videoTextureHeight);
|
||||
}
|
||||
|
||||
public void onBeforeGLDrawFrame() {
|
||||
if (videoTextureId == 0 || videoTextureWidth == 0 || videoTextureHeight == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isGLInitialized) {
|
||||
onGLReady();
|
||||
}
|
||||
}
|
||||
|
||||
public void onGLDrawFrame() {
|
||||
if (videoTextureId == 0 || videoTextureWidth == 0 || videoTextureHeight == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, renderTargetFboId);
|
||||
GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, videoTextureId, 0);
|
||||
GLES30.glViewport(0, 0, videoTextureWidth, videoTextureHeight);
|
||||
GLES30.glScissor(0, 0, videoTextureWidth, videoTextureHeight);
|
||||
onDrawCheck();
|
||||
videoTexture.updateTexture();
|
||||
quadScreen.draw(videoTexture.getOESTextureId(), videoTexture.getVideoMatrix());
|
||||
GLES30.glFlush();
|
||||
}
|
||||
|
||||
public void onGLDestroy() {
|
||||
Log.d(TAG, "onGLDestroy." + this.hashCode());
|
||||
if(renderTargetFboId > 0) {
|
||||
GLES30.glDeleteFramebuffers(1, new int[]{renderTargetFboId}, 0);
|
||||
renderTargetFboId = 0;
|
||||
}
|
||||
|
||||
if (videoTexture != null) {
|
||||
videoTexture.release();
|
||||
videoTexture = null;
|
||||
}
|
||||
|
||||
if (quadScreen != null) {
|
||||
quadScreen.release();
|
||||
quadScreen = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void simulateTouchDown(float ux, float uy) {
|
||||
isKeyDown = true;
|
||||
keyDownTime = SystemClock.uptimeMillis();
|
||||
float touchX = ux * getWidth();
|
||||
float touchY = getHeight() - uy * getHeight();
|
||||
post(() -> {
|
||||
MotionEvent motionEvent = MotionEvent.obtain(keyDownTime, SystemClock.uptimeMillis() + 100, MotionEvent.ACTION_DOWN, touchX, touchY, 0);
|
||||
motionEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
|
||||
dispatchTouchEvent(motionEvent);
|
||||
});
|
||||
}
|
||||
|
||||
public void simulateTouchMove(float ux, float uy) {
|
||||
if (isKeyDown) {
|
||||
post(() -> {
|
||||
float touchX = ux * getWidth();
|
||||
float touchY = getHeight() - uy * getHeight();
|
||||
MotionEvent motionEvent = MotionEvent.obtain(keyDownTime, SystemClock.uptimeMillis() + 100, MotionEvent.ACTION_MOVE, touchX, touchY, 0);
|
||||
motionEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
|
||||
dispatchTouchEvent(motionEvent);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void simulateTouchUp(float ux, float uy) {
|
||||
isKeyDown = false;
|
||||
float touchX = ux * getWidth();
|
||||
float touchY = getHeight() - uy * getHeight();
|
||||
post(() -> {
|
||||
MotionEvent motionEvent = MotionEvent.obtain(keyDownTime, SystemClock.uptimeMillis() + 100, MotionEvent.ACTION_UP, touchX, touchY, 0);
|
||||
motionEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
|
||||
dispatchTouchEvent(motionEvent);
|
||||
});
|
||||
}
|
||||
|
||||
public void loadUrl(String url) {
|
||||
if(webView != null) {
|
||||
HashMap<String, String> hashMap = new HashMap<>();
|
||||
hashMap.put("Referer", "https://www.cocos.com/");
|
||||
webView.loadUrl(url, hashMap);
|
||||
}
|
||||
}
|
||||
|
||||
public WebSettings getSettings() {
|
||||
return webView.getSettings();
|
||||
}
|
||||
|
||||
public void goForward() {
|
||||
webView.goForward();
|
||||
}
|
||||
|
||||
public void goBack() {
|
||||
webView.goBack();
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
webView.reload();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,450 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
|
||||
*
|
||||
* http://www.cocos.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
package com.cocos.lib.xr;
|
||||
|
||||
import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.opengl.EGL14;
|
||||
import android.opengl.EGLConfig;
|
||||
import android.opengl.EGLContext;
|
||||
import android.opengl.EGLDisplay;
|
||||
import android.opengl.EGLSurface;
|
||||
import android.opengl.GLES30;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.cocos.lib.JsbBridgeWrapper;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class CocosXRWebViewManager {
|
||||
private static final String TAG = "CocosXRWebViewManager";
|
||||
public static final String XR_WEBVIEW_EVENT_NAME = "xr-webview-";
|
||||
public static final String XR_WEBVIEW_EVENT_TAG_TO_ADD = "to-add";
|
||||
public static final String XR_WEBVIEW_EVENT_TAG_TO_REMOVE = "to-remove";
|
||||
public static final String XR_WEBVIEW_EVENT_TAG_ADDED = "added";
|
||||
public static final String XR_WEBVIEW_EVENT_TAG_REMOVED = "removed";
|
||||
public static final String XR_WEBVIEW_EVENT_TAG_TEXTUREINFO = "textureinfo";
|
||||
public static final String XR_WEBVIEW_EVENT_TAG_HOVER = "hover";
|
||||
public static final String XR_WEBVIEW_EVENT_TAG_CLICK_DOWN = "click-down";
|
||||
public static final String XR_WEBVIEW_EVENT_TAG_CLICK_UP = "click-up";
|
||||
public static final String XR_WEBVIEW_EVENT_TAG_GOFORWARD = "go-forward";
|
||||
public static final String XR_WEBVIEW_EVENT_TAG_GOBACK = "go-back";
|
||||
public static final String XR_WEBVIEW_EVENT_TAG_LOADURL = "load-url";
|
||||
public static final String XR_WEBVIEW_EVENT_TAG_RELOAD = "reload";
|
||||
final int MAX_COUNT = 3;
|
||||
|
||||
ConcurrentHashMap<String, CocosXRWebViewContainer> xrWebViewHashMap = new ConcurrentHashMap<>();
|
||||
private WeakReference<Activity> activityWeakReference;
|
||||
CocosXRWebViewGLThread webViewGLThread;
|
||||
|
||||
private final boolean isMobileUA = false;
|
||||
|
||||
public void createWebView(int webviewId, int textureWidth, int textureHeight, String url) {
|
||||
Log.d(TAG, "createWebView:" + webviewId + "," + url);
|
||||
if (activityWeakReference.get() != null) {
|
||||
activityWeakReference.get().runOnUiThread(() -> {
|
||||
CocosXRWebViewContainer xrWebViewContainer = new CocosXRWebViewContainer(activityWeakReference.get());
|
||||
View decorView = activityWeakReference.get().getWindow().getDecorView();
|
||||
if(decorView instanceof FrameLayout) {
|
||||
FrameLayout parentLayout = (FrameLayout) decorView;
|
||||
FrameLayout.LayoutParams frameLayoutParams = new FrameLayout.LayoutParams(textureWidth, textureHeight);
|
||||
parentLayout.addView(xrWebViewContainer, frameLayoutParams);
|
||||
xrWebViewContainer.setZ(-100 + webviewId);
|
||||
}
|
||||
if (url != null) {
|
||||
if (isMobileUA) {
|
||||
xrWebViewContainer.getSettings().setUserAgentString("Mozilla/5.0 (Linux; Android 11) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.82 Mobile Safari/537.36");
|
||||
} else {
|
||||
xrWebViewContainer.getSettings().setUserAgentString("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36");
|
||||
}
|
||||
xrWebViewContainer.loadUrl(url);
|
||||
}
|
||||
|
||||
xrWebViewHashMap.put(String.valueOf(webviewId), xrWebViewContainer);
|
||||
// notify
|
||||
JsbBridgeWrapper.getInstance().dispatchEventToScript(XR_WEBVIEW_EVENT_NAME.concat(String.valueOf(webviewId)), XR_WEBVIEW_EVENT_TAG_ADDED);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void removeWebView(int webviewId) {
|
||||
if (!xrWebViewHashMap.containsKey(String.valueOf(webviewId))) {
|
||||
return;
|
||||
}
|
||||
Log.d(TAG, "removeWebView:" + webviewId);
|
||||
if (activityWeakReference.get() != null) {
|
||||
activityWeakReference.get().runOnUiThread(() -> {
|
||||
CocosXRWebViewContainer xrWebViewContainer = xrWebViewHashMap.get(String.valueOf(webviewId));
|
||||
if (xrWebViewContainer != null) {
|
||||
View decorView = activityWeakReference.get().getWindow().getDecorView();
|
||||
if(decorView instanceof FrameLayout) {
|
||||
FrameLayout parentLayout = (FrameLayout) decorView;
|
||||
parentLayout.removeView(xrWebViewContainer);
|
||||
}
|
||||
|
||||
xrWebViewContainer.onDestroy();
|
||||
webViewGLThread.queueEvent(() -> {
|
||||
xrWebViewContainer.onGLDestroy();
|
||||
xrWebViewHashMap.remove(String.valueOf(webviewId));
|
||||
});
|
||||
// notify
|
||||
JsbBridgeWrapper.getInstance().dispatchEventToScript(XR_WEBVIEW_EVENT_TAG_REMOVED.concat(String.valueOf(webviewId)));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void goForward(int webviewId) {
|
||||
if (activityWeakReference.get() != null) {
|
||||
activityWeakReference.get().runOnUiThread(() -> {
|
||||
CocosXRWebViewContainer xrWebViewContainer = xrWebViewHashMap.get(String.valueOf(webviewId));
|
||||
if (xrWebViewContainer != null) {
|
||||
xrWebViewContainer.goForward();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void goBack(int webviewId) {
|
||||
if (activityWeakReference.get() != null) {
|
||||
activityWeakReference.get().runOnUiThread(() -> {
|
||||
CocosXRWebViewContainer xrWebViewContainer = xrWebViewHashMap.get(String.valueOf(webviewId));
|
||||
if (xrWebViewContainer != null) {
|
||||
xrWebViewContainer.goBack();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void reload(int webviewId) {
|
||||
if (activityWeakReference.get() != null) {
|
||||
activityWeakReference.get().runOnUiThread(() -> {
|
||||
CocosXRWebViewContainer xrWebViewContainer = xrWebViewHashMap.get(String.valueOf(webviewId));
|
||||
if (xrWebViewContainer != null) {
|
||||
xrWebViewContainer.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void loadUrl(int webviewId, String url) {
|
||||
if (activityWeakReference.get() != null) {
|
||||
activityWeakReference.get().runOnUiThread(() -> {
|
||||
CocosXRWebViewContainer xrWebViewContainer = xrWebViewHashMap.get(String.valueOf(webviewId));
|
||||
if (xrWebViewContainer != null) {
|
||||
if (isMobileUA) {
|
||||
xrWebViewContainer.getSettings().setUserAgentString("Mozilla/5.0 (Linux; Android 11) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.82 Mobile Safari/537.36");
|
||||
} else {
|
||||
xrWebViewContainer.getSettings().setUserAgentString("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36");
|
||||
}
|
||||
xrWebViewContainer.loadUrl(url);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void setTextureInfo(int webViewId, int textureId, int textureWidth, int textureHeight) {
|
||||
Log.d(TAG, "setTextureInfo:" + webViewId + "," + textureWidth + "x" + textureHeight + ":" + textureId);
|
||||
CocosXRWebViewContainer xrWebViewContainer = xrWebViewHashMap.get(String.valueOf(webViewId));
|
||||
if (xrWebViewContainer != null) {
|
||||
xrWebViewContainer.setTextureInfo(textureWidth, textureHeight, textureId);
|
||||
}
|
||||
|
||||
if (webViewGLThread == null) {
|
||||
webViewGLThread = new CocosXRWebViewGLThread();
|
||||
webViewGLThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
public void onCreate(Activity activity) {
|
||||
Log.d(TAG, "onCreate");
|
||||
activityWeakReference = new WeakReference<>(activity);
|
||||
for (int i = 0; i < MAX_COUNT; i++) {
|
||||
String eventName = XR_WEBVIEW_EVENT_NAME.concat(String.valueOf(i));
|
||||
int webviewId = i;
|
||||
JsbBridgeWrapper.getInstance().addScriptEventListener(eventName, arg -> {
|
||||
if (arg == null) {
|
||||
Log.e(TAG, "Invalid arg is null !!!");
|
||||
return;
|
||||
}
|
||||
String[] dataArray = arg.split("&");
|
||||
if (TextUtils.equals(dataArray[0], XR_WEBVIEW_EVENT_TAG_TEXTUREINFO)) {
|
||||
// id&w&h
|
||||
setTextureInfo(webviewId, Integer.parseInt(dataArray[1]), Integer.parseInt(dataArray[2]), Integer.parseInt(dataArray[3]));
|
||||
} else if (TextUtils.equals(dataArray[0], XR_WEBVIEW_EVENT_TAG_TO_ADD)) {
|
||||
int textureWidth = Integer.parseInt(dataArray[1]);
|
||||
int textureHeight = Integer.parseInt(dataArray[2]);
|
||||
String url = dataArray.length > 3 ? dataArray[3] : null;
|
||||
createWebView(webviewId, textureWidth, textureHeight, url);
|
||||
} else if (TextUtils.equals(dataArray[0], XR_WEBVIEW_EVENT_TAG_TO_REMOVE)) {
|
||||
removeWebView(webviewId);
|
||||
} else if (TextUtils.equals(dataArray[0], XR_WEBVIEW_EVENT_TAG_HOVER)) {
|
||||
CocosXRWebViewContainer xrWebViewContainer = xrWebViewHashMap.get(String.valueOf(webviewId));
|
||||
if (xrWebViewContainer != null) {
|
||||
xrWebViewContainer.simulateTouchMove(Float.parseFloat(dataArray[1]), Float.parseFloat(dataArray[2]));
|
||||
}
|
||||
} else if (TextUtils.equals(dataArray[0], XR_WEBVIEW_EVENT_TAG_CLICK_DOWN)) {
|
||||
CocosXRWebViewContainer xrWebViewContainer = xrWebViewHashMap.get(String.valueOf(webviewId));
|
||||
if (xrWebViewContainer != null) {
|
||||
xrWebViewContainer.simulateTouchDown(Float.parseFloat(dataArray[1]), Float.parseFloat(dataArray[2]));
|
||||
}
|
||||
} else if (TextUtils.equals(dataArray[0], XR_WEBVIEW_EVENT_TAG_CLICK_UP)) {
|
||||
CocosXRWebViewContainer xrWebViewContainer = xrWebViewHashMap.get(String.valueOf(webviewId));
|
||||
if (xrWebViewContainer != null) {
|
||||
xrWebViewContainer.simulateTouchUp(Float.parseFloat(dataArray[1]), Float.parseFloat(dataArray[2]));
|
||||
}
|
||||
} else if (TextUtils.equals(dataArray[0], XR_WEBVIEW_EVENT_TAG_LOADURL)) {
|
||||
loadUrl(webviewId, dataArray[1]);
|
||||
} else if (TextUtils.equals(dataArray[0], XR_WEBVIEW_EVENT_TAG_RELOAD)) {
|
||||
reload(webviewId);
|
||||
} else if (TextUtils.equals(dataArray[0], XR_WEBVIEW_EVENT_TAG_GOBACK)) {
|
||||
goBack(webviewId);
|
||||
} else if (TextUtils.equals(dataArray[0], XR_WEBVIEW_EVENT_TAG_GOFORWARD)) {
|
||||
goForward(webviewId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void onResume() {
|
||||
Log.d(TAG, "onResume");
|
||||
if (webViewGLThread != null) {
|
||||
webViewGLThread.onResume();
|
||||
}
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
Log.d(TAG, "onPause");
|
||||
if (webViewGLThread != null) {
|
||||
webViewGLThread.onPause();
|
||||
}
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
Log.d(TAG, "onDestroy");
|
||||
if (webViewGLThread != null) {
|
||||
webViewGLThread.onDestroy();
|
||||
webViewGLThread = null;
|
||||
}
|
||||
for (int i = 0; i < MAX_COUNT; i++) {
|
||||
JsbBridgeWrapper.getInstance().removeAllListenersForEvent(XR_WEBVIEW_EVENT_NAME.concat(String.valueOf(i)));
|
||||
}
|
||||
Set<Map.Entry<String, CocosXRWebViewContainer>> entrySets = xrWebViewHashMap.entrySet();
|
||||
for (Map.Entry<String, CocosXRWebViewContainer> entrySet : entrySets) {
|
||||
CocosXRWebViewContainer xrWebViewContainer = entrySet.getValue();
|
||||
if (activityWeakReference.get() != null) {
|
||||
View decorView = activityWeakReference.get().getWindow().getDecorView();
|
||||
if(decorView instanceof FrameLayout) {
|
||||
FrameLayout parentLayout = (FrameLayout) decorView;
|
||||
parentLayout.removeView(xrWebViewContainer);
|
||||
}
|
||||
}
|
||||
xrWebViewContainer.onDestroy();
|
||||
}
|
||||
xrWebViewHashMap.clear();
|
||||
}
|
||||
|
||||
class CocosXRWebViewGLThread extends Thread {
|
||||
private final ReentrantLock lockObj = new ReentrantLock(true);
|
||||
private final Condition pauseCondition = lockObj.newCondition();
|
||||
private boolean running = false;
|
||||
private boolean requestPaused = false;
|
||||
private boolean requestExited = false;
|
||||
long lastTickTime = System.nanoTime();
|
||||
private final ArrayList<Runnable> mEventQueue = new ArrayList<>();
|
||||
|
||||
EGLContext eglContext;
|
||||
EGLDisplay eglDisplay;
|
||||
EGLSurface pBufferSurface;
|
||||
EGLContext parentContext;
|
||||
|
||||
CocosXRWebViewGLThread() {
|
||||
parentContext = EGL14.eglGetCurrentContext();
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
init();
|
||||
while (true) {
|
||||
lockObj.lock();
|
||||
try {
|
||||
if (requestExited) {
|
||||
running = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (requestPaused) {
|
||||
running = false;
|
||||
pauseCondition.await();
|
||||
running = true;
|
||||
}
|
||||
|
||||
if (requestExited) {
|
||||
running = false;
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
lockObj.unlock();
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
if (!mEventQueue.isEmpty()) {
|
||||
Runnable event = mEventQueue.remove(0);
|
||||
if (event != null) {
|
||||
event.run();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tick();
|
||||
}
|
||||
|
||||
exit();
|
||||
}
|
||||
|
||||
void init() {
|
||||
running = true;
|
||||
eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
|
||||
int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL14.EGL_NONE};
|
||||
int[] attrList = new int[]{EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT,
|
||||
EGL14.EGL_RENDERABLE_TYPE, 0x00000040,
|
||||
EGL14.EGL_RED_SIZE, 8,
|
||||
EGL14.EGL_GREEN_SIZE, 8,
|
||||
EGL14.EGL_BLUE_SIZE, 8,
|
||||
EGL14.EGL_DEPTH_SIZE, 0,
|
||||
EGL14.EGL_SAMPLE_BUFFERS, 1,
|
||||
EGL14.EGL_SAMPLES, 1,
|
||||
EGL14.EGL_STENCIL_SIZE, 0,
|
||||
EGL14.EGL_NONE};
|
||||
EGLConfig[] configOut = new EGLConfig[1];
|
||||
int[] configNumOut = new int[1];
|
||||
EGL14.eglChooseConfig(eglDisplay, attrList, 0, configOut, 0, 1,
|
||||
configNumOut, 0);
|
||||
eglContext = EGL14.eglCreateContext(eglDisplay, configOut[0], parentContext, attrib_list, 0);
|
||||
|
||||
int[] sur_attrib_list = {EGL14.EGL_WIDTH, 1, EGL14.EGL_HEIGHT, 1, EGL14.EGL_NONE};
|
||||
pBufferSurface = EGL14.eglCreatePbufferSurface(eglDisplay, configOut[0], sur_attrib_list, 0);
|
||||
|
||||
EGL14.eglMakeCurrent(eglDisplay, pBufferSurface, pBufferSurface, eglContext);
|
||||
GLES30.glDisable(GLES30.GL_DEPTH_TEST);
|
||||
GLES30.glDisable(GLES30.GL_BLEND);
|
||||
GLES30.glDisable(GLES30.GL_CULL_FACE);
|
||||
|
||||
lastTickTime = System.nanoTime();
|
||||
Log.d(TAG, "CocosXRWebViewGLThread init");
|
||||
}
|
||||
|
||||
void tick() {
|
||||
// draw
|
||||
lastTickTime = System.nanoTime();
|
||||
|
||||
Set<Map.Entry<String, CocosXRWebViewContainer>> entrySets = xrWebViewHashMap.entrySet();
|
||||
for (Map.Entry<String, CocosXRWebViewContainer> entrySet : entrySets) {
|
||||
entrySet.getValue().onBeforeGLDrawFrame();
|
||||
if(entrySet.getValue().getVideoTextureWidth() == 0 || entrySet.getValue().getVideoTextureHeight() == 0) {
|
||||
continue;
|
||||
}
|
||||
entrySet.getValue().onGLDrawFrame();
|
||||
}
|
||||
|
||||
EGL14.eglSwapBuffers(eglDisplay, pBufferSurface);
|
||||
|
||||
if (System.nanoTime() - lastTickTime < 16666666) {
|
||||
// lock 60fps
|
||||
try {
|
||||
long sleepTimeNS = 16666666 - (System.nanoTime() - lastTickTime);
|
||||
Thread.sleep(sleepTimeNS / 1000000);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void exit() {
|
||||
running = false;
|
||||
Set<Map.Entry<String, CocosXRWebViewContainer>> entrySets = xrWebViewHashMap.entrySet();
|
||||
for (Map.Entry<String, CocosXRWebViewContainer> entrySet : entrySets) {
|
||||
entrySet.getValue().onGLDestroy();
|
||||
}
|
||||
EGL14.eglMakeCurrent(eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
|
||||
EGL14.eglDestroySurface(eglDisplay, pBufferSurface);
|
||||
EGL14.eglDestroyContext(eglDisplay, eglContext);
|
||||
Log.d(TAG, "CocosXRWebViewGLThread exit");
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
lockObj.lock();
|
||||
requestPaused = true;
|
||||
lockObj.unlock();
|
||||
Log.d(TAG, "CocosXRWebViewGLThread onPause");
|
||||
}
|
||||
|
||||
public void onResume() {
|
||||
lockObj.lock();
|
||||
requestPaused = false;
|
||||
pauseCondition.signalAll();
|
||||
lockObj.unlock();
|
||||
Log.d(TAG, "CocosXRWebViewGLThread onResume");
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
lockObj.lock();
|
||||
requestExited = true;
|
||||
pauseCondition.signalAll();
|
||||
lockObj.unlock();
|
||||
|
||||
try {
|
||||
join();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, e.getLocalizedMessage());
|
||||
}
|
||||
Log.d(TAG, "CocosXRWebViewGLThread onDestroy");
|
||||
}
|
||||
|
||||
public void queueEvent(Runnable r) {
|
||||
synchronized (this) {
|
||||
mEventQueue.add(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
|
||||
*
|
||||
* http://www.cocos.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
package com.cocos.lib.xr.permission;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.app.FragmentTransaction;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class CocosXRPermissionFragment extends Fragment {
|
||||
private static final int PERMISSION_REQUEST_CODE = 1101;
|
||||
private static final String TAG = "CocosPermissionFragment";
|
||||
private static final String PERMISSION_TAG = "TAG_PermissionFragment";
|
||||
|
||||
public static CocosXRPermissionFragment getInstance(Activity activity) {
|
||||
FragmentManager fm = activity.getFragmentManager();
|
||||
CocosXRPermissionFragment fragment = (CocosXRPermissionFragment) fm.findFragmentByTag(PERMISSION_TAG);
|
||||
if (fragment == null) {
|
||||
try {
|
||||
Log.d(TAG, "Creating CocosXRPermissionFragment");
|
||||
fragment = new CocosXRPermissionFragment();
|
||||
FragmentTransaction trans = fm.beginTransaction();
|
||||
trans.add(fragment, PERMISSION_TAG);
|
||||
trans.commit();
|
||||
fm.executePendingTransactions();
|
||||
} catch (Throwable th) {
|
||||
Log.e(TAG, "Cannot launch PermissionFragment:" + th.getMessage(), th);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setRetainInstance(true);
|
||||
Log.d(TAG, "onCreate");
|
||||
}
|
||||
|
||||
public void acquirePermissions(String[] permissions) {
|
||||
Handler handler = new Handler(Looper.getMainLooper());
|
||||
handler.post(() -> {
|
||||
final int[] grantResults = new int[permissions.length];
|
||||
Activity context = getActivity();
|
||||
if (context != null) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
String packageName = context.getPackageName();
|
||||
for (String permission : permissions) {
|
||||
if (PackageManager.PERMISSION_DENIED == packageManager.checkPermission(permission, packageName)) {
|
||||
CocosXRPermissionFragment.this.requestPermissions(permissions, PERMISSION_REQUEST_CODE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Arrays.fill(grantResults, PackageManager.PERMISSION_GRANTED);
|
||||
onRequestPermissionsResult(PERMISSION_REQUEST_CODE, permissions, grantResults);
|
||||
} else {
|
||||
Log.e(TAG, "acquirePermissions failed !");
|
||||
Arrays.fill(grantResults, PackageManager.PERMISSION_DENIED);
|
||||
onRequestPermissionsResult(PERMISSION_REQUEST_CODE, permissions, grantResults);
|
||||
}
|
||||
} else {
|
||||
Arrays.fill(grantResults, PackageManager.PERMISSION_DENIED);
|
||||
onRequestPermissionsResult(PERMISSION_REQUEST_CODE, permissions, grantResults);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||
if (requestCode == PERMISSION_REQUEST_CODE && permissions.length > 0) {
|
||||
CocosXRPermissionHelper.onAcquirePermissions(permissions, grantResults);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/****************************************************************************
|
||||
* Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
|
||||
*
|
||||
* http://www.cocos.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
package com.cocos.lib.xr.permission;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Process;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import com.cocos.lib.CocosHelper;
|
||||
import com.cocos.lib.JsbBridgeWrapper;
|
||||
import com.cocos.lib.xr.CocosXRApi;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class CocosXRPermissionHelper {
|
||||
private static final String LOG_TAG = "CocosXRPermissionHelper";
|
||||
public static final String XR_PERMISSION_EVENT_NAME = "xr-permission";
|
||||
public static final String XR_PERMISSION_TAG_CHECK = "check";
|
||||
public static final String XR_PERMISSION_TAG_REQUEST = "request";
|
||||
public interface PermissionCallback {
|
||||
void onRequestPermissionsResult(String[] permissions, int[] grantResults);
|
||||
}
|
||||
|
||||
private static PermissionCallback permissionCallback = null;
|
||||
public static void onScriptEvent(String arg) {
|
||||
String[] array = arg.split(":");
|
||||
if (TextUtils.equals(array[0], XR_PERMISSION_TAG_CHECK)) {
|
||||
int result = checkPermission(array[1]) ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED;
|
||||
CocosHelper.runOnGameThread(() -> JsbBridgeWrapper.getInstance().dispatchEventToScript(XR_PERMISSION_EVENT_NAME, XR_PERMISSION_TAG_CHECK + ":" + array[1] + ":" + result));
|
||||
} else if (TextUtils.equals(array[0], XR_PERMISSION_TAG_REQUEST)) {
|
||||
String[] permissionNames = array[1].split("&");
|
||||
acquirePermissions(permissionNames, (PermissionCallback) null);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean checkPermission(String permission) {
|
||||
Context context = CocosXRApi.getInstance().getContext();
|
||||
if (context == null)
|
||||
return false;
|
||||
if (context.checkPermission(permission, android.os.Process.myPid(), Process.myUid()) == PackageManager.PERMISSION_GRANTED) {
|
||||
Log.d(LOG_TAG, "checkPermission: " + permission + " has granted");
|
||||
return true;
|
||||
} else {
|
||||
Log.d(LOG_TAG, "checkPermission: " + permission + " has not granted");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void acquirePermissions(String[] permissions, PermissionCallback callback) {
|
||||
permissionCallback = callback;
|
||||
CocosXRPermissionHelper.acquirePermissions(permissions, CocosXRApi.getInstance().getActivity());
|
||||
}
|
||||
|
||||
public static void acquirePermissions(String[] permissions, Activity InActivity) {
|
||||
if (InActivity == null)
|
||||
return;
|
||||
final Activity activity = InActivity;
|
||||
activity.runOnUiThread(() -> {
|
||||
CocosXRPermissionFragment fragment = CocosXRPermissionFragment.getInstance(activity);
|
||||
if (fragment != null) {
|
||||
fragment.acquirePermissions(permissions);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void onAcquirePermissions(String[] permissions, int[] grantResults) {
|
||||
Log.d(LOG_TAG, "onAcquirePermissions:" + Arrays.toString(permissions) + "|" + Arrays.toString(grantResults));
|
||||
//
|
||||
CocosHelper.runOnGameThread(() -> {
|
||||
StringBuilder stringBuilder = new StringBuilder(XR_PERMISSION_TAG_REQUEST);
|
||||
stringBuilder.append(":");
|
||||
for (int i = 0; i < permissions.length; i++) {
|
||||
stringBuilder.append(permissions[i]).append("#").append(grantResults[i]);
|
||||
if (i != permissions.length - 1) {
|
||||
stringBuilder.append("&");
|
||||
}
|
||||
}
|
||||
JsbBridgeWrapper.getInstance().dispatchEventToScript(XR_PERMISSION_EVENT_NAME, stringBuilder.toString());
|
||||
});
|
||||
if (permissionCallback != null) {
|
||||
permissionCallback.onRequestPermissionsResult(permissions, grantResults);
|
||||
}
|
||||
}
|
||||
}
|
||||
46
cocos/platform/android/modules/Screen.cpp
Normal file
46
cocos/platform/android/modules/Screen.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "platform/android/modules/Screen.h"
|
||||
#include <android/sensor.h>
|
||||
#include <android/window.h>
|
||||
#include <android_native_app_glue.h>
|
||||
#include "platform/java/jni/JniHelper.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
int Screen::getDPI() const {
|
||||
static int dpi = -1;
|
||||
if (dpi == -1) {
|
||||
AConfiguration *config = AConfiguration_new();
|
||||
//AConfiguration_fromAssetManager(config, cocosApp.assetManager);
|
||||
int32_t density = AConfiguration_getDensity(config);
|
||||
AConfiguration_delete(config);
|
||||
const int stdDpi = 160;
|
||||
dpi = density * stdDpi;
|
||||
}
|
||||
return dpi;
|
||||
}
|
||||
|
||||
} // namespace cc
|
||||
36
cocos/platform/android/modules/Screen.h
Normal file
36
cocos/platform/android/modules/Screen.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "platform/java/modules/CommonScreen.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
class CC_DLL Screen : public CommonScreen {
|
||||
public:
|
||||
int getDPI() const override;
|
||||
};
|
||||
|
||||
} // namespace cc
|
||||
36
cocos/platform/android/modules/System.cpp
Normal file
36
cocos/platform/android/modules/System.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "platform/android/modules/System.h"
|
||||
|
||||
namespace cc {
|
||||
System::System() = default;
|
||||
|
||||
System::~System() = default;
|
||||
|
||||
System::OSType System::getOSType() const {
|
||||
return OSType::ANDROIDOS;
|
||||
}
|
||||
|
||||
} // namespace cc
|
||||
41
cocos/platform/android/modules/System.h
Normal file
41
cocos/platform/android/modules/System.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "platform/java/modules/CommonSystem.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
class CC_DLL System : public CommonSystem {
|
||||
public:
|
||||
System();
|
||||
~System() override;
|
||||
|
||||
OSType getOSType() const override;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
} // namespace cc
|
||||
68
cocos/platform/apple/FileUtils-apple.h
Normal file
68
cocos/platform/apple/FileUtils-apple.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cocos2d-x.org
|
||||
Copyright (c) 2011 Zynga Inc.
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "base/std/container/string.h"
|
||||
|
||||
#include "base/Macros.h"
|
||||
#include "platform/FileUtils.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
//! @brief Helper class to handle file operations
|
||||
class CC_DLL FileUtilsApple : public FileUtils {
|
||||
public:
|
||||
FileUtilsApple();
|
||||
~FileUtilsApple() override = default;
|
||||
/* override functions */
|
||||
ccstd::string getWritablePath() const override;
|
||||
ccstd::string getFullPathForDirectoryAndFilename(const ccstd::string &directory, const ccstd::string &filename) const override;
|
||||
|
||||
ValueMap getValueMapFromFile(const ccstd::string &filename) override;
|
||||
ValueMap getValueMapFromData(const char *filedata, int filesize) override;
|
||||
bool writeToFile(const ValueMap &dict, const ccstd::string &fullPath) override;
|
||||
|
||||
ValueVector getValueVectorFromFile(const ccstd::string &filename) override;
|
||||
#if CC_FILEUTILS_APPLE_ENABLE_OBJC
|
||||
void setBundle(NSBundle *bundle);
|
||||
#endif
|
||||
|
||||
bool createDirectory(const ccstd::string &path) override;
|
||||
|
||||
private:
|
||||
bool isFileExistInternal(const ccstd::string &filePath) const override;
|
||||
bool removeDirectory(const ccstd::string &dirPath) override;
|
||||
void valueMapCompact(ValueMap &valueMap) override;
|
||||
void valueVectorCompact(ValueVector &valueVector) override;
|
||||
|
||||
struct IMPL;
|
||||
std::unique_ptr<IMPL> pimpl_;
|
||||
};
|
||||
|
||||
} // namespace cc
|
||||
438
cocos/platform/apple/FileUtils-apple.mm
Normal file
438
cocos/platform/apple/FileUtils-apple.mm
Normal file
@@ -0,0 +1,438 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2010-2012 cc-x.org
|
||||
Copyright (c) 2011 Zynga Inc.
|
||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2022 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated engine source code (the "Software"), a limited,
|
||||
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
||||
to use Cocos Creator solely to develop games on your target platforms. You shall
|
||||
not use Cocos Creator software for developing other software or tools that's
|
||||
used for developing games. You are not granted to publish, distribute,
|
||||
sublicense, and/or sell copies of Cocos Creator.
|
||||
|
||||
The software or tools in this License Agreement are licensed, not sold.
|
||||
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include "platform/apple/FileUtils-apple.h"
|
||||
|
||||
#include <ftw.h>
|
||||
#include <stack>
|
||||
#include "base/Log.h"
|
||||
#include "base/memory/Memory.h"
|
||||
#include "base/std/container/string.h"
|
||||
#include "platform/FileUtils.h"
|
||||
#include "platform/SAXParser.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
struct FileUtilsApple::IMPL {
|
||||
IMPL(NSBundle *bundle) : bundle_([NSBundle mainBundle]) {}
|
||||
void setBundle(NSBundle *bundle) {
|
||||
bundle_ = bundle;
|
||||
}
|
||||
NSBundle *getBundle() const {
|
||||
return bundle_;
|
||||
}
|
||||
|
||||
private:
|
||||
NSBundle *bundle_;
|
||||
};
|
||||
|
||||
static id convertCCValueToNSObject(const cc::Value &value);
|
||||
static cc::Value convertNSObjectToCCValue(id object);
|
||||
|
||||
static void addNSObjectToCCMap(id nsKey, id nsValue, ValueMap &dict);
|
||||
static void addCCValueToNSDictionary(const ccstd::string &key, const Value &value, NSMutableDictionary *dict);
|
||||
static void addNSObjectToCCVector(id item, ValueVector &array);
|
||||
static void addCCValueToNSArray(const Value &value, NSMutableArray *array);
|
||||
|
||||
static id convertCCValueToNSObject(const cc::Value &value) {
|
||||
switch (value.getType()) {
|
||||
case Value::Type::NONE:
|
||||
return [NSNull null];
|
||||
|
||||
case Value::Type::STRING:
|
||||
return [NSString stringWithCString:value.asString().c_str() encoding:NSUTF8StringEncoding];
|
||||
|
||||
case Value::Type::BYTE:
|
||||
return [NSNumber numberWithInt:value.asByte()];
|
||||
|
||||
case Value::Type::INTEGER:
|
||||
return [NSNumber numberWithInt:value.asInt()];
|
||||
|
||||
case Value::Type::UNSIGNED:
|
||||
return [NSNumber numberWithUnsignedInt:value.asUnsignedInt()];
|
||||
|
||||
case Value::Type::FLOAT:
|
||||
return [NSNumber numberWithFloat:value.asFloat()];
|
||||
|
||||
case Value::Type::DOUBLE:
|
||||
return [NSNumber numberWithDouble:value.asDouble()];
|
||||
|
||||
case Value::Type::BOOLEAN:
|
||||
return [NSNumber numberWithBool:value.asBool()];
|
||||
|
||||
case Value::Type::VECTOR: {
|
||||
NSMutableArray *array = [NSMutableArray array];
|
||||
const ValueVector &vector = value.asValueVector();
|
||||
for (const auto &e : vector) {
|
||||
addCCValueToNSArray(e, array);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
case Value::Type::MAP: {
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
const ValueMap &map = value.asValueMap();
|
||||
for (auto iter = map.begin(); iter != map.end(); ++iter) {
|
||||
addCCValueToNSDictionary(iter->first, iter->second, dictionary);
|
||||
}
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
case Value::Type::INT_KEY_MAP:
|
||||
break;
|
||||
}
|
||||
|
||||
return [NSNull null];
|
||||
}
|
||||
|
||||
static cc::Value convertNSObjectToCCValue(id item) {
|
||||
// add string value into array
|
||||
if ([item isKindOfClass:[NSString class]]) {
|
||||
return Value([item UTF8String]);
|
||||
}
|
||||
|
||||
// add number value into array(such as int, float, bool and so on)
|
||||
// the value is a number
|
||||
if ([item isKindOfClass:[NSNumber class]]) {
|
||||
NSNumber *num = item;
|
||||
const char *numType = [num objCType];
|
||||
if (num == (void *)kCFBooleanFalse || num == (void *)kCFBooleanTrue) {
|
||||
bool v = [num boolValue];
|
||||
return Value(v);
|
||||
} else if (strcmp(numType, @encode(float)) == 0) {
|
||||
return Value([num floatValue]);
|
||||
} else if (strcmp(numType, @encode(double)) == 0) {
|
||||
return Value([num doubleValue]);
|
||||
} else {
|
||||
return Value([num intValue]);
|
||||
}
|
||||
}
|
||||
|
||||
// add dictionary value into array
|
||||
if ([item isKindOfClass:[NSDictionary class]]) {
|
||||
ValueMap dict;
|
||||
for (id subKey in [item allKeys]) {
|
||||
id subValue = [item objectForKey:subKey];
|
||||
addNSObjectToCCMap(subKey, subValue, dict);
|
||||
}
|
||||
|
||||
return Value(dict);
|
||||
}
|
||||
|
||||
// add array value into array
|
||||
if ([item isKindOfClass:[NSArray class]]) {
|
||||
ValueVector subArray;
|
||||
for (id subItem in item) {
|
||||
addNSObjectToCCVector(subItem, subArray);
|
||||
}
|
||||
return Value(subArray);
|
||||
}
|
||||
|
||||
return Value::VALUE_NULL;
|
||||
}
|
||||
|
||||
static void addNSObjectToCCVector(id item, ValueVector &array) {
|
||||
array.push_back(convertNSObjectToCCValue(item));
|
||||
}
|
||||
|
||||
static void addCCValueToNSArray(const Value &value, NSMutableArray *array) {
|
||||
[array addObject:convertCCValueToNSObject(value)];
|
||||
}
|
||||
|
||||
static void addNSObjectToCCMap(id nsKey, id nsValue, ValueMap &dict) {
|
||||
// the key must be a string
|
||||
CC_ASSERT([nsKey isKindOfClass:[NSString class]]);
|
||||
ccstd::string key = [nsKey UTF8String];
|
||||
dict[key] = convertNSObjectToCCValue(nsValue);
|
||||
}
|
||||
|
||||
static void addCCValueToNSDictionary(const ccstd::string &key, const Value &value, NSMutableDictionary *dict) {
|
||||
NSString *NSkey = [NSString stringWithCString:key.c_str() encoding:NSUTF8StringEncoding];
|
||||
[dict setObject:convertCCValueToNSObject(value) forKey:NSkey];
|
||||
}
|
||||
|
||||
FileUtils *createFileUtils() {
|
||||
// TODO(qgh):In the simulator, it will be called twice. So the judgment here is to prevent memory leaks.
|
||||
// But this is equivalent to using a singleton pattern,
|
||||
// which is not consistent with the current design and will be optimized later.
|
||||
if (!FileUtils::getInstance()) {
|
||||
return ccnew FileUtilsApple();
|
||||
}
|
||||
return FileUtils::getInstance();
|
||||
}
|
||||
|
||||
FileUtilsApple::FileUtilsApple() : pimpl_(ccnew IMPL([NSBundle mainBundle])) {
|
||||
init();
|
||||
}
|
||||
|
||||
#if CC_FILEUTILS_APPLE_ENABLE_OBJC
|
||||
void FileUtilsApple::setBundle(NSBundle *bundle) {
|
||||
pimpl_->setBundle(bundle);
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma mark - FileUtils
|
||||
|
||||
static NSFileManager *s_fileManager = [NSFileManager defaultManager];
|
||||
|
||||
ccstd::string FileUtilsApple::getWritablePath() const {
|
||||
if (_writablePath.length()) {
|
||||
return _writablePath;
|
||||
}
|
||||
|
||||
// save to document folder
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
NSString *documentsDirectory = [paths objectAtIndex:0];
|
||||
ccstd::string strRet = [documentsDirectory UTF8String];
|
||||
strRet.append("/");
|
||||
return strRet;
|
||||
}
|
||||
|
||||
bool FileUtilsApple::isFileExistInternal(const ccstd::string &filePath) const {
|
||||
if (filePath.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ret = false;
|
||||
|
||||
if (filePath[0] != '/') {
|
||||
ccstd::string path;
|
||||
ccstd::string file;
|
||||
size_t pos = filePath.find_last_of("/");
|
||||
if (pos != ccstd::string::npos) {
|
||||
file = filePath.substr(pos + 1);
|
||||
path = filePath.substr(0, pos + 1);
|
||||
} else {
|
||||
file = filePath;
|
||||
}
|
||||
|
||||
NSString *fullpath = [pimpl_->getBundle() pathForResource:[NSString stringWithUTF8String:file.c_str()]
|
||||
ofType:nil
|
||||
inDirectory:[NSString stringWithUTF8String:path.c_str()]];
|
||||
if (fullpath != nil) {
|
||||
ret = true;
|
||||
}
|
||||
} else {
|
||||
// Search path is an absolute path.
|
||||
if ([s_fileManager fileExistsAtPath:[NSString stringWithUTF8String:filePath.c_str()]]) {
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int unlink_cb(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
|
||||
auto ret = remove(fpath);
|
||||
if (ret) {
|
||||
CC_LOG_INFO("Fail to remove: %s ", fpath);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool FileUtilsApple::removeDirectory(const ccstd::string &path) {
|
||||
if (path.empty()) {
|
||||
CC_LOG_ERROR("Fail to remove directory, path is empty!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nftw(path.c_str(), unlink_cb, 64, FTW_DEPTH | FTW_PHYS))
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
ccstd::string FileUtilsApple::getFullPathForDirectoryAndFilename(const ccstd::string &directory, const ccstd::string &filename) const {
|
||||
if (directory[0] != '/') {
|
||||
NSString *dirStr = [NSString stringWithUTF8String:directory.c_str()];
|
||||
// The following logic is used for remove the "../" in the directory
|
||||
// Because method "pathForResource" will return nil if the directory contains "../".
|
||||
auto theIdx = directory.find("..");
|
||||
if (theIdx != ccstd::string::npos && theIdx > 0) {
|
||||
NSMutableArray<NSString *> *pathComps = [NSMutableArray arrayWithArray:[dirStr pathComponents]];
|
||||
NSUInteger idx = [pathComps indexOfObject:@".."];
|
||||
while (idx != NSNotFound && idx > 0) { // if found ".." & it's not at the beginning of the string
|
||||
[pathComps removeObjectAtIndex:idx]; // remove the item ".."
|
||||
[pathComps removeObjectAtIndex:idx - 1]; // remove the item before ".."
|
||||
idx = [pathComps indexOfObject:@".."]; // find ".." again
|
||||
}
|
||||
dirStr = [NSString pathWithComponents:pathComps];
|
||||
}
|
||||
|
||||
NSString *fullpath = [pimpl_->getBundle() pathForResource:[NSString stringWithUTF8String:filename.c_str()]
|
||||
ofType:nil
|
||||
inDirectory:dirStr];
|
||||
if (fullpath != nil) {
|
||||
return [fullpath UTF8String];
|
||||
}
|
||||
} else {
|
||||
ccstd::string fullPath = directory + filename;
|
||||
// Search path is an absolute path.
|
||||
if ([s_fileManager fileExistsAtPath:[NSString stringWithUTF8String:fullPath.c_str()]]) {
|
||||
return fullPath;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
ValueMap FileUtilsApple::getValueMapFromFile(const ccstd::string &filename) {
|
||||
auto d(FileUtils::getInstance()->getDataFromFile(filename));
|
||||
return getValueMapFromData(reinterpret_cast<char *>(d.getBytes()), static_cast<int>(d.getSize()));
|
||||
}
|
||||
|
||||
ValueMap FileUtilsApple::getValueMapFromData(const char *filedata, int filesize) {
|
||||
NSData *file = [NSData dataWithBytes:filedata length:filesize];
|
||||
NSPropertyListFormat format;
|
||||
NSError *error;
|
||||
NSDictionary *dict = [NSPropertyListSerialization propertyListWithData:file options:NSPropertyListImmutable format:&format error:&error];
|
||||
|
||||
ValueMap ret;
|
||||
|
||||
if (dict != nil) {
|
||||
for (id key in [dict allKeys]) {
|
||||
id value = [dict objectForKey:key];
|
||||
addNSObjectToCCMap(key, value, ret);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool FileUtilsApple::writeToFile(const ValueMap &dict, const ccstd::string &fullPath) {
|
||||
return writeValueMapToFile(dict, fullPath);
|
||||
}
|
||||
|
||||
bool FileUtils::writeValueMapToFile(const ValueMap &dict, const ccstd::string &fullPath) {
|
||||
valueMapCompact(const_cast<ValueMap &>(dict));
|
||||
//CC_LOG_DEBUG("iOS||Mac Dictionary %d write to file %s", dict->_ID, fullPath.c_str());
|
||||
NSMutableDictionary *nsDict = [NSMutableDictionary dictionary];
|
||||
|
||||
for (auto iter = dict.begin(); iter != dict.end(); ++iter) {
|
||||
addCCValueToNSDictionary(iter->first, iter->second, nsDict);
|
||||
}
|
||||
|
||||
NSString *file = [NSString stringWithUTF8String:fullPath.c_str()];
|
||||
// do it atomically
|
||||
return [nsDict writeToFile:file atomically:YES];
|
||||
}
|
||||
|
||||
void FileUtilsApple::valueMapCompact(ValueMap &valueMap) {
|
||||
auto itr = valueMap.begin();
|
||||
while (itr != valueMap.end()) {
|
||||
auto vtype = itr->second.getType();
|
||||
switch (vtype) {
|
||||
case Value::Type::NONE: {
|
||||
itr = valueMap.erase(itr);
|
||||
continue;
|
||||
} break;
|
||||
case Value::Type::MAP: {
|
||||
valueMapCompact(itr->second.asValueMap());
|
||||
} break;
|
||||
case Value::Type::VECTOR: {
|
||||
valueVectorCompact(itr->second.asValueVector());
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
itr++;
|
||||
}
|
||||
}
|
||||
|
||||
void FileUtilsApple::valueVectorCompact(ValueVector &valueVector) {
|
||||
auto itr = valueVector.begin();
|
||||
while (itr != valueVector.end()) {
|
||||
auto vtype = (*itr).getType();
|
||||
switch (vtype) {
|
||||
case Value::Type::NONE: {
|
||||
itr = valueVector.erase(itr);
|
||||
continue;
|
||||
} break;
|
||||
case Value::Type::MAP: {
|
||||
valueMapCompact((*itr).asValueMap());
|
||||
} break;
|
||||
case Value::Type::VECTOR: {
|
||||
valueVectorCompact((*itr).asValueVector());
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
itr++;
|
||||
}
|
||||
}
|
||||
|
||||
bool FileUtils::writeValueVectorToFile(const ValueVector &vecData, const ccstd::string &fullPath) {
|
||||
NSString *path = [NSString stringWithUTF8String:fullPath.c_str()];
|
||||
NSMutableArray *array = [NSMutableArray array];
|
||||
|
||||
for (const auto &e : vecData) {
|
||||
addCCValueToNSArray(e, array);
|
||||
}
|
||||
|
||||
[array writeToFile:path atomically:YES];
|
||||
|
||||
return true;
|
||||
}
|
||||
ValueVector FileUtilsApple::getValueVectorFromFile(const ccstd::string &filename) {
|
||||
// NSString* pPath = [NSString stringWithUTF8String:pFileName];
|
||||
// NSString* pathExtension= [pPath pathExtension];
|
||||
// pPath = [pPath stringByDeletingPathExtension];
|
||||
// pPath = [[NSBundle mainBundle] pathForResource:pPath ofType:pathExtension];
|
||||
// fixing cannot read data using Array::createWithContentsOfFile
|
||||
ccstd::string fullPath = fullPathForFilename(filename);
|
||||
NSString *path = [NSString stringWithUTF8String:fullPath.c_str()];
|
||||
NSArray *array = [NSArray arrayWithContentsOfFile:path];
|
||||
|
||||
ValueVector ret;
|
||||
|
||||
for (id value in array) {
|
||||
addNSObjectToCCVector(value, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool FileUtilsApple::createDirectory(const ccstd::string &path) {
|
||||
CC_ASSERT(!path.empty());
|
||||
|
||||
if (isDirectoryExist(path))
|
||||
return true;
|
||||
|
||||
NSError *error;
|
||||
|
||||
bool result = [s_fileManager createDirectoryAtPath:[NSString stringWithUTF8String:path.c_str()] withIntermediateDirectories:YES attributes:nil error:&error];
|
||||
|
||||
if (!result && error != nil) {
|
||||
CC_LOG_ERROR("Fail to create directory \"%s\": %s", path.c_str(), [error.localizedDescription UTF8String]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace cc
|
||||
37
cocos/platform/apple/JsbBridge.h
Normal file
37
cocos/platform/apple/JsbBridge.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef void (^ICallback)(NSString*, NSString*);
|
||||
|
||||
@interface JsbBridge : NSObject
|
||||
+ (instancetype)sharedInstance;
|
||||
- (void)setCallback:(ICallback)cb;
|
||||
- (bool)callByScript:(NSString*)arg0 arg1:(NSString*)arg1;
|
||||
- (void)sendToScript:(NSString*)arg0 arg1:(NSString*)arg1;
|
||||
- (void)sendToScript:(NSString*)arg0;
|
||||
@end
|
||||
98
cocos/platform/apple/JsbBridge.mm
Normal file
98
cocos/platform/apple/JsbBridge.mm
Normal file
@@ -0,0 +1,98 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2018-2022 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated engine source code (the "Software"), a limited,
|
||||
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
||||
to use Cocos Creator solely to develop games on your target platforms. You shall
|
||||
not use Cocos Creator software for developing other software or tools that's
|
||||
used for developing games. You are not granted to publish, distribute,
|
||||
sublicense, and/or sell copies of Cocos Creator.
|
||||
|
||||
The software or tools in this License Agreement are licensed, not sold.
|
||||
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "JsbBridge.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
#include "base/std/container/string.h"
|
||||
#include "cocos/bindings/manual/JavaScriptObjCBridge.h"
|
||||
#include "engine/EngineEvents.h"
|
||||
|
||||
bool callPlatformStringMethod(const ccstd::string &arg0, const ccstd::string &arg1) {
|
||||
NSString *oc_arg0 = [NSString stringWithCString:arg0.c_str() encoding:NSUTF8StringEncoding];
|
||||
NSString *oc_arg1 = [NSString stringWithCString:arg1.c_str() encoding:NSUTF8StringEncoding];
|
||||
JsbBridge *m = [JsbBridge sharedInstance];
|
||||
[m callByScript:oc_arg0 arg1:oc_arg1];
|
||||
return true;
|
||||
}
|
||||
|
||||
@implementation JsbBridge {
|
||||
ICallback callback;
|
||||
cc::events::Close::Listener closeListener;
|
||||
}
|
||||
|
||||
static JsbBridge *instance = nil;
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
static dispatch_once_t pred = 0;
|
||||
dispatch_once(&pred, ^{
|
||||
instance = [[super allocWithZone:NULL] init];
|
||||
NSAssert(instance != nil, @"alloc or init failed");
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
+ (id)allocWithZone:(struct _NSZone *)zone {
|
||||
return [JsbBridge sharedInstance];
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(struct _NSZone *)zone {
|
||||
return [JsbBridge sharedInstance];
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
if (self = [super init]) {
|
||||
closeListener.bind([&](){
|
||||
if ([JsbBridge sharedInstance] != nil) {
|
||||
[[JsbBridge sharedInstance] release];
|
||||
}
|
||||
});
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setCallback:(ICallback)cb {
|
||||
callback = cb;
|
||||
}
|
||||
|
||||
- (bool)callByScript:(NSString *)arg0 arg1:(NSString *)arg1 {
|
||||
if (callback != nil) {
|
||||
callback(arg0, arg1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
- (void)sendToScript:(NSString *)arg0 arg1:(NSString *)arg1 {
|
||||
const ccstd::string c_arg0{[arg0 UTF8String]};
|
||||
const ccstd::string c_arg1{[arg1 UTF8String]};
|
||||
callScript(c_arg0, c_arg1);
|
||||
}
|
||||
|
||||
- (void)sendToScript:(NSString *)arg0 {
|
||||
const ccstd::string c_arg0{[arg0 UTF8String]};
|
||||
callScript(c_arg0, "");
|
||||
}
|
||||
|
||||
@end
|
||||
59
cocos/platform/apple/JsbBridgeWrapper.h
Normal file
59
cocos/platform/apple/JsbBridgeWrapper.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef void (^OnScriptEventListener)(NSString*);
|
||||
|
||||
@interface JsbBridgeWrapper : NSObject
|
||||
/**
|
||||
* Get the instance of JsbBridgetWrapper
|
||||
*/
|
||||
+ (instancetype)sharedInstance;
|
||||
/**
|
||||
* Add a listener to specified event, if the event does not exist, the wrapper will create one. Concurrent listener will be ignored
|
||||
*/
|
||||
- (void)addScriptEventListener:(NSString*)eventName listener:(OnScriptEventListener)listener;
|
||||
/**
|
||||
* Remove listener for specified event, concurrent event will be deleted. Return false only if the event does not exist
|
||||
*/
|
||||
- (bool)removeScriptEventListener:(NSString*)eventName listener:(OnScriptEventListener)listener;
|
||||
/**
|
||||
* Remove all listener for event specified.
|
||||
*/
|
||||
- (void)removeAllListenersForEvent:(NSString*)eventName;
|
||||
/**
|
||||
* Remove all event registered. Use it carefully!
|
||||
*/
|
||||
- (void)removeAllListeners;
|
||||
/**
|
||||
* Dispatch the event with argument, the event should be registered in javascript, or other script language in future.
|
||||
*/
|
||||
- (void)dispatchEventToScript:(NSString*)eventName arg:(NSString*)arg;
|
||||
/**
|
||||
* Dispatch the event which is registered in javascript, or other script language in future.
|
||||
*/
|
||||
- (void)dispatchEventToScript:(NSString*)eventName;
|
||||
@end
|
||||
134
cocos/platform/apple/JsbBridgeWrapper.mm
Normal file
134
cocos/platform/apple/JsbBridgeWrapper.mm
Normal file
@@ -0,0 +1,134 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2018-2022 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated engine source code (the "Software"), a limited,
|
||||
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
||||
to use Cocos Creator solely to develop games on your target platforms. You shall
|
||||
not use Cocos Creator software for developing other software or tools that's
|
||||
used for developing games. You are not granted to publish, distribute,
|
||||
sublicense, and/or sell copies of Cocos Creator.
|
||||
|
||||
The software or tools in this License Agreement are licensed, not sold.
|
||||
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#include <iostream>
|
||||
#include "base/std/container/string.h"
|
||||
#include "JsbBridge.h"
|
||||
#include "JsbBridgeWrapper.h"
|
||||
#include "engine/EngineEvents.h"
|
||||
|
||||
@implementation JsbBridgeWrapper {
|
||||
JsbBridge* jb;
|
||||
NSMutableDictionary<NSString*, NSMutableArray<OnScriptEventListener>*>* cbDictionnary;
|
||||
cc::events::Close::Listener closeListener;
|
||||
}
|
||||
|
||||
static JsbBridgeWrapper* instance = nil;
|
||||
static ICallback cb = ^void(NSString* _event, NSString* _arg) {
|
||||
[[JsbBridgeWrapper sharedInstance] triggerEvent:_event arg:_arg];
|
||||
};
|
||||
+ (instancetype)sharedInstance {
|
||||
static dispatch_once_t pred = 0;
|
||||
dispatch_once(&pred, ^{
|
||||
instance = [[super allocWithZone:NULL] init];
|
||||
NSAssert(instance != nil, @"alloc or init failed");
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
+ (id)allocWithZone:(struct _NSZone*)zone {
|
||||
return [JsbBridgeWrapper sharedInstance];
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(struct _NSZone*)zone {
|
||||
return [JsbBridgeWrapper sharedInstance];
|
||||
}
|
||||
|
||||
- (void)addScriptEventListener:(NSString*)eventName listener:(OnScriptEventListener)listener {
|
||||
if (![cbDictionnary objectForKey:eventName]) {
|
||||
NSMutableArray *newArr = [[NSMutableArray<OnScriptEventListener> alloc] init];
|
||||
[cbDictionnary setValue:newArr forKey:eventName];
|
||||
[newArr release];
|
||||
}
|
||||
NSMutableArray* arr = [cbDictionnary objectForKey:eventName];
|
||||
if (![arr containsObject:listener]) {
|
||||
[arr addObject:listener];
|
||||
}
|
||||
[listener release];
|
||||
}
|
||||
|
||||
- (void)triggerEvent:(NSString*)eventName arg:(NSString*)arg {
|
||||
NSMutableArray<OnScriptEventListener>* arr = [cbDictionnary objectForKey:eventName];
|
||||
if (!arr) {
|
||||
return;
|
||||
}
|
||||
for (OnScriptEventListener listener : arr) {
|
||||
listener(arg);
|
||||
}
|
||||
}
|
||||
- (void)removeAllListenersForEvent:(NSString*)eventName {
|
||||
if (![cbDictionnary objectForKey:eventName]) {
|
||||
return;
|
||||
}
|
||||
//same as release all listeners.
|
||||
[cbDictionnary removeObjectForKey:eventName];
|
||||
}
|
||||
|
||||
- (bool)removeScriptEventListener:(NSString*)eventName listener:(OnScriptEventListener)listener {
|
||||
NSMutableArray<OnScriptEventListener>* arr = [cbDictionnary objectForKey:eventName];
|
||||
if (!arr) {
|
||||
return false;
|
||||
}
|
||||
[arr removeObject:listener];
|
||||
return true;
|
||||
}
|
||||
- (void)removeAllListeners {
|
||||
[cbDictionnary removeAllObjects];
|
||||
}
|
||||
- (void)dispatchEventToScript:(NSString*)eventName arg:(NSString*)arg {
|
||||
[jb sendToScript:eventName arg1:arg];
|
||||
}
|
||||
|
||||
- (void)dispatchEventToScript:(NSString*)eventName {
|
||||
[jb sendToScript:eventName];
|
||||
}
|
||||
- (id)init {
|
||||
if (self = [super init]) {
|
||||
cbDictionnary = [NSMutableDictionary new];
|
||||
if (cbDictionnary == nil) {
|
||||
[self release];
|
||||
return nil;
|
||||
}
|
||||
jb = [JsbBridge sharedInstance];
|
||||
if (jb == nil) {
|
||||
[self release];
|
||||
return nil;
|
||||
}
|
||||
[jb setCallback:cb];
|
||||
closeListener.bind([&](){
|
||||
if ([JsbBridgeWrapper sharedInstance] != nil) {
|
||||
[[JsbBridgeWrapper sharedInstance] release];
|
||||
}
|
||||
});
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (void)dealloc {
|
||||
for (NSMutableArray* arr : cbDictionnary) {
|
||||
[arr release];
|
||||
}
|
||||
[cbDictionnary release];
|
||||
[super dealloc];
|
||||
}
|
||||
@end
|
||||
218
cocos/platform/apple/Reachability.cpp
Normal file
218
cocos/platform/apple/Reachability.cpp
Normal file
@@ -0,0 +1,218 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "Reachability.h"
|
||||
#include <SystemConfiguration/SystemConfiguration.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include "base/DeferredReleasePool.h"
|
||||
#include "base/Macros.h"
|
||||
#include "base/memory/Memory.h"
|
||||
|
||||
namespace {
|
||||
|
||||
#define ShouldPrintReachabilityFlags 0
|
||||
|
||||
static void PrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char *comment) {
|
||||
#if ShouldPrintReachabilityFlags
|
||||
|
||||
printf("Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n",
|
||||
#if CC_PLATFORM == CC_PLATFORM_IOS
|
||||
(flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-',
|
||||
#else
|
||||
'-',
|
||||
#endif
|
||||
(flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-',
|
||||
|
||||
(flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-',
|
||||
(flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-',
|
||||
comment);
|
||||
#endif
|
||||
}
|
||||
|
||||
cc::Reachability::NetworkStatus getNetworkStatusForFlags(SCNetworkReachabilityFlags flags) {
|
||||
PrintReachabilityFlags(flags, "networkStatusForFlags");
|
||||
if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) {
|
||||
// The target host is not reachable.
|
||||
return cc::Reachability::NetworkStatus::NOT_REACHABLE;
|
||||
}
|
||||
|
||||
cc::Reachability::NetworkStatus returnValue = cc::Reachability::NetworkStatus::NOT_REACHABLE;
|
||||
|
||||
if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0) {
|
||||
/*
|
||||
If the target host is reachable and no connection is required then we'll assume (for now) that you're on Wi-Fi...
|
||||
*/
|
||||
returnValue = cc::Reachability::NetworkStatus::REACHABLE_VIA_WIFI;
|
||||
}
|
||||
|
||||
if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand) != 0) ||
|
||||
(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)) {
|
||||
/*
|
||||
... and the connection is on-demand (or on-traffic) if the calling application is using the CFSocketStream or higher APIs...
|
||||
*/
|
||||
|
||||
if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0) {
|
||||
/*
|
||||
... and no [user] intervention is needed...
|
||||
*/
|
||||
returnValue = cc::Reachability::NetworkStatus::REACHABLE_VIA_WIFI;
|
||||
}
|
||||
}
|
||||
|
||||
#if CC_PLATFORM == CC_PLATFORM_IOS
|
||||
if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN) {
|
||||
/*
|
||||
... but WWAN connections are OK if the calling application is using the CFNetwork APIs.
|
||||
*/
|
||||
returnValue = cc::Reachability::NetworkStatus::REACHABLE_VIA_WWAN;
|
||||
}
|
||||
#endif
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace cc {
|
||||
|
||||
Reachability *Reachability::createWithHostName(const ccstd::string &hostName) {
|
||||
Reachability *returnValue = nullptr;
|
||||
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(nullptr, hostName.c_str());
|
||||
if (reachability != nullptr) {
|
||||
returnValue = ccnew Reachability();
|
||||
returnValue->addRef();
|
||||
if (returnValue != nullptr) {
|
||||
cc::DeferredReleasePool::add(returnValue);
|
||||
returnValue->_reachabilityRef = reachability;
|
||||
} else {
|
||||
CFRelease(reachability);
|
||||
}
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
Reachability *Reachability::createWithAddress(const struct sockaddr *hostAddress) {
|
||||
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, hostAddress);
|
||||
|
||||
Reachability *returnValue = nullptr;
|
||||
|
||||
if (reachability != nullptr) {
|
||||
returnValue = ccnew Reachability();
|
||||
returnValue->addRef();
|
||||
if (returnValue != nullptr) {
|
||||
cc::DeferredReleasePool::add(returnValue);
|
||||
returnValue->_reachabilityRef = reachability;
|
||||
} else {
|
||||
CFRelease(reachability);
|
||||
}
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
Reachability *Reachability::createForInternetConnection() {
|
||||
struct sockaddr_in zeroAddress;
|
||||
bzero(&zeroAddress, sizeof(zeroAddress));
|
||||
zeroAddress.sin_len = sizeof(zeroAddress);
|
||||
zeroAddress.sin_family = AF_INET;
|
||||
|
||||
return createWithAddress((const struct sockaddr *)&zeroAddress);
|
||||
}
|
||||
|
||||
Reachability::Reachability()
|
||||
: _callback(nullptr),
|
||||
_userData(nullptr),
|
||||
_reachabilityRef(nullptr) {
|
||||
}
|
||||
|
||||
Reachability::~Reachability() {
|
||||
stopNotifier();
|
||||
if (_reachabilityRef != nullptr) {
|
||||
CFRelease(_reachabilityRef);
|
||||
}
|
||||
}
|
||||
|
||||
void Reachability::onReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) {
|
||||
CC_ASSERT_NOT_NULL(info);
|
||||
|
||||
cc::Reachability *thiz = reinterpret_cast<cc::Reachability *>(info);
|
||||
if (thiz->_callback != nullptr) {
|
||||
NetworkStatus status = getNetworkStatusForFlags(flags);
|
||||
thiz->_callback(thiz, status, thiz->_userData);
|
||||
}
|
||||
}
|
||||
|
||||
bool Reachability::startNotifier(const ReachabilityCallback &cb, void *userData) {
|
||||
_callback = cb;
|
||||
_userData = userData;
|
||||
|
||||
bool returnValue = false;
|
||||
SCNetworkReachabilityContext context = {0, this, nullptr, nullptr, nullptr};
|
||||
|
||||
if (SCNetworkReachabilitySetCallback(_reachabilityRef, onReachabilityCallback, &context)) {
|
||||
if (SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
|
||||
returnValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
void Reachability::stopNotifier() {
|
||||
if (_reachabilityRef != nullptr) {
|
||||
SCNetworkReachabilityUnscheduleFromRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||
}
|
||||
}
|
||||
|
||||
bool Reachability::isConnectionRequired() const {
|
||||
CC_ASSERT_NOT_NULL(_reachabilityRef);
|
||||
SCNetworkReachabilityFlags flags;
|
||||
|
||||
if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags)) {
|
||||
return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Reachability::NetworkStatus Reachability::getCurrentReachabilityStatus() const {
|
||||
CC_ASSERT_NOT_NULL(_reachabilityRef);
|
||||
NetworkStatus returnValue = NetworkStatus::NOT_REACHABLE;
|
||||
SCNetworkReachabilityFlags flags;
|
||||
|
||||
if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags)) {
|
||||
returnValue = getNetworkStatusForFlags(flags);
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
} // namespace cc
|
||||
86
cocos/platform/apple/Reachability.h
Normal file
86
cocos/platform/apple/Reachability.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "base/RefCounted.h"
|
||||
|
||||
#include <SystemConfiguration/SystemConfiguration.h>
|
||||
#include <functional>
|
||||
#include "base/std/container/string.h"
|
||||
|
||||
struct sockaddr;
|
||||
|
||||
namespace cc {
|
||||
|
||||
class Reachability final : public RefCounted {
|
||||
public:
|
||||
enum class NetworkStatus : uint8_t {
|
||||
NOT_REACHABLE,
|
||||
REACHABLE_VIA_WIFI,
|
||||
REACHABLE_VIA_WWAN
|
||||
};
|
||||
|
||||
/*!
|
||||
* Use to check the reachability of a given host name.
|
||||
*/
|
||||
static Reachability *createWithHostName(const ccstd::string &hostName);
|
||||
|
||||
/*!
|
||||
* Use to check the reachability of a given IP address.
|
||||
*/
|
||||
static Reachability *createWithAddress(const struct sockaddr *hostAddress);
|
||||
|
||||
/*!
|
||||
* Checks whether the default route is available. Should be used by applications that do not connect to a particular host.
|
||||
*/
|
||||
static Reachability *createForInternetConnection();
|
||||
|
||||
using ReachabilityCallback = std::function<void(Reachability *, NetworkStatus, void *)>;
|
||||
|
||||
/*!
|
||||
* Start listening for reachability notifications on the current run loop.
|
||||
*/
|
||||
bool startNotifier(const ReachabilityCallback &cb, void *userData);
|
||||
void stopNotifier();
|
||||
|
||||
NetworkStatus getCurrentReachabilityStatus() const;
|
||||
|
||||
/*!
|
||||
* WWAN may be available, but not active until a connection has been established. WiFi may require a connection for VPN on Demand.
|
||||
*/
|
||||
bool isConnectionRequired() const;
|
||||
|
||||
private:
|
||||
Reachability();
|
||||
~Reachability();
|
||||
|
||||
static void onReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info);
|
||||
|
||||
ReachabilityCallback _callback;
|
||||
void *_userData;
|
||||
SCNetworkReachabilityRef _reachabilityRef;
|
||||
};
|
||||
|
||||
} // namespace cc
|
||||
@@ -0,0 +1,89 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2018-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "base/csscolorparser.h"
|
||||
#include "base/std/container/array.h"
|
||||
#include "bindings/jswrapper/config.h"
|
||||
#include "math/Math.h"
|
||||
#include "platform/interfaces/modules/canvas/ICanvasRenderingContext2D.h"
|
||||
|
||||
#ifdef __OBJC__
|
||||
@class CanvasRenderingContext2DDelegateImpl;
|
||||
#else
|
||||
class CanvasRenderingContext2DDelegateImpl;
|
||||
#endif
|
||||
|
||||
namespace cc {
|
||||
|
||||
class CanvasRenderingContext2DDelegate : public ICanvasRenderingContext2D::Delegate {
|
||||
public:
|
||||
using Size = ccstd::array<float, 2>;
|
||||
using TextAlign = ICanvasRenderingContext2D::TextAlign;
|
||||
using TextBaseline = ICanvasRenderingContext2D::TextBaseline;
|
||||
CanvasRenderingContext2DDelegate();
|
||||
~CanvasRenderingContext2DDelegate() override;
|
||||
|
||||
void recreateBuffer(float w, float h) override;
|
||||
void beginPath() override;
|
||||
void closePath() override;
|
||||
void moveTo(float x, float y) override;
|
||||
void lineTo(float x, float y) override;
|
||||
void stroke() override;
|
||||
void saveContext() override;
|
||||
void restoreContext() override;
|
||||
void clearRect(float /*x*/, float /*y*/, float w, float h) override;
|
||||
void fill() override;
|
||||
void setLineCap(const ccstd::string &lineCap) override;
|
||||
void setLineJoin(const ccstd::string &lineJoin) override;
|
||||
void rect(float x, float y, float w, float h) override;
|
||||
void fillRect(float x, float y, float w, float h) override;
|
||||
void fillText(const ccstd::string &text, float x, float y, float /*maxWidth*/) override;
|
||||
void strokeText(const ccstd::string &text, float /*x*/, float /*y*/, float /*maxWidth*/) override;
|
||||
Size measureText(const ccstd::string &text) override;
|
||||
void updateFont(const ccstd::string &fontName, float fontSize, bool bold, bool italic, bool oblique, bool smallCaps) override;
|
||||
void setTextAlign(TextAlign align) override;
|
||||
void setTextBaseline(TextBaseline baseline) override;
|
||||
void setFillStyle(uint8_t r, uint8_t g, uint8_t b, uint8_t a) override;
|
||||
void setStrokeStyle(uint8_t r, uint8_t g, uint8_t b, uint8_t a) override;
|
||||
void setLineWidth(float lineWidth) override;
|
||||
const cc::Data &getDataRef() const override;
|
||||
void fillImageData(const Data &imageData, float imageWidth, float imageHeight, float offsetX, float offsetY) override;
|
||||
void updateData() override {}
|
||||
void setShadowBlur(float blur) override;
|
||||
void setShadowColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) override;
|
||||
void setShadowOffsetX(float offsetX) override;
|
||||
void setShadowOffsetY(float offsetY) override;
|
||||
|
||||
private:
|
||||
void fillData();
|
||||
void unMultiplyAlpha(unsigned char *ptr, uint32_t size) const;
|
||||
|
||||
public:
|
||||
private:
|
||||
CanvasRenderingContext2DDelegateImpl *_impl;
|
||||
};
|
||||
|
||||
} // namespace cc
|
||||
714
cocos/platform/apple/modules/CanvasRenderingContext2DDelegate.mm
Normal file
714
cocos/platform/apple/modules/CanvasRenderingContext2DDelegate.mm
Normal file
@@ -0,0 +1,714 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2021-2022 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated engine source code (the "Software"), a limited,
|
||||
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
||||
to use Cocos Creator solely to develop games on your target platforms. You shall
|
||||
not use Cocos Creator software for developing other software or tools that's
|
||||
used for developing games. You are not granted to publish, distribute,
|
||||
sublicense, and/or sell copies of Cocos Creator.
|
||||
|
||||
The software or tools in this License Agreement are licensed, not sold.
|
||||
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "platform/apple/modules/CanvasRenderingContext2DDelegate.h"
|
||||
#include "base/UTF8.h"
|
||||
#include "base/csscolorparser.h"
|
||||
#include "math/Math.h"
|
||||
|
||||
#include "cocos/bindings/jswrapper/SeApi.h"
|
||||
#include "cocos/bindings/manual/jsb_platform.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if CC_PLATFORM == CC_PLATFORM_MACOS
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#else
|
||||
#import <CoreText/CoreText.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#define NSBezierPath UIBezierPath
|
||||
#define NSFont UIFont
|
||||
#define NSColor UIColor
|
||||
#define NSSize CGSize
|
||||
#define NSZeroSize CGSizeZero
|
||||
#define NSPoint CGPoint
|
||||
#define NSMakePoint CGPointMake
|
||||
|
||||
#endif
|
||||
|
||||
#include <regex>
|
||||
|
||||
@interface CanvasRenderingContext2DDelegateImpl : NSObject {
|
||||
NSFont *_font;
|
||||
NSMutableDictionary *_tokenAttributesDict;
|
||||
NSString *_fontName;
|
||||
CGFloat _fontSize;
|
||||
CGFloat _width;
|
||||
CGFloat _height;
|
||||
CGContextRef _context;
|
||||
|
||||
#if CC_PLATFORM == CC_PLATFORM_MACOS
|
||||
NSGraphicsContext *_currentGraphicsContext;
|
||||
NSGraphicsContext *_oldGraphicsContext;
|
||||
#else
|
||||
CGContextRef _oldContext;
|
||||
#endif
|
||||
|
||||
CGColorSpaceRef _colorSpace;
|
||||
cc::Data _imageData;
|
||||
NSBezierPath *_path;
|
||||
|
||||
cc::ICanvasRenderingContext2D::TextAlign _textAlign;
|
||||
cc::ICanvasRenderingContext2D::TextBaseline _textBaseLine;
|
||||
ccstd::array<float, 4> _fillStyle;
|
||||
ccstd::array<float, 4> _strokeStyle;
|
||||
NSColor *_shadowColor;
|
||||
float _lineWidth;
|
||||
bool _bold;
|
||||
bool _italic;
|
||||
}
|
||||
|
||||
@property (nonatomic, strong) NSFont *font;
|
||||
@property (nonatomic, strong) NSMutableDictionary *tokenAttributesDict;
|
||||
@property (nonatomic, strong) NSString *fontName;
|
||||
@property (nonatomic, assign) cc::ICanvasRenderingContext2D::TextAlign textAlign;
|
||||
@property (nonatomic, assign) cc::ICanvasRenderingContext2D::TextBaseline textBaseLine;
|
||||
@property (nonatomic, assign) float lineWidth;
|
||||
@property (nonatomic, assign) float shadowBlur;
|
||||
@property (nonatomic, assign) float shadowOffsetX;
|
||||
@property (nonatomic, assign) float shadowOffsetY;
|
||||
|
||||
@end
|
||||
|
||||
@implementation CanvasRenderingContext2DDelegateImpl
|
||||
|
||||
@synthesize font = _font;
|
||||
@synthesize tokenAttributesDict = _tokenAttributesDict;
|
||||
@synthesize fontName = _fontName;
|
||||
@synthesize textAlign = _textAlign;
|
||||
@synthesize textBaseLine = _textBaseLine;
|
||||
@synthesize lineWidth = _lineWidth;
|
||||
|
||||
- (id)init {
|
||||
if (self = [super init]) {
|
||||
_lineWidth = 0;
|
||||
_textAlign = cc::ICanvasRenderingContext2D::TextAlign::LEFT;
|
||||
_textBaseLine = cc::ICanvasRenderingContext2D::TextBaseline::BOTTOM;
|
||||
_width = _height = 0;
|
||||
_shadowBlur = _shadowOffsetX = _shadowOffsetY = 0;
|
||||
_shadowColor = nil;
|
||||
_context = nil;
|
||||
_colorSpace = nil;
|
||||
|
||||
#if CC_PLATFORM == CC_PLATFORM_MACOS
|
||||
_currentGraphicsContext = nil;
|
||||
_oldGraphicsContext = nil;
|
||||
#endif
|
||||
_path = [NSBezierPath bezierPath];
|
||||
[_path retain];
|
||||
[self updateFontWithName:@"Arial" fontSize:30 bold:false italic:false];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
self.font = nil;
|
||||
self.tokenAttributesDict = nil;
|
||||
self.fontName = nil;
|
||||
if (_shadowColor) {
|
||||
[_shadowColor release];
|
||||
}
|
||||
_shadowColor = nil;
|
||||
CGColorSpaceRelease(_colorSpace);
|
||||
// release the context
|
||||
CGContextRelease(_context);
|
||||
[_path release];
|
||||
#if CC_PLATFORM == CC_PLATFORM_MACOS
|
||||
[_currentGraphicsContext release];
|
||||
#endif
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
#if CC_PLATFORM == CC_PLATFORM_MACOS
|
||||
|
||||
- (NSFont *)_createSystemFont {
|
||||
NSFontTraitMask mask = NSUnitalicFontMask;
|
||||
if (_italic) {
|
||||
mask = NSItalicFontMask;
|
||||
}
|
||||
if (_bold) {
|
||||
mask |= NSBoldFontMask;
|
||||
} else {
|
||||
mask |= NSUnboldFontMask;
|
||||
}
|
||||
|
||||
NSFont *font = [[NSFontManager sharedFontManager]
|
||||
fontWithFamily:_fontName
|
||||
traits:mask
|
||||
weight:0
|
||||
size:_fontSize];
|
||||
|
||||
if (font == nil) {
|
||||
const auto &familyMap = getFontFamilyNameMap();
|
||||
auto iter = familyMap.find([_fontName UTF8String]);
|
||||
if (iter != familyMap.end()) {
|
||||
font = [[NSFontManager sharedFontManager]
|
||||
fontWithFamily:[NSString stringWithUTF8String:iter->second.c_str()]
|
||||
traits:mask
|
||||
weight:0
|
||||
size:_fontSize];
|
||||
}
|
||||
}
|
||||
|
||||
if (font == nil) {
|
||||
font = [[NSFontManager sharedFontManager]
|
||||
fontWithFamily:@"Arial"
|
||||
traits:mask
|
||||
weight:0
|
||||
size:_fontSize];
|
||||
}
|
||||
return font;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
- (UIFont *)_createSystemFont {
|
||||
UIFont *font = nil;
|
||||
|
||||
if (_bold) {
|
||||
font = [UIFont fontWithName:[_fontName stringByAppendingString:@"-Bold"] size:_fontSize];
|
||||
} else {
|
||||
font = [UIFont fontWithName:_fontName size:_fontSize];
|
||||
}
|
||||
|
||||
if (font == nil) {
|
||||
const auto &familyMap = getFontFamilyNameMap();
|
||||
auto iter = familyMap.find([_fontName UTF8String]);
|
||||
if (iter != familyMap.end()) {
|
||||
font = [UIFont fontWithName:[NSString stringWithUTF8String:iter->second.c_str()] size:_fontSize];
|
||||
}
|
||||
}
|
||||
|
||||
if (font == nil) {
|
||||
if (_bold) {
|
||||
font = [UIFont boldSystemFontOfSize:_fontSize];
|
||||
} else {
|
||||
font = [UIFont systemFontOfSize:_fontSize];
|
||||
}
|
||||
}
|
||||
return font;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
- (void)updateFontWithName:(NSString *)fontName fontSize:(CGFloat)fontSize bold:(bool)bold italic:(bool)italic {
|
||||
_fontSize = fontSize;
|
||||
_bold = bold;
|
||||
_italic = italic;
|
||||
|
||||
self.fontName = fontName;
|
||||
self.font = [self _createSystemFont];
|
||||
|
||||
NSMutableParagraphStyle *paragraphStyle = [[[NSMutableParagraphStyle alloc] init] autorelease];
|
||||
paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
[paragraphStyle setAlignment:NSTextAlignmentCenter];
|
||||
|
||||
// color
|
||||
NSColor *foregroundColor = [NSColor colorWithRed:1.0f
|
||||
green:1.0f
|
||||
blue:1.0f
|
||||
alpha:1.0f];
|
||||
|
||||
// attribute
|
||||
if (_italic) {
|
||||
self.tokenAttributesDict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
|
||||
foregroundColor, NSForegroundColorAttributeName,
|
||||
_font, NSFontAttributeName,
|
||||
@(0.25f), NSObliquenessAttributeName,
|
||||
paragraphStyle, NSParagraphStyleAttributeName, nil];
|
||||
} else {
|
||||
self.tokenAttributesDict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
|
||||
foregroundColor, NSForegroundColorAttributeName,
|
||||
_font, NSFontAttributeName,
|
||||
paragraphStyle, NSParagraphStyleAttributeName, nil];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (void)recreateBufferWithWidth:(NSInteger)width height:(NSInteger)height {
|
||||
_width = width = width > 0 ? width : 1;
|
||||
_height = height = height > 0 ? height : 1;
|
||||
NSUInteger textureSize = width * height * 4;
|
||||
unsigned char *data = (unsigned char *)malloc(sizeof(unsigned char) * textureSize);
|
||||
memset(data, 0, textureSize);
|
||||
_imageData.fastSet(data, static_cast<uint32_t>(textureSize));
|
||||
|
||||
if (_context != nil) {
|
||||
CGContextRelease(_context);
|
||||
_context = nil;
|
||||
}
|
||||
|
||||
#if CC_PLATFORM == CC_PLATFORM_MACOS
|
||||
if (_currentGraphicsContext != nil) {
|
||||
[_currentGraphicsContext release];
|
||||
_currentGraphicsContext = nil;
|
||||
}
|
||||
#endif
|
||||
|
||||
// draw text
|
||||
_colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
_context = CGBitmapContextCreate(data,
|
||||
width,
|
||||
height,
|
||||
8,
|
||||
width * 4,
|
||||
_colorSpace,
|
||||
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
|
||||
if (nil == _context) {
|
||||
CGColorSpaceRelease(_colorSpace); //REFINE: HOWTO RELEASE?
|
||||
_colorSpace = nil;
|
||||
}
|
||||
|
||||
#if CC_PLATFORM == CC_PLATFORM_MACOS
|
||||
_currentGraphicsContext = [NSGraphicsContext graphicsContextWithCGContext:_context flipped:NO];
|
||||
[_currentGraphicsContext retain];
|
||||
#else
|
||||
// move Y rendering to the top of the image
|
||||
CGContextTranslateCTM(_context, 0.0f, _height);
|
||||
|
||||
//NOTE: NSString draws in UIKit referential i.e. renders upside-down compared to CGBitmapContext referential
|
||||
CGContextScaleCTM(_context, 1.0f, -1.0f);
|
||||
#endif
|
||||
}
|
||||
|
||||
- (NSSize)measureText:(NSString *)text {
|
||||
NSAttributedString *stringWithAttributes = [[[NSAttributedString alloc] initWithString:text
|
||||
attributes:_tokenAttributesDict] autorelease];
|
||||
|
||||
NSSize textRect = NSZeroSize;
|
||||
textRect.width = CGFLOAT_MAX;
|
||||
textRect.height = CGFLOAT_MAX;
|
||||
|
||||
NSSize dim = [stringWithAttributes boundingRectWithSize:textRect options:(NSStringDrawingOptions)(NSStringDrawingUsesLineFragmentOrigin)context:nil].size;
|
||||
|
||||
dim.width = ceilf(dim.width);
|
||||
dim.height = ceilf(dim.height);
|
||||
|
||||
return dim;
|
||||
}
|
||||
|
||||
- (NSPoint)convertDrawPoint:(NSPoint)point text:(NSString *)text {
|
||||
// The parameter 'point' is located at left-bottom position.
|
||||
// Need to adjust 'point' according 'text align' & 'text base line'.
|
||||
NSSize textSize = [self measureText:text];
|
||||
|
||||
if (_textAlign == cc::ICanvasRenderingContext2D::TextAlign::CENTER) {
|
||||
point.x -= textSize.width / 2.0f;
|
||||
} else if (_textAlign == cc::ICanvasRenderingContext2D::TextAlign::RIGHT) {
|
||||
point.x -= textSize.width;
|
||||
}
|
||||
|
||||
#if CC_PLATFORM == CC_PLATFORM_MACOS
|
||||
// The origin on macOS is bottom-left by default,
|
||||
// so we need to convert y from top-left origin to bottom-left origin.
|
||||
point.y = _height - point.y;
|
||||
if (_textBaseLine == cc::ICanvasRenderingContext2D::TextBaseline::TOP) {
|
||||
point.y += -textSize.height;
|
||||
} else if (_textBaseLine == cc::ICanvasRenderingContext2D::TextBaseline::MIDDLE) {
|
||||
point.y += -textSize.height / 2.0f;
|
||||
} else if (_textBaseLine == cc::ICanvasRenderingContext2D::TextBaseline::BOTTOM) {
|
||||
// drawAtPoint default
|
||||
} else if (_textBaseLine == cc::ICanvasRenderingContext2D::TextBaseline::ALPHABETIC) {
|
||||
point.y += _font.descender;
|
||||
}
|
||||
#else
|
||||
if (_textBaseLine == cc::ICanvasRenderingContext2D::TextBaseline::TOP) {
|
||||
// drawAtPoint default
|
||||
} else if (_textBaseLine == cc::ICanvasRenderingContext2D::TextBaseline::MIDDLE) {
|
||||
point.y += -textSize.height / 2.0f;
|
||||
} else if (_textBaseLine == cc::ICanvasRenderingContext2D::TextBaseline::BOTTOM) {
|
||||
point.y += -textSize.height;
|
||||
} else if (_textBaseLine == cc::ICanvasRenderingContext2D::TextBaseline::ALPHABETIC) {
|
||||
point.y -= _font.ascender;
|
||||
}
|
||||
#endif
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
- (bool) isShadowEnabled {
|
||||
if (_shadowColor && (_shadowBlur > 0 || _shadowOffsetX > 0 || _shadowOffsetY > 0)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
- (void)fillText:(NSString *)text x:(CGFloat)x y:(CGFloat)y maxWidth:(CGFloat)maxWidth {
|
||||
if (text.length == 0)
|
||||
return;
|
||||
|
||||
NSPoint drawPoint = [self convertDrawPoint:NSMakePoint(x, y) text:text];
|
||||
|
||||
NSMutableParagraphStyle *paragraphStyle = [[[NSMutableParagraphStyle alloc] init] autorelease];
|
||||
paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
|
||||
[_tokenAttributesDict removeObjectForKey:NSStrokeColorAttributeName];
|
||||
|
||||
[_tokenAttributesDict setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];
|
||||
[_tokenAttributesDict setObject:[NSColor colorWithRed:_fillStyle[0] green:_fillStyle[1] blue:_fillStyle[2] alpha:_fillStyle[3]]
|
||||
forKey:NSForegroundColorAttributeName];
|
||||
|
||||
[self saveContext];
|
||||
|
||||
// text color
|
||||
CGContextSetRGBFillColor(_context, _fillStyle[0], _fillStyle[1], _fillStyle[2], _fillStyle[3]);
|
||||
CGContextSetShouldSubpixelQuantizeFonts(_context, false);
|
||||
CGContextBeginTransparencyLayerWithRect(_context, CGRectMake(0, 0, _width, _height), nullptr);
|
||||
CGContextSetTextDrawingMode(_context, kCGTextFill);
|
||||
if ([self isShadowEnabled]) {
|
||||
// https://developer.apple.com/library/archive/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/GraphicsDrawingOverview/GraphicsDrawingOverview.html
|
||||
/* Core Graphics uses lower-left-origin coordinate system;
|
||||
but the api CGContextSetShadowWithColor's parameter 'offset' accept base space(relative to view: upper-left-origin coordinate system),
|
||||
and _shadowOffsetY is edited by lower-left-origin coordinate system, so here we need to multiply the change in y direction by -1
|
||||
*/
|
||||
CGContextSetShadowWithColor(_context, CGSizeMake(_shadowOffsetX, -_shadowOffsetY), _shadowBlur, _shadowColor.CGColor);
|
||||
}
|
||||
|
||||
NSAttributedString *stringWithAttributes = [[[NSAttributedString alloc] initWithString:text
|
||||
attributes:_tokenAttributesDict] autorelease];
|
||||
|
||||
[stringWithAttributes drawAtPoint:drawPoint];
|
||||
|
||||
CGContextEndTransparencyLayer(_context);
|
||||
|
||||
[self restoreContext];
|
||||
}
|
||||
|
||||
- (void)strokeText:(NSString *)text x:(CGFloat)x y:(CGFloat)y maxWidth:(CGFloat)maxWidth {
|
||||
if (text.length == 0)
|
||||
return;
|
||||
|
||||
NSPoint drawPoint = [self convertDrawPoint:NSMakePoint(x, y) text:text];
|
||||
|
||||
NSMutableParagraphStyle *paragraphStyle = [[[NSMutableParagraphStyle alloc] init] autorelease];
|
||||
paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
|
||||
[_tokenAttributesDict removeObjectForKey:NSForegroundColorAttributeName];
|
||||
|
||||
[_tokenAttributesDict setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];
|
||||
[_tokenAttributesDict setObject:[NSColor colorWithRed:_strokeStyle[0]
|
||||
green:_strokeStyle[1]
|
||||
blue:_strokeStyle[2]
|
||||
alpha:_strokeStyle[3]]
|
||||
forKey:NSStrokeColorAttributeName];
|
||||
|
||||
[self saveContext];
|
||||
|
||||
// text color
|
||||
CGContextSetRGBStrokeColor(_context, _strokeStyle[0], _strokeStyle[1], _strokeStyle[2], _strokeStyle[3]);
|
||||
CGContextSetRGBFillColor(_context, _fillStyle[0], _fillStyle[1], _fillStyle[2], _fillStyle[3]);
|
||||
CGContextSetLineWidth(_context, _lineWidth);
|
||||
CGContextSetLineJoin(_context, kCGLineJoinRound);
|
||||
CGContextSetShouldSubpixelQuantizeFonts(_context, false);
|
||||
CGContextBeginTransparencyLayerWithRect(_context, CGRectMake(0, 0, _width, _height), nullptr);
|
||||
|
||||
CGContextSetTextDrawingMode(_context, kCGTextStroke);
|
||||
if ([self isShadowEnabled]) {
|
||||
// https://developer.apple.com/library/archive/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/GraphicsDrawingOverview/GraphicsDrawingOverview.html
|
||||
/* Core Graphics uses lower-left-origin coordinate system;
|
||||
but the api CGContextSetShadowWithColor's parameter 'offset' accept base space(relative to view: upper-left-origin coordinate system),
|
||||
and _shadowOffsetY is edited by lower-left-origin coordinate system, so here we need to multiply the change in y direction by -1
|
||||
*/
|
||||
CGContextSetShadowWithColor(_context, CGSizeMake(_shadowOffsetX, -_shadowOffsetY), _shadowBlur, _shadowColor.CGColor);
|
||||
}
|
||||
|
||||
NSAttributedString *stringWithAttributes = [[[NSAttributedString alloc] initWithString:text
|
||||
attributes:_tokenAttributesDict] autorelease];
|
||||
|
||||
[stringWithAttributes drawAtPoint:drawPoint];
|
||||
|
||||
CGContextEndTransparencyLayer(_context);
|
||||
|
||||
[self restoreContext];
|
||||
}
|
||||
|
||||
- (void)setFillStyleWithRed:(CGFloat)r green:(CGFloat)g blue:(CGFloat)b alpha:(CGFloat)a {
|
||||
_fillStyle[0] = r;
|
||||
_fillStyle[1] = g;
|
||||
_fillStyle[2] = b;
|
||||
_fillStyle[3] = a;
|
||||
}
|
||||
|
||||
- (void)setStrokeStyleWithRed:(CGFloat)r green:(CGFloat)g blue:(CGFloat)b alpha:(CGFloat)a {
|
||||
_strokeStyle[0] = r;
|
||||
_strokeStyle[1] = g;
|
||||
_strokeStyle[2] = b;
|
||||
_strokeStyle[3] = a;
|
||||
}
|
||||
|
||||
- (void)setShadowColorWithRed:(CGFloat)r green:(CGFloat)g blue:(CGFloat)b alpha:(CGFloat)a {
|
||||
_shadowColor = [NSColor colorWithRed:r green:g blue:b alpha:a];
|
||||
[_shadowColor retain];
|
||||
}
|
||||
|
||||
- (const cc::Data &)getDataRef {
|
||||
return _imageData;
|
||||
}
|
||||
|
||||
- (void)clearRect:(CGRect)rect {
|
||||
if (_imageData.isNull())
|
||||
return;
|
||||
|
||||
rect.origin.x = floor(rect.origin.x);
|
||||
rect.origin.y = floor(rect.origin.y);
|
||||
rect.size.width = floor(rect.size.width);
|
||||
rect.size.height = floor(rect.size.height);
|
||||
|
||||
if (rect.origin.x < 0) rect.origin.x = 0;
|
||||
if (rect.origin.y < 0) rect.origin.y = 0;
|
||||
|
||||
if (rect.size.width < 1 || rect.size.height < 1)
|
||||
return;
|
||||
//REFINE:
|
||||
// CC_ASSERT(rect.origin.x == 0 && rect.origin.y == 0);
|
||||
memset((void *)_imageData.getBytes(), 0x00, _imageData.getSize());
|
||||
}
|
||||
|
||||
- (void)fillRect:(CGRect)rect {
|
||||
[self saveContext];
|
||||
|
||||
NSColor *color = [NSColor colorWithRed:_fillStyle[0] green:_fillStyle[1] blue:_fillStyle[2] alpha:_fillStyle[3]];
|
||||
[color setFill];
|
||||
#if CC_PLATFORM == CC_PLATFORM_MACOS
|
||||
CGRect tmpRect = CGRectMake(rect.origin.x, _height - rect.origin.y - rect.size.height, rect.size.width, rect.size.height);
|
||||
[NSBezierPath fillRect:tmpRect];
|
||||
#else
|
||||
NSBezierPath *path = [NSBezierPath bezierPathWithRect:rect];
|
||||
[path fill];
|
||||
#endif
|
||||
[self restoreContext];
|
||||
}
|
||||
|
||||
- (void)saveContext {
|
||||
#if CC_PLATFORM == CC_PLATFORM_MACOS
|
||||
// save the old graphics context
|
||||
_oldGraphicsContext = [NSGraphicsContext currentContext];
|
||||
// store the current context
|
||||
[NSGraphicsContext setCurrentContext:_currentGraphicsContext];
|
||||
// push graphics state to stack
|
||||
[NSGraphicsContext saveGraphicsState];
|
||||
[[NSGraphicsContext currentContext] setShouldAntialias:YES];
|
||||
#else
|
||||
// save the old graphics context
|
||||
_oldContext = UIGraphicsGetCurrentContext();
|
||||
// store the current context
|
||||
UIGraphicsPushContext(_context);
|
||||
CGContextSaveGState(_context);
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)restoreContext {
|
||||
#if CC_PLATFORM == CC_PLATFORM_MACOS
|
||||
// pop the context
|
||||
[NSGraphicsContext restoreGraphicsState];
|
||||
// reset the old graphics context
|
||||
[NSGraphicsContext setCurrentContext:_oldGraphicsContext];
|
||||
_oldGraphicsContext = nil;
|
||||
#else
|
||||
// pop the context
|
||||
CGContextRestoreGState(_context);
|
||||
// reset the old graphics context
|
||||
UIGraphicsPopContext();
|
||||
_oldContext = nil;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)beginPath {
|
||||
}
|
||||
|
||||
- (void)stroke {
|
||||
NSColor *color = [NSColor colorWithRed:_strokeStyle[0] green:_strokeStyle[1] blue:_strokeStyle[2] alpha:_strokeStyle[3]];
|
||||
[color setStroke];
|
||||
[_path setLineWidth:_lineWidth];
|
||||
[_path stroke];
|
||||
}
|
||||
|
||||
- (void)moveToX:(float)x y:(float)y {
|
||||
#if CC_PLATFORM == CC_PLATFORM_MACOS
|
||||
[_path moveToPoint:NSMakePoint(x, _height - y)];
|
||||
#else
|
||||
[_path moveToPoint:NSMakePoint(x, y)];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)lineToX:(float)x y:(float)y {
|
||||
#if CC_PLATFORM == CC_PLATFORM_MACOS
|
||||
[_path lineToPoint:NSMakePoint(x, _height - y)];
|
||||
#else
|
||||
[_path addLineToPoint:NSMakePoint(x, y)];
|
||||
#endif
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
namespace cc {
|
||||
CanvasRenderingContext2DDelegate::CanvasRenderingContext2DDelegate() {
|
||||
_impl = [[CanvasRenderingContext2DDelegateImpl alloc] init];
|
||||
}
|
||||
|
||||
CanvasRenderingContext2DDelegate::~CanvasRenderingContext2DDelegate() {
|
||||
[_impl release];
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::recreateBuffer(float w, float h) {
|
||||
[_impl recreateBufferWithWidth:w height:h];
|
||||
}
|
||||
|
||||
const cc::Data &CanvasRenderingContext2DDelegate::getDataRef() const {
|
||||
static Data data;
|
||||
data = [_impl getDataRef];
|
||||
unMultiplyAlpha(data.getBytes(), data.getSize());
|
||||
return data;
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::beginPath() {
|
||||
[_impl beginPath];
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::closePath() {
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::moveTo(float x, float y) {
|
||||
[_impl moveToX:x y:y];
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::lineTo(float x, float y) {
|
||||
[_impl lineToX:x y:y];
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::stroke() {
|
||||
[_impl stroke];
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::saveContext() {
|
||||
[_impl saveContext];
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::restoreContext() {
|
||||
[_impl restoreContext];
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::clearRect(float x, float y, float w, float h) {
|
||||
[_impl clearRect:CGRectMake(x, y, w, h)];
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::fill() {
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::setLineCap(const ccstd::string &lineCap) {
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::setLineJoin(const ccstd::string &lineJoin) {
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::rect(float x, float y, float w, float h) {
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::fillRect(float x, float y, float w, float h) {
|
||||
[_impl fillRect:CGRectMake(x, y, w, h)];
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::fillText(const ccstd::string &text, float x, float y, float maxWidth) {
|
||||
[_impl fillText:[NSString stringWithUTF8String:text.c_str()] x:x y:y maxWidth:maxWidth];
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::strokeText(const ccstd::string &text, float x, float y, float maxWidth) {
|
||||
[_impl strokeText:[NSString stringWithUTF8String:text.c_str()] x:x y:y maxWidth:maxWidth];
|
||||
}
|
||||
|
||||
CanvasRenderingContext2DDelegate::Size CanvasRenderingContext2DDelegate::measureText(const ccstd::string &text) {
|
||||
NSString *str = [NSString stringWithUTF8String:text.c_str()];
|
||||
if (str == nil) {
|
||||
ccstd::string textNew;
|
||||
cc::StringUtils::UTF8LooseFix(text, textNew);
|
||||
str = [NSString stringWithUTF8String:textNew.c_str()];
|
||||
}
|
||||
CGSize size = [_impl measureText:str];
|
||||
return CanvasRenderingContext2DDelegate::Size{(float)size.width, (float)size.height};
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::updateFont(const ccstd::string &fontName, float fontSize, bool bold, bool italic, bool oblique, bool smallCaps) {
|
||||
CGFloat gfloatFontSize = fontSize;
|
||||
[_impl updateFontWithName:[NSString stringWithUTF8String:fontName.c_str()] fontSize:gfloatFontSize bold:bold italic:italic];
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::setTextAlign(TextAlign align) {
|
||||
_impl.textAlign = align;
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::setTextBaseline(TextBaseline baseline) {
|
||||
_impl.textBaseLine = baseline;
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::setFillStyle(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
|
||||
[_impl setFillStyleWithRed:r / 255.0f green:g / 255.0f blue:b / 255.0f alpha:a / 255.0f];
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::setStrokeStyle(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
|
||||
[_impl setStrokeStyleWithRed:r / 255.0f green:g / 255.0f blue:b / 255.0f alpha:a / 255.0f];
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::setLineWidth(float lineWidth) {
|
||||
_impl.lineWidth = lineWidth;
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::fillImageData(const Data &imageData, float imageWidth, float imageHeight, float offsetX, float offsetY) {
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::fillData() {
|
||||
}
|
||||
|
||||
#define CLAMP(V, HI) std::min((V), (HI))
|
||||
void CanvasRenderingContext2DDelegate::unMultiplyAlpha(unsigned char *ptr, uint32_t size) const {
|
||||
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 CanvasRenderingContext2DDelegate::setShadowBlur(float blur) {
|
||||
_impl.shadowBlur = blur * 0.5f;
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::setShadowColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
|
||||
[_impl setShadowColorWithRed:r / 255.0f green:g / 255.0f blue:b / 255.0f alpha:a / 255.0f];
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::setShadowOffsetX(float offsetX) {
|
||||
_impl.shadowOffsetX = offsetX;
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::setShadowOffsetY(float offsetY) {
|
||||
_impl.shadowOffsetY = offsetY;
|
||||
}
|
||||
|
||||
} // namespace cc
|
||||
61
cocos/platform/empty/EmptyPlatform.cpp
Normal file
61
cocos/platform/empty/EmptyPlatform.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "platform/empty/EmptyPlatform.h"
|
||||
#include "modules/Accelerometer.h"
|
||||
#include "modules/Battery.h"
|
||||
#include "modules/Network.h"
|
||||
#include "modules/Screen.h"
|
||||
#include "modules/System.h"
|
||||
#include "modules/SystemWindow.h"
|
||||
#include "modules/Vibrator.h"
|
||||
#include "platform/interfaces/OSInterface.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
namespace cc {
|
||||
EmptyPlatform::EmptyPlatform() = default;
|
||||
EmptyPlatform::~EmptyPlatform() {
|
||||
}
|
||||
|
||||
int32_t EmptyPlatform::init() {
|
||||
registerInterface(std::make_shared<Accelerometer>());
|
||||
registerInterface(std::make_shared<Battery>());
|
||||
registerInterface(std::make_shared<Network>());
|
||||
registerInterface(std::make_shared<Screen>());
|
||||
registerInterface(std::make_shared<System>());
|
||||
registerInterface(std::make_shared<SystemWindow>());
|
||||
registerInterface(std::make_shared<Vibrator>());
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t EmptyPlatform::loop() {
|
||||
while (!_quit) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace cc
|
||||
50
cocos/platform/empty/EmptyPlatform.h
Normal file
50
cocos/platform/empty/EmptyPlatform.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "platform/UniversalPlatform.h"
|
||||
#include "platform/empty/modules/SystemWindow.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
class CC_DLL EmptyPlatform : public UniversalPlatform {
|
||||
public:
|
||||
EmptyPlatform();
|
||||
/**
|
||||
* Destructor of WindowPlatform.
|
||||
*/
|
||||
~EmptyPlatform() override;
|
||||
/**
|
||||
* Implementation of Windows platform initialization.
|
||||
*/
|
||||
int32_t init() override;
|
||||
|
||||
int32_t loop() override;
|
||||
|
||||
private:
|
||||
bool _quit{false};
|
||||
};
|
||||
|
||||
} // namespace cc
|
||||
39
cocos/platform/empty/modules/Accelerometer.cpp
Normal file
39
cocos/platform/empty/modules/Accelerometer.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "platform/empty/modules/Accelerometer.h"
|
||||
|
||||
namespace cc {
|
||||
void Accelerometer::setAccelerometerEnabled(bool isEnabled) {
|
||||
}
|
||||
|
||||
void Accelerometer::setAccelerometerInterval(float interval) {
|
||||
}
|
||||
|
||||
const Accelerometer::MotionValue &Accelerometer::getDeviceMotionValue() {
|
||||
static MotionValue motionValue;
|
||||
return motionValue;
|
||||
}
|
||||
|
||||
} // namespace cc
|
||||
49
cocos/platform/empty/modules/Accelerometer.h
Normal file
49
cocos/platform/empty/modules/Accelerometer.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "platform/interfaces/modules/IAccelerometer.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
class CC_DLL Accelerometer : public IAccelerometer {
|
||||
public:
|
||||
/**
|
||||
* To enable or disable accelerometer.
|
||||
*/
|
||||
void setAccelerometerEnabled(bool isEnabled) override;
|
||||
|
||||
/**
|
||||
* Sets the interval of accelerometer.
|
||||
*/
|
||||
void setAccelerometerInterval(float interval) override;
|
||||
|
||||
/**
|
||||
* Gets the motion value of current device.
|
||||
*/
|
||||
const MotionValue &getDeviceMotionValue() override;
|
||||
};
|
||||
|
||||
} // namespace cc
|
||||
32
cocos/platform/empty/modules/Battery.cpp
Normal file
32
cocos/platform/empty/modules/Battery.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#include "platform/empty/modules/Battery.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
float Battery::getBatteryLevel() const {
|
||||
return 1.0F;
|
||||
}
|
||||
|
||||
} // namespace cc
|
||||
36
cocos/platform/empty/modules/Battery.h
Normal file
36
cocos/platform/empty/modules/Battery.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "platform/interfaces/modules/IBattery.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
class CC_DLL Battery : public IBattery {
|
||||
public:
|
||||
float getBatteryLevel() const override;
|
||||
};
|
||||
|
||||
} // namespace cc
|
||||
@@ -0,0 +1,143 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "platform/empty/modules/CanvasRenderingContext2DDelegate.h"
|
||||
#include "platform/empty/EmptyPlatform.h"
|
||||
|
||||
namespace {
|
||||
#define RGB(r, g, b) (int)((int)r | (((int)g) << 8) | (((int)b) << 16))
|
||||
#define RGBA(r, g, b, a) (int)((int)r | (((int)g) << 8) | (((int)b) << 16) | (((int)a) << 24))
|
||||
} // namespace
|
||||
|
||||
namespace cc {
|
||||
|
||||
CanvasRenderingContext2DDelegate::CanvasRenderingContext2DDelegate() {
|
||||
}
|
||||
|
||||
CanvasRenderingContext2DDelegate::~CanvasRenderingContext2DDelegate() {
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::recreateBuffer(float w, float h) {
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::beginPath() {
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::closePath() {
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::moveTo(float x, float y) {
|
||||
//
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::lineTo(float x, float y) {
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::stroke() {
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::saveContext() {
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::restoreContext() {
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::clearRect(float x, float y, float w, float h) {
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::fillRect(float x, float y, float w, float h) {
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::fillText(const ccstd::string &text, float x, float y, float /*maxWidth*/) {
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::strokeText(const ccstd::string &text, float /*x*/, float /*y*/, float /*maxWidth*/) const {
|
||||
}
|
||||
|
||||
CanvasRenderingContext2DDelegate::Size CanvasRenderingContext2DDelegate::measureText(const ccstd::string &text) {
|
||||
return Size{0, 0};
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::updateFont(const ccstd::string &fontName,
|
||||
float fontSize,
|
||||
bool bold,
|
||||
bool italic,
|
||||
bool oblique,
|
||||
bool /* smallCaps */) {
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::setTextAlign(TextAlign align) {
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::setTextBaseline(TextBaseline baseline) {
|
||||
//
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::setFillStyle(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
|
||||
//
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::setStrokeStyle(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
|
||||
//
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::setLineWidth(float lineWidth) {
|
||||
//
|
||||
}
|
||||
|
||||
const cc::Data &CanvasRenderingContext2DDelegate::getDataRef() const {
|
||||
return _imageData;
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::fill() {
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::setLineCap(const ccstd::string &lineCap) {
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::setLineJoin(const ccstd::string &lineJoin) {
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::fillImageData(const Data & /* imageData */,
|
||||
float /* imageWidth */,
|
||||
float /* imageHeight */,
|
||||
float /* offsetX */,
|
||||
float /* offsetY */) {
|
||||
//XCreateImage(display, visual, DefaultDepth(display,DefaultScreen(display)), ZPixmap, 0, image32, width, height, 32, 0);
|
||||
//XPutImage(dpy, w, gc, image, 0, 0, 50, 60, 40, 30);
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::strokeText(const ccstd::string & /* text */,
|
||||
float /* x */,
|
||||
float /* y */,
|
||||
float /* maxWidth */) {
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2DDelegate::rect(float /* x */,
|
||||
float /* y */,
|
||||
float /* w */,
|
||||
float /* h */) {
|
||||
}
|
||||
|
||||
} // namespace cc
|
||||
@@ -0,0 +1,86 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "platform/interfaces/modules/canvas/ICanvasRenderingContext2D.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <regex>
|
||||
#include "base/csscolorparser.h"
|
||||
#include "base/std/container/array.h"
|
||||
#include "cocos/bindings/manual/jsb_platform.h"
|
||||
#include "math/Math.h"
|
||||
#include "platform/FileUtils.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
class CC_DLL CanvasRenderingContext2DDelegate : public ICanvasRenderingContext2D::Delegate {
|
||||
public:
|
||||
using Point = ccstd::array<float, 2>;
|
||||
using Vec2 = ccstd::array<float, 2>;
|
||||
using Size = ccstd::array<float, 2>;
|
||||
using Color4F = ccstd::array<float, 4>;
|
||||
using TextAlign = ICanvasRenderingContext2D::TextAlign;
|
||||
using TextBaseline = ICanvasRenderingContext2D::TextBaseline;
|
||||
CanvasRenderingContext2DDelegate();
|
||||
~CanvasRenderingContext2DDelegate() override;
|
||||
|
||||
void recreateBuffer(float w, float h) override;
|
||||
void beginPath() override;
|
||||
void closePath() override;
|
||||
void moveTo(float x, float y) override;
|
||||
void lineTo(float x, float y) override;
|
||||
void stroke() override;
|
||||
void saveContext() override;
|
||||
void restoreContext() override;
|
||||
void clearRect(float /*x*/, float /*y*/, float w, float h) override;
|
||||
void fillRect(float x, float y, float w, float h) override;
|
||||
void fillText(const ccstd::string &text, float x, float y, float /*maxWidth*/) override;
|
||||
void strokeText(const ccstd::string &text, float /*x*/, float /*y*/, float /*maxWidth*/) const;
|
||||
Size measureText(const ccstd::string &text) override;
|
||||
void updateFont(const ccstd::string &fontName, float fontSize, bool bold, bool italic, bool oblique, bool smallCaps) override;
|
||||
void setTextAlign(TextAlign align) override;
|
||||
void setTextBaseline(TextBaseline baseline) override;
|
||||
void setFillStyle(uint8_t r, uint8_t g, uint8_t b, uint8_t a) override;
|
||||
void setStrokeStyle(uint8_t r, uint8_t g, uint8_t b, uint8_t a) override;
|
||||
void setLineWidth(float lineWidth) override;
|
||||
const cc::Data &getDataRef() const override;
|
||||
void fill() override;
|
||||
void setLineCap(const ccstd::string &lineCap) override;
|
||||
void setLineJoin(const ccstd::string &lineCap) override;
|
||||
void fillImageData(const Data &imageData, float imageWidth, float imageHeight, float offsetX, float offsetY) override;
|
||||
void strokeText(const ccstd::string &text, float /*x*/, float /*y*/, float /*maxWidth*/) override;
|
||||
void rect(float x, float y, float w, float h) override;
|
||||
void updateData() override {}
|
||||
void setShadowBlur(float blur) override {}
|
||||
void setShadowColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) override {}
|
||||
void setShadowOffsetX(float offsetX) override {}
|
||||
void setShadowOffsetY(float offsetY) override {}
|
||||
|
||||
private:
|
||||
cc::Data _imageData;
|
||||
};
|
||||
|
||||
} // namespace cc
|
||||
32
cocos/platform/empty/modules/Network.cpp
Normal file
32
cocos/platform/empty/modules/Network.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2022-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#include "platform/empty/modules/Network.h"
|
||||
|
||||
namespace cc {
|
||||
|
||||
INetwork::NetworkType Network::getNetworkType() const {
|
||||
return INetwork::NetworkType::LAN;
|
||||
}
|
||||
|
||||
} // namespace cc
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user