将jcfw部分代码改为kotlin
This commit is contained in:
parent
b09c735324
commit
ed0f5f42da
2
.idea/kotlinc.xml
generated
2
.idea/kotlinc.xml
generated
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="KotlinJpsPluginSettings">
|
<component name="KotlinJpsPluginSettings">
|
||||||
<option name="version" value="1.8.0-release" />
|
<option name="version" value="1.7.20" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
@ -17,7 +17,8 @@
|
|||||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
|
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
|
||||||
|
|
||||||
#ifndef COM_JC_JCFW_CLASS_NAME
|
#ifndef COM_JC_JCFW_CLASS_NAME
|
||||||
#define COM_JC_JCFW_CLASS_NAME com_jc_jcfw_JcSDK
|
// Java_com_jc_jcfw_JcSDK_00024Companion_runJS
|
||||||
|
#define COM_JC_JCFW_CLASS_NAME com_jc_jcfw_JcSDK_00024Companion
|
||||||
#endif
|
#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)
|
||||||
|
|
||||||
@ -259,7 +260,7 @@ extern "C"
|
|||||||
return result == 0 ? 1 : 0;
|
return result == 0 ? 1 : 0;
|
||||||
}
|
}
|
||||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
|
#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)
|
JNIEXPORT jint JNICALL JNI_JCFW(runJS)(JNIEnv *env, jobject clazz, jstring j_fun_id, jstring j_method_name, jobjectArray j_params)
|
||||||
{
|
{
|
||||||
if (!_isStarted) {
|
if (!_isStarted) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -285,7 +286,7 @@ extern "C"
|
|||||||
return result == 0 ? 1 : 0;
|
return result == 0 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL JNI_JCFW(decryptPass)(JNIEnv *env, jclass clazz, jstring jaccount, jstring jpass)
|
JNIEXPORT jstring JNICALL JNI_JCFW(decryptPass)(JNIEnv *env, jobject clazz, jstring jaccount, jstring jpass)
|
||||||
{
|
{
|
||||||
std::string pass_encrypted = JniHelper::jstring2string(jpass);
|
std::string pass_encrypted = JniHelper::jstring2string(jpass);
|
||||||
std::string account = JniHelper::jstring2string(jaccount);
|
std::string account = JniHelper::jstring2string(jaccount);
|
||||||
@ -293,7 +294,7 @@ extern "C"
|
|||||||
std::string passDecrypt = decrypt_aes(pass_encrypted, keyStr);
|
std::string passDecrypt = decrypt_aes(pass_encrypted, keyStr);
|
||||||
return env->NewStringUTF(passDecrypt.c_str());
|
return env->NewStringUTF(passDecrypt.c_str());
|
||||||
}
|
}
|
||||||
JNIEXPORT void JNICALL JNI_JCFW(tick)(JNIEnv *env, jclass clazz, jfloat dt)
|
JNIEXPORT void JNICALL JNI_JCFW(tick)(JNIEnv *env, jobject clazz, jfloat dt)
|
||||||
{
|
{
|
||||||
tick2(dt);
|
tick2(dt);
|
||||||
}
|
}
|
||||||
@ -352,4 +353,4 @@ bool jsb_register_walletevent_modules(se::Object *global)
|
|||||||
getOrCreatePlainObject_r("jsb", global, &__jsbObj);
|
getOrCreatePlainObject_r("jsb", global, &__jsbObj);
|
||||||
__jsbObj->defineFunction("jcCallback", _SE(jsb_wallet_callback));
|
__jsbObj->defineFunction("jcCallback", _SE(jsb_wallet_callback));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
@ -7,6 +7,7 @@ plugins {
|
|||||||
id 'com.google.gms.google-services'
|
id 'com.google.gms.google-services'
|
||||||
// Add the Crashlytics Gradle plugin
|
// Add the Crashlytics Gradle plugin
|
||||||
id 'com.google.firebase.crashlytics'
|
id 'com.google.firebase.crashlytics'
|
||||||
|
id 'org.jetbrains.kotlin.android'
|
||||||
}
|
}
|
||||||
android {
|
android {
|
||||||
compileSdkVersion PROP_COMPILE_SDK_VERSION.toInteger()
|
compileSdkVersion PROP_COMPILE_SDK_VERSION.toInteger()
|
||||||
|
@ -100,14 +100,14 @@ class MainActivity : UnityPlayerActivity(), Cocos2dxHelperListener {
|
|||||||
initFacebookSDK()
|
initFacebookSDK()
|
||||||
// end of facebook login
|
// end of facebook login
|
||||||
tiktokOpenApi = TikTokOpenApiFactory.create(this)
|
tiktokOpenApi = TikTokOpenApiFactory.create(this)
|
||||||
val payClient = PayClient.getInstance()
|
val payClient = PayClient.instance
|
||||||
payClient.init(this)
|
payClient?.init(this)
|
||||||
mWalletUtil = WalletUtil(this)
|
mWalletUtil = WalletUtil(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
val paramArr = arrayOf<String>()
|
val paramArr = arrayOfNulls<String>(0)
|
||||||
JcSDK.callNativeJS("", "onGameResume", paramArr)
|
JcSDK.callNativeJS("", "onGameResume", paramArr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -483,19 +483,19 @@ class MainActivity : UnityPlayerActivity(), Cocos2dxHelperListener {
|
|||||||
Log.d(TAG, "need refresh accessToken")
|
Log.d(TAG, "need refresh accessToken")
|
||||||
mAppAuthSvr!!.refreshToken(funID) {
|
mAppAuthSvr!!.refreshToken(funID) {
|
||||||
mWalletUtil!!.downloadCfgWithApi(
|
mWalletUtil!!.downloadCfgWithApi(
|
||||||
state.accessToken
|
state.accessToken!!
|
||||||
) { mWalletUtil!!.passLocal }
|
) { mWalletUtil!!.passLocal }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.d(TAG, "access token no need refresh")
|
Log.d(TAG, "access token no need refresh")
|
||||||
mWalletUtil!!.downloadCfgWithApi(state.accessToken) { mWalletUtil!!.passLocal }
|
mWalletUtil!!.downloadCfgWithApi(state.accessToken!!) { mWalletUtil!!.passLocal }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "not login")
|
Log.w(TAG, "not login")
|
||||||
mExecutor!!.submit {
|
mExecutor!!.submit {
|
||||||
mAppAuthSvr!!.doAuth(funID) {
|
mAppAuthSvr!!.doAuth(funID) {
|
||||||
mWalletUtil!!.downloadCfgWithApi(
|
mWalletUtil!!.downloadCfgWithApi(
|
||||||
state.accessToken
|
state.accessToken!!
|
||||||
) { mWalletUtil!!.passLocal }
|
) { mWalletUtil!!.passLocal }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,15 +109,15 @@ class WalletUtil(private val mContext: Context) {
|
|||||||
fileId = DriveUtils.uploadAppFile(service, file, "application/json")
|
fileId = DriveUtils.uploadAppFile(service, file, "application/json")
|
||||||
}
|
}
|
||||||
Log.i(TAG, "File ID: $fileId")
|
Log.i(TAG, "File ID: $fileId")
|
||||||
func.accept(NativeResult(funID, null, fileId))
|
func.accept(NativeResult(funID!!, null, fileId))
|
||||||
successCB(fileId)
|
successCB(fileId)
|
||||||
} catch (e: GoogleJsonResponseException) {
|
} catch (e: GoogleJsonResponseException) {
|
||||||
Log.i(TAG, "Unable to create file: " + e.details)
|
Log.i(TAG, "Unable to create file: " + e.details)
|
||||||
func.accept(NativeResult(funID, e.message, null))
|
func.accept(NativeResult(funID!!, e.message, null))
|
||||||
errorCB("Unable to create file: " + e.message)
|
errorCB("Unable to create file: " + e.message)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
Log.i(TAG, e.message!!)
|
Log.i(TAG, e.message!!)
|
||||||
func.accept(NativeResult(funID, e.message, null))
|
func.accept(NativeResult(funID!!, e.message, null))
|
||||||
errorCB("Unable to create file: " + e.message)
|
errorCB("Unable to create file: " + e.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -155,7 +155,7 @@ class WalletUtil(private val mContext: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun downloadCfgWithApi(accessToken: String?, func: Consumer<File?>) {
|
fun downloadCfgWithApi(accessToken: String, func: Consumer<File?>) {
|
||||||
val api = DriverApiUtils(accessToken)
|
val api = DriverApiUtils(accessToken)
|
||||||
val fileName = generateFileName(account)
|
val fileName = generateFileName(account)
|
||||||
val fileLocal = getWalletCfgFile(account)
|
val fileLocal = getWalletCfgFile(account)
|
||||||
@ -186,7 +186,7 @@ class WalletUtil(private val mContext: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun uploadCfgWithApi(accessToken: String?, func: Consumer<NativeResult?>) {
|
fun uploadCfgWithApi(accessToken: String?, func: Consumer<NativeResult?>) {
|
||||||
val api = DriverApiUtils(accessToken)
|
val api = DriverApiUtils(accessToken!!)
|
||||||
val fileLocal = getWalletCfgFile(account)
|
val fileLocal = getWalletCfgFile(account)
|
||||||
try {
|
try {
|
||||||
val fileName = generateFileName(account)
|
val fileName = generateFileName(account)
|
||||||
@ -197,13 +197,13 @@ class WalletUtil(private val mContext: Context) {
|
|||||||
fileId = repData.getString("id")
|
fileId = repData.getString("id")
|
||||||
}
|
}
|
||||||
Log.i(TAG, "success upload file with api, ID: $fileId")
|
Log.i(TAG, "success upload file with api, ID: $fileId")
|
||||||
func.accept(NativeResult(funID, null, fileId))
|
func.accept(NativeResult(funID!!, null, fileId))
|
||||||
successCB(fileId)
|
successCB(fileId)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
func.accept(NativeResult(funID, e.message, null))
|
func.accept(NativeResult(funID!!, e.message, null))
|
||||||
errorCB("error upload file with api: " + e.message)
|
errorCB("error upload file with api: " + e.message)
|
||||||
} catch (e: JSONException) {
|
} catch (e: JSONException) {
|
||||||
func.accept(NativeResult(funID, e.message, null))
|
func.accept(NativeResult(funID!!, e.message, null))
|
||||||
errorCB("error upload file with api: " + e.message)
|
errorCB("error upload file with api: " + e.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,6 @@ class WalletInterface
|
|||||||
// sign
|
// sign
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun pageCall(dataStr: String?) {
|
fun pageCall(dataStr: String?) {
|
||||||
EventBus.getDefault().post(WebPageEvent(dataStr))
|
EventBus.getDefault().post(WebPageEvent(dataStr!!))
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,232 +0,0 @@
|
|||||||
package com.jc.jcfw;
|
|
||||||
|
|
||||||
import static com.ctf.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.ctf.games.release.MainActivity;
|
|
||||||
import com.ctf.games.release.MainApplication;
|
|
||||||
import com.ctf.games.release.ui.UIManager;
|
|
||||||
import com.ctf.games.release.webpage.events.CallJSEvent;
|
|
||||||
import com.ctf.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 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);
|
|
||||||
|
|
||||||
public static void initCommonCB(UnityCallback callBack) {
|
|
||||||
Log.i(TAG, "call init common callback from unity");
|
|
||||||
commonCB = callBack;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Deprecated
|
|
||||||
* 不使用该方法, 直接由unity调用cpp方法
|
|
||||||
* @param password
|
|
||||||
*/
|
|
||||||
public static void initWallet(String password) {
|
|
||||||
Log.i(TAG, "call init wallet from unity with password: " + password);
|
|
||||||
CocosJSHelper.initWallet(password);
|
|
||||||
commonCB.nativeCallback("", "wallet init success", 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 回调至c#
|
|
||||||
*/
|
|
||||||
public static void csCallback(String funId, String msg) {
|
|
||||||
if (!Objects.equals(funId, "") && funId.indexOf("js_") == 0) {
|
|
||||||
commonCB.nativeCallback(funId, msg, 1);
|
|
||||||
} else {
|
|
||||||
commonCB.nativeCallback(funId, msg, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* check if metamask installed and jump to metamask
|
|
||||||
*
|
|
||||||
* @param url
|
|
||||||
* sample:
|
|
||||||
* "https://metamask.app.link/wc?uri="+ExampleApplication.config.toWCUri();
|
|
||||||
*/
|
|
||||||
|
|
||||||
public static void toWallet(String url) {
|
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
|
||||||
Log.i(TAG, url);
|
|
||||||
try {
|
|
||||||
intent.setData(Uri.parse(url));
|
|
||||||
MainActivity.app.startActivity(intent);
|
|
||||||
} catch (ActivityNotFoundException e) {
|
|
||||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void showQRCode(String funid, String content) {
|
|
||||||
UIUtils.showQRCode(MainActivity.app, content, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void showWebPage(String funid, String url) {
|
|
||||||
MainActivity.app.showPage(funid, url);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void scanQRCode(String funid, String title) {
|
|
||||||
// MainActivity.app.showQRScan(funid, title);
|
|
||||||
UIManager.getSingleton().showQRScan(funid, title);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void signWithTiktok(String funid) {
|
|
||||||
MainActivity.app.signWithTiktok(funid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void signWithFacebook(String funid) {
|
|
||||||
MainActivity.app.signWithFacebook(funid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void shareWithFacebook(String content) {
|
|
||||||
MainActivity.app.shareWithFacebook(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void signWithTwitter(String funid) {
|
|
||||||
MainActivity.app.signWithTwitter(funid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void signWithGoogle(String funid) {
|
|
||||||
MainActivity.app.signWithGoogle(funid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void signWithApple(String funid) {
|
|
||||||
MainActivity.app.signWithApple(funid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void signOutGoogle(String funid) {
|
|
||||||
MainActivity.app.signOutGoogle(funid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void logEvent(String content) {
|
|
||||||
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) {
|
|
||||||
JSONObject result = new JSONObject();
|
|
||||||
try {
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
237
app/src/com/jc/jcfw/JcSDK.kt
Normal file
237
app/src/com/jc/jcfw/JcSDK.kt
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
package com.jc.jcfw
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.ActivityNotFoundException
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.util.Log
|
||||||
|
import com.ctf.games.release.MainActivity
|
||||||
|
import com.ctf.games.release.MainApplication
|
||||||
|
import com.ctf.games.release.ui.UIManager.Companion.singleton
|
||||||
|
import com.ctf.games.release.webpage.events.CallJSEvent
|
||||||
|
import com.ctf.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 org.cocos2dx.lib.CocosJSHelper
|
||||||
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
import org.json.JSONException
|
||||||
|
import org.json.JSONObject
|
||||||
|
import java.util.Arrays
|
||||||
|
|
||||||
|
const val FUNID_PREFIX = "webpage_"
|
||||||
|
class JcSDK {
|
||||||
|
companion object {
|
||||||
|
external fun runJS(funId: String?, methodName: String, params: Array<String?>): Int
|
||||||
|
external fun decryptPass(account: String?, pass: String?): String?
|
||||||
|
external fun tick(dt: Float)
|
||||||
|
|
||||||
|
private val TAG = JcSDK::class.java.simpleName
|
||||||
|
private var commonCB: UnityCallback? = null
|
||||||
|
@SuppressLint("StaticFieldLeak")
|
||||||
|
private var payClient: PayClient? = null
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun initCommonCB(callBack: UnityCallback?) {
|
||||||
|
Log.i(TAG, "call init common callback from unity")
|
||||||
|
commonCB = callBack
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Deprecated
|
||||||
|
* 不使用该方法, 直接由unity调用cpp方法
|
||||||
|
* @param password
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun initWallet(password: String) {
|
||||||
|
Log.i(TAG, "call init wallet from unity with password: $password")
|
||||||
|
CocosJSHelper.initWallet(password)
|
||||||
|
commonCB!!.nativeCallback("", "wallet init success", 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回调至c#
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun csCallback(funId: String, msg: String?) {
|
||||||
|
if (funId != "" && funId.indexOf("js_") == 0) {
|
||||||
|
commonCB!!.nativeCallback(funId, msg, 1)
|
||||||
|
} else {
|
||||||
|
commonCB!!.nativeCallback(funId, msg, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check if metamask installed and jump to metamask
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* sample:
|
||||||
|
* "https://metamask.app.link/wc?uri="+ExampleApplication.config.toWCUri();
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun toWallet(url: String) {
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW)
|
||||||
|
Log.i(TAG, url)
|
||||||
|
try {
|
||||||
|
intent.data = Uri.parse(url)
|
||||||
|
MainActivity.app!!.startActivity(intent)
|
||||||
|
} catch (e: ActivityNotFoundException) {
|
||||||
|
val i = Intent(Intent.ACTION_VIEW)
|
||||||
|
var 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.data = Uri.parse(downloadUrl)
|
||||||
|
MainActivity.app!!.startActivity(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun showQRCode(funid: String?, content: String?) {
|
||||||
|
UIUtils.showQRCode(MainActivity.app, content, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun showWebPage(funid: String?, url: String?) {
|
||||||
|
MainActivity.app!!.showPage(funid, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun scanQRCode(funid: String?, title: String?) {
|
||||||
|
// MainActivity.app.showQRScan(funid, title);
|
||||||
|
singleton!!.showQRScan(funid, title)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun signWithTiktok(funid: String?) {
|
||||||
|
MainActivity.app!!.signWithTiktok(funid!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun signWithFacebook(funid: String?) {
|
||||||
|
MainActivity.app!!.signWithFacebook(funid!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun shareWithFacebook(content: String?) {
|
||||||
|
MainActivity.app!!.shareWithFacebook(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun signWithTwitter(funid: String?) {
|
||||||
|
MainActivity.app!!.signWithTwitter(funid!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun signWithGoogle(funid: String?) {
|
||||||
|
MainActivity.app!!.signWithGoogle(funid)
|
||||||
|
}
|
||||||
|
@JvmStatic
|
||||||
|
fun signWithApple(funid: String?) {
|
||||||
|
MainActivity.app!!.signWithApple(funid!!)
|
||||||
|
}
|
||||||
|
@JvmStatic
|
||||||
|
fun signOutGoogle(funid: String?) {
|
||||||
|
MainActivity.app!!.signOutGoogle(funid)
|
||||||
|
}
|
||||||
|
@JvmStatic
|
||||||
|
fun logEvent(content: String?) {
|
||||||
|
MainActivity.app!!.logEvent(content)
|
||||||
|
}
|
||||||
|
@JvmStatic
|
||||||
|
fun queryProducts(funid: String?, skuListStr: String) {
|
||||||
|
Log.i(TAG, "queryProducts with: $skuListStr")
|
||||||
|
if (payClient == null) {
|
||||||
|
payClient = PayClient.instance
|
||||||
|
}
|
||||||
|
val skuList: MutableList<String> = ArrayList()
|
||||||
|
if (skuListStr.contains(",")) {
|
||||||
|
val skuArr = skuListStr.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||||
|
skuList.addAll(Arrays.asList(*skuArr))
|
||||||
|
} else {
|
||||||
|
skuList.add(skuListStr)
|
||||||
|
}
|
||||||
|
payClient!!.queryProductList(funid, skuList)
|
||||||
|
}
|
||||||
|
@JvmStatic
|
||||||
|
fun buyProduct(funid: String?, productId: String, orderId: String?) {
|
||||||
|
Log.i(TAG, "buyProduct with: $productId")
|
||||||
|
if (payClient == null) {
|
||||||
|
payClient = PayClient.instance
|
||||||
|
}
|
||||||
|
payClient!!.buyOne(funid, productId, orderId)
|
||||||
|
}
|
||||||
|
@JvmStatic
|
||||||
|
fun queryPurchase(funid: String?) {
|
||||||
|
Log.i(TAG, "queryPurchase")
|
||||||
|
if (payClient == null) {
|
||||||
|
payClient = PayClient.instance
|
||||||
|
}
|
||||||
|
payClient!!.queryPurchase(funid)
|
||||||
|
}
|
||||||
|
@JvmStatic
|
||||||
|
fun passStorageState(funid: String?, account: String) {
|
||||||
|
Log.i(TAG, "passStorageState with: $account")
|
||||||
|
MainActivity.app!!.passStorageState(funid, account)
|
||||||
|
}
|
||||||
|
@JvmStatic
|
||||||
|
fun storagePass(funid: String?, account: String?, password: String?) {
|
||||||
|
MainActivity.app!!.storagePass(funid, account, password)
|
||||||
|
}
|
||||||
|
@JvmStatic
|
||||||
|
fun authGetStoragePass(funid: String?, account: String?) {
|
||||||
|
MainActivity.app!!.authGetStoragePass(funid, account!!)
|
||||||
|
}
|
||||||
|
@JvmStatic
|
||||||
|
fun storageGameData(data: String?) {
|
||||||
|
MainApplication.application!!.gameData = data
|
||||||
|
}
|
||||||
|
@JvmStatic
|
||||||
|
fun getClientId(funid: String?) {
|
||||||
|
Log.i(TAG, "getClientId ")
|
||||||
|
MainActivity.app!!.getClientId(funid)
|
||||||
|
}
|
||||||
|
@JvmStatic
|
||||||
|
fun onProxyCB(funId: String?, data: String?) {
|
||||||
|
EventBus.getDefault().post(ProxyCBEvent(funId!!, data!!))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun nativeCb(funId: String?, error: String?, dataStr: String?) {
|
||||||
|
var funId = funId
|
||||||
|
val result = JSONObject()
|
||||||
|
try {
|
||||||
|
if (Strings.isNullOrEmpty(error)) {
|
||||||
|
result.put("errcode", 0)
|
||||||
|
result.put("data", dataStr)
|
||||||
|
} else {
|
||||||
|
result.put("errcode", 1)
|
||||||
|
result.put("errmessage", error)
|
||||||
|
}
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
Log.e(TAG, "JSONException: " + e.message)
|
||||||
|
}
|
||||||
|
if (funId == null || funId.isEmpty()) {
|
||||||
|
funId = MainActivity.app!!.funId
|
||||||
|
}
|
||||||
|
Log.i(TAG, String.format("%s native cb, error: %s, data: %s", funId, error, dataStr))
|
||||||
|
if (funId != null && funId.startsWith(FUNID_PREFIX)) {
|
||||||
|
EventBus.getDefault().post(CallJSEvent(funId, result.toString()))
|
||||||
|
} else {
|
||||||
|
val finalFunId = funId
|
||||||
|
ThreadUtils.runInMain { runJS(finalFunId, "jniCallback", arrayOf(result.toString())) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun callNativeJS(funId: String?, methodName: String, params: Array<String?>) {
|
||||||
|
ThreadUtils.runInMain { runJS(funId, methodName, params) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun nativeCb(result: NativeResult) {
|
||||||
|
nativeCb(result.funid, result.error, result.dataStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
3
app/src/com/jc/jcfw/NativeResult.kt
Normal file
3
app/src/com/jc/jcfw/NativeResult.kt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package com.jc.jcfw
|
||||||
|
|
||||||
|
class NativeResult(var funid: String, var error: String?, var dataStr: String?)
|
@ -1,5 +0,0 @@
|
|||||||
package com.jc.jcfw;
|
|
||||||
|
|
||||||
public interface UnityCallback {
|
|
||||||
public void nativeCallback(String funId, String str, int type);
|
|
||||||
}
|
|
5
app/src/com/jc/jcfw/UnityCallback.kt
Normal file
5
app/src/com/jc/jcfw/UnityCallback.kt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package com.jc.jcfw
|
||||||
|
|
||||||
|
interface UnityCallback {
|
||||||
|
fun nativeCallback(funId: String?, str: String?, type: Int)
|
||||||
|
}
|
@ -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.ctf.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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
250
app/src/com/jc/jcfw/google/PayClient.kt
Normal file
250
app/src/com/jc/jcfw/google/PayClient.kt
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
package com.jc.jcfw.google
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
|
import com.android.billingclient.api.BillingClient
|
||||||
|
import com.android.billingclient.api.BillingClientStateListener
|
||||||
|
import com.android.billingclient.api.BillingFlowParams
|
||||||
|
import com.android.billingclient.api.BillingFlowParams.ProductDetailsParams
|
||||||
|
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.ctf.games.release.MainActivity
|
||||||
|
import com.jc.jcfw.JcSDK
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONException
|
||||||
|
import org.json.JSONObject
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
class PayClient : Activity(), PurchasesUpdatedListener {
|
||||||
|
private var mContext: Context? = null
|
||||||
|
private var mFunId: String? = null
|
||||||
|
fun init(context: Context?) {
|
||||||
|
mContext = context
|
||||||
|
skuDetailsMap = ConcurrentHashMap()
|
||||||
|
billingClient =
|
||||||
|
BillingClient.newBuilder(context!!).enablePendingPurchases().setListener(this).build()
|
||||||
|
connectToPlay()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun connectToPlay() {
|
||||||
|
billingClient!!.startConnection(object : BillingClientStateListener {
|
||||||
|
override fun onBillingSetupFinished(billingResult: BillingResult) {
|
||||||
|
Log.i(TAG, "onBillingSetupFinished, response code: " + billingResult.responseCode)
|
||||||
|
if (billingResult.responseCode == 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.responseCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBillingServiceDisconnected() {
|
||||||
|
// Try to restart the connection on the next request to
|
||||||
|
// Google Play by calling the startConnection() method.
|
||||||
|
connectToPlay()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(JSONException::class)
|
||||||
|
fun handlePurchase(dataArr: JSONArray, purchase: Purchase): JSONArray {
|
||||||
|
if (purchase.purchaseState == 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.originalJson)
|
||||||
|
Log.i(TAG, "purchase sign:" + purchase.signature)
|
||||||
|
if (!purchase.isAcknowledged && purchase.products.size > 0) {
|
||||||
|
// consumables 消耗型
|
||||||
|
val data = JSONObject()
|
||||||
|
data.put("id", purchase.products[0])
|
||||||
|
data.put("token", purchase.purchaseToken)
|
||||||
|
dataArr.put(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dataArr
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?) {
|
||||||
|
Log.i(TAG, "onPurchasesUpdated with status: " + billingResult.responseCode)
|
||||||
|
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK
|
||||||
|
&& purchases != null
|
||||||
|
) {
|
||||||
|
val dataArr = JSONArray()
|
||||||
|
var hasErr = false
|
||||||
|
for (purchase in purchases) {
|
||||||
|
try {
|
||||||
|
handlePurchase(dataArr, purchase)
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
hasErr = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasErr) {
|
||||||
|
purchaseUpdateCb(null, "error parse purchase data", null)
|
||||||
|
} else {
|
||||||
|
purchaseUpdateCb(null, null, dataArr.toString())
|
||||||
|
}
|
||||||
|
} else if (billingResult.responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
|
||||||
|
purchaseUpdateCb(null, "user cancel buy", null)
|
||||||
|
} else {
|
||||||
|
var errmsg = billingResult.debugMessage
|
||||||
|
if (errmsg.isEmpty()) {
|
||||||
|
errmsg = "other error"
|
||||||
|
}
|
||||||
|
purchaseUpdateCb(null, errmsg, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun purchaseUpdateCb(funId: String?, error: String?, dataStr: String?) {
|
||||||
|
if (funId == null || funId.isEmpty()) {
|
||||||
|
if (mFunId != null && !mFunId!!.isEmpty()) {
|
||||||
|
runOnUiThread { JcSDK.nativeCb(mFunId, error, dataStr) }
|
||||||
|
mFunId = null
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
runOnUiThread { JcSDK.nativeCb(funId, error, dataStr) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseProductDetails(dataArr: JSONArray, skuDetails: ProductDetails): Boolean {
|
||||||
|
val data = JSONObject()
|
||||||
|
return try {
|
||||||
|
data.put("name", skuDetails.title)
|
||||||
|
data.put("description", skuDetails.description)
|
||||||
|
data.put("id", skuDetails.productId)
|
||||||
|
data.put("type", skuDetails.productType)
|
||||||
|
if (skuDetails.productType == BillingClient.ProductType.INAPP) {
|
||||||
|
data.put(
|
||||||
|
"currencyCode",
|
||||||
|
skuDetails.oneTimePurchaseOfferDetails!!.priceCurrencyCode
|
||||||
|
)
|
||||||
|
data.put("priceValue", skuDetails.oneTimePurchaseOfferDetails!!.priceAmountMicros)
|
||||||
|
data.put("priceShow", skuDetails.oneTimePurchaseOfferDetails!!.formattedPrice)
|
||||||
|
}
|
||||||
|
dataArr.put(data)
|
||||||
|
true
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun queryProductList(funId: String?, productIds: List<String?>) {
|
||||||
|
val productList: MutableList<QueryProductDetailsParams.Product> = ArrayList()
|
||||||
|
for (productId in productIds) {
|
||||||
|
productList.add(
|
||||||
|
QueryProductDetailsParams.Product.newBuilder()
|
||||||
|
.setProductId(productId!!)
|
||||||
|
.setProductType(BillingClient.ProductType.INAPP)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val params = QueryProductDetailsParams.newBuilder()
|
||||||
|
.setProductList(productList)
|
||||||
|
.build()
|
||||||
|
billingClient!!.queryProductDetailsAsync(
|
||||||
|
params
|
||||||
|
) { billingResult: BillingResult?, productDetailsList: List<ProductDetails> ->
|
||||||
|
// Process the result
|
||||||
|
val pMap: MutableMap<String, ProductDetails> = HashMap()
|
||||||
|
for (details in productDetailsList) {
|
||||||
|
skuDetailsMap!![details.productId] = details
|
||||||
|
pMap[details.productId] = details
|
||||||
|
}
|
||||||
|
val dataArr = JSONArray()
|
||||||
|
var hasErr = false
|
||||||
|
for ((_, skuDetails) in pMap) {
|
||||||
|
if (!parseProductDetails(dataArr, skuDetails)) {
|
||||||
|
hasErr = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasErr) {
|
||||||
|
purchaseUpdateCb(funId, "parse product detail json error", null)
|
||||||
|
} else {
|
||||||
|
purchaseUpdateCb(funId, null, dataArr.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun queryPurchase(funId: String?) {
|
||||||
|
billingClient!!.queryPurchasesAsync(
|
||||||
|
QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.INAPP).build()
|
||||||
|
) { billingResult: BillingResult?, purchases: List<Purchase> ->
|
||||||
|
// Process the result
|
||||||
|
val dataArr = JSONArray()
|
||||||
|
var hasErr = false
|
||||||
|
for (purchase in purchases) {
|
||||||
|
try {
|
||||||
|
handlePurchase(dataArr, purchase)
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
hasErr = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasErr) {
|
||||||
|
purchaseUpdateCb(funId, "error parse purchase data", null)
|
||||||
|
} else {
|
||||||
|
purchaseUpdateCb(funId, null, dataArr.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun buyOne(funId: String?, productId: String, orderId: String?) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
val productDetails = skuDetailsMap!![productId]
|
||||||
|
// Set the parameters for the offer that will be presented
|
||||||
|
// in the billing flow creating separate productDetailsParamsList variable
|
||||||
|
val productDetailsParamsList = listOf(
|
||||||
|
ProductDetailsParams.newBuilder()
|
||||||
|
.setProductDetails(productDetails!!)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
val billingFlowParams = BillingFlowParams.newBuilder()
|
||||||
|
.setProductDetailsParamsList(productDetailsParamsList)
|
||||||
|
.setObfuscatedAccountId(orderId!!)
|
||||||
|
.setObfuscatedProfileId(orderId)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
// Launch the billing flow
|
||||||
|
mFunId = funId
|
||||||
|
MainActivity.app!!.runOnUiThread {
|
||||||
|
billingClient!!.launchBillingFlow(
|
||||||
|
(mContext as Activity?)!!, billingFlowParams
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "GooglePayClient"
|
||||||
|
|
||||||
|
@Volatile
|
||||||
|
private var mInstance: PayClient? = null
|
||||||
|
private var billingClient: BillingClient? = null
|
||||||
|
private var skuDetailsMap: ConcurrentHashMap<String, ProductDetails>? = null
|
||||||
|
val instance: PayClient?
|
||||||
|
get() {
|
||||||
|
if (null == mInstance) {
|
||||||
|
synchronized(PayClient::class.java) {
|
||||||
|
if (null == mInstance) {
|
||||||
|
mInstance = PayClient()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mInstance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
96
app/src/com/jc/jcfw/util/DriveUtils.kt
Normal file
96
app/src/com/jc/jcfw/util/DriveUtils.kt
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
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 java.io.ByteArrayOutputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.OutputStream
|
||||||
|
|
||||||
|
object DriveUtils {
|
||||||
|
private val TAG = DriveUtils::class.java.simpleName
|
||||||
|
fun generateService(credential: GoogleAccountCredential?, appName: String?): Drive {
|
||||||
|
return Drive.Builder(
|
||||||
|
AndroidHttp.newCompatibleTransport(),
|
||||||
|
AndroidJsonFactory.getDefaultInstance(),
|
||||||
|
credential
|
||||||
|
)
|
||||||
|
.setApplicationName(appName)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* query app file by filename
|
||||||
|
*
|
||||||
|
* @param service
|
||||||
|
* @param fileName
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
fun queryOneAppFile(service: Drive, fileName: String): String {
|
||||||
|
var querySuccess = false
|
||||||
|
var fileId = ""
|
||||||
|
while (!querySuccess) {
|
||||||
|
try {
|
||||||
|
val files = service.files().list()
|
||||||
|
.setSpaces("appDataFolder")
|
||||||
|
.setFields("nextPageToken, files(id, name)")
|
||||||
|
.setPageSize(100)
|
||||||
|
.execute()
|
||||||
|
querySuccess = true
|
||||||
|
for (file in files.files) {
|
||||||
|
Log.i(TAG, String.format("Found file: %s (%s)\n", file.name, file.id))
|
||||||
|
if (file.name == fileName) {
|
||||||
|
fileId = file.id
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Log.i(TAG, "error query file from drive")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fileId
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* download one file from drive
|
||||||
|
*
|
||||||
|
* @param service
|
||||||
|
* @param fileId
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun downloadFile(service: Drive, fileId: String?): String {
|
||||||
|
val outputStream: OutputStream = ByteArrayOutputStream()
|
||||||
|
service.files()[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
|
||||||
|
*/
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun uploadAppFile(service: Drive, filePath: File, fileType: String?): String {
|
||||||
|
val fileName = filePath.name
|
||||||
|
val fileMetadata = com.google.api.services.drive.model.File()
|
||||||
|
fileMetadata.name = fileName
|
||||||
|
fileMetadata.parents = listOf("appDataFolder")
|
||||||
|
// "application/json"
|
||||||
|
val mediaContent = FileContent(fileType, filePath)
|
||||||
|
val file = service.files().create(fileMetadata, mediaContent)
|
||||||
|
.setFields("id")
|
||||||
|
.execute()
|
||||||
|
Log.i(TAG, String.format("%s upload success, File ID: %s", fileName, file.id))
|
||||||
|
return file.id
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
98
app/src/com/jc/jcfw/util/DriverApiUtils.kt
Normal file
98
app/src/com/jc/jcfw/util/DriverApiUtils.kt
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
package com.jc.jcfw.util
|
||||||
|
|
||||||
|
|
||||||
|
import okhttp3.MediaType
|
||||||
|
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||||
|
import okhttp3.MultipartBody
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Request.Builder
|
||||||
|
import okhttp3.RequestBody
|
||||||
|
import okhttp3.RequestBody.Companion.asRequestBody
|
||||||
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONException
|
||||||
|
import org.json.JSONObject
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
class DriverApiUtils {
|
||||||
|
private val TAG = javaClass.simpleName
|
||||||
|
var token = ""
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
constructor(token: String) {
|
||||||
|
this.token = token
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class, JSONException::class)
|
||||||
|
fun fileList(): JSONArray {
|
||||||
|
val client = OkHttpClient().newBuilder()
|
||||||
|
.build()
|
||||||
|
val request: Request = Builder()
|
||||||
|
.url(driveApiBase + "?spaces=appDataFolder&fields=files(id, name, modifiedTime)")
|
||||||
|
.get()
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.build()
|
||||||
|
client.newCall(request).execute().use { response ->
|
||||||
|
val resStr = response.body!!.string()
|
||||||
|
val resJson = JSONObject(resStr)
|
||||||
|
return resJson.getJSONArray("files")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class, JSONException::class)
|
||||||
|
fun queryOneAppFile(fileName: String): String {
|
||||||
|
val fileList = fileList()
|
||||||
|
for (i in 0 until fileList.length()) {
|
||||||
|
val file = fileList.getJSONObject(i)
|
||||||
|
if (file.getString("name") == fileName) {
|
||||||
|
return file.getString("id")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun fileInfo(fileId: String?): String {
|
||||||
|
val client = OkHttpClient().newBuilder()
|
||||||
|
.build()
|
||||||
|
val request: Request = Builder()
|
||||||
|
.url(String.format("%s/%s?alt=media", driveApiBase, fileId))
|
||||||
|
.get()
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.build()
|
||||||
|
client.newCall(request).execute().use { response -> return response.body!!.string() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun uploadFile(filePath: File, fileType: String): String {
|
||||||
|
val client = OkHttpClient().newBuilder()
|
||||||
|
.build()
|
||||||
|
val fileName = filePath.name
|
||||||
|
val optionStr = "{\"name\":\"$fileName\",\"parents\":[\"appDataFolder\"]}"
|
||||||
|
val body: RequestBody = MultipartBody.Builder().setType(MultipartBody.FORM)
|
||||||
|
.addFormDataPart(
|
||||||
|
"", "upload-options.json",
|
||||||
|
optionStr.toRequestBody("application/json".toMediaTypeOrNull())
|
||||||
|
)
|
||||||
|
.addFormDataPart(
|
||||||
|
"", fileName,
|
||||||
|
filePath.asRequestBody("application/octet-stream".toMediaTypeOrNull())
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
val request: Request = Builder()
|
||||||
|
.url("$driveApiUploadBase?uploadType=multipart")
|
||||||
|
.post(body)
|
||||||
|
.addHeader("Content-Type", fileType)
|
||||||
|
.addHeader("Authorization", "Bearer $token")
|
||||||
|
.build()
|
||||||
|
val response = client.newCall(request).execute()
|
||||||
|
return response.body!!.string()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val driveApiBase = "https://www.googleapis.com/drive/v3/files"
|
||||||
|
private const val driveApiUploadBase = "https://www.googleapis.com/upload/drive/v3/files"
|
||||||
|
}
|
||||||
|
}
|
@ -1,240 +0,0 @@
|
|||||||
package com.jc.jcfw.util;
|
|
||||||
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.ContentUris;
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.BitmapFactory;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Build;
|
|
||||||
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();
|
|
||||||
/**
|
|
||||||
* check if path specificed exists, create it if not exists
|
|
||||||
*
|
|
||||||
* @param fileName path
|
|
||||||
* @return TRUE or FALSE
|
|
||||||
*/
|
|
||||||
public static boolean fileIsExist(String fileName) {
|
|
||||||
File file = new File(fileName);
|
|
||||||
if (file.exists())
|
|
||||||
return true;
|
|
||||||
else {
|
|
||||||
return file.mkdirs();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
public static String getPath(Context context) {
|
|
||||||
String fileName = "";
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
||||||
fileName = context.getExternalFilesDir("").getAbsolutePath() + "/current/";
|
|
||||||
} else {
|
|
||||||
if ("Xiaomi".equalsIgnoreCase(Build.BRAND)) {
|
|
||||||
fileName = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/";
|
|
||||||
} else if ("HUAWEI".equalsIgnoreCase(Build.BRAND)) {
|
|
||||||
fileName = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/";
|
|
||||||
} else if ("HONOR".equalsIgnoreCase(Build.BRAND)) {
|
|
||||||
fileName = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/";
|
|
||||||
} else if ("OPPO".equalsIgnoreCase(Build.BRAND)) {
|
|
||||||
fileName = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/";
|
|
||||||
} else if ("vivo".equalsIgnoreCase(Build.BRAND)) {
|
|
||||||
fileName = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/";
|
|
||||||
} else if ("samsung".equalsIgnoreCase(Build.BRAND)) {
|
|
||||||
fileName = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/";
|
|
||||||
} else {
|
|
||||||
fileName = Environment.getExternalStorageDirectory().getPath() + "/DCIM/";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File file = new File(fileName);
|
|
||||||
if (file.mkdirs()) {
|
|
||||||
return fileName;
|
|
||||||
}
|
|
||||||
return fileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String insertImageIntoGallery(Context activity, Bitmap source, String filename, String title) {
|
|
||||||
ContentResolver cr = activity.getContentResolver();
|
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
values.put(MediaStore.Images.Media.TITLE, title);
|
|
||||||
values.put(MediaStore.Images.Media.DISPLAY_NAME, title);
|
|
||||||
values.put(MediaStore.Images.Media.DESCRIPTION, title);
|
|
||||||
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
|
|
||||||
// Add the date meta data to ensure the image is added at the front of the gallery
|
|
||||||
values.put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis());
|
|
||||||
values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
||||||
values.put(MediaStore.Video.Media.IS_PENDING, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Uri url = null;
|
|
||||||
String stringUrl = null; /* value to be returned */
|
|
||||||
|
|
||||||
try {
|
|
||||||
url = cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
|
|
||||||
|
|
||||||
if (source != null) {
|
|
||||||
OutputStream imageOut = cr.openOutputStream(url);
|
|
||||||
try {
|
|
||||||
source.compress(Bitmap.CompressFormat.JPEG, 100, imageOut);
|
|
||||||
} finally {
|
|
||||||
imageOut.close();
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
||||||
values.put(MediaStore.Video.Media.IS_PENDING, 0);
|
|
||||||
cr.update(url, values, null, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cr.delete(url, null, null);
|
|
||||||
return storeToAlternateSd(activity, source, filename);
|
|
||||||
// url = null;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
if (url != null) {
|
|
||||||
cr.delete(url, null, null);
|
|
||||||
// url = null;
|
|
||||||
}
|
|
||||||
return storeToAlternateSd(activity, source, filename);
|
|
||||||
}
|
|
||||||
if (url != null) {
|
|
||||||
stringUrl = url.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return stringUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If we have issues saving into our MediaStore, save it directly to our SD card. We can then interact with this file
|
|
||||||
* directly, opposed to pulling from the MediaStore. Again, this is a backup method if things don't work out as we
|
|
||||||
* would expect (seeing as most devices will have a MediaStore).
|
|
||||||
*
|
|
||||||
* @param src
|
|
||||||
* @return - the file's path
|
|
||||||
*/
|
|
||||||
private static String storeToAlternateSd(Context activity, Bitmap src, String filename){
|
|
||||||
if(src == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
String sdCardDirectory = getPath(activity);
|
|
||||||
File image = new File(sdCardDirectory, filename + ".jpg");
|
|
||||||
try {
|
|
||||||
FileOutputStream imageOut = new FileOutputStream(image);
|
|
||||||
src.compress(Bitmap.CompressFormat.JPEG, 100, imageOut);
|
|
||||||
imageOut.close();
|
|
||||||
return image.getAbsolutePath();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* save bitmap to system gallery
|
|
||||||
*/
|
|
||||||
public static String saveBitmap(Context activity, String oid, Bitmap bitmap) {
|
|
||||||
String title = "wallet_key_" + oid;
|
|
||||||
String imageName = "wallet_key_" + oid;
|
|
||||||
String uri = insertImageIntoGallery(activity, bitmap, imageName, title);
|
|
||||||
Log.i(TAG, "save image success: " + uri);
|
|
||||||
return uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static Bitmap loadImgData(Context activity, String oid) {
|
|
||||||
Uri uri = readImageFromGallery(activity, oid);
|
|
||||||
Bitmap data;
|
|
||||||
if (uri != null) {
|
|
||||||
try {
|
|
||||||
data = MediaStore.Images.Media.getBitmap(activity.getContentResolver(),uri);
|
|
||||||
} catch (IOException e) {
|
|
||||||
data = readImageFromExt(activity, oid);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
data = readImageFromExt(activity, oid);
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Bitmap readImageFromExt(Context activity, String oid) {
|
|
||||||
String sdCardDirectory = getPath(activity);
|
|
||||||
String imageName = "wallet_key_" + oid;
|
|
||||||
File file = new File(sdCardDirectory, imageName + ".jpg");
|
|
||||||
if (!file.exists()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Bitmap bitmap = null;
|
|
||||||
try {
|
|
||||||
bitmap = BitmapFactory.decodeFile(file.getPath());
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return bitmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Uri readImageFromGallery(Context activity, String oid) {
|
|
||||||
String filename = "wallet_key_" + oid;
|
|
||||||
Uri uri;
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
||||||
uri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
|
|
||||||
} else {
|
|
||||||
uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
|
||||||
}
|
|
||||||
Uri result = null;
|
|
||||||
String[] projection = {MediaStore.Images.Media._ID,
|
|
||||||
MediaStore.Images.Media.DATA,
|
|
||||||
MediaStore.Images.Media.DISPLAY_NAME};
|
|
||||||
final String orderBy = MediaStore.Images.Media.DATE_ADDED;
|
|
||||||
Cursor cursor = activity.getContentResolver().query(uri, projection, null, null, orderBy + " DESC");
|
|
||||||
String imageName = "wallet_key_" + oid;
|
|
||||||
if (cursor != null) {
|
|
||||||
int nameColumn = cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME);
|
|
||||||
int idColumn = cursor.getColumnIndex(MediaStore.Images.Media._ID);
|
|
||||||
int dataColumn = cursor.getColumnIndex(MediaStore.Images.Media.DATA);
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
String name = cursor.getString(nameColumn);
|
|
||||||
long id = cursor.getLong(idColumn);
|
|
||||||
Log.i(TAG, "img name: " + name + " id: " + id);
|
|
||||||
if (name.contains(imageName)) {
|
|
||||||
String data = cursor.getString(dataColumn);
|
|
||||||
result = ContentUris.withAppendedId(MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL), id);
|
|
||||||
Log.i(TAG, "img name: " + name + " id: " + id + " data:" + data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cursor.close();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
237
app/src/com/jc/jcfw/util/FileUtils.kt
Normal file
237
app/src/com/jc/jcfw/util/FileUtils.kt
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
package com.jc.jcfw.util
|
||||||
|
|
||||||
|
import android.content.ContentUris
|
||||||
|
import android.content.ContentValues
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
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.FileOutputStream
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.RandomAccessFile
|
||||||
|
|
||||||
|
object FileUtils {
|
||||||
|
private val TAG = FileUtils::class.java.simpleName
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check if path specificed exists, create it if not exists
|
||||||
|
*
|
||||||
|
* @param fileName path
|
||||||
|
* @return TRUE or FALSE
|
||||||
|
*/
|
||||||
|
fun fileIsExist(fileName: String?): Boolean {
|
||||||
|
val file = File(fileName)
|
||||||
|
return if (file.exists()) true else {
|
||||||
|
file.mkdirs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun writeFile(filePath: File?, content: String) {
|
||||||
|
val out = FileOutputStream(filePath)
|
||||||
|
out.write(content.toByteArray())
|
||||||
|
out.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class, JSONException::class)
|
||||||
|
fun readJsonFromFile(filePath: File?): JSONObject {
|
||||||
|
val f = RandomAccessFile(filePath, "r")
|
||||||
|
val bytes = ByteArray(f.length().toInt())
|
||||||
|
f.readFully(bytes)
|
||||||
|
f.close()
|
||||||
|
return JSONObject(String(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get image base path of external storage
|
||||||
|
*/
|
||||||
|
fun getPath(context: Context): String {
|
||||||
|
var fileName = ""
|
||||||
|
fileName = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
context.getExternalFilesDir("")!!.absolutePath + "/current/"
|
||||||
|
} else {
|
||||||
|
if ("Xiaomi".equals(Build.BRAND, ignoreCase = true)) {
|
||||||
|
Environment.getExternalStorageDirectory().path + "/DCIM/Camera/"
|
||||||
|
} else if ("HUAWEI".equals(Build.BRAND, ignoreCase = true)) {
|
||||||
|
Environment.getExternalStorageDirectory().path + "/DCIM/Camera/"
|
||||||
|
} else if ("HONOR".equals(Build.BRAND, ignoreCase = true)) {
|
||||||
|
Environment.getExternalStorageDirectory().path + "/DCIM/Camera/"
|
||||||
|
} else if ("OPPO".equals(Build.BRAND, ignoreCase = true)) {
|
||||||
|
Environment.getExternalStorageDirectory().path + "/DCIM/Camera/"
|
||||||
|
} else if ("vivo".equals(Build.BRAND, ignoreCase = true)) {
|
||||||
|
Environment.getExternalStorageDirectory().path + "/DCIM/Camera/"
|
||||||
|
} else if ("samsung".equals(Build.BRAND, ignoreCase = true)) {
|
||||||
|
Environment.getExternalStorageDirectory().path + "/DCIM/Camera/"
|
||||||
|
} else {
|
||||||
|
Environment.getExternalStorageDirectory().path + "/DCIM/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val file = File(fileName)
|
||||||
|
return if (file.mkdirs()) {
|
||||||
|
fileName
|
||||||
|
} else fileName
|
||||||
|
}
|
||||||
|
|
||||||
|
fun insertImageIntoGallery(
|
||||||
|
activity: Context,
|
||||||
|
source: Bitmap?,
|
||||||
|
filename: String,
|
||||||
|
title: String?
|
||||||
|
): String? {
|
||||||
|
val cr = activity.contentResolver
|
||||||
|
val values = ContentValues()
|
||||||
|
values.put(MediaStore.Images.Media.TITLE, title)
|
||||||
|
values.put(MediaStore.Images.Media.DISPLAY_NAME, title)
|
||||||
|
values.put(MediaStore.Images.Media.DESCRIPTION, title)
|
||||||
|
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
|
||||||
|
// Add the date meta data to ensure the image is added at the front of the gallery
|
||||||
|
values.put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis())
|
||||||
|
values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis())
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
values.put(MediaStore.Video.Media.IS_PENDING, 1)
|
||||||
|
}
|
||||||
|
var url: Uri? = null
|
||||||
|
var stringUrl: String? = null /* value to be returned */
|
||||||
|
try {
|
||||||
|
url = cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
|
||||||
|
if (source != null) {
|
||||||
|
val imageOut = cr.openOutputStream(url!!)
|
||||||
|
try {
|
||||||
|
source.compress(Bitmap.CompressFormat.JPEG, 100, imageOut)
|
||||||
|
} finally {
|
||||||
|
imageOut!!.close()
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
values.put(MediaStore.Video.Media.IS_PENDING, 0)
|
||||||
|
cr.update(url, values, null, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cr.delete(url!!, null, null)
|
||||||
|
return storeToAlternateSd(activity, source, filename)
|
||||||
|
// url = null;
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
if (url != null) {
|
||||||
|
cr.delete(url, null, null)
|
||||||
|
// url = null;
|
||||||
|
}
|
||||||
|
return storeToAlternateSd(activity, source, filename)
|
||||||
|
}
|
||||||
|
if (url != null) {
|
||||||
|
stringUrl = url.toString()
|
||||||
|
}
|
||||||
|
return stringUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If we have issues saving into our MediaStore, save it directly to our SD card. We can then interact with this file
|
||||||
|
* directly, opposed to pulling from the MediaStore. Again, this is a backup method if things don't work out as we
|
||||||
|
* would expect (seeing as most devices will have a MediaStore).
|
||||||
|
*
|
||||||
|
* @param src
|
||||||
|
* @return - the file's path
|
||||||
|
*/
|
||||||
|
private fun storeToAlternateSd(activity: Context, src: Bitmap?, filename: String): String? {
|
||||||
|
if (src == null) return null
|
||||||
|
val sdCardDirectory = getPath(activity)
|
||||||
|
val image = File(sdCardDirectory, "$filename.jpg")
|
||||||
|
return try {
|
||||||
|
val imageOut = FileOutputStream(image)
|
||||||
|
src.compress(Bitmap.CompressFormat.JPEG, 100, imageOut)
|
||||||
|
imageOut.close()
|
||||||
|
image.absolutePath
|
||||||
|
} catch (ex: IOException) {
|
||||||
|
ex.printStackTrace()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* save bitmap to system gallery
|
||||||
|
*/
|
||||||
|
fun saveBitmap(activity: Context, oid: String, bitmap: Bitmap?): String? {
|
||||||
|
val title = "wallet_key_$oid"
|
||||||
|
val imageName = "wallet_key_$oid"
|
||||||
|
val uri = insertImageIntoGallery(activity, bitmap, imageName, title)
|
||||||
|
Log.i(TAG, "save image success: $uri")
|
||||||
|
return uri
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadImgData(activity: Context, oid: String): Bitmap? {
|
||||||
|
val uri = readImageFromGallery(activity, oid)
|
||||||
|
val data: Bitmap?
|
||||||
|
data = if (uri != null) {
|
||||||
|
try {
|
||||||
|
MediaStore.Images.Media.getBitmap(activity.contentResolver, uri)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
readImageFromExt(activity, oid)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
readImageFromExt(activity, oid)
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readImageFromExt(activity: Context, oid: String): Bitmap? {
|
||||||
|
val sdCardDirectory = getPath(activity)
|
||||||
|
val imageName = "wallet_key_$oid"
|
||||||
|
val file = File(sdCardDirectory, "$imageName.jpg")
|
||||||
|
if (!file.exists()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
var bitmap: Bitmap? = null
|
||||||
|
try {
|
||||||
|
bitmap = BitmapFactory.decodeFile(file.path)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
return bitmap
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readImageFromGallery(activity: Context, oid: String): Uri? {
|
||||||
|
val filename = "wallet_key_$oid"
|
||||||
|
val uri: Uri
|
||||||
|
uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
|
||||||
|
} else {
|
||||||
|
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
|
||||||
|
}
|
||||||
|
var result: Uri? = null
|
||||||
|
val projection = arrayOf(
|
||||||
|
MediaStore.Images.Media._ID,
|
||||||
|
MediaStore.Images.Media.DATA,
|
||||||
|
MediaStore.Images.Media.DISPLAY_NAME
|
||||||
|
)
|
||||||
|
val orderBy = MediaStore.Images.Media.DATE_ADDED
|
||||||
|
val cursor = activity.contentResolver.query(uri, projection, null, null, "$orderBy DESC")
|
||||||
|
val imageName = "wallet_key_$oid"
|
||||||
|
if (cursor != null) {
|
||||||
|
val nameColumn = cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME)
|
||||||
|
val idColumn = cursor.getColumnIndex(MediaStore.Images.Media._ID)
|
||||||
|
val dataColumn = cursor.getColumnIndex(MediaStore.Images.Media.DATA)
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
val name = cursor.getString(nameColumn)
|
||||||
|
val id = cursor.getLong(idColumn)
|
||||||
|
Log.i(TAG, "img name: $name id: $id")
|
||||||
|
if (name.contains(imageName)) {
|
||||||
|
val data = cursor.getString(dataColumn)
|
||||||
|
result = ContentUris.withAppendedId(
|
||||||
|
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL),
|
||||||
|
id
|
||||||
|
)
|
||||||
|
Log.i(TAG, "img name: $name id: $id data:$data")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cursor.close()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
@ -1,31 +0,0 @@
|
|||||||
package com.jc.jcfw.util;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
public class JsonUtils {
|
|
||||||
public static Bundle convertJsonToBundle(String jsonString) throws JSONException {
|
|
||||||
Bundle bundle = new Bundle();
|
|
||||||
JSONObject jsonObject = new JSONObject(jsonString);
|
|
||||||
traverseJsonObject(jsonObject, bundle);
|
|
||||||
return bundle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void traverseJsonObject(JSONObject jsonObject, Bundle bundle) throws JSONException {
|
|
||||||
Iterator<String> keys = jsonObject.keys();
|
|
||||||
while (keys.hasNext()) {
|
|
||||||
String key = keys.next();
|
|
||||||
Object value = jsonObject.get(key);
|
|
||||||
// if (value instanceof JSONObject) {
|
|
||||||
// traverseJsonObject((JSONObject) value, bundle);
|
|
||||||
// } else {
|
|
||||||
// bundle.putString(key, value.toString());
|
|
||||||
// }
|
|
||||||
bundle.putString(key, value.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
30
app/src/com/jc/jcfw/util/JsonUtils.kt
Normal file
30
app/src/com/jc/jcfw/util/JsonUtils.kt
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package com.jc.jcfw.util
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import org.json.JSONException
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
object JsonUtils {
|
||||||
|
@Throws(JSONException::class)
|
||||||
|
fun convertJsonToBundle(jsonString: String?): Bundle {
|
||||||
|
val bundle = Bundle()
|
||||||
|
val jsonObject = JSONObject(jsonString)
|
||||||
|
traverseJsonObject(jsonObject, bundle)
|
||||||
|
return bundle
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(JSONException::class)
|
||||||
|
fun traverseJsonObject(jsonObject: JSONObject, bundle: Bundle) {
|
||||||
|
val keys = jsonObject.keys()
|
||||||
|
while (keys.hasNext()) {
|
||||||
|
val key = keys.next()
|
||||||
|
val value = jsonObject[key]
|
||||||
|
// if (value instanceof JSONObject) {
|
||||||
|
// traverseJsonObject((JSONObject) value, bundle);
|
||||||
|
// } else {
|
||||||
|
// bundle.putString(key, value.toString());
|
||||||
|
// }
|
||||||
|
bundle.putString(key, value.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
67
app/src/com/jc/jcfw/util/SharedPreferencesHelper.kt
Normal file
67
app/src/com/jc/jcfw/util/SharedPreferencesHelper.kt
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package com.jc.jcfw.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
|
||||||
|
class SharedPreferencesHelper(context: Context, FILE_NAME: String?) {
|
||||||
|
private val sharedPreferences: SharedPreferences
|
||||||
|
private val editor: SharedPreferences.Editor
|
||||||
|
|
||||||
|
init {
|
||||||
|
sharedPreferences = context.getSharedPreferences(
|
||||||
|
FILE_NAME,
|
||||||
|
Context.MODE_PRIVATE
|
||||||
|
)
|
||||||
|
editor = sharedPreferences.edit()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun put(key: String?, `object`: Any) {
|
||||||
|
if (`object` is String) {
|
||||||
|
editor.putString(key, `object`)
|
||||||
|
} else if (`object` is Int) {
|
||||||
|
editor.putInt(key, `object`)
|
||||||
|
} else if (`object` is Boolean) {
|
||||||
|
editor.putBoolean(key, `object`)
|
||||||
|
} else if (`object` is Float) {
|
||||||
|
editor.putFloat(key, `object`)
|
||||||
|
} else if (`object` is Long) {
|
||||||
|
editor.putLong(key, `object`)
|
||||||
|
} else {
|
||||||
|
editor.putString(key, `object`.toString())
|
||||||
|
}
|
||||||
|
editor.commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSharedPreference(key: String?, defaultObject: Any?): Any? {
|
||||||
|
return if (defaultObject is String) {
|
||||||
|
sharedPreferences.getString(key, defaultObject as String?)
|
||||||
|
} else if (defaultObject is Int) {
|
||||||
|
sharedPreferences.getInt(key, (defaultObject as Int?)!!)
|
||||||
|
} else if (defaultObject is Boolean) {
|
||||||
|
sharedPreferences.getBoolean(key, (defaultObject as Boolean?)!!)
|
||||||
|
} else if (defaultObject is Float) {
|
||||||
|
sharedPreferences.getFloat(key, (defaultObject as Float?)!!)
|
||||||
|
} else if (defaultObject is Long) {
|
||||||
|
sharedPreferences.getLong(key, (defaultObject as Long?)!!)
|
||||||
|
} else {
|
||||||
|
sharedPreferences.getString(key, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun remove(key: String?) {
|
||||||
|
editor.remove(key)
|
||||||
|
editor.commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
|
editor.clear()
|
||||||
|
editor.commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun contain(key: String?): Boolean {
|
||||||
|
return sharedPreferences.contains(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
val all: Map<String, *>
|
||||||
|
get() = sharedPreferences.all
|
||||||
|
}
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
23
app/src/com/jc/jcfw/util/ThreadUtils.kt
Normal file
23
app/src/com/jc/jcfw/util/ThreadUtils.kt
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package com.jc.jcfw.util
|
||||||
|
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
|
||||||
|
object ThreadUtils {
|
||||||
|
/**
|
||||||
|
* check if current thread is main thread
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private val isMainThread: Boolean
|
||||||
|
get() = Looper.getMainLooper() == Looper.myLooper()
|
||||||
|
|
||||||
|
fun runInMain(action: Runnable) {
|
||||||
|
if (isMainThread) {
|
||||||
|
action.run()
|
||||||
|
} else {
|
||||||
|
val mainHandler = Handler(Looper.getMainLooper())
|
||||||
|
mainHandler.post(action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,36 +0,0 @@
|
|||||||
package com.jc.jcfw.util;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import androidx.annotation.MainThread;
|
|
||||||
|
|
||||||
import com.ctf.games.release.dialog.QRCodeActivity;
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
35
app/src/com/jc/jcfw/util/UIUtils.kt
Normal file
35
app/src/com/jc/jcfw/util/UIUtils.kt
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package com.jc.jcfw.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.annotation.MainThread
|
||||||
|
import com.ctf.games.release.dialog.QRCodeActivity
|
||||||
|
|
||||||
|
object UIUtils {
|
||||||
|
private var toast: Toast? = null
|
||||||
|
@MainThread
|
||||||
|
fun showToastReal(context: Context?, text: String?) {
|
||||||
|
if (toast == null) {
|
||||||
|
toast = Toast.makeText(context, text, Toast.LENGTH_SHORT)
|
||||||
|
} else {
|
||||||
|
toast!!.duration = Toast.LENGTH_SHORT
|
||||||
|
toast!!.setText(text)
|
||||||
|
}
|
||||||
|
toast!!.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showToast(context: Context?, text: String?) {
|
||||||
|
ThreadUtils.runInMain { showToastReal(context, text) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainThread
|
||||||
|
fun showQRCodeReal(context: Context?, str: String?, title: String?) {
|
||||||
|
val qrCodeActivity = QRCodeActivity(context!!)
|
||||||
|
qrCodeActivity.showQRCode(str, title)
|
||||||
|
qrCodeActivity.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showQRCode(context: Context?, str: String?, title: String?) {
|
||||||
|
ThreadUtils.runInMain { showQRCodeReal(context, str, title) }
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
apply from: "config.gradle"
|
apply from: "config.gradle"
|
||||||
buildscript {
|
buildscript {
|
||||||
|
ext.kotlin_version = '1.7.20'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
@ -15,6 +16,7 @@ buildscript {
|
|||||||
classpath 'com.google.gms:google-services:4.3.15'
|
classpath 'com.google.gms:google-services:4.3.15'
|
||||||
|
|
||||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.6'
|
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.6'
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name" translatable="false">CEBG</string>
|
<string name="app_name" translatable="false">CF</string>
|
||||||
<string name="game_view_content_description" translatable="false">Game view</string>
|
<string name="game_view_content_description" translatable="false">Game view</string>
|
||||||
<string name="permission_camera" translatable="false">Scan QRCode need camera permission</string>
|
<string name="permission_camera" translatable="false">Scan QRCode need camera permission</string>
|
||||||
<string name="qr_code_title" translatable="false">QRCode</string>
|
<string name="qr_code_title" translatable="false">QRCode</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user