Compare commits

..

No commits in common. "master" and "passhash" have entirely different histories.

59 changed files with 1217 additions and 3948 deletions

View File

@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

2
.idea/compiler.xml generated
View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
<bytecodeTargetLevel target="1.8" />
</component>
</project>

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
<targetSelectedWithDropDown>
<Target>
<type value="QUICK_BOOT_TARGET" />
<deviceKey>
<Key>
<type value="VIRTUAL_DEVICE_PATH" />
<value value="$USER_HOME$/.android/avd/Pixel_6_API_33.avd" />
</Key>
</deviceKey>
</Target>
</targetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2023-09-01T11:09:07.848547Z" />
</component>
</project>

View File

@ -1,8 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="AutoCloseableResource" enabled="true" level="WARNING" enabled_by_default="true">
<option name="METHOD_MATCHER_CONFIG" value="java.util.Formatter,format,java.io.Writer,append,com.google.common.base.Preconditions,checkNotNull,org.hibernate.Session,close,java.io.PrintWriter,printf,java.io.PrintStream,printf,org.cocos2dx.okhttp3.Call,execute" />
</inspection_tool>
</profile>
</component>

3
.idea/misc.xml generated
View File

@ -1,3 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DesignSurface">
<option name="filePathToZoomLevelMap">
@ -14,7 +15,7 @@
</map>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="adopt-1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View File

@ -4,31 +4,31 @@
"name": "Mac",
"includePath": [
"${workspaceFolder}/**",
"/Users/zhl/Documents/workspace/crypto/cocos_js/**"
"/Users/zhl/Documents/workspace/cocos/cocos2d-x/**"
],
"defines": [],
"macFrameworkPath": [
"/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks"
"/Library/Developer/CommandLineTools/SDKs/MacOSX11.sdk/System/Library/Frameworks"
],
"compilerPath": "/opt/homebrew/opt/llvm/bin/clang",
"compilerPath": "/usr/local/opt/llvm/bin/clang",
"cStandard": "c17",
"cppStandard": "c++14",
"intelliSenseMode": "macos-clang-arm64"
"intelliSenseMode": "macos-clang-x64"
},
{
"name": "wallet",
"includePath": [
"${workspaceFolder}/**",
"/Users/zhl/Documents/workspace/crypto/cocos_js/**"
"/Users/zhl/Documents/workspace/cocos/cocos2d-x/**"
],
"defines": [],
"macFrameworkPath": [
"/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks"
"/Library/Developer/CommandLineTools/SDKs/MacOSX11.sdk/System/Library/Frameworks"
],
"compilerPath": "/opt/homebrew/opt/llvm/bin/clang",
"compilerPath": "/usr/local/opt/llvm/bin/clang",
"cStandard": "c17",
"cppStandard": "c++14",
"intelliSenseMode": "macos-clang-arm64"
"intelliSenseMode": "macos-clang-x64"
}
],
"version": 4

View File

@ -87,6 +87,5 @@
"vector": "cpp",
"__bits": "cpp",
"__verbose_abort": "cpp"
},
"cmake.configureOnOpen": false
}
}

38
.vscode/tasks.json vendored
View File

@ -16,43 +16,7 @@
}
},
{
"label": "Clean:App",
"type": "shell",
"command": "${workspaceRoot}/gradlew app:clean",
"windows": {
"command": "${workspaceRoot}\\gradlew.bat clean "
},
"group": "test",
"presentation": {
"reveal": "always"
}
},
{
"label": "Clean:Unity",
"type": "shell",
"command": "${workspaceRoot}/gradlew unityLibrary:clean",
"windows": {
"command": "${workspaceRoot}\\gradlew.bat clean "
},
"group": "test",
"presentation": {
"reveal": "always"
}
},
{
"label": "Clean:Cocos",
"type": "shell",
"command": "${workspaceRoot}/gradlew libcocos2dx:clean",
"windows": {
"command": "${workspaceRoot}\\gradlew.bat clean "
},
"group": "test",
"presentation": {
"reveal": "always"
}
},
{
"label": "CleanAll",
"label": "CleanApp",
"type": "shell",
"command": "${workspaceRoot}/gradlew clean",
"windows": {

View File

@ -54,6 +54,7 @@ bool AppDelegate::applicationDidFinishLaunching()
// Enable debugger here
// jsb_enable_debugger("0.0.0.0", 6086, false);
#endif
se->setExceptionCallback([](const char *location, const char *message, const char *stack) {
// Send exception information to server like Tencent Bugly.
cocos2d::log("\nUncaught Exception:\n - location : %s\n - msg : %s\n - detail : \n %s\n", location, message, stack);
@ -64,7 +65,6 @@ bool AppDelegate::applicationDidFinishLaunching()
se->start();
se::AutoHandleScope hs;
jsb_run_code("window.platform='game_android';");
jsb_run_script("Data/js/jsb-adapter/jsb-builtin.js");
jsb_run_script("Data/js/jcwallet.js");
jsb_run_script("Data/js/platform.js");

View File

@ -2,7 +2,7 @@
#include "WalletEvent.h"
#include <atomic>
#include <iostream>
#include <cstdarg>
#include "stdarg.h"
#include <string>
#include "cocos/scripting/js-bindings/jswrapper/SeApi.h"
#include "cocos/scripting/js-bindings/manual/jsb_global.h"
@ -12,17 +12,21 @@
#include "base/CCScheduler.h"
#include "scrypt/native-crypto.h"
#include <jni.h>
#include <jni/JniImp.h>
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC)
#include "AppDelegate.h"
#endif
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#ifndef COM_JC_JCFW_CLASS_NAME
#define COM_JC_JCFW_CLASS_NAME com_jc_jcfw_JcSDK
#endif
#define JNI_JCFW(FUNC) JNI_METHOD1(COM_JC_JCFW_CLASS_NAME, FUNC)
#define JNI_JCFW(FUNC) JNI_METHOD1(COM_JC_JCFW_CLASS_NAME,FUNC)
#define JNI_IMP_LOG_TAG "JcWallet"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, JNI_IMP_LOG_TAG, __VA_ARGS__)
#define JNI_IMP_LOG_TAG "JcWallet"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,JNI_IMP_LOG_TAG,__VA_ARGS__)
cocos2d::Application *cocos_android_app_init(int width, int height);
@ -30,278 +34,221 @@ cocos2d::Application *cocos_android_app_init(int width, int height);
using namespace cocos2d;
NS_CC_BEGIN
cocos2d::Application *g_app = nullptr;
JcWallet *JcWallet::_instance = nullptr;
uv_loop_t *loop = nullptr;
bool _isStarted = false;
uv_async_t gasync = {nullptr};
cocos2d::Application *g_app = nullptr;
JcWallet *JcWallet::_instance = nullptr;
uv_loop_t *loop = nullptr;
bool _isStarted = false;
uv_async_t gasync = {0};
struct AsyncTaskData
{
std::mutex mtx;
std::list<std::function<void()>> tasks;
};
struct AsyncTaskData {
std::mutex mtx;
std::list<std::function<void()> > tasks;
};
// run in server thread loop
void flush_tasks_in_server_loop_cb(uv_async_t *asyn)
{
auto *data = (AsyncTaskData *)asyn->data;
std::lock_guard<std::mutex> guard(data->mtx);
while (!data->tasks.empty())
{
// fetch task, run task
data->tasks.front()();
// drop task
data->tasks.pop_front();
}
}
void init_libuv_async_handle(uv_async_t *async)
{
memset(async, 0, sizeof(uv_async_t));
// uv_loop_t *loop = uv_default_loop();
loop = (uv_loop_t *)malloc(sizeof(uv_loop_t));
uv_loop_init(loop);
uv_async_init(loop, async, cocos2d::flush_tasks_in_server_loop_cb);
async->data = new AsyncTaskData();
uv_run(loop, UV_RUN_DEFAULT);
}
// run in game thread, dispatch runnable object into server loop
int schedule_task_into_server_thread_task_queue(uv_async_t *asyn, const std::function<void()>& func)
{
auto *data = (AsyncTaskData *)asyn->data;
{
// run in server thread loop
void flush_tasks_in_server_loop_cb(uv_async_t *asyn) {
AsyncTaskData *data = (AsyncTaskData *) asyn->data;
std::lock_guard<std::mutex> guard(data->mtx);
data->tasks.emplace_back(func);
while (!data->tasks.empty()) {
// fetch task, run task
data->tasks.front()();
// drop task
data->tasks.pop_front();
}
}
// notify server thread to invoke `flush_tasks_in_server_loop_cb()`
return uv_async_send(asyn);
}
#define RUN_IN_GAMETHREAD(task) \
do \
{ \
JcWallet::getInstance()->performFunctionInCocosThread([=]() { task; }); \
} while (0)
#define RUN_IN_SERVERTHREAD(task) \
do \
{ \
schedule_task_into_server_thread_task_queue(&gasync, [=]() { task; }); \
} while (0)
bool runGlobalMethod(const char *name, const se::ValueArray& args, se::Value *value)
{
se::AutoHandleScope scope;
bool ok = false;
auto global = se::ScriptEngine::getInstance()->getGlobalObject();
se::Value func;
if (global->getProperty(name, &func) && func.isObject())
{
ok = func.toObject()->call(args, global, value);
void init_libuv_async_handle(uv_async_t *async) {
memset(async, 0, sizeof(uv_async_t));
// uv_loop_t *loop = uv_default_loop();
loop = (uv_loop_t*)malloc(sizeof(uv_loop_t));
uv_loop_init(loop);
uv_async_init(loop, async, cocos2d::flush_tasks_in_server_loop_cb);
async->data = new AsyncTaskData();
uv_run(loop, UV_RUN_DEFAULT);
}
return ok;
}
bool addToArgArray(se::ValueArray *args, const char *valChar)
{
std::string strVal(valChar);
se::Value tmpVal;
bool ok = true;
ok &= std_string_to_seval(strVal, &tmpVal);
args->push_back(tmpVal);
return ok;
}
// run in game thread, dispatch runnable object into server loop
int schedule_task_into_server_thread_task_queue(uv_async_t *asyn, std::function<void()> func) {
JcWallet::JcWallet()
{
JcWallet::_instance = this;
_functionsToPerform.reserve(30);
std::unique_lock<std::mutex> lock(_performMutex);
}
AsyncTaskData *data = (AsyncTaskData *) asyn->data;
{
std::lock_guard<std::mutex> guard(data->mtx);
data->tasks.emplace_back(func);
}
//notify server thread to invoke `flush_tasks_in_server_loop_cb()`
return uv_async_send(asyn);
}
#define RUN_IN_GAMETHREAD(task) \
do { \
JcWallet::getInstance()->performFunctionInCocosThread([=]() { \
task; \
});\
} while(0)
#define RUN_IN_SERVERTHREAD(task) do { \
schedule_task_into_server_thread_task_queue(&gasync, [=](){ \
task; \
}); \
} while(0)
void JcWallet::initEnv()
{
if (!_isStarted)
{
bool runGlobalMethod(const char *name, se::ValueArray args, se::Value *value) {
se::AutoHandleScope scope;
bool ok = false;
auto global = se::ScriptEngine::getInstance()->getGlobalObject();
se::Value func;
if (global->getProperty(name, &func) && func.isObject()) {
ok = func.toObject()->call(args, global, value);
}
return ok;
}
bool addToArgArray(se::ValueArray *args, const char *valChar) {
std::string strVal(valChar);
se::Value tmpVal;
bool ok = true;
ok &= std_string_to_seval(strVal, &tmpVal);
args->push_back(tmpVal);
return ok;
}
JcWallet::JcWallet() {
JcWallet::_instance = this;
_functionsToPerform.reserve(30);
std::unique_lock<std::mutex> lock(_performMutex);
}
void JcWallet::initEnv() {
if (!_isStarted) {
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC)
g_app = new AppDelegate(1, 1);
g_app = new AppDelegate(1, 1);
#elif (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
g_app = cocos_android_app_init(1, 1);
g_app = cocos_android_app_init(1, 1);
#endif
EventDispatcher::init();
g_app->start();
_isStarted = true;
WalletEvent::Emit("wallet_init_event", "{}");
cocos2d::init_libuv_async_handle(&gasync);
}
}
void JcWallet::initJSThread(const std::shared_ptr<JcWallet>& wallet)
{
JcWallet::initEnv();
}
void JcWallet::tick(float dt)
{
g_app->getScheduler()->update(dt);
EventDispatcher::dispatchTickEvent(0);
}
JcWallet::~JcWallet()
{
EventDispatcher::destroy();
se::ScriptEngine::destroyInstance();
uv_loop_close(loop);
JcWallet::_instance = nullptr;
}
char *JcWallet::runJsMethod(const std::shared_ptr<JSMethodParam>& data)
{
cocos2d::log("thread: %ld call method %s params: %lu", uv_thread_self(), data->methodName.c_str(), data->args.size());
se::Value value;
bool ok = cocos2d::runGlobalMethod(data->methodName.c_str(), data->args, &value);
static std::string result;
if (value.isString())
{
if (ok && !value.isNullOrUndefined())
{
result = value.toString();
EventDispatcher::init();
g_app->start();
_isStarted = true;
WalletEvent::Emit("wallet_init_event", "{}");
// RUN_IN_GAMETHREAD(JcWallet::getInstance()->jsToUnity("wallet_init_event", "{}"));
cocos2d::init_libuv_async_handle(&gasync);
}
else
{
result = "";
}
cocos2d::log("method: %s ::result: %s", data->methodName.c_str(), result.c_str());
RUN_IN_GAMETHREAD(JcWallet::jsToUnity(data->funId, result));
// WalletEvent::Emit(data->funId.c_str(), result.c_str());
}
return const_cast<char *>(result.c_str());
}
void JcWallet::performFunctionInCocosThread(const std::function<void()> &function)
{
_performMutex.lock();
_functionsToPerform.push_back(function);
_performMutex.unlock();
}
void JcWallet::tickRun()
{
_performMutex.lock();
if (!_functionsToPerform.empty())
{
// fixed #4123: Save the callback functions, they must be invoked after '_performMutex.unlock()', otherwise if new functions are added in callback, it will cause thread deadlock.
auto temp = _functionsToPerform;
for (const auto &function : temp)
{
function();
}
_functionsToPerform.clear();
void JcWallet::initJSThread(std::shared_ptr<JcWallet> wallet) {
wallet->initEnv();
}
_performMutex.unlock();
}
void JcWallet::jsToUnity(const std::string& funId, const std::string& msg)
{
WalletEvent::Emit(funId.c_str(), msg.c_str());
}
void JcWallet::tick(float dt) {
g_app->getScheduler()->update(dt);
EventDispatcher::dispatchTickEvent(0);
}
extern "C"
{
void initEnv()
{
if (!_isStarted)
{
JcWallet::~JcWallet() {
EventDispatcher::destroy();
se::ScriptEngine::destroyInstance();
uv_loop_close(loop);
JcWallet::_instance = nullptr;
}
char *JcWallet::runJsMethod(std::shared_ptr<JSMethodParam> data) {
cocos2d::log("thread: %ld call method %s params: %d", uv_thread_self(), data->methodName.c_str(), data->args.size());
se::Value value;
bool ok = cocos2d::runGlobalMethod(data->methodName.c_str(), data->args, &value);
static std::string result;
if (value.isString()) {
if (ok && !value.isNullOrUndefined()) {
result = value.toString();
} else {
result = "";
}
cocos2d::log("method: %s ::result: %s", data->methodName.c_str(), result.c_str());
RUN_IN_GAMETHREAD(JcWallet::getInstance()->jsToUnity(data->funId, result));
// WalletEvent::Emit(data->funId.c_str(), result.c_str());
}
return const_cast<char *>(result.c_str());
}
void JcWallet::performFunctionInCocosThread( const std::function<void()> &function) {
_performMutex.lock();
_functionsToPerform.push_back(function);
_performMutex.unlock();
}
void JcWallet::tickRun() {
_performMutex.lock();
if( !_functionsToPerform.empty() ) {
// fixed #4123: Save the callback functions, they must be invoked after '_performMutex.unlock()', otherwise if new functions are added in callback, it will cause thread deadlock.
auto temp = _functionsToPerform;
for( const auto &function : temp ) {
function();
}
_functionsToPerform.clear();
}
_performMutex.unlock();
}
void JcWallet::jsToUnity(std::string funId, std::string msg) {
WalletEvent::Emit(funId.c_str(), msg.c_str());
}
extern "C" {
void initEnv() {
if (!_isStarted) {
new JcWallet();
std::shared_ptr<JcWallet> wallet(JcWallet::getInstance());
std::thread([=]()
{ JcWallet::initJSThread(wallet); })
.detach();
std::thread([=]() {
JcWallet::initJSThread(wallet);
}).detach();
}
}
void tick(float dt)
{
if (_isStarted)
{
schedule_task_into_server_thread_task_queue(&gasync, [=]()
{ JcWallet::getInstance()->tick(dt); });
JcWallet::getInstance()->tickRun();
}
void tick(float dt) {
if (_isStarted) {
schedule_task_into_server_thread_task_queue(&gasync, [=](){
JcWallet::getInstance()->tick(dt);
});
JcWallet::getInstance()->tickRun();
}
}
void tick2(float dt)
{
// if (_isStarted)
// {
// schedule_task_into_server_thread_task_queue(&gasync, [=]()
// { JcWallet::getInstance()->tick(dt); });
// JcWallet::getInstance()->tickRun();
// }
}
int runWalletMethod(const char *funId, const char *methodName, int paramCount, char **paramList)
{
auto *data = new JSMethodParam();
int runWalletMethod(const char *funId, const char *methodName, int paramCount, char **paramList) {
JSMethodParam *data = new JSMethodParam();
std::string methodNameStr(methodName);
data->methodName = methodNameStr;
data->funId = funId;
data->paramCount = paramCount;
addToArgArray(&data->args, funId);
for (int i = 0; i < paramCount; i++)
{
for (int i = 0; i < paramCount; i++) {
char *arg = *(paramList + i);
addToArgArray(&data->args, arg);
}
std::shared_ptr<JSMethodParam> params(data);
int result = schedule_task_into_server_thread_task_queue(&gasync, [=]()
{ JcWallet::runJsMethod(params); });
int result = schedule_task_into_server_thread_task_queue(&gasync, [=](){
JcWallet::getInstance()->runJsMethod(params);
});
return result == 0 ? 1 : 0;
}
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
JNIEXPORT jint JNICALL JNI_JCFW(runJS)(JNIEnv *env, jclass clazz, jstring j_fun_id, jstring j_method_name, jobjectArray j_params)
{
if (!_isStarted) {
return 0;
}
std::string funId = JniHelper::jstring2string(j_fun_id);
std::string methodName = JniHelper::jstring2string(j_method_name);
JNIEXPORT jint JNICALL JNI_JCFW(runJS)(JNIEnv *env, jclass clazz, jstring jfunId, jstring jmethodName, jstring jparams) {
std::string funId = JniHelper::jstring2string(jfunId);
std::string methodName = JniHelper::jstring2string(jmethodName);
LOGD("jni call %s | %s", methodName.c_str(), funId.c_str());
auto *data = new JSMethodParam();
JSMethodParam *data = new JSMethodParam();
data->methodName = methodName;
data->funId = funId;
jsize count = env->GetArrayLength(j_params);
data->paramCount = count;
data->paramCount = 1;
addToArgArray(&data->args, funId.c_str());
for (int i = 0; i < count; i++)
{
auto j_str = (jstring)env->GetObjectArrayElement(j_params, i);
std::string s_str = JniHelper::jstring2string(j_str);
addToArgArray(&data->args, s_str.c_str());
}
std::string sstr = JniHelper::jstring2string(jparams);
addToArgArray(&data->args, sstr.c_str());
std::shared_ptr<JSMethodParam> params(data);
int result = schedule_task_into_server_thread_task_queue(&gasync, [=]()
{ JcWallet::runJsMethod(params); });
int result = schedule_task_into_server_thread_task_queue(&gasync, [=](){
JcWallet::getInstance()->runJsMethod(params);
});
return result == 0 ? 1 : 0;
}
JNIEXPORT jstring JNICALL JNI_JCFW(decryptPass)(JNIEnv *env, jclass clazz, jstring jaccount, jstring jpass)
{
std::string pass_encrypted = JniHelper::jstring2string(jpass);
std::string account = JniHelper::jstring2string(jaccount);
std::string keyStr = account + "0x741482aE1480E552735E44Ff3A733448AcBbeD8d";
std::string passDecrypt = decrypt_aes(pass_encrypted, keyStr);
return env->NewStringUTF(passDecrypt.c_str());
}
JNIEXPORT void JNICALL JNI_JCFW(tick)(JNIEnv *env, jclass clazz, jfloat dt)
{
tick2(dt);
}
#endif
}
}
NS_CC_END
static bool getOrCreatePlainObject_r(const char *name, se::Object *parent, se::Object **outObj)
static bool getOrCreatePlainObject_r(const char* name, se::Object* parent, se::Object** outObj)
{
assert(parent != nullptr);
assert(outObj != nullptr);
@ -320,12 +267,10 @@ static bool getOrCreatePlainObject_r(const char *name, se::Object *parent, se::O
return true;
}
bool jsb_wallet_callback(se::State &s)
{
const auto &args = s.args();
bool jsb_wallet_callback(se::State& s) {
const auto& args = s.args();
size_t argc = args.size();
if (argc >= 2)
{
if (argc >= 2) {
bool ok;
std::string funId;
ok = seval_to_std_string(args[0], &funId);
@ -333,23 +278,18 @@ bool jsb_wallet_callback(se::State &s)
std::string msg;
ok = seval_to_std_string(args[1], &msg);
SE_PRECONDITION2(ok, false, "Error processing arguments");
if (funId.find("webpage_") == 0)
{
onProxyCBJNI(funId, msg);
}
else
{
RUN_IN_GAMETHREAD(JcWallet::jsToUnity(funId, msg));
}
// WalletEvent::Emit(funId.c_str(), msg.c_str());
RUN_IN_GAMETHREAD(JcWallet::getInstance()->jsToUnity(funId, msg));
return true;
}
return false;
}
SE_BIND_FUNC(jsb_wallet_callback)
bool jsb_register_walletevent_modules(se::Object *global)
{
bool jsb_register_walletevent_modules(se::Object* global) {
getOrCreatePlainObject_r("jsb", global, &__jsbObj);
__jsbObj->defineFunction("jcCallback", _SE(jsb_wallet_callback));
return true;
}

View File

@ -20,7 +20,7 @@ NS_CC_BEGIN
class CC_DLL JcWallet {
public:
static void initEnv();
void initEnv();
JcWallet();
@ -28,9 +28,9 @@ NS_CC_BEGIN
static JcWallet *getInstance() { return _instance; }
static char *runJsMethod(const std::shared_ptr<JSMethodParam>& data);
char *runJsMethod(std::shared_ptr<JSMethodParam> data);
static void initJSThread(const std::shared_ptr<JcWallet>& wallet);
static void initJSThread(std::shared_ptr<JcWallet> wallet);
static void tick(float dt);
@ -38,7 +38,7 @@ NS_CC_BEGIN
void tickRun();
static void jsToUnity(const std::string& funId, const std::string& msg);
void jsToUnity(std::string funId, std::string msg);
private:
static JcWallet *_instance;

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,4 @@
console.log('>> begin load wallet main file.');
!window.jc || !jc.wallet ? new jcwallet.default() : jc.wallet;
function promiseCb(funId, promiseFun, dataParser) {
dataParser = dataParser || ((v) => v);
promiseFun
@ -13,51 +12,23 @@ function promiseCb(funId, promiseFun, dataParser) {
}
/**
* oauth login before init internal wallet
* @param {string} channel:
* 0: google,
* 1: apple,
* 2: tiktok,
* 3: facebook,
* 4: twitter
* 5: tg,
* 6: email,
* 7: discord
* 10: client
* @param {string} env: dev release
* @param {string} account: guest account to bind
* @return {string} {token: string, address: string | null}
* token: token for wallet services
* address: address of wallet if already created (optional)
* @param {*} channel 0: google, 1: apple, 2: tiktok, 3: facebook, 4: twitter 5: tg
*/
function walletLogin(funId, channel, env, account) {
function walletLogin(funId, channel) {
channel = parseInt(channel);
env = env || 'dev';
console.log('walletLogin: ' + channel);
promiseCb(funId, jc.wallet.preLogin(channel, env, account));
}
function logout(funId, channel) {
channel = parseInt(channel || '0');
promiseCb(funId, jc.wallet.logout(channel));
}
function updateGameInfo(funId, info) {
jsb.updateGameInfo(funId, info);
promiseCb(funId, Promise.resolve(1));
const wallet = !window.jc || !jc.wallet ? new jcwallet.default({ type: 0 }) : jc.wallet;
promiseCb(funId, wallet.preLogin(channel));
}
/**
* init internal wallet with password
* @param {string} chain: chain id
* @param {string} pass: password for wallet
* @param {string} env: dev release
* @param {string} useApi: 1: yes, 0: no
* @return {string} address
* @throws {Error} if password is wrong
* @param {number | string} chain chain id
* @param {string} pass
*/
function initInternalWallet(funId, chain, pass, env, useApi) {
function initInternalWallet(funId, chain, pass, env) {
chain = parseInt(chain);
useApi = (useApi && useApi == '1') ? true: false
promiseCb(funId, jc.wallet.initInternalWallet(chain, pass, env, useApi), () => {
const wallet = !window.jc || !jc.wallet ? new jcwallet.default({ type: 0 }) : jc.wallet;
promiseCb(funId, wallet.initInternalWallet(chain, pass, env), () => {
return jc.wallet.nativeAccount;
});
}
@ -70,58 +41,41 @@ function verifyPassword(funId, pass) {
promiseCb(funId, jc.wallet.verifyLocalPass(pass));
}
/**
* @Deprecated
* init third party wallet
* @param {number | string} chain chain id
*/
function initThirdPartyWallet(funId, chain, env, wallettype, provider) {
function initThirdPartyWallet(funId, chain, env) {
chain = parseInt(chain);
wallettype = parseInt(wallettype || '1');
promiseCb(funId, jc.wallet.initThirdPartyWallet(chain, env, wallettype, provider));
}
function initRelayWallet(funId, chain, env) {
chain = parseInt(chain);
promiseCb(funId, jc.wallet.initRelayWallet(chain, env));
const wallet = !window.jc || !jc.wallet ? new jcwallet.default({ type: 1 }) : jc.wallet;
promiseCb(funId, wallet.initThirdPartyWallet(chain, env), () => {
return jc.wallet.currentAccount();
});
}
/**
* all chain list we supported
* @return {string} JSON string of
* [{ name: string
* type: string
* rpc: string
* id: number
* network?: string
* symbol?: string
* explorerurl?: string
* decimals?: number
* }]
*/
function chainList(funId) {
let data = jc.wallet.chainList;
promiseCb(funId, Promise.resolve(data));
try {
let data = jc.wallet.chainList;
return JSON.stringify({ errcode: 0, data });
} catch (err) {
return JSON.stringify({ errcode: 1, errmsg: err.message || err });
}
}
/**
* current actived chain info
* @return {string} JSON string of
* { name: string
* type: string
* rpc: string
* id: number
* network?: string
* symbol?: string
* explorerurl?: string
* decimals?: number
* }
* chain active
*/
function currentChain(funId) {
let data = jc.wallet.currentChain;
promiseCb(funId, Promise.resolve(data));
try {
let data = jc.wallet.currentChain;
return JSON.stringify({ errcode: 0, data });
} catch (err) {
return JSON.stringify({ errcode: 1, errmsg: err.message || err });
}
}
/**
* change current actived chain
* [BOTH]change chain
*/
function changeChain(funId, chainId) {
chainId = parseInt(chainId);
@ -142,18 +96,18 @@ function loginSign(funId, nonce, tips) {
* if account is null, we`ll query for current account of wallet
*/
function getEthBalance(funId, account) {
promiseCb(funId, jc.wallet.chainCommon.getBalance(account));
promiseCb(funId, jc.wallet.getBalance(account));
}
/**
* send ETH from current account
* @param {string} to: target account
* @param {string} amount:
* @param {string} estimate: 0: execute; 1: only estimate gas price; default: 0
* @param {number} estimate: 1: only estimate gas price
*/
function sendEth(funId, to, amount, estimate) {
estimate = (estimate || '0') | 0;
promiseCb(funId, jc.wallet.chainCommon.sendEth(to, amount, estimate));
promiseCb(funId, jc.wallet.sendEth(to, amount, estimate));
}
/**
@ -184,30 +138,22 @@ function erc20Info(funId, address) {
* @param {string} account:
*/
function erc20Balance(funId, address, account) {
promiseCb(funId, jc.wallet.erc20Standard.getBalanceOf(address, account));
promiseCb(funId, jc.wallet.erc20Balance(address, account));
}
/**
* send ERC20 token to to
* @param {string} address: contract address of ERC20
* @param {string} to: target account
* @param {string} amount: amount of token to send
* @param {string} estimate: 0: execute; 1: only estimate gas price; default: 0
*/
function sendErc20(funId, address, to, amount, estimate) {
estimate = (estimate || '0') | 0;
promiseCb(funId, jc.wallet.erc20Standard.transfer({address, to, amount, estimate}));
promiseCb(funId, jc.wallet.sendErc20(address, to, amount, estimate));
}
/**
* send ERC721 NFT to to
* @param {string} address: contract address of NFT
* @param {string} to: target account
* @param {string} tokenId: nft id of NFT
* @param {string} estimate: 0: execute; 1: only estimate gas price; default: 0
*/
function sendErc721(funId, address, to, tokenId, estimate) {
estimate = (estimate || '0') | 0;
promiseCb(funId, jc.wallet.erc721Standard.transfer({address, to, tokenId, estimate}));
promiseCb(funId, jc.wallet.sendNFT(address, to, tokenId, estimate));
}
/**
@ -222,42 +168,33 @@ function erc721Balance(funId, address, account, chainId) {
/**
* get balance of ERC1155
* @param {string} address: contract address of NFT
* @param {string} account: wallet address
* @param {string} tokenId: nft id of NFT
* @param {string} address:
* @param {string} account:
* @param {string} tokenId:
*/
function erc1155Balance(funId, address, account, tokenId) {
promiseCb(funId, jc.wallet.erc1155Standard.getBalanceOf(address, account, tokenId));
promiseCb(funId, jc.wallet.erc1155Balance(address, account, tokenId));
}
/**
* send ERC1155 to to
* @param {string} address: contract address of NFT
* @param {string} to: target account
* @param {string} tokenIds: nft id of NFT, json string of array
* @param {string} amounts: amount of token to send, json string of array
* @param {string} estimate: 0: execute; 1: only estimate gas price; default: 0
*/
function sendErc1155(funId, address, to, tokenIds, amounts, estimate) {
tokenIds = JSON.parse(tokenIds);
amounts = JSON.parse(amounts);
estimate = (estimate || '0') | 0;
promiseCb(funId, jc.wallet.erc1155Standard.transferBatch({address, to, tokenIds, amounts, estimate}));
promiseCb(funId, jc.wallet.sendErc1155(address, to, tokenIds, amounts, estimate));
}
/**
* show QRCode for content
* @param {string} content: content to show
*/
function showQRCode(funId, content) {
jsb.showQRCode(funId, content);
promiseCb(funId, Promise.resolve(1));
try {
jsb.showQRCode(funId, content);
return JSON.stringify({ errcode: 0, data: 1 });
} catch (err) {
return JSON.stringify({ errcode: 1, errmsg: err.message || err });
}
}
/**
* show webpage
* don't call this when in web page
* @param {string} url: url to show
*/
function showWebPage(funId, url) {
try {
jsb.showWebPage(funId, url);
@ -268,37 +205,23 @@ function showWebPage(funId, url) {
}
}
/**
* show QRCode scaner
* @param {string} title: title of scaner
*/
function scanQRCode(funId, title) {
console.log('scanQRCode: ' + title);
promiseCb(funId, jc.wallet.nativeSvr.scanQRCode(title));
}
/**
* export wallet private key
* @param {string} pass: password of wallet
*/
function exportWalletSecKey(funId, pass) {
let data = jc.wallet.exportPrivateKey(pass);
promiseCb(funId, Promise.resolve(data));
try {
let key = jc.wallet.exportPrivateKey(pass);
return JSON.stringify({ errcode: 0, data: key });
} catch (err) {
return JSON.stringify({ errcode: 1, errmsg: err.message || err });
}
}
// ======= begin of interact with contract =======
/**
* mint NFT
* @param {string} address: contract address of NFT
* @param {string} tokenIds: token id of NFT, JSON string of string array
* @param {string} startTime: time of signature generation
* @param {string} saltNonce: nonce of signature
* @param {string} signature: signature
* @param {string} estimate: 1: only estimate gas price
*/
function mintNFT(funId, address, tokenIds, startTime, saltNonce, signature, estimate) {
tokenIds = JSON.parse(tokenIds);
estimate = (estimate || '0') | 0;
promiseCb(
funId,
jc.wallet.jcStandard.mintNFT({
@ -315,10 +238,8 @@ function mintNFT(funId, address, tokenIds, startTime, saltNonce, signature, esti
// ======= end of interact with contract =======
// ======= begin of pay =======
/**
* begin buy crypto with alchemy
* @param {string} network: 'mainnet' or 'testnet'
* @param {string} crypto: 'CEC' or 'CEG', 'ETH'
* @param {string} address: wallet address of user
* crypto: 'CEC' or 'CEG', 'ETH'
* address: wallet address of user
* fiat: 'USD' or 'CNY'
* fiatAmount: '100'
* payWayCode: '10001'
@ -326,21 +247,7 @@ function mintNFT(funId, address, tokenIds, startTime, saltNonce, signature, esti
* accountId: account id of game user
* orderId: from pre pay
*/
function beginPay(
funId,
network,
crypto,
address,
fiat,
fiatAmount,
payWayCode,
country,
accountId,
orderId,
timestamp,
salt,
sign
) {
function beginPay(funId, crypto, address, fiat, fiatAmount, payWayCode, country, accountId, orderId) {
promiseCb(
funId,
jc.wallet.paySvr.alchemyPrePay({
@ -352,33 +259,23 @@ function beginPay(
country,
accountId,
orderId,
network,
timestamp,
salt,
sign,
})
);
}
// ======= end of pay =======
// ======= begin of transaction history =======
/**
* query eth transaction history
* @param {string} start
* @param {string} limit
* @param {JSON string} moreParam e.g. {timeBegin: 1655716867832, timeEnd: 1655716867832}
*
* @param {*} funId
* @param {*} start
* @param {*} limit
* @param {*} moreParam e.g. {timeBegin: 1655716867832, timeEnd: 1655716867832}
*/
function ethHistory(funId, start, limit, moreParam) {
moreParam = moreParam ? JSON.parse(moreParam) : {};
promiseCb(funId, jc.wallet.historySvr.ethRecords(start, limit, moreParam));
}
/**
* query token transaction history
* @param {string} start
* @param {string} limit
* @param {string} address
* @param {string} tokenId
* @param {JSON string} moreParam e.g. {timeBegin: 1655716867832, timeEnd: 1655716867832}
*/
function tokenHistory(funId, start, limit, address, tokenId, moreParam) {
moreParam = moreParam ? JSON.parse(moreParam) : {};
var data = { start, limit, address, tokenId };
@ -394,16 +291,17 @@ function emailInfo(funId) {
}
/**
* send code with email
* @param {string} email
* @param {string} type
* @param {*} email
* @param {*} type
*/
function sendEmailCode(funId, email, type) {
promiseCb(funId, jc.wallet.emailVerifySvr.sendEmailCode(email, type));
const wallet = !window.jc || !jc.wallet ? new jcwallet.default({ type: 0 }) : jc.wallet;
promiseCb(funId, wallet.emailVerifySvr.sendEmailCode(email, type));
}
/**
* verify email with code, and update email
* @param {string} email
* @param {string} code
* @param {*} email
* @param {*} code
*/
function verifyEmail(funId, email, code) {
promiseCb(funId, jc.wallet.emailVerifySvr.updateEmailVerify(email, code));
@ -411,10 +309,11 @@ function verifyEmail(funId, email, code) {
/**
* check if email had already been registed
* @param {string} email
* @param {*} email
*/
function checkEmailExists(funId, email) {
promiseCb(funId, jc.wallet.emailVerifySvr.isEmailRegister(email));
const wallet = !window.jc || !jc.wallet ? new jcwallet.default({ type: 0 }) : jc.wallet;
promiseCb(funId, wallet.emailVerifySvr.isEmailRegister(email));
}
/**
* regist with email
@ -423,7 +322,8 @@ function checkEmailExists(funId, email) {
* @param {*} code
*/
function emailRegist(funId, email, password, code) {
promiseCb(funId, jc.wallet.emailVerifySvr.registByEmail(email, password, code));
const wallet = !window.jc || !jc.wallet ? new jcwallet.default({ type: 0 }) : jc.wallet;
promiseCb(funId, wallet.emailVerifySvr.registByEmail(email, password, code));
}
/**
@ -432,15 +332,20 @@ function emailRegist(funId, email, password, code) {
* @param {*} password
*/
function emailLogin(funId, email, password) {
promiseCb(funId, jc.wallet.emailLogin(email, password));
const wallet = !window.jc || !jc.wallet ? new jcwallet.default({ type: 0 }) : jc.wallet;
promiseCb(funId, wallet.emailLogin(email, password));
}
/**
* token list of current chain
*/
function tokenList(funId) {
let data = jc.wallet.currentChainCfg.tokens;
promiseCb(funId, Promise.resolve(data));
try {
let data = jc.wallet.currentChainCfg.tokens;
return JSON.stringify({ errcode: 0, data });
} catch (err) {
return JSON.stringify({ errcode: 1, errmsg: err.message || err });
}
}
/**
* calc token price of USD
@ -458,45 +363,22 @@ function tokenPrice(funId, tokenName, amount) {
function fiatList(funId) {
promiseCb(funId, jc.wallet.paySvr.fetchFiatList());
}
/**
* query price of crypto -> usd
* @param {string} crypto
* @param {number} chain chain id,
*/
function getCryptoPriceOfUSD(funId, crypto, chain) {
let chainData = jc.wallet.currentChain;
if (chain) {
chainData = jc.wallet.chainList.find((v) => v.chainId === +chain);
}
let network = chainData.type !== 'Testnet' ? chainData.network || chainData.symbol : 'ARBITRUM';
network = network || 'ARBITRUM';
promiseCb(funId, jc.wallet.paySvr.queryTokenPrice(network, crypto));
}
/**
* format price
* @param {string} value
* @param {string} decimal: decimal of price
* @param {string} fixed: fixed of price
*/
function formatPrice(funId, value, decimal, fixed) {
let data = jc.wallet.formatPrice(value, decimal, fixed);
promiseCb(funId, Promise.resolve(data));
try {
let data = jc.wallet.formatPrice(value, decimal, fixed);
return JSON.stringify({ errcode: 0, data });
} catch (err) {
return JSON.stringify({ errcode: 1, errmsg: err.message || err });
}
}
// begin of market
// begin sell nft with market
/**
* sell nft with market
* @param {string} nftToken: address of nft token to sell
* @param {string} currency: address of currency
* @param {string} tokenId: token id of nft to sell
* @param {string} price: price of nft
* @param {string} amount: amount of nft to sell, must be 1 for ERC721
* @param {string} estimate: 0: execute; 1: only estimate gas price; default: 0
*/
function marketSellNFT(funId, nftToken, currency, tokenId, price, amount, estimate) {
estimate = (estimate || '0') | 0;
promiseCb(
funId,
jc.wallet.jcStandard.marketSellNFT({
@ -510,14 +392,8 @@ function marketSellNFT(funId, nftToken, currency, tokenId, price, amount, estima
(v) => JSON.stringify(v)
);
}
/**
* update price of existed order
* @param {string} orderId: order id
* @param {string} price: new price
* @param {string} estimate: 0: execute; 1: only estimate gas price; default: 0
*/
// update price of order
function marketUpdatePrice(funId, orderId, price, estimate) {
estimate = (estimate || '0') | 0;
promiseCb(
funId,
jc.wallet.jcStandard.marketUpdatePrice({
@ -528,13 +404,8 @@ function marketUpdatePrice(funId, orderId, price, estimate) {
(v) => JSON.stringify(v)
);
}
/**
* cancel order
* @param {string} orderId: order id
* @param {string} estimate: 0: execute; 1: only estimate gas price; default: 0
*/
// cancel order
function marketCancelOrder(funId, orderId, estimate) {
estimate = (estimate || '0') | 0;
promiseCb(
funId,
jc.wallet.jcStandard.marketCancelOrder({
@ -544,44 +415,19 @@ function marketCancelOrder(funId, orderId, estimate) {
(v) => JSON.stringify(v)
);
}
/**
* buy order
* @param {string} orderId: order id
* @param {string} price: price of order
* @param {string} estimate: 0: execute; 1: only estimate gas price; default: 0
*/
// buy order
function marketBuy(funId, orderId, price, estimate) {
estimate = (estimate || '0') | 0;
promiseCb(
funId,
jc.wallet.jcStandard.marketBuy({
orderId,
price,
estimate,
}),
(v) => JSON.stringify(v)
);
}
/**
* get order info from chain
* @param {string} orderId: order id
*/
function marketOrderInfo(funId, orderId) {
promiseCb(funId, jc.wallet.jcStandard.marketOrderInfo(orderId));
}
/**
* buy item of game from market
* @param {string} orderId: order id
* @param {string} seller: seller address
* @param {string} currency: address of currency
* @param {string} price: price of order
* @param {string} startTime: time for signature
* @param {string} saltNonce: nonce for signature
* @param {string} signature: signature
* @param {string} estimate: 0: execute; 1: only estimate gas price; default: 0
*/
// buy item of game from market
function gameMarketBuy(funId, orderId, seller, currency, price, startTime, saltNonce, signature, estimate) {
estimate = (estimate || '0') | 0;
promiseCb(
funId,
jc.wallet.jcStandard.gameMarketBuy({
@ -597,21 +443,15 @@ function gameMarketBuy(funId, orderId, seller, currency, price, startTime, saltN
(v) => JSON.stringify(v)
);
}
// get order info from chain
function marketOrderInfo(funId, orderId) {
promiseCb(funId, jc.wallet.jcStandard.marketOrderInfo(orderId));
}
// end of market
// begin of mall
/**
* buy item of game from mall
* @param {string} orderId: order id
* @param {string} currency: address of currency
* @param {string} price: price of order
* @param {string} startTime: time for signature
* @param {string} saltNonce: nonce for signature
* @param {string} signature: signature
* @param {string} estimate: 0: execute; 1: only estimate gas price; default: 0
*/
// buy item of game from mall
function gameMallBuy(funId, orderId, currency, price, startTime, saltNonce, signature, estimate) {
estimate = (estimate || '0') | 0;
promiseCb(
funId,
jc.wallet.jcStandard.gameMallBuy({
@ -626,24 +466,10 @@ function gameMallBuy(funId, orderId, currency, price, startTime, saltNonce, sign
(v) => JSON.stringify(v)
);
}
// end of mall
/**
* buy nft from mall
* @param {string} currency: address of currency
* @param {string} addresses: address of nft token, JSON string of array
* @param {string} ids: token id of nft, JSON string of array
* @param {string} amounts: amount of nft, JSON string of array
* @param {string} values: JSON string, e.g. [orderId, price, startTime, saltNonce]
* orderId: order id
* price: price of order
* startTime: time for signature
* saltNonce: nonce for signature
* @param {string} signature: signature
* @param {string} gas: gas price
* @param {string} estimate: 0: execute; 1: only estimate gas price; default: 0
*/
// begin of NFT mall
function nftMallBuy(funId, currency, addresses, ids, amounts, values, signature, gas, estimate) {
estimate = (estimate || '0') | 0;
addresses = JSON.parse(addresses);
ids = JSON.parse(ids);
amounts = JSON.parse(amounts);
@ -664,190 +490,4 @@ function nftMallBuy(funId, currency, addresses, ids, amounts, values, signature,
);
}
/**
* buy ceg with usdt, usdc
* @param {string} currency: address of currency
* @param {string} amount: amount of currency
* @param {string} gas: gas price
* @param {string} estimate: 0: execute; 1: only estimate gas price; default: 0
*/
function buyTokenWithErc20(funId, currency, amount, gas, estimate) {
estimate = (estimate || '0') | 0;
promiseCb(
funId,
jc.wallet.jcStandard.buyTokenWithErc20({
currency,
amount,
estimate,
}),
(v) => JSON.stringify(v)
);
}
// end of mall
// begin of in-app pay
/**
* query google or ios products with product ids
* @param {string} productIds: product id for query, JSON string of array
*/
function queryGoogleProducts(funId, productIds) {
let ids = JSON.parse(productIds);
console.log('queryGoogleProducts:: ' + productIds);
if (window.JavascriptJavaBridge) {
promiseCb(funId, jc.wallet.paySvr.queryGoogleProducts(ids));
} else {
promiseCb(funId, jc.wallet.paySvr.queryIOSProducts(ids));
}
}
/**
* query google or ios purchases unfinished
*/
function queryGooglePurchases(funId) {
if (window.JavascriptJavaBridge) {
promiseCb(funId, jc.wallet.paySvr.queryGooglePurchases());
} else {
promiseCb(funId, jc.wallet.paySvr.queryIOSPurchases());
}
}
/**
* begin google or ios pay
* @param {string} productId: product id
* @param {string} orderId: order id
*/
function beginGoogleBuy(funId, productId, orderId) {
if (window.JavascriptJavaBridge) {
promiseCb(funId, jc.wallet.paySvr.buyGoogleProduct(productId, orderId));
} else {
promiseCb(funId, jc.wallet.paySvr.beginIOSPurchase(productId, orderId));
}
}
// end of in-app pay
// begin of staking
/**
* stake nft
* @param {string} nfts: address of nft token, JSON string of array
* @param {string} tokenIds: token id of nft, JSON string of array
* @param {string} staketimes: staking time of nft, JSON string of array
* @param {string} gas: gas price
* @param {string} estimate: 0: execute; 1: only estimate gas price; default: 0
*/
function stakeNfts(funId, nfts, tokenIds, staketimes, gas, estimate) {
estimate = (estimate || '0') | 0;
nfts = JSON.parse(nfts);
tokenIds = JSON.parse(tokenIds);
staketimes = JSON.parse(staketimes);
promiseCb(funId, jc.wallet.jcStandard.stakeNfts({ nfts, tokenIds, staketimes, gas, estimate }), (v) =>
JSON.stringify(v)
);
}
/**
* redeem nft
* @param {string} nfts: address of nft token, JSON string of array
* @param {string} tokenIds: token id of nft, JSON string of array
* @param {string} gas: gas price
* @param {string} estimate: 0: execute; 1: only estimate gas price; default: 0
*/
function redeemNfts(funId, nfts, tokenIds, gas, estimate) {
estimate = (estimate || '0') | 0;
nfts = JSON.parse(nfts);
tokenIds = JSON.parse(tokenIds);
promiseCb(funId, jc.wallet.jcStandard.redeemNfts({ nfts, tokenIds, gas, estimate }), (v) => JSON.stringify(v));
}
/**
* query nft stake info
* @param {string} nft: address of nft token
* @param {string} tokenId: token id of nft
*/
function nftStakeInfo(funId, nft, tokenId) {
promiseCb(funId, jc.wallet.jcStandard.nftStakeInfo({ nft, tokenId }));
}
// end of staking
// begin of gold bricks
/**
* mint Gold Bricks
* @param {string} address: contract address of NFT
* @param {string} tokenIds: token id of NFT, JSON string of string array
* @param {string} startTime: time of signature generation
* @param {string} saltNonce: nonce of signature
* @param {string} signature: signature
* @param {string} estimate: 1: only estimate gas price
*/
function mintBricks(funId, address, tokenIds, startTime, saltNonce, signature, estimate) {
tokenIds = JSON.parse(tokenIds);
estimate = (estimate || '0') | 0;
promiseCb(
funId,
jc.wallet.jcStandard.mintBricks({
address,
tokenIds,
startTime,
saltNonce,
signature,
estimate,
}),
(v) => JSON.stringify(v)
);
}
/**
* decompose Gold Bricks
* @param {string} address: contract address of NFT
* @param {string} tokenIds: token id of NFT, JSON string of string array
* @param {string} startTime: time of signature generation
* @param {string} saltNonce: nonce of signature
* @param {string} signature: signature
* @param {string} estimate: 1: only estimate gas price
*/
function decomposeBricks(funId, address, tokenIds, startTime, saltNonce, signature, estimate) {
tokenIds = JSON.parse(tokenIds);
estimate = (estimate || '0') | 0;
promiseCb(
funId,
jc.wallet.jcStandard.decomposeBricks({
address,
tokenIds,
startTime,
saltNonce,
signature,
estimate,
}),
(v) => JSON.stringify(v)
);
}
// end of gold bricks
/**
* delete account
* delete account will delete game data for current account
* wallet for current account will be remained
*/
function deleteAccount(funId) {
promiseCb(funId, jc.wallet.deleteAccount());
}
/**
* reset wallet address for current account
*/
function resetWalletAddress(funId) {
promiseCb(funId, jc.wallet.resetWalletAddress());
}
/**
* storage pass with google drive
* @param {string} key: current account address
* @param {string} val: pass for current account
*/
function storePassLocal(funId, key, val) {
promiseCb(funId, jc.wallet.nativeSvr.storagePass(key, val));
}
/**
* restore pass from google drive
* @param {string} key: current account address
*/
function restorePassLocal(funId, key) {
promiseCb(funId, jc.wallet.nativeSvr.authGetStoragePass(key));
}
function getLocalPassState(funId, key) {
promiseCb(funId, jc.wallet.nativeSvr.passStorageState(key));
}
// end of NFT mall

View File

@ -1,37 +1,23 @@
if (window.JavascriptJavaBridge) {
console.log('regist android jsb.reflection')
jsb.reflection = new JavascriptJavaBridge();
console.log('register android jsb.reflection')
jsb.reflection = new JavascriptJavaBridge()
} else if (window.JavaScriptObjCBridge) {
jsb.reflection = new JavaScriptObjCBridge();
}
window.jumpToWallet = function(url) {
url = url || 'wc://';
if (window.JavascriptJavaBridge) {
url = 'metamask://wc?uri=' + url;
} else {
url = `https://metamask.app.link/wc?uri=${encodeURIComponent(url)}`;
}
console.log('open native: ' + url);
jsb.toWallet(url);
jsb.reflection = new JavaScriptObjCBridge()
}
window.toRelayPage = function(url) {
// https://metamask.app.link/dapp/www.sample.com/page.html
// okx://wallet/dapp/details?dappUrl=https://www.sample.com/page.html
let okxUrl = `okx://wallet/dapp/details?dappUrl=${url}`;
//let okxUrl = `https://metamask.app.link/dapp/${url.replace('https://', '')}`;
jsb.toWallet(okxUrl);
window.jumpToWallet = function (url) {
url = url || 'wc://'
console.log('jumpToWallet: ' + url)
url = 'metamask://wc?uri=' + url
// url = 'imtokenv2://wc?uri='+url
jsb.toWallet(url)
// jsb.reflection.callStaticMethod(
// 'com/jc/jcfw/JcSDK',
// 'toWallet',
// '(Ljava/lang/String;)V',
// url || 'wc://'
// )
}
function nativeCallBack(...args) {
console.log(`jniCallback: ${args[0]}`);
jc.wallet.nativeSvr.handleNativeCallback(...args);
function nativeCallback(funId, msg) {
console.log(`native call back:: funid: ${funId} msg: ${msg}`)
}
function onGamePause() {
console.log('game pause');
}
function onGameResume() {
console.log('game resume');
jc.wallet.relaySvr.checkResult();
}

View File

@ -5,7 +5,6 @@
<application
android:allowBackup="true"
android:requestLegacyExternalStorage="true"
android:name=".MainApplication"
android:icon="@mipmap/ic_launcher"
tools:replace="android:icon"
@ -18,9 +17,6 @@
<meta-data
android:name="android.app.lib_name"
android:value="cocos2djs" />
<meta-data
android:name="firebase_crashlytics_collection_enabled"
android:value="true" />
<meta-data
android:name="com.facebook.sdk.ApplicationId"
android:value="@string/facebook_app_id" />
@ -64,43 +60,19 @@
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:host="www.cebg.games"/>
<data android:pathPrefix="/client"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
<data android:host="www.cebg.games"/>
<data android:pathPrefix="/client"/>
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:host="www.counterfire.games"/>
<data android:pathPrefix="/client"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
<data android:host="www.counterfire.games"/>
<data android:pathPrefix="/client"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="tebg"
android:path="/relay_cb" />
android:scheme="http"
android:host="www.cebg.games"
android:pathPrefix="/client" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="www.cebg.games"
android:pathPrefix="/client" />
</intent-filter>
</activity>
@ -109,15 +81,7 @@
android:theme="@style/CaptureTheme" />
<activity
android:name=".activity.WebPageActivity"
android:screenOrientation="sensorLandscape"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout"
android:theme="@style/WebViewTheme" />
<activity
android:name=".activity.BiometricActivity"
android:theme="@style/DayNightActivity"
android:screenOrientation="sensorLandscape"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|layoutDirection"
/>
<activity
android:name=".activity.CustomCaptureActivity"
android:theme="@style/CaptureTheme" />
@ -145,35 +109,20 @@
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="com.googleusercontent.apps.53206975661-ih3r0ubph3rqejdq97b029difbrk2bqj" />
<data android:scheme="com.googleusercontent.apps.53206975661-asnf3qe4bg29p8h981pgf099osvrjbme" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:host="oauth-svr.cebggame.com"/>
<data android:pathPrefix="/google"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
<data android:host="oauth-svr.cebggame.com"/>
<data android:pathPrefix="/google"/>
</intent-filter>
</activity>
<activity
android:name=".tiktokapi.TikTokEntryActivity"
android:launchMode="singleTask"
android:taskAffinity="com.cege.games.release"
android:exported="true" />
<activity
android:name=".oauth.OAuthLoginCbActivity"
tools:node="replace"
<activity
android:name=".apple.AppleLoginActivity"
android:theme="@style/WebViewTheme" />
<activity
android:name=".apple.AppleLoginCbActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@ -181,7 +130,9 @@
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="cfoauthcb" android:path="/login_result" />
<data
android:scheme="cebg"
android:path="/apple_login_result" />
</intent-filter>
</activity>
<activity
@ -209,8 +160,7 @@
<category android:name="android.intent.category.default" />
</intent-filter>
</service>
</application>
</application>
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
@ -229,15 +179,13 @@
<uses-feature
android:name="android.hardware.touchscreen.multitouch.distinct"
android:required="false" />
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="com.google.android.gms.permission.AD_ID" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission
@ -250,6 +198,7 @@
android:name="android.permission.CAPTURE_VIDEO_OUTPUT"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
@ -257,9 +206,7 @@
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<queries>
<package android:name="com.zhiliaoapp.musically" />
<package android:name="com.ss.android.ugc.trill" />
@ -269,4 +216,4 @@
<provider android:authorities="com.facebook.orca.provider.PlatformProvider" />
<!-- allows sharing to Messenger app -->
</queries>
</manifest>
</manifest>

View File

@ -29,8 +29,11 @@ android {
applicationId "com.cege.games.release"
minSdkVersion PROP_MIN_SDK_VERSION
targetSdkVersion PROP_TARGET_SDK_VERSION
versionCode 46
versionName "1.0.46"
versionCode 12
versionName "1.0.10"
ndk{
abiFilters 'armeabi-v7a','arm64-v8a'
}
externalNativeBuild {
ndkBuild {
@ -94,14 +97,8 @@ android {
buildTypes {
debug {
signingConfig signingConfigs.debug
ndk {
abiFilters 'arm64-v8a'
}
}
release {
ndk {
abiFilters 'armeabi-v7a','arm64-v8a'
}
minifyEnabled false
signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
@ -118,17 +115,6 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
packagingOptions {
exclude("META-INF/DEPENDENCIES")
exclude("META-INF/LICENSE")
exclude("META-INF/LICENSE.txt")
exclude("META-INF/license.txt")
exclude("META-INF/NOTICE")
exclude("META-INF/NOTICE.txt")
exclude("META-INF/notice.txt")
exclude("META-INF/ASL2.0")
exclude("META-INF/*.kotlin_module")
}
}
android.applicationVariants.all { variant ->
@ -138,11 +124,11 @@ android.applicationVariants.all { variant ->
variant.mergeAssetsProvider.get().doLast {
def sourceDir = rootProject.ext.cfgs.jsFilePath
copy{
from "${sourceDir}"
include "Data/js/**"
into outputDir
}
// copy{
// from "${sourceDir}"
// include "Data/js/**"
// into outputDir
// }
copy {
from "${sourceDir}/cert/cacert.pem"
into outputDir
@ -170,30 +156,17 @@ dependencies {
implementation 'com.github.jenly1314:zxing-lite:2.1.1'
implementation 'net.openid:appauth:0.11.1'
implementation "com.squareup.okio:okio:2.10.0"
implementation 'com.android.volley:volley:1.2.1'
implementation 'org.greenrobot:eventbus:3.0.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'com.bytedance.ies.ugc.aweme:opensdk-oversea-external:0.2.1.0'
implementation 'com.google.android.play:core:1.10.0' //PAD资源分发
implementation 'com.facebook.android:facebook-core:latest.release'
implementation 'com.facebook.android:facebook-login:latest.release'
implementation 'com.facebook.android:facebook-share:latest.release'
implementation "com.squareup.okhttp3:okhttp:4.10.0"
// begin of firebase
implementation platform('com.google.firebase:firebase-bom:32.1.1')
implementation 'com.google.firebase:firebase-analytics'
implementation 'com.google.firebase:firebase-crashlytics-ndk'
implementation "org.greenrobot:eventbus:3.3.1"
// end of firebase
// google pay
implementation "com.android.billingclient:billing:6.0.1"
// google drive
implementation('com.google.api-client:google-api-client-android:2.2.0') {
exclude group: 'org.apache.httpcomponents'
exclude module: 'guava-jdk5'
}
implementation 'com.google.http-client:google-http-client-android:1.23.0'
implementation('com.google.apis:google-api-services-drive:v3-rev20230815-2.0.0') {
exclude group: 'org.apache.httpcomponents'
exclude module: 'guava-jdk5'
}
}

View File

@ -2,5 +2,11 @@ package com.cege.games.release;
public class Constants {
public static final String PREF_NAME = "jcwallet";
public static final String FUNID_PREFIX = "webpage_";
public static final String APPLE_CLIENT_ID = "wallet.cebggame.com";
public static final String APPLE_REDIRECT_URI = "https://wallet.cebggame.com/apple/oauth_redirect";
public static final String APPLE_SCOPE = "name%20email";
public static final String APPLE_AUTH_URL = "https://appleid.apple.com/auth/authorize";
}

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,6 @@ import com.bytedance.sdk.open.tiktok.TikTokOpenConfig;
public class MainApplication extends Application {
public static MainApplication application;
public static SharedPreferences _pref;
private String gameData;
public void onCreate() {
super.onCreate();
@ -18,12 +17,4 @@ public class MainApplication extends Application {
String tiktokClientKey = "awqbuzh2qymmq8hs";
TikTokOpenApiFactory.init(new TikTokOpenConfig(tiktokClientKey));
}
public String getGameData() {
return gameData;
}
public void setGameData(String gameData) {
this.gameData = gameData;
}
}

View File

@ -1,96 +0,0 @@
package com.cege.games.release.activity;
import static androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG;
import static androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL;
import static androidx.biometric.BiometricPrompt.ERROR_USER_CANCELED;
import android.content.Intent;
import android.os.Bundle;
import android.security.keystore.KeyInfo;
import android.util.Base64;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import androidx.biometric.BiometricManager;
import androidx.biometric.BiometricPrompt;
import com.cege.games.release.R;
import com.jc.jcfw.security.BiometricHelper;
import com.jc.jcfw.security.CryptographyManager;
import com.jc.jcfw.security.CryptographyManagerImpl;
import com.jc.jcfw.security.EncryptedData;
import javax.crypto.Cipher;
public class BiometricActivity extends AppCompatActivity {
private static final String TAG = BiometricActivity.class.getSimpleName();
private BiometricPrompt.PromptInfo promptInfo;
private CryptographyManager cryptographyManager;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_biometric);
Log.i(TAG, "onCreate: " + getIntent().getDataString());
promptInfo = BiometricHelper.createPromptInfo(this);
cryptographyManager = new CryptographyManagerImpl();
Intent intent = getIntent();
String action = intent.getStringExtra("action");
String funId = intent.getStringExtra("funid");
String account = intent.getStringExtra("account");
// check if action is exists
if ("encrypt".equals(action)) {
String password = intent.getStringExtra("password");
authenticateToEncrypt(funId, password);
} else if ("decrypt".equals(action)) {
String iv = intent.getStringExtra("iv");
authenticateToDecrypt(account, iv);
}
}
public void authenticateToEncrypt(String funId, String text) {
if (BiometricManager.from(this)
.canAuthenticate(BIOMETRIC_STRONG | DEVICE_CREDENTIAL) == BiometricManager.BIOMETRIC_SUCCESS) {
Cipher cipher = cryptographyManager.getInitializedCipherForEncryption("cebg_wallet_key");
BiometricPrompt biometricPrompt = BiometricHelper.createBiometricPrompt(this, _result -> {
if (_result.isError()) {
if (_result.getErrcode() == ERROR_USER_CANCELED) {
// close current activity
finish();
}
return;
}
EncryptedData encryptedData = cryptographyManager.encryptData(text, _result.getCipher());
String encryptedString = Base64.encodeToString(encryptedData.getCiphertext(), Base64.DEFAULT);
String ivString = Base64.encodeToString(encryptedData.getInitializationVector(), Base64.DEFAULT);
Log.i(TAG, "encrypted msg: " + encryptedString);
Log.i(TAG, "encrypted iv: " + ivString);
finish();
});
biometricPrompt.authenticate(promptInfo, new BiometricPrompt.CryptoObject(cipher));
}
}
public void authenticateToDecrypt(String text, String iv) {
if (BiometricManager.from(this)
.canAuthenticate(BIOMETRIC_STRONG | DEVICE_CREDENTIAL) == BiometricManager.BIOMETRIC_SUCCESS) {
byte[] ivData = Base64.decode(iv, Base64.DEFAULT);
byte[] textData = Base64.decode(text, Base64.DEFAULT);
Cipher cipher = cryptographyManager.getInitializedCipherForDecryption("cebg_wallet_key", ivData);
BiometricPrompt biometricPrompt = BiometricHelper.createBiometricPrompt(this, _result -> {
if (_result.isError()) {
if (_result.getErrcode() == ERROR_USER_CANCELED) {
// close current activity
finish();
}
return;
}
String decryptedMsg = cryptographyManager.decryptData(textData, _result.getCipher());
Log.i(TAG, "decrypted msg: " + decryptedMsg);
finish();
});
biometricPrompt.authenticate(promptInfo, new BiometricPrompt.CryptoObject(cipher));
}
}
}

View File

@ -1,8 +1,5 @@
package com.cege.games.release.activity;
import static com.cege.games.release.ui.UIManager.JUMP_CODE_PHOTO;
import static com.cege.games.release.ui.UIManager.KEY_TITLE;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Button;
@ -11,6 +8,7 @@ import android.widget.TextView;
import com.cege.games.release.R;
import com.king.zxing.CaptureActivity;
import static com.cege.games.release.MainActivity.KEY_TITLE;
public class CustomCaptureActivity extends CaptureActivity {
private static final String TAG = CustomCaptureActivity.class.getSimpleName();
@ -40,7 +38,8 @@ public class CustomCaptureActivity extends CaptureActivity {
protected void onClickLocalImg(){
Intent ins=new Intent();
setResult(JUMP_CODE_PHOTO, ins);
ins.putExtra("localImg", true);
setResult(1, ins);
finish();
}

View File

@ -1,168 +0,0 @@
package com.cege.games.release.activity;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Window;
import androidx.appcompat.app.AppCompatActivity;
import com.unity3d.player.IUnityPlayerLifecycleEvents;
import com.unity3d.player.MultiWindowSupport;
import com.unity3d.player.UnityPlayer;
public class UnityPlayerActivity extends AppCompatActivity implements IUnityPlayerLifecycleEvents
{
protected UnityPlayer mUnityPlayer; // don't change the name of this variable; referenced from native code
// Override this in your custom UnityPlayerActivity to tweak the command line arguments passed to the Unity Android Player
// The command line arguments are passed as a string, separated by spaces
// UnityPlayerActivity calls this from 'onCreate'
// Supported: -force-gles20, -force-gles30, -force-gles31, -force-gles31aep, -force-gles32, -force-gles, -force-vulkan
// See https://docs.unity3d.com/Manual/CommandLineArguments.html
// @param cmdLine the current command line arguments, may be null
// @return the modified command line string or null
protected String updateUnityCommandLineArguments(String cmdLine)
{
return cmdLine;
}
// Setup activity layout
@Override protected void onCreate(Bundle savedInstanceState)
{
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
String cmdLine = updateUnityCommandLineArguments(getIntent().getStringExtra("unity"));
getIntent().putExtra("unity", cmdLine);
mUnityPlayer = new UnityPlayer(this, this);
setContentView(mUnityPlayer);
mUnityPlayer.requestFocus();
}
// When Unity player unloaded move task to background
@Override public void onUnityPlayerUnloaded() {
moveTaskToBack(true);
}
// Callback before Unity player process is killed
@Override public void onUnityPlayerQuitted() {
}
@Override protected void onNewIntent(Intent intent) {
// To support deep linking, we need to make sure that the client can get access to
// the last sent intent. The clients access this through a JNI api that allows them
// to get the intent set on launch. To update that after launch we have to manually
// replace the intent with the one caught here.
super.onNewIntent(intent);
setIntent(intent);
mUnityPlayer.newIntent(intent);
}
// Quit Unity
@Override protected void onDestroy ()
{
mUnityPlayer.destroy();
super.onDestroy();
}
// If the activity is in multi window mode or resizing the activity is allowed we will use
// onStart/onStop (the visibility callbacks) to determine when to pause/resume.
// Otherwise it will be done in onPause/onResume as Unity has done historically to preserve
// existing behavior.
@Override protected void onStop()
{
super.onStop();
if (!MultiWindowSupport.getAllowResizableWindow(this))
return;
mUnityPlayer.pause();
}
@Override protected void onStart()
{
super.onStart();
if (!MultiWindowSupport.getAllowResizableWindow(this))
return;
mUnityPlayer.resume();
}
// Pause Unity
@Override protected void onPause()
{
super.onPause();
MultiWindowSupport.saveMultiWindowMode(this);
if (MultiWindowSupport.getAllowResizableWindow(this))
return;
mUnityPlayer.pause();
}
// Resume Unity
@Override protected void onResume()
{
super.onResume();
if (MultiWindowSupport.getAllowResizableWindow(this) && !MultiWindowSupport.isMultiWindowModeChangedToTrue(this))
return;
mUnityPlayer.resume();
}
// Low Memory Unity
@Override public void onLowMemory()
{
super.onLowMemory();
mUnityPlayer.lowMemory();
}
// Trim Memory Unity
@Override public void onTrimMemory(int level)
{
super.onTrimMemory(level);
if (level == TRIM_MEMORY_RUNNING_CRITICAL)
{
mUnityPlayer.lowMemory();
}
}
// This ensures the layout will be correct.
@Override public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
mUnityPlayer.configurationChanged(newConfig);
}
// Notify Unity of the focus change.
@Override public void onWindowFocusChanged(boolean hasFocus)
{
super.onWindowFocusChanged(hasFocus);
mUnityPlayer.windowFocusChanged(hasFocus);
}
// For some reason the multiple keyevent type is not supported by the ndk.
// Force event injection by overriding dispatchKeyEvent().
@SuppressLint("RestrictedApi")
@Override public boolean dispatchKeyEvent(KeyEvent event)
{
if (event.getAction() == KeyEvent.ACTION_MULTIPLE)
return mUnityPlayer.injectEvent(event);
return super.dispatchKeyEvent(event);
}
// Pass any events not handled by (unfocused) views straight to UnityPlayer
@Override public boolean onKeyUp(int keyCode, KeyEvent event) { return mUnityPlayer.injectEvent(event); }
@Override public boolean onKeyDown(int keyCode, KeyEvent event) { return mUnityPlayer.injectEvent(event); }
@Override public boolean onTouchEvent(MotionEvent event) { return mUnityPlayer.injectEvent(event); }
/*API12*/ public boolean onGenericMotionEvent(MotionEvent event) { return mUnityPlayer.injectEvent(event); }
}

View File

@ -2,8 +2,9 @@ package com.cege.games.release.activity;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Build;
import android.util.Log;
import android.webkit.ConsoleMessage;
import android.webkit.CookieManager;
@ -15,41 +16,17 @@ import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.annotation.NonNull;
import com.cege.games.release.MainApplication;
import com.cege.games.release.R;
import com.cege.games.release.ui.UIManager;
import com.cege.games.release.webpage.events.CallJSEvent;
import com.cege.games.release.webpage.events.ProxyCBEvent;
import com.cege.games.release.webpage.events.WebPageEvent;
import com.cege.games.release.webpage.WalletInterface;
import com.google.common.collect.Maps;
import com.jc.jcfw.JcSDK;
import com.jc.jcfw.util.UIUtils;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Map;
import java.util.function.Consumer;
import com.cege.games.release.dialog.BaseDialog;
public class WebPageActivity extends Activity {
private WebView mWebView;
private final String FUNID_PREFIX = "webpage_";
private final Map<String, Consumer<JSONObject>> actionMap = Maps.newHashMap();
private static final String TAG = WebPageActivity.class.getSimpleName();
@SuppressLint({ "SetJavaScriptEnabled", "DefaultLocale" })
@SuppressLint("SetJavaScriptEnabled")
@Override
protected void onCreate(android.os.Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
resistActions();
setContentView(R.layout.activity_web_page);
WebView.setWebContentsDebuggingEnabled(true);
mWebView = findViewById(R.id.web_view);
@ -63,15 +40,12 @@ public class WebPageActivity extends Activity {
webSettings.setUseWideViewPort(true);
webSettings.setLoadWithOverviewMode(true);
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
// custom user agent
webSettings.setUserAgentString(webSettings.getUserAgentString() + " android_game_web");
CookieManager.getInstance().setAcceptThirdPartyCookies(mWebView, true);
CookieManager.getInstance().setAcceptThirdPartyCookies(mWebView,true);
// get url from intent
Intent intent = getIntent();
String url = intent.getStringExtra("url");
// show web view
mWebView.loadUrl(url);
mWebView.addJavascriptInterface(new WalletInterface(this), "cfwallet_JuEd8Ql5over8kneww");
mWebView.setWebChromeClient(new WebChromeClient() {
@Override
public void onReceivedTitle(WebView view, String title) {
@ -81,7 +55,7 @@ public class WebPageActivity extends Activity {
@Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
Log.e(TAG, consoleMessage.message());
Log.e("TAG", consoleMessage.message());
return true;
}
@ -96,20 +70,11 @@ public class WebPageActivity extends Activity {
// }
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
Log.i(TAG, "onPageStarted: " + url);
String gameData = MainApplication.application.getGameData();
String jsCode = String.format("window.platform='android_game_web'; window.gameData='%s';", gameData);
callPageJS(jsCode);
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
Log.i(TAG, "onPageFinished: " + url);
}
@Override
public void onLoadResource(WebView view, String url) {
Log.i(TAG, "onLoadResource: " + url);
@ -129,116 +94,10 @@ public class WebPageActivity extends Activity {
@Override
public void onReceivedSslError(WebView view, android.webkit.SslErrorHandler handler,
android.net.http.SslError error) {
android.net.http.SslError error) {
Log.e(TAG, "onReceivedSslError: " + error.toString());
}
});
EventBus.getDefault().register(this);
}
@Override
public void onStart() {
super.onStart();
UIManager.getSingleton().onStartActivity(this);
}
@Override
public void onDestroy() {
EventBus.getDefault().unregister(this);
super.onDestroy();
}
private void asyncThread(Runnable runnable) {
new Thread(runnable).start();
}
private void resistActions() {
actionMap.put("closepage", _data -> {
finish();
});
actionMap.put("proxyMethod", _data -> {
try {
String funId = FUNID_PREFIX + _data.getString("funid");
String methodName = _data.getString("methodname");
JSONArray params = _data.getJSONArray("params");
String[] paramArr = new String[params.length()];
for (int i = 0; i < params.length(); i++) {
paramArr[i] = params.getString(i);
}
JcSDK.callNativeJS(funId, methodName, paramArr);
} catch (JSONException e) {
throw new RuntimeException(e);
}
});
actionMap.put("scanQRCode", _data -> {
try {
String funId = FUNID_PREFIX + _data.getString("funid");
String title = _data.getString("title");
UIManager.getSingleton().showQRScan(funId, title);
} catch (JSONException e) {
throw new RuntimeException(e);
}
});
actionMap.put("showQRCode", _data -> {
try {
String funId = FUNID_PREFIX + _data.getString("funid");
String title = _data.getString("title");
UIUtils.showQRCode(this, title, "");
} catch (JSONException e) {
throw new RuntimeException(e);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
UIManager.getSingleton().onActivityResult(this, requestCode, resultCode, data);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// Forward results to EasyPermissions
UIManager.getSingleton().onRequestPermissionsResult(requestCode, permissions, grantResults);
}
public void callPageJS(String javascript) {
mWebView.evaluateJavascript(javascript, s -> {
Log.i("PAGE", s);
});
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onPageEvent(WebPageEvent event) {
Log.i(TAG, "on page event: " + event.getDataStr());
try {
JSONObject data = new JSONObject(event.getDataStr());
String action = data.getString("action");
// JSONArray params = data.getJSONArray("params");
if (actionMap.containsKey(action)) {
actionMap.get(action).accept(data);
} else {
Log.w(TAG, String.format("unknown action: %s, data: %s", action, event.getDataStr()));
}
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onCallJSEvent(CallJSEvent event) {
String funId = event.getFunId();
if (funId.indexOf(FUNID_PREFIX) == 0) {
funId = funId.replace(FUNID_PREFIX, "");
}
callPageJS("nativeCall('" + funId + "', '" + event.getData() + "');");
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onProxyCBEvent(ProxyCBEvent event) {
String funId = event.getFunId();
if (funId.indexOf(FUNID_PREFIX) == 0) {
funId = funId.replace(FUNID_PREFIX, "");
}
callPageJS("proxyCallback('" + funId + "', '" + event.getData() + "');");
}
}

View File

@ -1,393 +0,0 @@
package com.cege.games.release.appauth;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import androidx.browser.customtabs.CustomTabsIntent;
import com.jc.jcfw.JcSDK;
import com.jc.jcfw.appauth.AuthStateManager;
import com.jc.jcfw.appauth.JConfiguration;
import com.jc.jcfw.util.ThreadUtils;
import net.openid.appauth.AppAuthConfiguration;
import net.openid.appauth.AuthState;
import net.openid.appauth.AuthorizationException;
import net.openid.appauth.AuthorizationRequest;
import net.openid.appauth.AuthorizationResponse;
import net.openid.appauth.AuthorizationService;
import net.openid.appauth.AuthorizationServiceConfiguration;
import net.openid.appauth.ClientAuthentication;
import net.openid.appauth.ClientSecretBasic;
import net.openid.appauth.RegistrationRequest;
import net.openid.appauth.RegistrationResponse;
import net.openid.appauth.ResponseTypeValues;
import net.openid.appauth.TokenRequest;
import net.openid.appauth.TokenResponse;
import net.openid.appauth.browser.AnyBrowserMatcher;
import net.openid.appauth.browser.BrowserMatcher;
import java.util.Collections;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
public class AppAuthSvr {
private final String TAG = getClass().getSimpleName();
private final Activity activity;
private AuthorizationService mAuthService;
private AuthStateManager mAuthStateManager;
private JConfiguration mConfiguration;
private ExecutorService mExecutor;
private final AtomicReference<String> mClientId = new AtomicReference<>();
private final AtomicReference<AuthorizationRequest> mAuthRequest = new AtomicReference<>();
private final AtomicReference<CustomTabsIntent> mAuthIntent = new AtomicReference<>();
private CountDownLatch mAuthIntentLatch = new CountDownLatch(1);
private String mFunID;
private static final int RC_AUTH = 0X04;
private Consumer<String> loginCbAction = null;
@NonNull
private final BrowserMatcher mBrowserMatcher = AnyBrowserMatcher.INSTANCE;
public AppAuthSvr(Activity activity) {
this.activity = activity;
}
public void init(ExecutorService executorService) {
mExecutor = executorService;
mAuthStateManager = AuthStateManager.getInstance(activity);
mConfiguration = JConfiguration.getInstance(activity);
mExecutor.submit(this::initializeAppAuth);
}
public void setFunID(String funID) {
this.mFunID = funID;
}
public void setLoginCbAction(Consumer<String> func) {
this.loginCbAction = func;
}
public AuthState getCurrentState() {
return mAuthStateManager.getCurrent();
}
/**
* Performs the authorization request, using the browser selected in the
* spinner,
* and a user-provided `login_hint` if available.
*/
@WorkerThread
public void doAuth(String funID, Consumer<String> func) {
this.mFunID = funID;
this.loginCbAction = func;
try {
mAuthIntentLatch.await();
} catch (InterruptedException ex) {
Log.w(TAG, "Interrupted while waiting for auth intent");
}
Intent intent = mAuthService.getAuthorizationRequestIntent(
mAuthRequest.get(),
mAuthIntent.get());
activity.startActivityForResult(intent, RC_AUTH);
}
public void parseLoginResult(@NonNull Intent data) {
AuthorizationResponse response = AuthorizationResponse.fromIntent(data);
AuthorizationException ex = AuthorizationException.fromIntent(data);
if (response != null || ex != null) {
mAuthStateManager.updateAfterAuthorization(response, ex);
}
if (response != null && response.authorizationCode != null) {
// authorization code exchange is required
mAuthStateManager.updateAfterAuthorization(response, ex);
exchangeAuthorizationCode(response);
} else if (ex != null) {
Log.i(TAG, "Authorization flow failed: " + ex.getMessage());
errorCB("Authorization flow failed: " + ex.getMessage());
} else {
Log.i(TAG, "No authorization state retained - reauthorization required");
errorCB("No authorization state retained - reauthorization required");
}
}
public void refreshToken(String funID, Consumer<String> func) {
this.mFunID = funID;
this.loginCbAction = func;
AuthState state = getCurrentState();
performTokenRequest(state.createTokenRefreshRequest(), this::handleCodeExchangeResponse);
}
/**
* Initializes the authorization service configuration if necessary, either from
* the local
* static values or by retrieving an OpenID discovery document.
*/
@WorkerThread
private void initializeAppAuth() {
Log.i(TAG, "Initializing AppAuth");
recreateAuthorizationService();
if (mAuthStateManager.getCurrent().getAuthorizationServiceConfiguration() != null) {
// configuration is already created, skip to client initialization
Log.i(TAG, "auth config already established");
initializeClient();
return;
}
// if we are not using discovery, build the authorization service configuration
// directly
// from the static configuration values.
if (mConfiguration.getDiscoveryUri() == null) {
Log.i(TAG, "Creating auth config from res/raw/auth_config.json");
AuthorizationServiceConfiguration config = new AuthorizationServiceConfiguration(
mConfiguration.getAuthEndpointUri(),
mConfiguration.getTokenEndpointUri(),
mConfiguration.getRegistrationEndpointUri(),
mConfiguration.getEndSessionEndpoint());
mAuthStateManager.replace(new AuthState(config));
initializeClient();
return;
}
// WrongThread inference is incorrect for lambdas
// noinspection WrongThread
Log.i(TAG, "Retrieving OpenID discovery doc");
AuthorizationServiceConfiguration.fetchFromUrl(
mConfiguration.getDiscoveryUri(),
this::handleConfigurationRetrievalResult,
mConfiguration.getConnectionBuilder());
}
@MainThread
private void handleConfigurationRetrievalResult(
AuthorizationServiceConfiguration config,
AuthorizationException ex) {
if (config == null) {
Log.i(TAG, "Failed to retrieve discovery document", ex);
return;
}
Log.i(TAG, "Discovery document retrieved");
mAuthStateManager.replace(new AuthState(config));
mExecutor.submit(this::initializeClient);
}
private void recreateAuthorizationService() {
if (mAuthService != null) {
Log.i(TAG, "Discarding existing AuthService instance");
mAuthService.dispose();
}
mAuthService = createAuthorizationService();
mAuthRequest.set(null);
mAuthIntent.set(null);
}
private AuthorizationService createAuthorizationService() {
Log.i(TAG, "Creating authorization service");
AppAuthConfiguration.Builder builder = new AppAuthConfiguration.Builder();
builder.setBrowserMatcher(mBrowserMatcher);
builder.setConnectionBuilder(mConfiguration.getConnectionBuilder());
return new AuthorizationService(activity, builder.build());
}
private void createAuthRequest(@Nullable String loginHint) {
Log.i(TAG, "Creating auth request for login hint: " + loginHint);
AuthorizationRequest.Builder authRequestBuilder = new AuthorizationRequest.Builder(
mAuthStateManager.getCurrent().getAuthorizationServiceConfiguration(),
mClientId.get(),
ResponseTypeValues.CODE,
mConfiguration.getRedirectUri())
// apple need `form_post` when authorization_scope has `name email `
// .setResponseMode("form_post")
// .setResponseType("code id_token")
.setScope(mConfiguration.getScope());
if (!TextUtils.isEmpty(loginHint)) {
authRequestBuilder.setLoginHint(loginHint);
}
mAuthRequest.set(authRequestBuilder.build());
}
private void warmUpBrowser() {
mAuthIntentLatch = new CountDownLatch(1);
mExecutor.execute(() -> {
Log.i(TAG, "Warming up browser instance for auth request");
Uri uri = mAuthRequest.get().toUri();
Log.d(TAG, "URI: " + uri);
CustomTabsIntent.Builder intentBuilder = mAuthService.createCustomTabsIntentBuilder(uri);
mAuthIntent.set(intentBuilder.build());
mAuthIntentLatch.countDown();
});
}
@MainThread
private void initializeAuthRequest() {
createAuthRequest("");
warmUpBrowser();
displayAuthOptions();
}
/**
* Initiates a dynamic registration request if a client ID is not provided by
* the static
* configuration.
*/
@WorkerThread
private void initializeClient() {
if (mConfiguration.getClientId() != null) {
Log.i(TAG, "Using static client ID: " + mConfiguration.getClientId());
// use a statically configured client ID
mClientId.set(mConfiguration.getClientId());
ThreadUtils.runInMain(this::initializeAuthRequest);
return;
}
RegistrationResponse lastResponse = mAuthStateManager.getCurrent().getLastRegistrationResponse();
if (lastResponse != null) {
Log.i(TAG, "Using dynamic client ID: " + lastResponse.clientId);
// already dynamically registered a client ID
mClientId.set(lastResponse.clientId);
ThreadUtils.runInMain(this::initializeAuthRequest);
return;
}
// WrongThread inference is incorrect for lambdas
// noinspection WrongThread
Log.i(TAG, "Dynamically registering client");
RegistrationRequest registrationRequest = new RegistrationRequest.Builder(
mAuthStateManager.getCurrent().getAuthorizationServiceConfiguration(),
Collections.singletonList(mConfiguration.getRedirectUri()))
.setTokenEndpointAuthenticationMethod(ClientSecretBasic.NAME)
.build();
mAuthService.performRegistrationRequest(
registrationRequest,
this::handleRegistrationResponse);
}
private void handleRegistrationResponse(
RegistrationResponse response,
AuthorizationException ex) {
mAuthStateManager.updateAfterRegistration(response, ex);
if (response == null) {
Log.i(TAG, "Failed to dynamically register client", ex);
return;
}
Log.i(TAG, "Dynamically registered client: " + response.clientId);
mClientId.set(response.clientId);
initializeAuthRequest();
}
private void displayAuthOptions() {
AuthState state = mAuthStateManager.getCurrent();
AuthorizationServiceConfiguration config = state.getAuthorizationServiceConfiguration();
String authEndpointStr;
if (config.discoveryDoc != null) {
authEndpointStr = "Discovered auth endpoint: \n";
} else {
authEndpointStr = "Static auth endpoint: \n";
}
authEndpointStr += config.authorizationEndpoint;
Log.i(TAG, authEndpointStr);
String clientIdStr;
if (state.getLastRegistrationResponse() != null) {
clientIdStr = "Dynamic client ID: \n";
} else {
clientIdStr = "Static client ID: \n";
}
clientIdStr += mClientId;
Log.i(TAG, clientIdStr);
}
@MainThread
private void exchangeAuthorizationCode(AuthorizationResponse authorizationResponse) {
Log.d(TAG, "Exchanging authorization code");
performTokenRequest(
authorizationResponse.createTokenExchangeRequest(),
this::handleCodeExchangeResponse);
}
@MainThread
private void performTokenRequest(
TokenRequest request,
AuthorizationService.TokenResponseCallback callback) {
ClientAuthentication clientAuthentication;
try {
clientAuthentication = mAuthStateManager.getCurrent().getClientAuthentication();
} catch (ClientAuthentication.UnsupportedAuthenticationMethod ex) {
Log.d(TAG, "Token request cannot be made, client authentication for the token "
+ "endpoint could not be constructed (%s)", ex);
return;
}
mAuthService.performTokenRequest(
request,
clientAuthentication,
callback);
}
@WorkerThread
private void handleCodeExchangeResponse(
@Nullable TokenResponse tokenResponse,
@Nullable AuthorizationException authException) {
mAuthStateManager.updateAfterTokenResponse(tokenResponse, authException);
if (!mAuthStateManager.getCurrent().isAuthorized()) {
final String message = "Authorization Code exchange failed: "
+ ((authException != null) ? authException.error : "");
Log.d(TAG, message);
errorCB(message);
} else {
checkAuthStateAndCB();
}
}
private void checkAuthStateAndCB() {
AuthState state = mAuthStateManager.getCurrent();
Log.d(TAG, "login success, auth state: " + state.isAuthorized());
Log.d(TAG, "id token : " + state.getIdToken());
Log.d(TAG, "access token: " + state.getAccessToken());
Log.d(TAG, "refresh token: " + state.getRefreshToken());
mAuthStateManager.replace(state);
if (loginCbAction != null) {
mExecutor.submit(() -> {
loginCbAction.accept("success");
clearParams();
});
} else {
successCB(state.getIdToken());
}
}
private void clearParams() {
this.mFunID = null;
this.loginCbAction = null;
}
public void errorCB(String msg) {
JcSDK.nativeCb(mFunID, msg, null);
clearParams();
}
public void successCB(String dataStr) {
JcSDK.nativeCb(mFunID, null, dataStr);
clearParams();
}
}

View File

@ -0,0 +1,80 @@
package com.cege.games.release.apple;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Rect;
import android.net.Uri;
import android.util.Log;
import android.view.ViewGroup;
import android.view.Window;
import android.webkit.CookieManager;
import android.webkit.WebResourceRequest;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.cege.games.release.Constants;
import com.cege.games.release.R;
public class AppleLoginActivity extends Activity {
private static final String TAG = AppleLoginActivity.class.getSimpleName();
@SuppressLint("SetJavaScriptEnabled")
@Override
public void onCreate(android.os.Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
setContentView(R.layout.activity_web_page);
WebView.setWebContentsDebuggingEnabled(true);
WebView mWebView = findViewById(R.id.web_view);
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setDomStorageEnabled(true);
webSettings.setDatabaseEnabled(true);
webSettings.setAllowContentAccess(true);
webSettings.setAppCacheEnabled(true);
webSettings.setBuiltInZoomControls(true);
webSettings.setUseWideViewPort(true);
webSettings.setLoadWithOverviewMode(true);
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
CookieManager.getInstance().setAcceptThirdPartyCookies(mWebView,true);
// get url from intent
String funId = intent.getStringExtra("funId");
String url = Constants.APPLE_AUTH_URL
+ "?response_type=code%20id_token&v=1.1.6&response_mode=form_post&client_id="
+ Constants.APPLE_CLIENT_ID + "&scope=" + Constants.APPLE_SCOPE + "&state=" + funId + "&redirect_uri="
+ Constants.APPLE_REDIRECT_URI;
// show web view
mWebView.loadUrl(url);
mWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
String url = request.getUrl().toString();
Log.i(TAG, url);
if (url.startsWith("cebg")) {
// Close the dialog after getting the authorization code
Intent myapp_intent = new Intent(Intent.ACTION_VIEW);
myapp_intent.setData(Uri.parse(url));
startActivity(myapp_intent);
finish();
return true;
}
return false;
}
// @Override
// public void onPageFinished(WebView view, String url) {
// super.onPageFinished(view, url);
// Rect displayRectangle = new Rect();
// Window window = getWindow();
// window.getDecorView().getWindowVisibleDisplayFrame(displayRectangle);
// ViewGroup.LayoutParams layoutparms = view.getLayoutParams();
// layoutparms.height = displayRectangle.height();
// layoutparms.width = displayRectangle.width();
// view.setLayoutParams(layoutparms);
// }
});
}
}

View File

@ -0,0 +1,34 @@
package com.cege.games.release.apple;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.Nullable;
import com.jc.jcfw.JcSDK;
public class AppleLoginCbActivity extends Activity {
private static final String TAG = AppleLoginCbActivity.class.getSimpleName();
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "receive apple login callback");
Intent intent = getIntent();
if (Intent.ACTION_VIEW.equals(intent.getAction())) {
Uri uri = intent.getData();
String error = uri.getQueryParameter("error");
String state = uri.getQueryParameter("state");
if (null != error && !error.isEmpty()) {
JcSDK.nativeCb(state, error, null);
} else {
String token = uri.getQueryParameter("token");
JcSDK.nativeCb(state, null, token);
}
finish();
}
}
}

View File

@ -1,26 +1,36 @@
package com.cege.games.release.dialog;
import android.Manifest;
import android.app.Dialog;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import com.cege.games.release.MainActivity;
import com.cege.games.release.R;
import com.jc.jcfw.util.ThreadUtils;
import com.jc.jcfw.util.UIUtils;
import com.jc.jcfw.util.FileUtils;
import com.king.zxing.util.CodeUtils;
import java.util.List;
import androidx.annotation.NonNull;
import pub.devrel.easypermissions.AfterPermissionGranted;
import pub.devrel.easypermissions.EasyPermissions;
public class QRCodeActivity extends Dialog implements EasyPermissions.PermissionCallbacks {
public class QRCodeActivity extends Dialog {
private final Context baseContent;
private TextView titleLabel;
private String mContent;
private ImageView ivCode;
private String oid;
private Bitmap bitmap;
private Button localBtn;
public static final int RC_SAVE_QR = 0X111;
public QRCodeActivity(Context context) {
super(context, R.style.DialogStyle);
@ -33,27 +43,77 @@ public class QRCodeActivity extends Dialog {
setContentView(R.layout.qrcode_view);
ivCode = findViewById(R.id.ivCode);
titleLabel = findViewById(R.id.qrTitleLabel);
ivCode.setOnClickListener(v -> onClickImg());
localBtn = findViewById(R.id.qrSaveBtn);
if (oid != null && !"".equals(oid)) {
localBtn.setVisibility(View.VISIBLE);
} else {
localBtn.setVisibility(View.INVISIBLE);
}
localBtn.setOnClickListener(v -> onClickSaveImg());
}
public void showQRCode(String content, String title) {
this.mContent = content;
public void showQRCode(String content, String title, String _oid) {
oid = _oid;
if (localBtn != null && oid != null && !"".equals(oid)) {
localBtn.setVisibility(View.VISIBLE);
}
new Thread(() -> {
bitmap = CodeUtils.createQRCode(content, 500, null);
ThreadUtils.runInMain(() -> {
MainActivity.app.runOnUiThread(() -> {
titleLabel.setText(title);
ivCode.setImageBitmap(bitmap);
});
}).start();
}
public void saveAndClose() {
new Thread(() -> {
String uri = FileUtils.saveBitmap(baseContent, oid, bitmap);
MainActivity.app.runOnUiThread(() -> {
if (uri != null && !"".equals(uri)) {
MainActivity.app.showToast("Wallet restore key had save to System Album");
dismiss();
} else {
MainActivity.app.showToast("Wallet restore key save fail");
}
});
}).start();
}
protected void onClickImg() {
ThreadUtils.runInMain(() -> {
ClipboardManager myClipboard = (ClipboardManager)baseContent.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData myClip = ClipData.newPlainText("text", mContent);
myClipboard.setPrimaryClip(myClip);
UIUtils.showToast(baseContent, "Content copied to clipboard.");
});
protected void onClickSaveImg() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
saveAndClose();
} else {
checkCameraPermissions();
}
}
@Override
public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
}
@Override
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
MainActivity.app.showToast("We need Write WRITE_EXTERNAL_STORAGE for backup Wallet Restore Key");
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
}
@AfterPermissionGranted(RC_SAVE_QR)
private void checkCameraPermissions() {
String[] perms = { Manifest.permission.WRITE_EXTERNAL_STORAGE };
if (EasyPermissions.hasPermissions(this.baseContent, perms)) {
saveAndClose();
} else {
// Do not have permissions, request them now
EasyPermissions.requestPermissions(MainActivity.app,
"We need Write WRITE_EXTERNAL_STORAGE for save QRCode to System Album",
RC_SAVE_QR, perms);
}
}
}

View File

@ -1,52 +0,0 @@
package com.cege.games.release.oauth;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.Nullable;
import com.cege.games.release.MainActivity;
import com.jc.jcfw.JcSDK;
public class OAuthLoginCbActivity extends Activity {
private static final String TAG = OAuthLoginCbActivity.class.getSimpleName();
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "receive oauth login callback");
Intent intent = getIntent();
if (Intent.ACTION_VIEW.equals(intent.getAction())) {
Uri uri = intent.getData();
String error = uri.getQueryParameter("error");
String state = uri.getQueryParameter("state");
if (null != error && !error.isEmpty()) {
JcSDK.nativeCb(state, error, null);
} else {
String token = uri.getQueryParameter("token");
// if token is null, get token and state from uri fragment
if (null == token || "undefined".equals(token) || token.isEmpty()){
String fragment = uri.getFragment();
String[] vals = fragment.split("&");
for (String val : vals) {
if (val.startsWith("token=")) {
token = val.substring(6);
} else if (val.startsWith("id_token=")) {
token = val.substring(9);
} else if (val.startsWith("state=")) {
state = val.substring(6);
}
}
}
JcSDK.nativeCb(state, null, token);
}
Intent intentMain = new Intent(this, MainActivity.class);
startActivity(intentMain);
finish();
}
}
}

View File

@ -1,61 +0,0 @@
package com.cege.games.release.oauth;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import net.openid.appauth.AuthorizationRequest;
import net.openid.appauth.AuthorizationService;
import net.openid.appauth.AuthorizationServiceConfiguration;
import org.json.JSONException;
import org.json.JSONObject;
public class OAuthUtil {
private final Activity mActivity;
public OAuthUtil(Activity activity) {
this.mActivity = activity;
}
/**
*
* @param funId
* @param jsonData: {
* "endpoint": "",
* "client_id": "",
* "redirect_uri": "",
* "response_type": "code",
* "response_mode": "form_post", nullable
* "scopes": "",
* }
* @throws JSONException
*/
public void startLogin(String funId, String jsonData) throws JSONException {
JSONObject data = new JSONObject(jsonData);
AuthorizationServiceConfiguration config = new AuthorizationServiceConfiguration(
Uri.parse(data.getString("endpoint")),
Uri.parse("")
);
AuthorizationRequest.Builder authRequestBuilder = new AuthorizationRequest.Builder(
config,
data.getString("client_id"),
data.getString("response_type"),
Uri.parse(data.getString("redirect_uri"))
);
String[] scopes = data.getString("scopes").split(" +");
if (data.has("response_mode")) {
authRequestBuilder.setResponseMode(data.getString("response_mode"));
}
AuthorizationRequest authRequest = authRequestBuilder
.setScopes(scopes)
.setCodeVerifier(null, null, null)
.setState(funId)
.setNonce(funId)
.build();
AuthorizationService service = new AuthorizationService(this.mActivity);
Intent authIntent = service.getAuthorizationRequestIntent(authRequest);
this.mActivity.startActivity(authIntent);
}
}

View File

@ -1,177 +0,0 @@
package com.cege.games.release.ui;
import static android.app.Activity.RESULT_OK;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.provider.MediaStore;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityOptionsCompat;
import com.cege.games.release.R;
import com.cege.games.release.activity.CustomCaptureActivity;
import com.google.common.base.Strings;
import com.jc.jcfw.JcSDK;
import com.king.zxing.CameraScan;
import com.king.zxing.util.CodeUtils;
import java.lang.ref.WeakReference;
import java.util.List;
import pub.devrel.easypermissions.AfterPermissionGranted;
import pub.devrel.easypermissions.EasyPermissions;
public class UIManager implements EasyPermissions.PermissionCallbacks{
private final String TAG = this.getClass().getSimpleName();
private volatile static UIManager singleton;
private WeakReference<Activity> mContext;
private UIManager() {
}
private String mFunID;
private String mTitle;
public static final int REQUEST_CODE_SCAN = 0X101;
public static final int REQUEST_CODE_PHOTO = 0X102;
public static final int JUMP_CODE_PHOTO = 0X201;
public static final int RC_CAMERA = 0X103;
public static final int RC_READ_PHOTO = 0X104;
public static final String KEY_TITLE = "key_title";
public static final String KEY_IS_CONTINUOUS = "key_continuous_scan";
public static UIManager getSingleton() {
if (singleton == null) {
synchronized (UIManager.class) {
if (singleton == null) {
singleton = new UIManager();
}
}
}
return singleton;
}
public void onStartActivity(Activity context) {
mContext = new WeakReference<>(context);
}
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK && data != null) {
switch (requestCode) {
case REQUEST_CODE_SCAN:
String result = CameraScan.parseScanResult(data);
Log.i(TAG, "scan qrcode with funId: " + mFunID + " result: " + result);
successCb(result);
break;
case REQUEST_CODE_PHOTO:
parsePhoto(data);
break;
}
} else {
if (resultCode == JUMP_CODE_PHOTO && requestCode == REQUEST_CODE_SCAN) {
startPhotoCode();
} else if (requestCode == REQUEST_CODE_SCAN || requestCode == REQUEST_CODE_PHOTO) {
errorCb("activity result with code: " + resultCode);
}
}
}
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
// Forward results to EasyPermissions
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
@Override
public void onPermissionsGranted(int requestCode, @NonNull List<String> list) {
// Some permissions have been granted
}
@Override
public void onPermissionsDenied(int requestCode, @NonNull List<String> list) {
// Some permissions have been denied
if ((requestCode == RC_CAMERA || requestCode == RC_READ_PHOTO) && null != mFunID && !"".equals(mFunID)) {
errorCb("User cancel");
}
}
public void showQRScan(String funId, String title) {
this.mFunID = funId;
this.mTitle = title;
this.checkCameraPermissions();
}
/**
* check if had permission for camera
*/
@AfterPermissionGranted(RC_CAMERA)
private void checkCameraPermissions() {
String[] perms = { Manifest.permission.CAMERA };
Activity context = mContext.get();
if (EasyPermissions.hasPermissions(context, perms)) {
startScan(mTitle);
} else {
// Do not have permissions, request them now
EasyPermissions.requestPermissions(context, context.getString(R.string.permission_camera), RC_CAMERA, perms);
}
}
/**
* scan qrcode
*/
private void startScan(String title) {
Activity context = mContext.get();
ActivityOptionsCompat optionsCompat = ActivityOptionsCompat.makeCustomAnimation(context, R.anim.in, R.anim.out);
Intent intent = new Intent(context, CustomCaptureActivity.class);
intent.putExtra(KEY_TITLE, title);
intent.putExtra(KEY_IS_CONTINUOUS, false);
context.runOnUiThread(() -> context.startActivityForResult(intent, REQUEST_CODE_SCAN, optionsCompat.toBundle()));
}
private void parsePhoto(Intent data) {
Activity context = mContext.get();
try {
Bitmap bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), data.getData());
parsePhotoData(bitmap);
} catch (Exception e) {
errorCb(e.toString());
}
}
private void asyncThread(Runnable runnable) {
new Thread(runnable).start();
}
private void parsePhotoData(Bitmap bitmap) {
asyncThread(() -> {
final String result = CodeUtils.parseQRCode(bitmap);
if (Strings.isNullOrEmpty(result)) {
errorCb("no qrdeata");
} else {
successCb(result);
}
});
}
/**
* start image scan
*/
public void startPhotoCode() {
Intent pickIntent = new Intent(Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
pickIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
mContext.get().startActivityForResult(pickIntent, REQUEST_CODE_PHOTO);
}
private void successCb(String result) {
JcSDK.nativeCb(mFunID, null, result);
mFunID = "";
mTitle = "";
}
private void errorCb(String errMsg) {
JcSDK.nativeCb(mFunID, errMsg, null);
mFunID = "";
mTitle = "";
}
}

View File

@ -1,222 +0,0 @@
package com.cege.games.release.wallet;
import android.content.Context;
import android.util.Log;
import com.cege.games.release.R;
import com.google.android.gms.auth.api.signin.GoogleSignIn;
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.DriveScopes;
import com.google.common.base.Strings;
import com.jc.jcfw.JcSDK;
import com.jc.jcfw.NativeResult;
import com.jc.jcfw.util.DriveUtils;
import com.jc.jcfw.util.DriverApiUtils;
import com.jc.jcfw.util.FileUtils;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.function.Consumer;
public class WalletUtil {
private final Context mContext;
private String funID;
private String account;
public WalletUtil(Context context) {
this.mContext = context;
}
private final String TAG = getClass().getSimpleName();
public void updateParams(String funID, String account) {
this.funID = funID;
this.account = account;
}
public void clearParams() {
this.funID = null;
this.account = null;
}
public String getAccount() {
return account;
}
public void errorCB(String msg) {
JcSDK.nativeCb(funID, msg, null);
clearParams();
}
public void successCB(String dataStr) {
JcSDK.nativeCb(funID, null, dataStr);
clearParams();
}
public static String generateFileName(String account) {
return String.format("wallet_%s.json", account);
}
public File getWalletCfgFile(String account) {
String filename = generateFileName(account);
File dic = new File(mContext.getFilesDir() + "/wallets");
if (!dic.exists()) {
dic.mkdir();
}
return new File(mContext.getFilesDir() + "/wallets", filename);
}
public void savePassToLocal(String pass, Consumer<File> func) {
File filePath = getWalletCfgFile(account);
JSONObject content = new JSONObject();
try {
content.put("pass", pass);
FileUtils.writeFile(filePath, content.toString());
func.accept(filePath);
} catch (JSONException | IOException e) {
errorCB(e.getMessage());
}
}
public void getPassLocal() {
try {
JSONObject json = loadLocalCfg(account);
String passEncrypted = json.getString("pass");
String passDecrypted = JcSDK.decryptPass(account, passEncrypted);
successCB(passDecrypted);
} catch (JSONException | IOException e) {
errorCB(e.getMessage());
}
}
public boolean localCfgExists(String account) {
File filePath = getWalletCfgFile(account);
return filePath.exists();
}
public JSONObject loadLocalCfg(String account) throws JSONException, IOException {
File filePath = getWalletCfgFile(account);
if (!filePath.exists()) {
return null;
}
return FileUtils.readJsonFromFile(filePath);
}
public void uploadCfgWithGPS(Consumer<NativeResult> func) {
GoogleSignInAccount ga = GoogleSignIn.getLastSignedInAccount(mContext);
GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(mContext,
Collections.singletonList(DriveScopes.DRIVE_APPDATA));
credential.setSelectedAccount(ga.getAccount());
Drive service = DriveUtils.generateService(credential, mContext.getString(R.string.app_name));
try {
File file = getWalletCfgFile(account);
String fileName = file.getName();
String fileId = DriveUtils.queryOneAppFile(service, fileName);
if (Strings.isNullOrEmpty(fileId)) {
Log.i(TAG, String.format("%s not exists in drive, upload...", fileName));
fileId = DriveUtils.uploadAppFile(service, file, "application/json");
}
Log.i(TAG, "File ID: " + fileId);
func.accept(new NativeResult(funID, null, fileId));
successCB(fileId);
} catch (GoogleJsonResponseException e) {
Log.i(TAG, "Unable to create file: " + e.getDetails());
func.accept(new NativeResult(funID, e.getMessage(), null));
errorCB("Unable to create file: " +e.getMessage());
} catch (IOException e) {
Log.i(TAG, e.getMessage());
func.accept(new NativeResult(funID, e.getMessage(), null));
errorCB("Unable to create file: " + e.getMessage());
}
}
public void downloadCfgWithGPS(Consumer<File> func) {
GoogleSignInAccount ga = GoogleSignIn.getLastSignedInAccount(mContext);
GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(mContext,
Collections.singletonList(DriveScopes.DRIVE_APPDATA));
credential.setSelectedAccount(ga.getAccount());
Drive service = DriveUtils.generateService(credential, mContext.getString(R.string.app_name));
String fileName = generateFileName(account);
String fileId = DriveUtils.queryOneAppFile(service, fileName);
if (Strings.isNullOrEmpty(fileId)) {
Log.i(TAG, "file not found in drive");
errorCB("file not found in drive");
return;
}
boolean downloadSuccess = false;
int count = 0;
File fileLocal = getWalletCfgFile(account);
while (!downloadSuccess && count++ < 10) {
Log.i(TAG, String.format("download file: %s with GPS, try: %d", fileName, count));
try {
String jsonStr = DriveUtils.downloadFile(service, fileId);
FileUtils.writeFile(fileLocal, jsonStr);
downloadSuccess = true;
} catch (IOException e) {
Log.i(TAG, "error download file with GPS");
}
}
if (downloadSuccess) {
func.accept(fileLocal);
} else {
errorCB("error download file with GPS");
}
}
public void downloadCfgWithApi(String accessToken, Consumer<File> func) {
DriverApiUtils api = new DriverApiUtils(accessToken);
String fileName = generateFileName(account);
File fileLocal = getWalletCfgFile(account);
boolean downloadSuccess = false;
int count = 0;
while (!downloadSuccess && count++ < 10) {
Log.i(TAG, String.format("download file: %s with api, try: %d", fileName, count));
try {
String fileId = api.queryOneAppFile(fileName);
if (Strings.isNullOrEmpty(fileId)) {
Log.i(TAG, "file not found in drive");
throw new IOException();
}
String jsonStr = api.fileInfo(fileId);
FileUtils.writeFile(fileLocal, jsonStr);
downloadSuccess = true;
} catch (IOException | JSONException e) {
Log.i(TAG, "error download file with api");
}
}
if (downloadSuccess) {
func.accept(fileLocal);
} else {
errorCB("error download file with api");
}
}
public void uploadCfgWithApi(String accessToken, Consumer<NativeResult> func) {
DriverApiUtils api = new DriverApiUtils(accessToken);
File fileLocal = getWalletCfgFile(account);
try {
String fileName = generateFileName(account);
String fileId = api.queryOneAppFile(fileName);
if (Strings.isNullOrEmpty(fileId)) {
String rep = api.uploadFile(fileLocal, "application/json");
JSONObject repData = new JSONObject(rep);
fileId = repData.getString("id");
}
Log.i(TAG, "success upload file with api, ID: " + fileId);
func.accept(new NativeResult(funID, null, fileId));
successCB(fileId);
} catch (IOException | JSONException e) {
func.accept(new NativeResult(funID, e.getMessage(), null));
errorCB("error upload file with api: " + e.getMessage());
}
}
}

View File

@ -1,24 +0,0 @@
package com.cege.games.release.webpage;
import android.content.Context;
import android.webkit.JavascriptInterface;
import com.cege.games.release.webpage.events.WebPageEvent;
import org.greenrobot.eventbus.EventBus;
public class WalletInterface {
Context mContext;
/** Instantiate the interface and set the context */
public WalletInterface(Context c) {
mContext = c;
}
// sign
@JavascriptInterface
public void pageCall(String dataStr) {
EventBus.getDefault().post(new WebPageEvent(dataStr));
}
}

View File

@ -1,27 +0,0 @@
package com.cege.games.release.webpage.events;
public class CallJSEvent {
private String data;
private String funId;
public CallJSEvent(String funId, String data) {
this.data = data;
this.funId = funId;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public String getFunId() {
return funId;
}
public void setFunId(String funId) {
this.funId = funId;
}
}

View File

@ -1,27 +0,0 @@
package com.cege.games.release.webpage.events;
public class ProxyCBEvent {
private String data;
private String funId;
public ProxyCBEvent(String funId, String data) {
this.data = data;
this.funId = funId;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public String getFunId() {
return funId;
}
public void setFunId(String funId) {
this.funId = funId;
}
}

View File

@ -1,17 +0,0 @@
package com.cege.games.release.webpage.events;
public class WebPageEvent {
private String dataStr;
public WebPageEvent(String dataStr) {
this.dataStr = dataStr;
}
public String getDataStr() {
return dataStr;
}
public void setDataStr(String dataStr) {
this.dataStr = dataStr;
}
}

View File

@ -1,46 +1,21 @@
package com.jc.jcfw;
import static com.cege.games.release.Constants.FUNID_PREFIX;
import android.annotation.SuppressLint;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.util.Log;
import com.cege.games.release.MainActivity;
import com.cege.games.release.MainApplication;
import com.cege.games.release.ui.UIManager;
import com.cege.games.release.webpage.events.CallJSEvent;
import com.cege.games.release.webpage.events.ProxyCBEvent;
import com.google.common.base.Strings;
import com.jc.jcfw.google.PayClient;
import com.jc.jcfw.util.ThreadUtils;
import com.jc.jcfw.util.UIUtils;
import com.unity3d.player.UnityPlayer;
import org.cocos2dx.lib.CocosJSHelper;
import org.greenrobot.eventbus.EventBus;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
public class JcSDK {
private static final String TAG = JcSDK.class.getSimpleName();
private static UnityCallback commonCB;
@SuppressLint("StaticFieldLeak")
private static PayClient payClient;
private static native int runJS(final String funId, final String methodName, final String[] params);
public static native String decryptPass(final String account, final String pass);
public static native void tick(float dt);
private static native int runJS(final String funId, final String methodName, final String params);
public static void initCommonCB(UnityCallback callBack) {
Log.i(TAG, "call init common callback from unity");
@ -60,9 +35,12 @@ public class JcSDK {
/**
* 回调至c#
*
* @param funId
* @param msg
*/
public static void csCallback(String funId, String msg) {
if (!Objects.equals(funId, "") && funId.indexOf("js_") == 0) {
if (funId != "" && funId.indexOf("js_") == 0) {
commonCB.nativeCallback(funId, msg, 1);
} else {
commonCB.nativeCallback(funId, msg, 0);
@ -88,8 +66,6 @@ public class JcSDK {
String downloadUrl = "https://metamask.io/download/";
if (url.startsWith("imtokenv2")) {
downloadUrl = "https://token.im/download";
} else if (url.startsWith("okx://")) {
downloadUrl = "https://www.okx.com/download";
}
i.setData(Uri.parse(downloadUrl));
MainActivity.app.startActivity(i);
@ -97,7 +73,7 @@ public class JcSDK {
}
public static void showQRCode(String funid, String content) {
UIUtils.showQRCode(MainActivity.app, content, "");
MainActivity.app.showQRCode(funid, content, "", "");
}
public static void showWebPage(String funid, String url) {
@ -105,8 +81,7 @@ public class JcSDK {
}
public static void scanQRCode(String funid, String title) {
// MainActivity.app.showQRScan(funid, title);
UIManager.getSingleton().showQRScan(funid, title);
MainActivity.app.showQRScan(funid, title);
}
public static void signWithTiktok(String funid) {
@ -130,11 +105,7 @@ public class JcSDK {
}
public static void signWithApple(String funid) {
// MainActivity.app.signWithApple(funid);
}
public static void signWithOAuth(String funid, String jsonData) {
MainActivity.app.oauthLogin(funid, jsonData);
MainActivity.app.signWithApple(funid);
}
public static void signOutGoogle(String funid) {
@ -145,72 +116,15 @@ public class JcSDK {
MainActivity.app.logEvent(content);
}
public static void queryProducts(String funid, String skuListStr) {
Log.i(TAG, "queryProducts with: " + skuListStr);
if (payClient == null) {
payClient = PayClient.getInstance();
}
List<String> skuList = new ArrayList<>();
if (skuListStr.contains(",")) {
String[] skuArr = skuListStr.split(",");
skuList.addAll(Arrays.asList(skuArr));
} else {
skuList.add(skuListStr);
}
payClient.queryProductList(funid, skuList);
}
public static void buyProduct(String funid, String productId, String orderId) {
Log.i(TAG, "buyProduct with: " + productId);
if (payClient == null) {
payClient = PayClient.getInstance();
}
payClient.buyOne(funid, productId, orderId);
}
public static void queryPurchase(String funid) {
Log.i(TAG, "queryPurchase");
if (payClient == null) {
payClient = PayClient.getInstance();
}
payClient.queryPurchase(funid);
}
public static void passStorageState(String funid, String account) {
Log.i(TAG, "passStorageState with: " + account);
MainActivity.app.passStorageState(funid, account);
}
public static void storagePass(String funid, String account, String password) {
MainActivity.app.storagePass(funid, account, password);
}
public static void authGetStoragePass(String funid, String account) {
MainActivity.app.authGetStoragePass(funid, account);
}
public static void storageGameData(String data) {
MainApplication.application.setGameData(data);
}
public static void getClientId(String funid) {
Log.i(TAG, "getClientId ");
MainActivity.app.getClientId(funid);
}
public static void onProxyCB(String funId, String data) {
EventBus.getDefault().post(new ProxyCBEvent(funId, data));
}
public static void nativeCb(String funId, String error, String dataStr) {
public static void nativeCb(String funId, String error, String idToken) {
JSONObject result = new JSONObject();
try {
if (Strings.isNullOrEmpty(error)) {
result.put("errcode", 0);
result.put("data", dataStr);
} else {
if (error != null && !error.isEmpty()) {
result.put("errcode", 1);
result.put("errmessage", error);
} else {
result.put("errcode", 0);
result.put("data", idToken);
}
} catch (JSONException e) {
Log.e(TAG, "JSONException: " + e.getMessage());
@ -218,37 +132,7 @@ public class JcSDK {
if (funId == null || funId.isEmpty()) {
funId = MainActivity.app.getFunId();
}
Log.i(TAG, String.format("%s native cb, error: %s, data: %s", funId, error, dataStr));
if (funId.startsWith(FUNID_PREFIX)) {
EventBus.getDefault().post(new CallJSEvent(funId, result.toString()));
} else {
String finalFunId = funId;
ThreadUtils.runInMain(() -> JcSDK.runJS(finalFunId, "jniCallback", new String[]{result.toString()}));
}
}
public static void callNativeJS(String funId, String methodName, String[] params) {
ThreadUtils.runInMain(() -> JcSDK.runJS(funId, methodName, params));
}
public static void nativeCb(NativeResult result) {
nativeCb(result.getFunid(), result.getError(), result.getDataStr());
}
public static void toUnity(String funId, String error, String dataStr) {
JSONObject result = new JSONObject();
try {
result.put("funid", funId);
if (Strings.isNullOrEmpty(error)) {
result.put("errcode", 0);
result.put("data", dataStr);
} else {
result.put("errcode", 1);
result.put("errmessage", error);
}
} catch (JSONException e) {
Log.e(TAG, "JSONException: " + e.getMessage());
}
UnityPlayer.UnitySendMessage("WalletPanel1", "onNativeCallback", result.toString());
JcSDK.runJS(funId, "jniCallback", result.toString());
}
}

View File

@ -1,37 +0,0 @@
package com.jc.jcfw;
public class NativeResult {
private String funid;
private String error;
private String dataStr;
public NativeResult(String funid, String error, String dataStr) {
this.funid = funid;
this.error = error;
this.dataStr = dataStr;
}
public String getFunid() {
return funid;
}
public void setFunid(String funid) {
this.funid = funid;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public String getDataStr() {
return dataStr;
}
public void setDataStr(String dataStr) {
this.dataStr = dataStr;
}
}

View File

@ -1,56 +0,0 @@
package com.jc.jcfw.accountmanager;
import android.accounts.AbstractAccountAuthenticator;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.NetworkErrorException;
import android.content.Context;
import android.os.Bundle;
/**
* Created by sebastian on 07-03-16.
*/
public class Authenticator extends AbstractAccountAuthenticator {
private Context context;
public Authenticator(Context context) {
super(context);
this.context = context;
}
@Override
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
return null;
}
@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public String getAuthTokenLabel(String authTokenType) {
return authTokenType;
}
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
return null;
}
}

View File

@ -1,19 +0,0 @@
package com.jc.jcfw.accountmanager;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class AuthenticatorService extends Service {
private Authenticator authenticator;
@Override
public void onCreate() {
authenticator = new Authenticator(this);
}
@Override
public IBinder onBind(Intent intent) {
return authenticator.getIBinder();
}
}

View File

@ -18,7 +18,6 @@ import org.json.JSONObject;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -177,7 +176,7 @@ public final class JConfiguration {
Buffer configData = new Buffer();
try {
configSource.readAll(configData);
mConfigJson = new JSONObject(configData.readString(StandardCharsets.UTF_8));
mConfigJson = new JSONObject(configData.readString(Charset.forName("UTF-8")));
} catch (IOException ex) {
throw new InvalidConfigurationException(
"Failed to read configuration: " + ex.getMessage());

View File

@ -1,251 +0,0 @@
package com.jc.jcfw.google;
import android.app.Activity;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingClientStateListener;
import com.android.billingclient.api.BillingFlowParams;
import com.android.billingclient.api.BillingResult;
import com.android.billingclient.api.ProductDetails;
import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.PurchasesUpdatedListener;
import com.android.billingclient.api.QueryProductDetailsParams;
import com.android.billingclient.api.QueryPurchasesParams;
import com.cege.games.release.MainActivity;
import com.jc.jcfw.JcSDK;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class PayClient extends Activity implements PurchasesUpdatedListener {
private static final String TAG = "GooglePayClient";
private static volatile PayClient mInstance = null;
private static BillingClient billingClient;
private static ConcurrentHashMap<String, ProductDetails> skuDetailsMap;
private Context mContext = null;
private String mFunId;
public static PayClient getInstance() {
if (null == mInstance) {
synchronized (PayClient.class) {
if (null == mInstance) {
mInstance = new PayClient();
}
}
}
return mInstance;
}
public void init(Context context) {
this.mContext = context;
skuDetailsMap = new ConcurrentHashMap<>();
billingClient = BillingClient.newBuilder(context).enablePendingPurchases().setListener(this).build();
connectToPlay();
}
private void connectToPlay() {
billingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
Log.i(TAG, "onBillingSetupFinished, response code: " + billingResult.getResponseCode());
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
// The BillingClient is ready. You can query purchases here.
Log.i(TAG, "BillingClient is ready");
} else {
Log.i(TAG, "error init BillingClient with error:: " +billingResult.getResponseCode());
}
}
@Override
public void onBillingServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
connectToPlay();
}
});
}
JSONArray handlePurchase(JSONArray dataArr, Purchase purchase) throws JSONException {
if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED
// || purchase.getPurchaseState() == Purchase.PurchaseState.PENDING
) {
// Acknowledge purchase and grant the item to the user
// Grant entitlement to the user.
Log.i(TAG, "handlePurchase:" + purchase.getOriginalJson());
Log.i(TAG, "purchase sign:" + purchase.getSignature());
if (!purchase.isAcknowledged() && purchase.getProducts().size() > 0) {
// consumables 消耗型
JSONObject data = new JSONObject();
data.put("id", purchase.getProducts().get(0));
data.put("token", purchase.getPurchaseToken());
dataArr.put(data);
}
}
return dataArr;
}
@Override
public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
Log.i(TAG, "onPurchasesUpdated with status: " + billingResult.getResponseCode());
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
&& purchases != null) {
final JSONArray dataArr = new JSONArray();
boolean hasErr = false;
for (Purchase purchase : purchases) {
try {
handlePurchase(dataArr, purchase);
} catch (JSONException e) {
hasErr = true;
break;
}
}
if (hasErr) {
purchaseUpdateCb(null,"error parse purchase data", null);
} else {
purchaseUpdateCb(null,null, dataArr.toString());
}
} else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
purchaseUpdateCb(null,"user cancel buy", null);
} else {
String errmsg = billingResult.getDebugMessage();
if (errmsg.isEmpty()) {
errmsg = "other error";
}
purchaseUpdateCb(null, errmsg, null);
}
}
private void purchaseUpdateCb(String funId, String error, String dataStr) {
if (funId == null || funId.isEmpty()) {
if (mFunId != null && !mFunId.isEmpty()) {
final String _funId = mFunId;
runOnUiThread(() -> JcSDK.nativeCb(_funId, error, dataStr));
mFunId = null;
}
} else {
runOnUiThread(() -> JcSDK.nativeCb(funId, error, dataStr));
}
}
private boolean parseProductDetails(JSONArray dataArr, ProductDetails skuDetails) {
JSONObject data = new JSONObject();
try {
data.put("name", skuDetails.getTitle());
data.put("description", skuDetails.getDescription());
data.put("id", skuDetails.getProductId());
data.put("type", skuDetails.getProductType());
if (skuDetails.getProductType().equals(BillingClient.ProductType.INAPP)) {
data.put("currencyCode",
skuDetails.getOneTimePurchaseOfferDetails().getPriceCurrencyCode());
data.put("priceValue", skuDetails.getOneTimePurchaseOfferDetails().getPriceAmountMicros());
data.put("priceShow", skuDetails.getOneTimePurchaseOfferDetails().getFormattedPrice());
}
dataArr.put(data);
return true;
} catch (JSONException e) {
e.printStackTrace();
return false;
}
}
public void queryProductList(String funId, List<String> productIds) {
List<QueryProductDetailsParams.Product> productList = new ArrayList<>();
for (String productId : productIds) {
productList.add(
QueryProductDetailsParams.Product.newBuilder()
.setProductId(productId)
.setProductType(BillingClient.ProductType.INAPP)
.build());
}
QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder()
.setProductList(productList)
.build();
billingClient.queryProductDetailsAsync(
params,
(billingResult, productDetailsList) -> {
// Process the result
Map<String, ProductDetails> pMap = new HashMap<>();
for (ProductDetails details : productDetailsList) {
skuDetailsMap.put(details.getProductId(), details);
pMap.put(details.getProductId(), details);
}
final JSONArray dataArr = new JSONArray();
boolean hasErr = false;
for (Map.Entry<String, ProductDetails> entry : pMap.entrySet()) {
ProductDetails skuDetails = entry.getValue();
if (!parseProductDetails(dataArr, skuDetails)) {
hasErr = true;
break;
}
}
if (hasErr) {
purchaseUpdateCb(funId, "parse product detail json error", null);
} else {
purchaseUpdateCb(funId, null, dataArr.toString());
}
});
}
public void queryPurchase(String funId) {
billingClient.queryPurchasesAsync(
QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.INAPP).build(),
(billingResult, purchases) -> {
// Process the result
final JSONArray dataArr = new JSONArray();
boolean hasErr = false;
for (Purchase purchase : purchases) {
try {
handlePurchase(dataArr, purchase);
} catch (JSONException e) {
hasErr = true;
break;
}
}
if (hasErr) {
purchaseUpdateCb(funId, "error parse purchase data", null);
} else {
purchaseUpdateCb(funId, null, dataArr.toString());
}
});
}
public void buyOne(String funId, String productId, String orderId) {
if (mFunId != null && !mFunId.isEmpty()) {
purchaseUpdateCb(funId, "another purchase is in progress", null);
return;
}
if (!skuDetailsMap.containsKey(productId)) {
purchaseUpdateCb(funId, "product with : "+productId+ " not found", null);
return;
}
ProductDetails productDetails = skuDetailsMap.get(productId);
// Set the parameters for the offer that will be presented
// in the billing flow creating separate productDetailsParamsList variable
List<BillingFlowParams.ProductDetailsParams> productDetailsParamsList = Collections
.singletonList(BillingFlowParams.ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.build());
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
.setProductDetailsParamsList(productDetailsParamsList)
.setObfuscatedAccountId(orderId)
.setObfuscatedProfileId(orderId)
.build();
// Launch the billing flow
this.mFunId = funId;
MainActivity.app.runOnUiThread(() -> {
billingClient.launchBillingFlow((Activity) mContext, billingFlowParams);
});
}
}

View File

@ -1,63 +0,0 @@
package com.jc.jcfw.security;
import static androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG;
import static androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.biometric.BiometricPrompt;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentActivity;
import com.cege.games.release.R;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import javax.crypto.Cipher;
public class BiometricHelper {
public static final int ERROR_BIOMETRIC_FAIL = 100;
public static final int ERROR_BIOMETRIC_NO_CIPHER = 101;
private static final String TAG = BiometricHelper.class.getSimpleName();
public static BiometricPrompt.PromptInfo createPromptInfo(Context context) {
BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder()
.setTitle(context.getString(R.string.prompt_info_title)) // e.g. "Sign in"
.setSubtitle(context.getString(R.string.prompt_info_subtitle)) // e.g. "Biometric for My App"
.setDescription(context.getString(R.string.prompt_info_description)) // e.g. "Confirm biometric to continue"
.setConfirmationRequired(false)
.setAllowedAuthenticators(BIOMETRIC_STRONG | DEVICE_CREDENTIAL)
// .setDeviceCredentialAllowed(true) // Allow PIN/pattern/password authentication.
// Also note that setDeviceCredentialAllowed and setNegativeButtonText are
// incompatible so that if you uncomment one you must comment out the other
.build();
return promptInfo;
}
public static BiometricPrompt createBiometricPrompt(FragmentActivity activity, Consumer<BiometricResult> func) {
Executor executor = ContextCompat.getMainExecutor(activity.getApplicationContext());
return new BiometricPrompt(activity, executor, new BiometricPrompt.AuthenticationCallback() {
@Override
public void onAuthenticationError(int errcode, @NonNull CharSequence errString) {
super.onAuthenticationError(errcode, errString);
Log.i(TAG, "Authentication error: " + errcode +" | "+ errString);
func.accept(new BiometricResult(errcode, (String) errString));
}
@Override
public void onAuthenticationFailed() {
super.onAuthenticationFailed();
Log.i(TAG, "Authentication failed!");
func.accept(new BiometricResult(ERROR_BIOMETRIC_FAIL, "Authentication failed"));
}
@Override
public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
super.onAuthenticationSucceeded(result);
Log.i(TAG, "Authentication succeeded!");
func.accept(new BiometricResult(result.getCryptoObject().getCipher()));
}
});
}
}

View File

@ -1,61 +0,0 @@
package com.jc.jcfw.security;
import androidx.annotation.Nullable;
import javax.crypto.Cipher;
public class BiometricResult {
private Cipher cipher;
private boolean error;
private int errcode;
private String errmsg;
public BiometricResult(Cipher cipher) {
if (null != cipher) {
this.cipher = cipher;
this.error = false;
} else {
this.error = true;
this.errcode = 101;
this.errmsg = "cipher is null";
}
}
public BiometricResult(int errcode, String errmsg) {
this.error = true;
this.errcode = errcode;
this.errmsg = errmsg;
}
@Nullable
public Cipher getCipher() {
return cipher;
}
public void setCipher(Cipher cipher) {
this.cipher = cipher;
}
public boolean isError() {
return error;
}
public void setError(boolean error) {
this.error = error;
}
public int getErrcode() {
return errcode;
}
public void setErrcode(int errcode) {
this.errcode = errcode;
}
public String getErrmsg() {
return errmsg;
}
public void setErrmsg(String errmsg) {
this.errmsg = errmsg;
}
}

View File

@ -1,27 +0,0 @@
package com.jc.jcfw.security;
import javax.crypto.Cipher;
public interface CryptographyManager {
/**
* This method first gets or generates an instance of SecretKey and then initializes the Cipher
* with the key. The secret key uses [ENCRYPT_MODE][Cipher.ENCRYPT_MODE] is used.
*/
Cipher getInitializedCipherForEncryption(String keyName);
/**
* This method first gets or generates an instance of SecretKey and then initializes the Cipher
* with the key. The secret key uses [DECRYPT_MODE][Cipher.DECRYPT_MODE] is used.
*/
Cipher getInitializedCipherForDecryption(String keyName, byte[] initializationVector);
/**
* The Cipher created with [getInitializedCipherForEncryption] is used here
*/
EncryptedData encryptData(String plaintext, Cipher cipher);
/**
* The Cipher created with [getInitializedCipherForDecryption] is used here
*/
String decryptData(byte[] ciphertext, Cipher cipher);
}

View File

@ -1,101 +0,0 @@
package com.jc.jcfw.security;
import static android.security.keystore.KeyProperties.BLOCK_MODE_CBC;
import static android.security.keystore.KeyProperties.ENCRYPTION_PADDING_PKCS7;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.util.Log;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class CryptographyManagerImpl implements CryptographyManager {
@Override
public Cipher getInitializedCipherForEncryption(String keyName) {
Cipher cipher;
try {
cipher = getCipher();
SecretKey secretKey = getOrCreateSecretKey(keyName);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
} catch (Exception e) {
throw new RuntimeException("Failed to initialize Cipher for encryption", e);
}
return cipher;
}
@Override
public Cipher getInitializedCipherForDecryption(String keyName, byte[] initializationVector) {
Cipher cipher;
try {
cipher = getCipher();
SecretKey secretKey = getOrCreateSecretKey(keyName);
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(initializationVector));
} catch (Exception e) {
throw new RuntimeException("Failed to initialize Cipher for decryption", e);
}
return cipher;
}
@Override
public EncryptedData encryptData(String plaintext, Cipher cipher) {
try {
byte[] ciphertext = cipher.doFinal(plaintext.getBytes(Charset.defaultCharset()));
return new EncryptedData(ciphertext, cipher.getIV());
} catch (Exception e) {
throw new RuntimeException("Failed to encrypt data", e);
}
}
@Override
public String decryptData(byte[] ciphertext, Cipher cipher) {
try {
byte[] plaintext = cipher.doFinal(ciphertext);
return new String(plaintext, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new RuntimeException("Failed to encrypt data", e);
}
}
private Cipher getCipher() throws Exception {
return Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ BLOCK_MODE_CBC + "/"
+ ENCRYPTION_PADDING_PKCS7);
}
private SecretKey getOrCreateSecretKey(String keyName) throws Exception {
String ANDROID_KEYSTORE = "AndroidKeyStore";
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
keyStore.load(null); // Keystore must be loaded before it can be accessed
SecretKey existingKey = (SecretKey) keyStore.getKey(keyName, null);
if (existingKey != null) {
return existingKey;
}
// if you reach here, then a new SecretKey must be generated for that keyName
KeyGenParameterSpec.Builder paramsBuilder = new KeyGenParameterSpec.Builder(keyName,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT);
paramsBuilder.setBlockModes(BLOCK_MODE_CBC);
paramsBuilder.setEncryptionPaddings(ENCRYPTION_PADDING_PKCS7);
int KEY_SIZE = 256;
paramsBuilder.setKeySize(KEY_SIZE);
paramsBuilder.setUserAuthenticationRequired(false);
paramsBuilder.setUserAuthenticationValidityDurationSeconds(30);
KeyGenParameterSpec keyGenParams = paramsBuilder.build();
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES,
ANDROID_KEYSTORE);
keyGenerator.init(keyGenParams);
return keyGenerator.generateKey();
}
}

View File

@ -1,19 +0,0 @@
package com.jc.jcfw.security;
public class EncryptedData {
private final byte[] ciphertext;
private final byte[] initializationVector;
public EncryptedData(byte[] ciphertext, byte[] initializationVector) {
this.ciphertext = ciphertext;
this.initializationVector = initializationVector;
}
public byte[] getCiphertext() {
return ciphertext;
}
public byte[] getInitializationVector() {
return initializationVector;
}
}

View File

@ -1,101 +0,0 @@
package com.jc.jcfw.util;
import android.util.Log;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.extensions.android.json.AndroidJsonFactory;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.http.FileContent;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.model.File;
import com.google.api.services.drive.model.FileList;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collections;
import java.util.function.Consumer;
public class DriveUtils {
private static final String TAG = DriveUtils.class.getSimpleName();
public static Drive generateService(GoogleAccountCredential credential, String appName) {
return new Drive.Builder(
AndroidHttp.newCompatibleTransport(),
AndroidJsonFactory.getDefaultInstance(),
credential)
.setApplicationName(appName)
.build();
}
/**
* query app file by filename
*
* @param service
* @param fileName
* @throws IOException
*/
public static String queryOneAppFile(Drive service, String fileName) {
boolean querySuccess = false;
String fileId = "";
while (!querySuccess) {
try {
FileList files = service.files().list()
.setSpaces("appDataFolder")
.setFields("nextPageToken, files(id, name)")
.setPageSize(100)
.execute();
querySuccess = true;
for (File file : files.getFiles()) {
Log.i(TAG, String.format("Found file: %s (%s)\n", file.getName(), file.getId()));
if (file.getName().equals(fileName)) {
fileId = file.getId();
break;
}
}
} catch (IOException e) {
Log.i(TAG, "error query file from drive");
}
}
return fileId;
}
/**
* download one file from drive
*
* @param service
* @param fileId
* @throws IOException
*/
public static String downloadFile(Drive service, String fileId) throws IOException {
OutputStream outputStream = new ByteArrayOutputStream();
service.files().get(fileId).executeMediaAndDownloadTo(outputStream);
// convert outputStream to JSON string
// String json = outputStream.toString();
return outputStream.toString();
}
/**
* upload one file to Drive
*
* @param service
* @param filePath file absolute path
* @param fileType application/json
* @throws IOException
*/
public static String uploadAppFile(Drive service, java.io.File filePath, String fileType) throws IOException {
String fileName = filePath.getName();
File fileMetadata = new File();
fileMetadata.setName(fileName);
fileMetadata.setParents(Collections.singletonList("appDataFolder"));
// "application/json"
FileContent mediaContent = new FileContent(fileType, filePath);
File file = service.files().create(fileMetadata, mediaContent)
.setFields("id")
.execute();
Log.i(TAG, String.format("%s upload success, File ID: %s", fileName, file.getId()));
return file.getId();
}
}

View File

@ -1,97 +0,0 @@
package com.jc.jcfw.util;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class DriverApiUtils {
private static final String driveApiBase = "https://www.googleapis.com/drive/v3/files";
private static final String driveApiUploadBase = "https://www.googleapis.com/upload/drive/v3/files";
private final String TAG = getClass().getSimpleName();
private String token = "";
public DriverApiUtils() {
}
public DriverApiUtils(String token) {
this.token = token;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public JSONArray fileList() throws IOException, JSONException {
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
Request request = new Request.Builder()
.url(driveApiBase + "?spaces=appDataFolder&fields=files(id, name, modifiedTime)")
.get()
.addHeader("Authorization", "Bearer " + token)
.build();
try (Response response = client.newCall(request).execute()) {
String resStr = response.body().string();
JSONObject resJson = new JSONObject(resStr);
return resJson.getJSONArray("files");
}
}
public String queryOneAppFile(String fileName) throws IOException, JSONException {
JSONArray fileList = fileList();
for (int i = 0; i < fileList.length(); i++) {
JSONObject file = fileList.getJSONObject(i);
if (file.getString("name").equals(fileName)) {
return file.getString("id");
}
}
return "";
}
public String fileInfo(String fileId) throws IOException {
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
Request request = new Request.Builder()
.url(String.format("%s/%s?alt=media", driveApiBase, fileId))
.get()
.addHeader("Authorization", "Bearer " + token)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
public String uploadFile(java.io.File filePath, String fileType) throws IOException {
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
String fileName = filePath.getName();
String optionStr = "{\"name\":\""+fileName+"\",\"parents\":[\"appDataFolder\"]}";
RequestBody body = new MultipartBody.Builder().setType(MultipartBody.FORM)
.addFormDataPart("", "upload-options.json",
RequestBody.create(optionStr, MediaType.parse("application/json")))
.addFormDataPart("", fileName,
RequestBody.create(filePath, MediaType.parse("application/octet-stream")))
.build();
Request request = new Request.Builder()
.url(driveApiUploadBase + "?uploadType=multipart")
.post(body)
.addHeader("Content-Type", fileType)
.addHeader("Authorization", "Bearer " + token)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
}

View File

@ -13,15 +13,10 @@ import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
public class FileUtils {
private static final String TAG = FileUtils.class.getSimpleName();
@ -31,7 +26,7 @@ public class FileUtils {
* @param fileName path
* @return TRUE or FALSE
*/
public static boolean fileIsExist(String fileName) {
static boolean fileIsExist(String fileName) {
File file = new File(fileName);
if (file.exists())
return true;
@ -40,20 +35,6 @@ public class FileUtils {
}
}
public static void writeFile(File filePath, String content) throws IOException {
FileOutputStream out = new FileOutputStream(filePath);
out.write(content.getBytes());
out.close();
}
public static JSONObject readJsonFromFile(File filePath) throws IOException, JSONException {
RandomAccessFile f = new RandomAccessFile(filePath, "r");
byte[] bytes = new byte[(int) f.length()];
f.readFully(bytes);
f.close();
return new JSONObject(new String(bytes));
}
/**
* get image base path of external storage
*/

View File

@ -1,72 +0,0 @@
package com.jc.jcfw.util;
import android.content.Context;
import android.content.SharedPreferences;
import java.util.Map;
public class SharedPreferencesHelper {
private final SharedPreferences sharedPreferences;
private SharedPreferences.Editor editor;
public SharedPreferencesHelper(Context context, String FILE_NAME) {
sharedPreferences = context.getSharedPreferences(FILE_NAME,
Context.MODE_PRIVATE);
editor = sharedPreferences.edit();
}
public void put(String key, Object object) {
if (object instanceof String) {
editor.putString(key, (String) object);
} else if (object instanceof Integer) {
editor.putInt(key, (Integer) object);
} else if (object instanceof Boolean) {
editor.putBoolean(key, (Boolean) object);
} else if (object instanceof Float) {
editor.putFloat(key, (Float) object);
} else if (object instanceof Long) {
editor.putLong(key, (Long) object);
} else {
editor.putString(key, object.toString());
}
editor.commit();
}
public Object getSharedPreference(String key, Object defaultObject) {
if (defaultObject instanceof String) {
return sharedPreferences.getString(key, (String) defaultObject);
} else if (defaultObject instanceof Integer) {
return sharedPreferences.getInt(key, (Integer) defaultObject);
} else if (defaultObject instanceof Boolean) {
return sharedPreferences.getBoolean(key, (Boolean) defaultObject);
} else if (defaultObject instanceof Float) {
return sharedPreferences.getFloat(key, (Float) defaultObject);
} else if (defaultObject instanceof Long) {
return sharedPreferences.getLong(key, (Long) defaultObject);
} else {
return sharedPreferences.getString(key, null);
}
}
public void remove(String key) {
editor.remove(key);
editor.commit();
}
public void clear() {
editor.clear();
editor.commit();
}
public Boolean contain(String key) {
return sharedPreferences.contains(key);
}
public Map<String, ?> getAll() {
return sharedPreferences.getAll();
}
}

View File

@ -1,24 +0,0 @@
package com.jc.jcfw.util;
import android.os.Handler;
import android.os.Looper;
public class ThreadUtils {
/**
* check if current thread is main thread
*
* @return
*/
public static boolean isMainThread() {
return Looper.getMainLooper() == Looper.myLooper();
}
public static void runInMain(Runnable action) {
if (ThreadUtils.isMainThread()) {
action.run();
} else {
Handler mainHandler = new Handler(Looper.getMainLooper());
mainHandler.post(action);
}
}
}

View File

@ -1,43 +0,0 @@
package com.jc.jcfw.util;
import static androidx.core.content.ContextCompat.getSystemService;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.widget.Toast;
import androidx.annotation.MainThread;
import com.cege.games.release.MainActivity;
import com.cege.games.release.MainApplication;
import com.cege.games.release.dialog.QRCodeActivity;
import com.jc.jcfw.JcSDK;
public class UIUtils {
private static Toast toast;
@MainThread
public static void showToastReal(Context context,String text) {
if (toast == null) {
toast = Toast.makeText(context, text, Toast.LENGTH_SHORT);
} else {
toast.setDuration(Toast.LENGTH_SHORT);
toast.setText(text);
}
toast.show();
}
public static void showToast(Context context, String text) {
ThreadUtils.runInMain(() -> showToastReal(context, text));
}
@MainThread
public static void showQRCodeReal(Context context, String str, String title) {
QRCodeActivity qrCodeActivity = new QRCodeActivity(context);
qrCodeActivity.showQRCode(str, title);
qrCodeActivity.show();
}
public static void showQRCode(Context context, String str, String title) {
ThreadUtils.runInMain(() -> showQRCodeReal(context, str, title));
}
}

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
</LinearLayout>

View File

@ -23,23 +23,24 @@
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center_horizontal"
android:text="Title Label"
android:text="sample label"
android:textColor="#0D47A1"
app:layout_constraintBottom_toTopOf="@id/ivCode"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tipLabel"
<Button
android:id="@+id/qrSaveBtn"
android:gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/round_btn"
android:minHeight="32dp"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:paddingVertical="5dp"
android:text="Click Image to copy content"
android:text="Save Image to Local"
android:textColor="#0D47A1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"

View File

@ -1,12 +0,0 @@
{
"client_id": "wallet.cebggame.com",
"redirect_uri": "https://wallet.cebggame.com/apple/oauth_redirect",
"end_session_redirect_uri": "com.apple.apps.wallet.cebggame.com:/oauth2redirect",
"authorization_scope": "name email",
"discovery_uri": "https://appleid.apple.com/.well-known/openid-configuration",
"authorization_endpoint_uri": "",
"token_endpoint_uri": "",
"registration_endpoint_uri": "",
"user_info_endpoint_uri": "",
"https_required": true
}

View File

@ -2,7 +2,7 @@
"client_id": "53206975661-asnf3qe4bg29p8h981pgf099osvrjbme.apps.googleusercontent.com",
"redirect_uri": "com.googleusercontent.apps.53206975661-asnf3qe4bg29p8h981pgf099osvrjbme:/oauth2redirect",
"end_session_redirect_uri": "com.googleusercontent.apps.53206975661-asnf3qe4bg29p8h981pgf099osvrjbme:/oauth2redirect",
"authorization_scope": "openid email profile https://www.googleapis.com/auth/drive.appdata",
"authorization_scope": "openid email profile",
"discovery_uri": "https://accounts.google.com/.well-known/openid-configuration",
"authorization_endpoint_uri": "",
"token_endpoint_uri": "",

View File

@ -9,10 +9,4 @@
<string name="facebook_app_id" translatable="false">1204701000119770</string>
<string name="fb_login_protocol_scheme" translatable="false">fb1204701000119770</string>
<string name="facebook_client_token" translatable="false">3e29f2606ae15a99bb3824d2ef1a9d0b</string>
<string name="prompt_info_title" translatable="false">Biometric login for Wallet</string>
<string name="prompt_info_subtitle" translatable="false">Login using your biometric credential</string>
<string name="prompt_info_description" translatable="false">Confirm biometric to continue</string>
<string name="account_manager_description" translatable="false">store you account</string>
</resources>

View File

@ -6,26 +6,6 @@
</style>
<style name="BaseUnityTheme" parent="android:Theme.Holo.Light.NoActionBar.Fullscreen">
</style>
<style name="AppCompatTranslucent" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowAnimationStyle">@android:style/Animation.Translucent</item>
</style>
<style name="DayNightActivity" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="android:windowNoTitle">true</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowFullscreen">true</item>
</style>
<style name="CebgMainActivity" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowBackground">@android:color/black</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>
<style name="WebViewTheme" parent="android:Theme.Holo.Light.NoActionBar.Fullscreen">
<item name="windowActionBar">false</item>
<item name="colorPrimary">@android:color/black</item>

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<account-authenticator
xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="com.cege.games.auth"
android:label="@string/account_manager_description"/>