diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3bc60d6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# Android Studio +.idea/ +*.iml +.gradle/ +local.properties + +# Build outputs +/build/ +/proj/ +android/app/build/ +android/build/ + +# Gradle cache +.gradle/ + +# NDK / CMake generated files +.cxx/ +android/app/.cxx/ + +# Keystore (不要提交签名文件) +*.jks +*.keystore +/data/ +# OS +.DS_Store +Thumbs.db diff --git a/.gradle/8.11.1/checksums/checksums.lock b/.gradle/8.11.1/checksums/checksums.lock deleted file mode 100644 index 5b2fb61..0000000 Binary files a/.gradle/8.11.1/checksums/checksums.lock and /dev/null differ diff --git a/.gradle/8.11.1/checksums/md5-checksums.bin b/.gradle/8.11.1/checksums/md5-checksums.bin deleted file mode 100644 index 7efdabe..0000000 Binary files a/.gradle/8.11.1/checksums/md5-checksums.bin and /dev/null differ diff --git a/.gradle/8.11.1/checksums/sha1-checksums.bin b/.gradle/8.11.1/checksums/sha1-checksums.bin deleted file mode 100644 index 2bf4f7a..0000000 Binary files a/.gradle/8.11.1/checksums/sha1-checksums.bin and /dev/null differ diff --git a/.gradle/8.11.1/executionHistory/executionHistory.lock b/.gradle/8.11.1/executionHistory/executionHistory.lock deleted file mode 100644 index 104dc3f..0000000 Binary files a/.gradle/8.11.1/executionHistory/executionHistory.lock and /dev/null differ diff --git a/.gradle/8.11.1/fileChanges/last-build.bin b/.gradle/8.11.1/fileChanges/last-build.bin deleted file mode 100644 index f76dd23..0000000 Binary files a/.gradle/8.11.1/fileChanges/last-build.bin and /dev/null differ diff --git a/.gradle/8.11.1/fileHashes/fileHashes.bin b/.gradle/8.11.1/fileHashes/fileHashes.bin deleted file mode 100644 index 606d764..0000000 Binary files a/.gradle/8.11.1/fileHashes/fileHashes.bin and /dev/null differ diff --git a/.gradle/8.11.1/fileHashes/fileHashes.lock b/.gradle/8.11.1/fileHashes/fileHashes.lock deleted file mode 100644 index e969356..0000000 Binary files a/.gradle/8.11.1/fileHashes/fileHashes.lock and /dev/null differ diff --git a/.gradle/8.11.1/fileHashes/resourceHashesCache.bin b/.gradle/8.11.1/fileHashes/resourceHashesCache.bin deleted file mode 100644 index 19d5076..0000000 Binary files a/.gradle/8.11.1/fileHashes/resourceHashesCache.bin and /dev/null differ diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock deleted file mode 100644 index dfc627e..0000000 Binary files a/.gradle/buildOutputCleanup/buildOutputCleanup.lock and /dev/null differ diff --git a/.gradle/buildOutputCleanup/cache.properties b/.gradle/buildOutputCleanup/cache.properties deleted file mode 100644 index 241c7ea..0000000 --- a/.gradle/buildOutputCleanup/cache.properties +++ /dev/null @@ -1,2 +0,0 @@ -#Tue Mar 10 14:43:18 CST 2026 -gradle.version=8.11.1 diff --git a/.gradle/config.properties b/.gradle/config.properties deleted file mode 100644 index 2817b08..0000000 --- a/.gradle/config.properties +++ /dev/null @@ -1,2 +0,0 @@ -#Tue Mar 10 14:42:45 CST 2026 -java.home=D\:\\Program Files\\Android\\Android Studio\\jbr diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 26d3352..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/.idea/.name b/.idea/.name deleted file mode 100644 index aae990d..0000000 --- a/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -magotest \ No newline at end of file diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml deleted file mode 100644 index 4a53bee..0000000 --- a/.idea/AndroidProjectSystem.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/caches/deviceStreaming.xml b/.idea/caches/deviceStreaming.xml deleted file mode 100644 index 23267a6..0000000 --- a/.idea/caches/deviceStreaming.xml +++ /dev/null @@ -1,1490 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index b86273d..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml deleted file mode 100644 index 3eaf5c6..0000000 --- a/.idea/deploymentTargetSelector.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index fe9675f..0000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml deleted file mode 100644 index f8051a6..0000000 --- a/.idea/migrations.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 74dd639..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 16660f1..0000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/android/app/AndroidManifest.xml b/android/app/AndroidManifest.xml index d4f80c6..d25fa14 100644 --- a/android/app/AndroidManifest.xml +++ b/android/app/AndroidManifest.xml @@ -1,16 +1,140 @@ - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/build.gradle b/android/app/build.gradle index 9f5e206..37b09e3 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,6 +1,7 @@ import org.apache.tools.ant.taskdefs.condition.Os apply plugin: 'com.android.application' +apply plugin: 'com.google.gms.google-services' RES_PATH = RES_PATH.replace("\\", "/") COCOS_ENGINE_PATH = COCOS_ENGINE_PATH.replace("\\", "/") @@ -35,7 +36,7 @@ android { sourceSets.main { java.srcDirs "../src", "src" - res.srcDirs "../res", 'res', "${RES_PATH}/proj/res" + res.srcDirs "../res", 'res' jniLibs.srcDirs "../libs", 'libs' manifest.srcFile "AndroidManifest.xml" assets.srcDir "${RES_PATH}/data" @@ -116,4 +117,38 @@ dependencies { implementation 'com.google.android.libraries.play.games:inputmapping:1.1.0-beta' implementation "org.jetbrains.kotlin:kotlin-stdlib:1.4.10" } + + // Google + implementation 'com.google.android.gms:play-services-location:21.2.0' + implementation 'com.google.android.gms:play-services-auth:21.0.0' + implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1' + implementation 'com.android.billingclient:billing:7.0.0' + implementation 'com.google.android.gms:play-services-games:23.1.0' + + // Firebase + implementation platform('com.google.firebase:firebase-bom:32.3.1') + implementation 'com.google.firebase:firebase-messaging' + implementation 'com.google.firebase:firebase-analytics' + + // Adjust + implementation 'com.adjust.sdk:adjust-android:4.38.3' + implementation 'com.android.installreferrer:installreferrer:2.2' + + // Bugly + implementation 'com.tencent.bugly:crashreport:4.0.0' + implementation 'com.tencent.bugly:nativecrashreport:3.9.2' + + // Zego + implementation 'im.zego:express-audio:3.18.0' + + // Glide + implementation 'com.github.bumptech.glide:glide:4.11.0' + + // PictureSelector + implementation 'io.github.lucksiege:pictureselector:v3.11.1' + implementation 'io.github.lucksiege:compress:v3.11.1' + implementation 'io.github.lucksiege:ucrop:v3.11.1' + implementation 'io.github.lucksiege:camerax:v3.11.1' + + implementation 'org.jetbrains:annotations:15.0' } diff --git a/android/app/google-services.json b/android/app/google-services.json new file mode 100644 index 0000000..00ad545 --- /dev/null +++ b/android/app/google-services.json @@ -0,0 +1,55 @@ +{ + "project_info": { + "project_number": "77777777777", + "project_id": "dsfdsfsdfsdfsdfsdfsd", + "storage_bucket": "dsfdsfsdfsdfsdfsdfsd.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:77777777777:android:0c23bf41a19dbf9031520a", + "android_client_info": { + "package_name": "com.ddddd.one" + } + }, + "oauth_client": [ + { + "client_id": "77777777777-31c3v95gqmgiitcbdbm7g89g2dvbo3g3.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.ddddd.one", + "certificate_hash": "88c6508575eb69e0170802f358beaa008c14ec1a" + } + }, + { + "client_id": "77777777777-mst1841ado9heklp3h3gda3o9at4q8op.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.ddddd.one", + "certificate_hash": "7db6c283bbdb01a4a21a1755b4c9d52bab58966e" + } + }, + { + "client_id": "77777777777-pe7pidhpuc9ohkg5t8aqip7qboire346.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBnPj5TP43ELp4TAeRKjSmEvPvGKFPI_RA" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "77777777777-pe7pidhpuc9ohkg5t8aqip7qboire346.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/android/app/src/com/cocos/game/AdjustUtil.java b/android/app/src/com/cocos/game/AdjustUtil.java new file mode 100644 index 0000000..d533d80 --- /dev/null +++ b/android/app/src/com/cocos/game/AdjustUtil.java @@ -0,0 +1,95 @@ +package com.cocos.game; + +import android.util.Log; + +import com.adjust.sdk.Adjust; +import com.adjust.sdk.AdjustAttribution; +import com.adjust.sdk.AdjustEvent; +import com.adjust.sdk.OnDeviceIdsRead; + +import org.json.JSONObject; + +public class AdjustUtil { + + private static final String TAG = "AdjustUtil"; + private static AdjustUtil sInstance = null; + + public static AdjustUtil getInstance() { + if (sInstance == null) { + sInstance = new AdjustUtil(); + } + return sInstance; + } + + /** + * 获取Adjust归因信息 + * + * @return + */ + public String getAdjustTrackerToken() { + try { + AdjustAttribution attribution = Adjust.getAttribution(); + Log.d(TAG, "getAdjustTrackerToken: " + attribution); + // Attribution: tt:r3wk5qm tn:CC-Test net:CC-Test cam: adg: cre: cl: adid:43b4d43b1f581a69e0f53fb8614f16ff ct: ca:NaN cc: + if (null != attribution && null != attribution.trackerToken) { + String clickLabel = null == attribution.clickLabel ? "" : attribution.clickLabel; + String trackerName = null == attribution.trackerName ? "" : attribution.trackerName; + String network = null == attribution.network ? "" : attribution.network; + String trackerToken = null == attribution.trackerToken ? "" : attribution.trackerToken; + String adjustTT = trackerToken + "@" + trackerName + "@" + network + "@" + clickLabel; + return adjustTT; + } + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } + + /** + * 获取google adid、用户游客登录 + */ + public void getAdidByVisitor(AppActivity nAppActivity) { + try { + Adjust.getGoogleAdId(nAppActivity, new OnDeviceIdsRead() { + @Override + public void onGoogleAdIdRead(String googleAdId) { + String _adid = googleAdId != null ? googleAdId : Adjust.getAdid(); + _adid = _adid + "|" + Adjust.getAdid(); + Log.d(TAG, "getAdidByVisitor: " + _adid); + EventToScriptUtil.getInstance().setToScript(nAppActivity, EventNameUtil.Android_Adid, _adid == null ? "" : _adid); + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Adjust 事件 供LUA调用 + * + * @param jsonStr eventToken,revenue, + */ + public void onTrackAdjustEventFun(String jsonStr) { + try { + Log.d(TAG, "onTrackAdjustEventFun:=>jsonStr :" + jsonStr); + // 获取数据 + JSONObject json = new JSONObject(jsonStr); + String eventToken = json.getString("eventToken");//事件编码 + double revenue = json.getDouble("revenue");//金额 + String currency = json.getString("currency");//货币代码 + String orderId = json.getString("orderId");//剔重编码 + // 事件推送 + AdjustEvent event = new AdjustEvent(eventToken); + // 金额大于0 + if (revenue > 0) { + event.setRevenue(revenue, currency); + } + if (orderId != "") { + event.setOrderId(orderId); + } + Adjust.trackEvent(event); + } catch (Exception e) { + Log.d(TAG, "onTrackAdjustEventFun:=>error:" + e.getMessage()); + } + } +} diff --git a/android/app/src/com/cocos/game/AdsHelper.java b/android/app/src/com/cocos/game/AdsHelper.java new file mode 100644 index 0000000..cdf2173 --- /dev/null +++ b/android/app/src/com/cocos/game/AdsHelper.java @@ -0,0 +1,303 @@ +package com.cocos.game; + +import android.Manifest; +import android.app.Service; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.net.Uri; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.os.Build; +import android.os.Bundle; +import android.os.Vibrator; +import android.provider.Settings; +import android.telephony.TelephonyManager; +import android.util.Log; +import android.widget.Toast; + +import com.cocos.lib.JsbBridgeWrapper; +import com.ddddd.one.R; + +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class AdsHelper { + private static final String TAG = "AdsHelper"; + private static AdsHelper sInstance = null; + + public static AdsHelper getInstance() { + if (sInstance == null) { + sInstance = new AdsHelper(); + } + return sInstance; + } + + // JAVA + private JsbBridgeWrapper jbw = null; + + public JsbBridgeWrapper GetJsbBridgeWrapper() { + if (jbw == null) { + jbw = JsbBridgeWrapper.getInstance(); + } + return jbw; + } + + public void initEventListener(AppActivity nAppActivity) { + GetSysVersion(nAppActivity); + GetMachineName(nAppActivity); + GetHDID(nAppActivity); + GetMacAddress(nAppActivity); + GetBiosID(nAppActivity); + GetCpuID(nAppActivity); + GetChannel(nAppActivity); + + SetShare(nAppActivity); + SetShake(nAppActivity); + SetCopy(nAppActivity); + SetOpenUri(nAppActivity); + } + + //#region 基础信息获取 + public void GetSysVersion(AppActivity nAppActivity) { + GetJsbBridgeWrapper().addScriptEventListener(EventNameUtil.Android_AppVersion, arg -> { + EventToScriptUtil.getInstance().setToScript(nAppActivity, EventNameUtil.Android_AppVersion, getPackageInfo(nAppActivity).versionName); + }); + } + + public void GetMachineName(AppActivity nAppActivity) { + GetJsbBridgeWrapper().addScriptEventListener(EventNameUtil.Android_MachineName, arg -> { + EventToScriptUtil.getInstance().setToScript(nAppActivity, EventNameUtil.Android_MachineName, android.os.Build.MODEL); + }); + } + + public void GetHDID(AppActivity nAppActivity) { + GetJsbBridgeWrapper().addScriptEventListener(EventNameUtil.Android_HDID, arg -> { + EventToScriptUtil.getInstance().setToScript(nAppActivity, EventNameUtil.Android_HDID, getDeviceId(nAppActivity)); + }); + } + + public void GetMacAddress(AppActivity nAppActivity) { + GetJsbBridgeWrapper().addScriptEventListener(EventNameUtil.Android_MacAddress, arg -> { + EventToScriptUtil.getInstance().setToScript(nAppActivity, EventNameUtil.Android_MacAddress, getLocalMacAddress(nAppActivity)); + }); + } + + public void GetBiosID(AppActivity nAppActivity) { + GetJsbBridgeWrapper().addScriptEventListener(EventNameUtil.Android_BiosID, arg -> { + EventToScriptUtil.getInstance().setToScript(nAppActivity, EventNameUtil.Android_BiosID, getAndroidId(nAppActivity)); + }); + } + + public void GetCpuID(AppActivity nAppActivity) { + GetJsbBridgeWrapper().addScriptEventListener(EventNameUtil.Android_CpuID, arg -> { + EventToScriptUtil.getInstance().setToScript(nAppActivity, EventNameUtil.Android_CpuID, android.os.Build.MODEL); + }); + } + + private PackageInfo getPackageInfo(Context context) { + PackageInfo pi = null; + try { + PackageManager pm = context.getPackageManager(); + pi = pm.getPackageInfo(context.getPackageName(), + PackageManager.GET_CONFIGURATIONS); + return pi; + } catch (Exception e) { + e.printStackTrace(); + } + return pi; + } + + private String getDeviceId(Context context) { + String deviceId; + //android api版本不同导致的,我们使用的28版本,没有Q,29版本有Q + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + deviceId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); + } else { + final TelephonyManager mTelephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (context.checkSelfPermission(Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) { + return ""; + } + } + assert mTelephony != null; + if (mTelephony.getDeviceId() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + deviceId = mTelephony.getImei(); + } else { + deviceId = mTelephony.getDeviceId(); + } + } else { + deviceId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); + } + } + return deviceId; + } + + private String getLocalMacAddress(Context context) { + //Android 获取本机Mac地址,需要开启wifi + WifiManager wifi = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE); + WifiInfo info = wifi.getConnectionInfo(); + return info.getMacAddress(); + } + + private String getAndroidId(Context context) { + String ANDROID_ID = Settings.Secure.getString(context.getContentResolver(), + Settings.Secure.ANDROID_ID); + return ANDROID_ID; + } + //#endregion + + //#region 基础操作 + public void SetShare(AppActivity nAppActivity) { + GetJsbBridgeWrapper().addScriptEventListener(EventNameUtil.Android_Share, arg -> { + Log.d(TAG, "SetShare: " + arg); + if (arg != "") { + shareGameLink(nAppActivity, arg); + } + }); + } + + public void SetShake(AppActivity nAppActivity) { + GetJsbBridgeWrapper().addScriptEventListener(EventNameUtil.Android_Shake, arg -> { + Log.d(TAG, "SetShake: " + arg); + try { + Vibrator vib = (Vibrator) nAppActivity.getSystemService(Service.VIBRATOR_SERVICE); + boolean b = vib.hasVibrator(); + if (b) { + //当前设备有振动器 + vib.vibrate(new long[]{300, 500}, -1); + } else { + //当前设备没有振动器 + } + } catch (Exception e) { + e.printStackTrace(); + } + }); + + } + + public void SetCopy(AppActivity nAppActivity) { + GetJsbBridgeWrapper().addScriptEventListener(EventNameUtil.Android_Copy, arg -> { + Log.d(TAG, "SetCopy: " + arg); + if (arg != "") { + nAppActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + ClipboardManager cm = (ClipboardManager) AppActivity.nAppActivity + .getSystemService(Context.CLIPBOARD_SERVICE); + // 创建普通字符型ClipData + ClipData mClipData = ClipData.newPlainText("Label", arg); + // 将ClipData内容放到系统剪贴板里。 + cm.setPrimaryClip(mClipData); + //已复制到剪贴板 + Toast.makeText(AppActivity.nAppActivity, "Kopyalandı", + Toast.LENGTH_LONG).show(); + } + }); + } + }); + } + + public void SetOpenUri(AppActivity nAppActivity) { + GetJsbBridgeWrapper().addScriptEventListener(EventNameUtil.Android_Open, arg -> { + Log.d(TAG, "SetOpenUri: " + arg); + try { + Intent mIntent = new Intent(Intent.ACTION_VIEW); + mIntent.setData(Uri.parse(arg)); + nAppActivity.startActivity(mIntent); + } catch (Exception e) { + // 防止crash (如果手机上没有安装处理某个scheme开头的url的APP, + // 会导致crash) + } + }); + } + + + private void shareGameLink(AppActivity nAppActivity, String jsonStr) { + try { + JSONObject jo = new JSONObject(jsonStr); + String title = jo.getString("title"); + String text = jo.getString("text"); + String sendType = jo.getString("type"); + String filter = jo.getString("filter"); + String[] filterArr = filter.split("\\|"); + //for (int i = 0; i < filterArr.length; i++) { + // Log.i("FILTER:::: ", filterArr[i]); + //} + + Resources r = nAppActivity.getApplicationContext().getResources(); + Uri uri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + + r.getResourcePackageName(R.mipmap.ic_launcher) + "/" + + r.getResourceTypeName(R.mipmap.ic_launcher) + "/" + + r.getResourceEntryName(R.mipmap.ic_launcher)); + + Intent sendIntent = new Intent(Intent.ACTION_SEND); + sendIntent.setType(sendType); + sendIntent.putExtra(Intent.EXTRA_TEXT, text); + //sendIntent.putExtra(Intent.EXTRA_STREAM, uri); + + PackageManager pm = nAppActivity.getPackageManager(); + List activityList = pm.queryIntentActivities(sendIntent, 0); + List shareIntents = new ArrayList(); + + HashMap hashMap = new HashMap(); + for (ResolveInfo info : activityList) { + String packageName = info.activityInfo.packageName; + if (!hashMap.containsKey(packageName) && isFilterPackageName(packageName, filterArr)) { + Intent targeted = new Intent(Intent.ACTION_SEND); + if (packageName.contains("facebook")) { + targeted.setType("text/plain"); + } else { + targeted.setType(sendType); + } + targeted.setPackage(packageName); + targeted.putExtra(Intent.EXTRA_TEXT, text); + //targeted.putExtra(Intent.EXTRA_STREAM, uri); + shareIntents.add(targeted); + Log.i("__ADD:::: ", packageName); + hashMap.put(packageName, true); + } + } + + if (shareIntents.size() > 0) { + Intent chooserIntent = Intent.createChooser(shareIntents.remove(0), title); + chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, shareIntents.toArray(new Intent[0])); + //getInstance().startActivity(chooserIntent); + nAppActivity.startActivityForResult(chooserIntent, nAppActivity.REQUEST_CODE_SHARE_GAME_LINK); + } else { + nAppActivity.startActivityForResult(sendIntent, nAppActivity.REQUEST_CODE_SHARE_GAME_LINK); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + private boolean isFilterPackageName(String sname, String[] filterArr) { + for (int i = 0; i < filterArr.length; i++) { + if (sname.contains(filterArr[i])) { + return true; + } + } + return false; + } + //#endregion + + //#region 获取渠道编号 + public void GetChannel(AppActivity nAppActivity) { + GetJsbBridgeWrapper().addScriptEventListener(EventNameUtil.Android_Channel, arg -> { + EventToScriptUtil.getInstance().setToScript(nAppActivity, EventNameUtil.Android_Channel, "liuhaotest"); + }); + } + //#endregion +} diff --git a/android/app/src/com/cocos/game/AppActivity.java b/android/app/src/com/cocos/game/AppActivity.java index 5aaece8..dec9369 100644 --- a/android/app/src/com/cocos/game/AppActivity.java +++ b/android/app/src/com/cocos/game/AppActivity.java @@ -1,44 +1,217 @@ /**************************************************************************** -Copyright (c) 2015-2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + Copyright (c) 2015-2016 Chukong Technologies Inc. + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. -http://www.cocos2d-x.org + http://www.cocos2d-x.org -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -****************************************************************************/ + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ****************************************************************************/ package com.cocos.game; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.content.Intent; import android.content.res.Configuration; +import android.os.Handler; +import android.os.Message; +import android.util.Log; +import android.view.View; +import android.view.WindowManager; +import android.widget.FrameLayout; +import android.widget.ImageView; +import com.cocos.lib.JsbBridgeWrapper; import com.cocos.service.SDKWrapper; import com.cocos.lib.CocosActivity; +import com.google.android.gms.auth.api.signin.GoogleSignIn; +import com.google.android.gms.auth.api.signin.GoogleSignInAccount; +import com.google.android.gms.auth.api.signin.GoogleSignInClient; +import com.google.android.gms.auth.api.signin.GoogleSignInOptions; +import com.google.android.gms.common.Scopes; +import com.google.android.gms.common.api.Scope; +import com.google.android.gms.tasks.Task; +import com.luck.picture.lib.basic.PictureSelector; +import com.luck.picture.lib.config.SelectMimeType; +import com.luck.picture.lib.config.SelectModeConfig; +import com.luck.picture.lib.entity.LocalMedia; +import com.luck.picture.lib.interfaces.OnResultCallbackListener; +import com.luck.picture.lib.language.LanguageConfig; +import com.ddddd.one.R; +import com.tencent.bugly.crashreport.CrashReport; +import com.cocos.game.PermissionsUtil.IPermissionsCallback; -public class AppActivity extends CocosActivity { +import java.io.File; +import java.util.ArrayList; +import java.util.Locale; + +public class AppActivity extends CocosActivity implements + IPermissionsCallback { + + public static final int REQUEST_CODE_SHARE_GAME_LINK = 1001; //游戏分享 + public static final int REQUEST_CODE_GOOGLE_LOGIN = 9001; //Google登录 + public static final int REQUEST_CODE_GOOGLE_PAY_SERVICES = 10001; //Google支付服务 + + //动态权限 + private static final int MY_PERMISSIONS_REQUEST_CAMERA = 0; // 相机相册 + private static final int MY_PERMISSIONS_REQUEST_StART = 1; // 启动游戏 + private static final int MY_PERMISSIONS_REQUEST_RECORD_AUDIO = 3;//语音 + + private static final String TAG = "AppActivity"; + private GoogleSignInClient mGoogleSignInClient; + private JsbBridgeWrapper jbw = null; + ShakeDetector mShaker; + static SharedPreferences sharedPreferences; + public static int selectImageIndex = 0; + public static PermissionsUtil permissionsUtil; + + private static CocosActivity sContext = null; + public static AppActivity nAppActivity = null; + + public AppActivity() { + nAppActivity = this; + } + + public static Context getContext() { + return sContext; + } + + private static ImageView sSplashBgImageView = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // DO OTHER INITIALIZATION BELOW SDKWrapper.shared().init(this); + sContext = this; + if (jbw == null) + jbw = JsbBridgeWrapper.getInstance(); + + jbw.addScriptEventListener(EventNameUtil.Android_CloseSplash, arg -> { + if (sSplashBgImageView != null) { + Log.d(TAG, "onCreate: " + EventNameUtil.Android_CloseSplash); + hideSplash(); + } + }); + showSplash(); + + CrashReport.initCrashReport(getApplicationContext()); + sharedPreferences = getSharedPreferences("path", MODE_PRIVATE); + + // 初始化 RtcEngine,并加入频道。 + ZegoUtil.getInstance().zegoInitializeEngine(nAppActivity, getApplicationContext()); + + // google play game service sign + GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN) + //.requestScopes(new Scope(Scopes.PROFILE)) + .requestScopes(new Scope(Scopes.PROFILE)) + .requestServerAuthCode(getString(R.string.google_client_id)) + .requestEmail() + .build(); +// GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) +// .requestIdToken(getString(R.string.google_client_id)) +// .requestEmail() +// .build(); + // Build a GoogleSignInClient with the options specified by gso. + mGoogleSignInClient = GoogleSignIn.getClient(nAppActivity, gso); + + GoogleUtil.getInstance().InitLogin(nAppActivity, getApplicationContext(), mGoogleSignInClient); + GoogleUtil.getInstance().InitLoginOut(nAppActivity, getApplicationContext(), mGoogleSignInClient); + GoogleUtil.getInstance().initGooglePay(nAppActivity, getApplicationContext()); + + + //游客账号 + jbw.addScriptEventListener(EventNameUtil.Android_Adid, arg -> { + AdjustUtil.getInstance().getAdidByVisitor(nAppActivity); + }); + //adjust归因 + jbw.addScriptEventListener(EventNameUtil.Android_AdjustTT, arg -> { + final String adjustTT = AdjustUtil.getInstance().getAdjustTrackerToken(); + EventToScriptUtil.getInstance().actCallBack(nAppActivity, adjustTT); + }); + //adjust发送事件 + jbw.addScriptEventListener(EventNameUtil.Android_AdjustSend, arg -> { + AdjustUtil.getInstance().onTrackAdjustEventFun(arg); + }); + + //图片上传 + jbw.addScriptEventListener(EventNameUtil.Android_FeedImage, arg -> { + Log.d(TAG, "onCreate: Android_FeedImage"); + selectImageIndex = 1; + Message msg = new Message(); + msg.what = HANDLER_IMAGE_CHOOSE; + msg.obj = ""; + handler.sendMessage(msg); + }); + jbw.addScriptEventListener(EventNameUtil.Android_HeadImage, arg -> { + Log.d(TAG, "onCreate: Android_HeadImage"); + selectImageIndex = 0; + Message msg = new Message(); + msg.what = HANDLER_IMAGE_CHOOSE; + msg.obj = ""; + handler.sendMessage(msg); + }); + + //基础信息 + AdsHelper.getInstance().initEventListener(nAppActivity); + try { + mShaker = new ShakeDetector(this); + mShaker.registerOnShakeListener(new ShakeDetector.OnShakeListener() { + @Override + public void onShake() { + // TODO Auto-generated method stub + Log.d("shake2", "shake2: " + "shake2"); + } + }); + mShaker.start(); + + } catch (Exception e) { + e.printStackTrace(); + } + // 防止屏幕休眠 + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + SDKWrapper.shared().onActivityResult(requestCode, resultCode, data); + Log.d(TAG, "onActivityResult: " + requestCode); + if (requestCode == REQUEST_CODE_SHARE_GAME_LINK) { + EventToScriptUtil.getInstance().setToScript(nAppActivity, EventNameUtil.Android_Share, resultCode + ""); + } else if (requestCode == REQUEST_CODE_GOOGLE_LOGIN) { + Log.d(TAG, "onActivityResult: " + requestCode); + Task task = GoogleSignIn.getSignedInAccountFromIntent(data); + GoogleUtil.getInstance().HandleSignInResult(nAppActivity, getApplicationContext(), task); + } else if (requestCode == REQUEST_CODE_GOOGLE_PAY_SERVICES) { + // The Task returned from this call is always completed, no need to attach + // a listener. + Log.d(TAG, "onActivityResult: REQUEST_CODE_GOOGLE_PAY_SERVICES"); + } } @Override @@ -60,13 +233,10 @@ public class AppActivity extends CocosActivity { if (!isTaskRoot()) { return; } - SDKWrapper.shared().onDestroy(); - } - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - SDKWrapper.shared().onActivityResult(requestCode, resultCode, data); + SDKWrapper.shared().onDestroy(); + mShaker.stop(); + ZegoUtil.getInstance().zegoDestroyEngine(); } @Override @@ -122,4 +292,253 @@ public class AppActivity extends CocosActivity { SDKWrapper.shared().onLowMemory(); super.onLowMemory(); } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + // TODO Auto-generated method stub + + //应用第一次安装,并且权限被禁用时,返回true + //权限被禁用时,返回true + //权限被禁用且不再提示时,返回false + //已授权时返回false + int mRequestPermissionCount = sharedPreferences.getInt(requestCode + "", 0); + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_DENIED) { + mRequestPermissionCount = mRequestPermissionCount + 1; + //// Should we show an explanation? + //// 1.第一次拒绝后提示用户为何需要此类权限、做第二次授权请求 + //if (ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[0])) { + // //Show permission explanation dialog... + // permissionsUtil.onRequestPermissionsResult(requestCode, permissions, grantResults); + //} else { + // // 用户已经选择:禁用且不再提示、此时一般不需要再次提示授权说明 + // //Never ask again selected, or device policy prohibits the app from having that permission. + // //So, disable that feature, or fall back to another situation... + // permissionsUtil.onRequestPermissionsResult(requestCode, permissions, grantResults); + //} + // permissionsUtil.onRequestPermissionsResult(requestCode, permissions, grantResults); + } else { + mRequestPermissionCount = 0; + } + sharedPreferences.edit().putInt(requestCode + "", mRequestPermissionCount).apply(); + if (requestCode == MY_PERMISSIONS_REQUEST_RECORD_AUDIO && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + Log.d(TAG, "onRequestPermissionsResult:joinChannel " + requestCode); + ZegoUtil.getInstance().zegoLoginFun(nAppActivity, getContext(), ZegoUtil.ZegoJoinJsonStr); + } + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + + // /////////////////////////////////////////////////////////////////////// + // 动态权限获取结果回调 + // /////////////////////////////////////////////////////////////////////// + @Override + public void onPermissionsGranted(int requestCode, String... permission) { + Log.d("cocos1", "onPermissionsGranted joinChannel " + requestCode); + // 权限获取回调 + switch (requestCode) { + case MY_PERMISSIONS_REQUEST_CAMERA: // 打开相机相册 + ChooseImageDialog(); + break; + case MY_PERMISSIONS_REQUEST_StART: // 启动游戏 + break; + case MY_PERMISSIONS_REQUEST_RECORD_AUDIO://打开语音 + ZegoUtil.getInstance().zegoLoginFun(nAppActivity, getContext(), ZegoUtil.ZegoJoinJsonStr); + break; + default: + break; + } + } + + @Override + public void onPermissionsDenied(int requestCode, String... permission) { + // 权限被拒绝回调 + switch (requestCode) { + case MY_PERMISSIONS_REQUEST_CAMERA:// 打开相机相册 + + break; + case MY_PERMISSIONS_REQUEST_StART:// 启动游戏 + nAppActivity.finish(); + System.exit(0); + break; + case MY_PERMISSIONS_REQUEST_RECORD_AUDIO://打开语音 + Log.d(TAG, "onPermissionsDenied: " + "1"); + EventToScriptUtil.getInstance().dispatchEventToScript(EventNameUtil.Zg_Login, "1"); + break; + default: + break; + } + } + + private static final int HANDLER_IMAGE_CHOOSE = 1; + public static final int HANDLER_ZEGOLOGIN = 3;//加入频道 + + public static Handler handler = new Handler() { + public void handleMessage(Message msg) { + switch (msg.what) { + case HANDLER_IMAGE_CHOOSE: + ChooseImageDialog(); + break; + case HANDLER_ZEGOLOGIN: { + // 版本判断。当手机系统大于 23 时,才有必要去判断权限是否获取 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + permissionsUtil = PermissionsUtil + .with(nAppActivity) + .requestCode(MY_PERMISSIONS_REQUEST_RECORD_AUDIO) + .isDebug(true) + .requestPermissionCount(sharedPreferences.getInt(MY_PERMISSIONS_REQUEST_RECORD_AUDIO + "", 0)) + // 开启log + .permissions( + PermissionsUtil.Permission.Microphone.RECORD_AUDIO) + .request(); + } else { + String strParam = (String) msg.obj; + ZegoUtil.getInstance().zegoLoginFun(nAppActivity, getContext(), strParam); + } + } + break; + } + } + }; + + /** + * 弹出选择图片的选择框 android选择图片 + */ + public static void ChooseImageDialog() { + int languageConfig = LanguageConfig.ENGLISH; + Locale _locale = Locale.getDefault(); + Log.i(TAG, "onCancel: "+_locale); + if (_locale.equals(Locale.ENGLISH) +// || _locale.equals(Locale.CHINESE) +// || _locale.equals(Locale.SIMPLIFIED_CHINESE) +// || _locale.equals(Locale.TRADITIONAL_CHINESE) + || _locale.equals(Locale.KOREA) + || _locale.equals(Locale.GERMANY) + || _locale.equals(Locale.FRANCE) + || _locale.equals(Locale.JAPAN) + || _locale.equals(new Locale("vi")) + || _locale.equals(new Locale("es", "ES")) + || _locale.equals(new Locale("pt", "PT")) + || _locale.equals(new Locale("ar", "AE")) + || _locale.equals(new Locale("ru", "rRU")) + || _locale.equals(new Locale("cs", "rCZ")) + || _locale.equals(new Locale("kk", "rKZ")) + ) { + languageConfig = LanguageConfig.SYSTEM_LANGUAGE; + } + if (_locale.equals(Locale.CHINESE) + || _locale.equals(Locale.SIMPLIFIED_CHINESE) + || _locale.equals(Locale.TRADITIONAL_CHINESE)) { + languageConfig = LanguageConfig.ENGLISH; + } + if (selectImageIndex == 0) { + PictureSelector.create(nAppActivity) + .openGallery(SelectMimeType.ofImage()) + .setImageEngine(GlideEngine.createGlideEngine())//设置相册图片加载引擎 + .setSelectionMode(SelectModeConfig.SINGLE)//单选或是多选 + .setCompressEngine(new PictureSelectlucksiege.ImageFileCompressEngine())//设置相册压缩引擎 + .setCropEngine(new PictureSelectlucksiege.ImageFileCropEngine())//设置相册裁剪引擎 + //.setCameraInterceptListener(new PictureSelectlucksiege.MeOnCameraInterceptListener())//自定义拍照 + //.setPermissionDescriptionListener(new PictureSelectlucksiege.MeOnPermissionDescriptionListener()) + .setPermissionDeniedListener(new PictureSelectlucksiege.MeOnPermissionDeniedListener()) + .setLanguage(languageConfig) + .setDefaultLanguage(languageConfig) + .forResult(new OnResultCallbackListener() { + @Override + public void onResult(ArrayList result) { + if (result != null && result.size() > 0) { + Log.i(TAG, "onResult: " + result.get(0).getAvailablePath()); + EventToScriptUtil.getInstance().dispatchEventToScript(EventNameUtil.Android_HeadImage, result.get(0).getAvailablePath()); + } + } + + @Override + public void onCancel() { + Log.i(TAG, "onCancel: "); + } + }); + } else { + PictureSelector.create(nAppActivity) + .openGallery(SelectMimeType.ofImage()) + .setImageEngine(GlideEngine.createGlideEngine())//设置相册图片加载引擎 + .setSelectionMode(SelectModeConfig.SINGLE)//单选或是多选 + .setCompressEngine(new PictureSelectlucksiege.ImageFileCompressEngine())//设置相册压缩引擎 + .setCropEngine(new PictureSelectlucksiege.ImageFileCropEngine())//设置相册裁剪引擎 + .setCameraInterceptListener(new PictureSelectlucksiege.MeOnCameraInterceptListener())//自定义拍照 + //.setPermissionDescriptionListener(new PictureSelectlucksiege.MeOnPermissionDescriptionListener()) + .setPermissionDeniedListener(new PictureSelectlucksiege.MeOnPermissionDeniedListener()) + .setLanguage(languageConfig) + .setDefaultLanguage(languageConfig) + .forResult(new OnResultCallbackListener() { + @Override + public void onResult(ArrayList result) { + if (result != null && result.size() > 0) { + Log.i(TAG, "onResult: " + result.get(0).getAvailablePath()); + EventToScriptUtil.getInstance().dispatchEventToScript(EventNameUtil.Android_FeedImage, result.get(0).getAvailablePath()); + } + } + + @Override + public void onCancel() { + Log.i(TAG, "onCancel: "); + } + }); + } + } + + /** + * 供 JS 通过 jsb.reflection 调用的 Adjust 事件上报方法 + */ + public static void trackAdjustEvent(String eventToken) { + try { + com.adjust.sdk.AdjustEvent event = new com.adjust.sdk.AdjustEvent(eventToken); + com.adjust.sdk.Adjust.trackEvent(event); + Log.d(TAG, "trackAdjustEvent: " + eventToken); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 开屏背景 + */ + private static void showSplash() { + sSplashBgImageView = new ImageView(sContext); + // sSplashBgImageView.setBackgroundColor( + // // sContext.getResources().getColor(R.color.splashBg) + // sContext.getResources().getColor(R.color.viewfinder_laser, null) + //); + //全屏背景图 + sSplashBgImageView.setImageResource(R.drawable.splash); + //logo居中、其他区域颜色覆盖 + //sSplashBgImageView.setImageResource(R.drawable.splash_slogan_with_bg); + //sSplashBgImageView.setScaleType(ImageView.ScaleType.FIT_XY); + sContext.addContentView(sSplashBgImageView, + new WindowManager.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT + ) + ); + } + + /** + * 这是给 CC JS 调用的隐藏原生开屏背景的方法 + */ + public static void hideSplash() { + sContext.runOnUiThread(new Runnable() { + @Override + public void run() { + if (sSplashBgImageView != null) { + // sSplashBgImageView.setVisibility(View.GONE); + sSplashBgImageView.animate() + .alpha(0f) + .setDuration(1000) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + sSplashBgImageView.setVisibility(View.GONE); + } + }); + } + } + }); + } } diff --git a/android/app/src/com/cocos/game/EventNameUtil.java b/android/app/src/com/cocos/game/EventNameUtil.java new file mode 100644 index 0000000..183553a --- /dev/null +++ b/android/app/src/com/cocos/game/EventNameUtil.java @@ -0,0 +1,39 @@ +package com.cocos.game; + + +public class EventNameUtil { + public static final String Android_AppVersion = "Native_AppVersion"; + public static final String Android_MachineName = "Native_MachineName"; + public static final String Android_MacAddress = "Native_MacAddress"; + public static final String Android_HDID = "Native_HDID"; + public static final String Android_BiosID = "Native_BiosID"; + public static final String Android_CpuID = "Native_CpuID"; + public static final String Android_Channel = "Native_Channel"; + + public static final String Android_CloseSplash = "Native_CloseSplash"; + + public static final String Android_Share = "Native_Share"; + public static final String Android_Copy = "Native_Copy"; + public static final String Android_Open = "Native_Open"; + public static final String Android_Shake = "Native_Shake"; + + public static final String Android_Adid = "Native_Adid"; + public static final String Android_AdjustTT = "Native_AdjustTT"; + public static final String Android_AdjustSend = "Native_AdjustSend"; + + public static final String Android_FeedImage = "Native_FeedImage";//反馈图片 + public static final String Android_HeadImage = "Native_HeadImage";//头像图片 + + public static final String Android_Login = "Native_GoogleLogin"; + public static final String Android_LoginOut = "Native_GoogleLoginOut"; + public static final String Android_Pay = "Native_GooglePay"; + + public static final String Zg_Login = "Native_ZgLogin"; + public static final String Zg_LoginOut = "Native_ZgLoginOut"; + public static final String Zg_StopPlay = "Native_ZgStopPlay"; + public static final String Zg_StartPlay = "Native_ZgStartPlay"; + public static final String Zg_StopPublish = "Native_ZgStopPublish"; + public static final String Zg_StartPublish = "Native_ZgStartPublish"; + public static final String Zg_AllPlay = "Native_ZgAllPlay"; + public static final String Zg_AudioVolume = "Native_ZgAudioVolume"; +} diff --git a/android/app/src/com/cocos/game/EventToScriptUtil.java b/android/app/src/com/cocos/game/EventToScriptUtil.java new file mode 100644 index 0000000..b124548 --- /dev/null +++ b/android/app/src/com/cocos/game/EventToScriptUtil.java @@ -0,0 +1,69 @@ +package com.cocos.game; + +import android.util.Log; + +import com.cocos.lib.JsbBridgeWrapper; + +public class EventToScriptUtil { + + private static final String TAG = "EventToScriptUtil"; + private static EventToScriptUtil _instance = null; + + public static EventToScriptUtil getInstance() { + if (_instance == null) { + _instance = new EventToScriptUtil(); + } + return _instance; + } + + private AppActivity curAppActivity = null; + private String adjustTT = ""; + private JsbBridgeWrapper jbw = null; + + public void actCallBack(AppActivity nAppActivity, String _adjustTT) { + Log.i(TAG, "actCallBack: " + _adjustTT); + if (this.curAppActivity == null) + this.curAppActivity = nAppActivity; + if (_adjustTT != "") { + this.callBackToLua(_adjustTT); + } else if (this.adjustTT != "") { + this.callBackToLua(this.adjustTT); + } + } + + public void appCallBack(String _adjustTT) { + Log.i(TAG, "appCallBack: " + _adjustTT); + if (_adjustTT != "" && this.curAppActivity != null) { + this.adjustTT = _adjustTT; + this.callBackToLua(_adjustTT); + } + } + + public void setToScript(AppActivity nAppActivity, String _eventName, String _val) { + if (this.curAppActivity == null) + this.curAppActivity = nAppActivity; + + Log.d(TAG, "setToScript: " + _eventName + " " + _val); + dispatchEventToScript(_eventName, _val); + } + + private void callBackToLua(final String adjustTT) { + dispatchEventToScript(EventNameUtil.Android_AdjustTT, adjustTT); + } + + + public void dispatchEventToScript(final String _eventName, final String _val) { +// if (this.curAppActivity == null) { +// return; +// } + try { + if (jbw == null) { + jbw = JsbBridgeWrapper.getInstance(); + } + jbw.dispatchEventToScript(_eventName, _val); + + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/android/app/src/com/cocos/game/GlideEngine.java b/android/app/src/com/cocos/game/GlideEngine.java new file mode 100644 index 0000000..847de52 --- /dev/null +++ b/android/app/src/com/cocos/game/GlideEngine.java @@ -0,0 +1,113 @@ +package com.cocos.game; + +import android.content.Context; +import android.widget.ImageView; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.resource.bitmap.CenterCrop; +import com.bumptech.glide.load.resource.bitmap.RoundedCorners; +import com.luck.picture.lib.engine.ImageEngine; +import com.luck.picture.lib.utils.ActivityCompatHelper; + +import com.ddddd.one.R; + +public class GlideEngine implements ImageEngine { + + /** + * 加载图片 + * + * @param context 上下文 + * @param url 资源url + * @param imageView 图片承载控件 + */ + @Override + public void loadImage(Context context, String url, ImageView imageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return; + } + Glide.with(context) + .load(url) + .into(imageView); + } + + @Override + public void loadImage(Context context, ImageView imageView, String url, int maxWidth, int maxHeight) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return; + } + Glide.with(context) + .load(url) + .override(maxWidth, maxHeight) + .into(imageView); + } + + /** + * 加载相册目录封面 + * + * @param context 上下文 + * @param url 图片路径 + * @param imageView 承载图片ImageView + */ + @Override + public void loadAlbumCover(Context context, String url, ImageView imageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return; + } + Glide.with(context) + .asBitmap() + .load(url) + .override(180, 180) + .sizeMultiplier(0.5f) + .transform(new CenterCrop(), new RoundedCorners(8)) + .placeholder(R.drawable.ps_image_placeholder) + .into(imageView); + } + + + /** + * 加载图片列表图片 + * + * @param context 上下文 + * @param url 图片路径 + * @param imageView 承载图片ImageView + */ + @Override + public void loadGridImage(Context context, String url, ImageView imageView) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return; + } + Glide.with(context) + .load(url) + .override(200, 200) + .centerCrop() + .placeholder(R.drawable.ps_image_placeholder) + .into(imageView); + } + + @Override + public void pauseRequests(Context context) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return; + } + Glide.with(context).pauseRequests(); + } + + @Override + public void resumeRequests(Context context) { + if (!ActivityCompatHelper.assertValidRequest(context)) { + return; + } + Glide.with(context).resumeRequests(); + } + + private GlideEngine() { + } + + private static final class InstanceHolder { + static final GlideEngine instance = new GlideEngine(); + } + + public static GlideEngine createGlideEngine() { + return InstanceHolder.instance; + } +} \ No newline at end of file diff --git a/android/app/src/com/cocos/game/GoogleUtil.java b/android/app/src/com/cocos/game/GoogleUtil.java new file mode 100644 index 0000000..abd9414 --- /dev/null +++ b/android/app/src/com/cocos/game/GoogleUtil.java @@ -0,0 +1,855 @@ +package com.cocos.game; + +import android.content.Context; +import android.content.Intent; +import android.util.Log; +import android.widget.Toast; + +import androidx.annotation.NonNull; + +import com.adjust.sdk.Adjust; +import com.adjust.sdk.OnDeviceIdsRead; +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.ConsumeParams; +import com.android.billingclient.api.ConsumeResponseListener; +import com.android.billingclient.api.ProductDetails; +import com.android.billingclient.api.ProductDetailsResponseListener; +import com.android.billingclient.api.Purchase; +import com.android.billingclient.api.PurchasesResponseListener; +import com.android.billingclient.api.PurchasesUpdatedListener; +import com.android.billingclient.api.QueryProductDetailsParams; +import com.android.billingclient.api.QueryPurchasesParams; +import com.android.billingclient.api.SkuDetails; +import com.android.billingclient.api.SkuDetailsParams; +import com.android.billingclient.api.SkuDetailsResponseListener; +import com.cocos.lib.JsbBridgeWrapper; +import com.google.android.gms.auth.api.signin.GoogleSignIn; +import com.google.android.gms.auth.api.signin.GoogleSignInAccount; +import com.google.android.gms.auth.api.signin.GoogleSignInClient; +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.GoogleApiAvailability; +import com.google.android.gms.common.api.ApiException; +import com.google.android.gms.games.Games; +import com.google.android.gms.games.Player; +import com.google.android.gms.games.PlayersClient; +import com.google.android.gms.tasks.OnCompleteListener; +import com.google.android.gms.tasks.Task; +import com.tencent.bugly.crashreport.CrashReport; + +import org.jetbrains.annotations.NotNull; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class GoogleUtil { + + private static final String TAG = "GoogleUtil"; + private static GoogleUtil sInstance = null; + /** + * 缓存的SKU列表 + */ + private static Map skuDetailsMap = new HashMap<>(); + private static Map productDetailsMap = new HashMap<>(); + private static PurchasesUpdatedListener purchasesUpdatedListener; + private static BillingClient billingClient; + + public static GoogleUtil getInstance() { + if (sInstance == null) { + sInstance = new GoogleUtil(); + } + return sInstance; + } + + // JAVA + private JsbBridgeWrapper jbw = null; + + public JsbBridgeWrapper GetJsbBridgeWrapper() { + if (jbw == null) { + jbw = JsbBridgeWrapper.getInstance(); + } + return jbw; + } + + private static Context mContext; + private static AppActivity mActivity; + + /** + * 初始化信息 + * + * @param activity + */ + private static void InitInfo(AppActivity activity, Context _context) { + if (mActivity == null) + mActivity = activity; + if (mContext == null) + mContext = _context; + } + + /** + * 初始化Google登录信息 + * + * @param nAppActivity + * @param _context + * @param mGoogleSignInClient + */ + public void InitLogin(AppActivity nAppActivity, Context _context, GoogleSignInClient mGoogleSignInClient) { + InitInfo(nAppActivity, _context); + + GetJsbBridgeWrapper().addScriptEventListener(EventNameUtil.Android_Login, arg -> { + Log.d(TAG, "InitLogin: " + arg); + // 判断Google服务是否可用 + if (!IsGooglePlayServices()) { + GoogleLoginSuccess(-3, null); + return; + } + // 检查现有的登录用户 + GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(mContext);//getApplicationContext() + if (null != account && account.getServerAuthCode() != null) { + //未登录 + GoogleLoginSuccess(1, account); + return; + } + try { + //设置回调时requestCode + Intent signInIntent = mGoogleSignInClient.getSignInIntent(); + mActivity.startActivityForResult(signInIntent, nAppActivity.REQUEST_CODE_GOOGLE_LOGIN); + } catch (Exception e) { + GoogleLoginSuccess(-2, null); + e.printStackTrace(); + } + }); + } + + /** + * 初始化Google登出信息 + * + * @param nAppActivity + * @param _context + * @param mGoogleSignInClient + */ + public void InitLoginOut(AppActivity nAppActivity, Context _context, GoogleSignInClient mGoogleSignInClient) { + InitInfo(nAppActivity, _context); + GetJsbBridgeWrapper().addScriptEventListener(EventNameUtil.Android_LoginOut, arg -> { + Log.d(TAG, "InitLogin: " + arg); + // 判断Google服务是否可用 + if (!IsGooglePlayServicesNoShow()) { + Log.d(TAG, "InitLoginOut: IsGooglePlayServicesNoShow"); + return; + } + // 检查现有的登录用户 + GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(mContext); + if (null != account && account.getServerAuthCode() != null) { + mGoogleSignInClient.signOut().addOnCompleteListener(mActivity, + new OnCompleteListener() { + @Override + public void onComplete(@NonNull Task task) { + // at this point, the user is signed out. + Log.d(TAG, "onComplete:signOut "); + } + }); + } + }); + } + + /** + * 初始化google支付 + */ + public void initGooglePay(AppActivity nAppActivity, Context _context) { + InitInfo(nAppActivity, _context); + + GetJsbBridgeWrapper().addScriptEventListener(EventNameUtil.Android_Pay, strParam -> { + try { + JSONObject jo = new JSONObject(strParam); + final String sku_id = jo.getString("sku_id"); + final String order_Id = jo.getString("order_Id"); + final int pay_Type = jo.getInt("pay_Type"); + final String o_uid = jo.getString("uid"); + + CrashReport.setUserId(o_uid);// Bugly 设置用户ID + + // 判断Google服务是否可用 + if (!IsGooglePlayServicesNoShow()) { + if (pay_Type == 1) { + googlePaySuccess(-3, "", sku_id, "", pay_Type); + } + return; + } + // 初始化(监听支付事件) + if (billingClient == null) { + purchasesUpdatedListener = new PurchasesUpdatedListener() { + @Override + public void onPurchasesUpdated(BillingResult billingResult, List purchases) { + onPurchasesUpdatedFun(billingResult, purchases, sku_id, pay_Type); + } + }; + billingClient = BillingClient.newBuilder(nAppActivity) + .setListener(purchasesUpdatedListener) + .enablePendingPurchases() + .build(); + } + if (billingClient != null) { + // 连接Google服务器(必须) + if (!billingClient.isReady()) { + Log.d(TAG, "BillingClient: Start connection..."); + billingClient.startConnection(new BillingClientStateListener() { + @Override + public void onBillingSetupFinished(BillingResult billingResult) { + if (billingResult != null) { + Log.d(TAG, "BillingClient: Start connection..." + billingResult.getResponseCode()); + if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) { + // The BillingClient is ready. You can query purchases here. + if (pay_Type == 1) { + // 判断Google服务是否支持ProductDetails服务支付 + if (IsUseProductDetails()) + getGoogleProductDetail(sku_id, order_Id, pay_Type, o_uid); + else + getGoogleSkuDetail(sku_id, order_Id, pay_Type, o_uid); + } else { + if (IsUseProductDetails()) + queryProductGoogleHistoryOrder(); + else + queryGoogleHistoryOrder(); + } + } else { + Log.d(TAG, "onBillingSetupFinished--error==" + billingResult.getDebugMessage() + "====" + billingResult.getResponseCode()); + googlePaySuccess(-3, "", sku_id, "", pay_Type); + // Toast.makeText(nAppActivity, "Bu cihaz desteklenmiyor", Toast.LENGTH_SHORT).show();//不支持此设备 + } + } else { + Log.d(TAG, "onBillingSetupFinished: billingResult ==== null"); + googlePaySuccess(-3, "", sku_id, "", pay_Type); + // Toast.makeText(nAppActivity, "Bu cihaz desteklenmiyor", Toast.LENGTH_SHORT).show();//不支持此设备 + } + } + + @Override + public void onBillingServiceDisconnected() { + Log.d(TAG, "onBillingServiceDisconnected"); + // Try to restart the connection on the next request to + // Google Play by calling the startConnection() method. + //if(billingClient != null) + // billingClient.startConnection(this); + } + }); + } else { + if (pay_Type == 1) { + if (IsUseProductDetails()) + getGoogleProductDetail(sku_id, order_Id, pay_Type, o_uid); + else + getGoogleSkuDetail(sku_id, order_Id, pay_Type, o_uid); + } else { + if (IsUseProductDetails()) + queryProductGoogleHistoryOrder(); + else + queryGoogleHistoryOrder(); + } + } + } + } catch (Exception e) { + Log.i(TAG, "initGooglePay=>json_error " + e.toString()); + } + }); + } + + /** + * @param purchase + */ + public void handlePurchase(Purchase purchase) { + // Purchase retrieved from BillingClient#queryPurchasesAsync or your PurchasesUpdatedListener. + + // Verify the purchase. + // Ensure entitlement was not already granted for this purchaseToken. + // Grant entitlement to the user. + // 已支付状态处理 + if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) { + String sku_id = purchase.getSkus().get(0); + String packageName = purchase.getPackageName(); +// {"orderId":"GPA.3328-7219-1739-02590","packageName":"com.cxhdgame.kxddz","productId":"cx101_1","purchaseTime":1636445521030,"purchaseState":0,"purchaseToken":"edmllomemkamjnhijmiainna.AO-J1OyyWeKcqednsYd1xS3hCTj1dPU2VmClddFHAwu5k8tI2a3uUYSmwyl7syqweO1aNRri3eSno94iBSE_kiKlktdk_pYbQg","quantity":1,"acknowledged":false} + Log.d(TAG, "handlePurchase: packageName:" + sku_id); + Log.d(TAG, "handlePurchase: productId:" + packageName); + Log.d(TAG, "handlePurchase: purchase" + purchase); + +// googlePaySuccess(1, purchase.getPurchaseToken()); + + ConsumeParams consumeParams = + ConsumeParams.newBuilder() + .setPurchaseToken(purchase.getPurchaseToken()) + .build(); + ConsumeResponseListener listener = new ConsumeResponseListener() { + @Override + public void onConsumeResponse(BillingResult billingResult, String purchaseToken) { + if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) { + // Handle the success of the consume operation. + googlePaySuccess(1, purchaseToken, sku_id, packageName, 1); + } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) { + googlePaySuccess(-1, "", sku_id, packageName, 1); + } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED) { + googlePaySuccess(-3, "", sku_id, packageName, 1); + } else { + googlePaySuccess(-2, "", sku_id, packageName, 1); + } + } + }; + billingClient.consumeAsync(consumeParams, listener); + } else if (purchase.getPurchaseState() == Purchase.PurchaseState.PENDING) { + googlePaySuccess(-4, "", "", "", 1); + } + } + + //#region 登录相关方法 + + /** + * Google登录返回 + * + * @param completedTask + */ + public void HandleSignInResult(AppActivity nAppActivity, Context _context, Task completedTask) { + try { + InitInfo(nAppActivity, _context); + Log.d(TAG, "HandleSignInResult: "); + GoogleSignInAccount account = completedTask.getResult(ApiException.class); + GoogleLoginSuccess(1, account); + // Signed in successfully, show authenticated UI. + // updateUI(account); + } catch (ApiException e) { + // The ApiException status code indicates the detailed failure reason. + // Please refer to the GoogleSignInStatusCodes class reference for more information. + // updateUI(null); + Log.d(TAG, "HandleSignInResult: signInResult:failed code=" + e.toString()); + // https://developers.google.com/android/reference/com/google/android/gms/auth/api/signin/GoogleSignInStatusCodes + // 12500 使用当前帐户登录尝试失败。 + // 12501 用户已取消登录。即用户取消了一些登录解决方案,例如帐户选择或 OAuth 同意。 + // 12502 当前正在进行登录过程,当前无法继续。例如,用户多次单击 SignInButton 并且启动了多个登录意图。 + GoogleLoginSuccess(e.getStatusCode() == 12501 ? -1 : -2, null); + } + } + + /** + * 验证Google服务是否可用、有弹窗 + * + * @return boolean + */ + private boolean IsGooglePlayServices() { + GoogleApiAvailability googleApiAvailability = GoogleApiAvailability.getInstance(); + int resultCode = googleApiAvailability.isGooglePlayServicesAvailable(mActivity); + // ConnectionResult:SUCCESS、SERVICE_MISSING、SERVICE_UPDATING、SERVICE_VERSION_UPDATE_REQUIRED、SERVICE_DISABLED、SERVICE_INVALID + if (resultCode != ConnectionResult.SUCCESS) { + if (googleApiAvailability.isUserResolvableError(resultCode)) + googleApiAvailability.getErrorDialog(mActivity, resultCode, mActivity.REQUEST_CODE_GOOGLE_PAY_SERVICES).show(); + else + Log.d(TAG, "IsGooglePlayServices: This device is not supported"); + return false; + } + return true; + } + + /** + * 验证Google服务是否可用、无弹窗 + * + * @return boolean + */ + private boolean IsGooglePlayServicesNoShow() { + GoogleApiAvailability googleApiAvailability = GoogleApiAvailability.getInstance(); + int resultCode = googleApiAvailability.isGooglePlayServicesAvailable(mActivity); + // ConnectionResult:SUCCESS、SERVICE_MISSING、SERVICE_UPDATING、SERVICE_VERSION_UPDATE_REQUIRED、SERVICE_DISABLED、SERVICE_INVALID + if (resultCode != ConnectionResult.SUCCESS) { + return false; + } + return true; + } + + /** + * google登录成功、回传Lua + * + * @param state 状态:-1.用户取消登录、1.登录成功、-2.登录异常、-3.不支持此设备 + * @param account 用户登录信息 + */ + private void GoogleLoginSuccess(int state, GoogleSignInAccount account) { + if (state == 1) { + try { + PlayersClient mPlayersClient = Games.getPlayersClient(mActivity, account); + mPlayersClient.getCurrentPlayer() + .addOnCompleteListener(new OnCompleteListener() { + @Override + public void onComplete(@NonNull Task task) { + if (task.isSuccessful()) { + Log.d(TAG, "GoogleLoginSuccess onComplete:getDisplayName " + task.getResult().getDisplayName()); + googleLoginSendToLua(task.getResult().getDisplayName(), state, account); + } else { + Log.d(TAG, "GoogleLoginSuccess onComplete: task.getResult().getDisplayName() error "); + googleLoginSendToLua("", state, account); + } + + } + }); + } catch (Exception e) { + googleLoginSendToLua("", state, account); + Log.d(TAG, "GoogleLoginSuccess: " + e.getMessage()); + } + } else { + // 不支持此设备 + String toast_msg = ""; + if (state == -1) + toast_msg = "Kullanıcı girişi iptal etti";//用户取消登录 + else if (state == -2) + toast_msg = "Oturum açma istisnası";//登录异常 + else + toast_msg = "Bu cihaz desteklenmiyor";//不支持此设备 + + Toast.makeText(mActivity, toast_msg, Toast.LENGTH_SHORT).show(); + googleLoginSendToLua("", state, account); + } + } + + /** + * google登录成功、回传Lua + * + * @param accountName + * @param state 状态:-1.用户取消登录、1.登录成功、-2.登录异常、-3.不支持此设备 + * @param account 用户登录信息 + */ + private void googleLoginSendToLua(String accountName, int state, GoogleSignInAccount account) { + //状态|ID|token|名称|归因信息|adid_idfa|adid + String reStr = state + "||||||"; + if (null != account) { + if (null == accountName || accountName == "") { + accountName = null == account.getDisplayName() ? "" : account.getDisplayName(); + accountName = null == accountName ? "" : null == account.getGivenName() ? "" : account.getGivenName(); + } + reStr = state + + "|" + account.getId() + //+ "|" + account.getIdToken() + + "|" + account.getServerAuthCode() + + "|" + accountName + + "|" + AdjustUtil.getInstance().getAdjustTrackerToken() + + "|" + "" + + "|" + Adjust.getAdid(); + } + + EventToScriptUtil.getInstance().dispatchEventToScript(EventNameUtil.Android_Login, reStr); + } + //#endregion + + //#region 支付相关方法 + + /** + * 获取Google待支付商品信息 + */ + private void getGoogleSkuDetail(String sku_id, String order_Id, int pay_type, String o_uid) { + // 检查是否在缓存中 + if (skuDetailsMap != null && skuDetailsMap.containsKey(sku_id)) { + SkuDetails curr_skuDetails = skuDetailsMap.get(sku_id); + Log.d(TAG, "getGoogleSkuDetail skuDetailsMap => curr_skuDetails: ..." + curr_skuDetails); + if (curr_skuDetails != null) { + launchGooglePay(curr_skuDetails, order_Id, o_uid); + } + return; + } + + // 查询SKU商品是否存在 + List skuList = new ArrayList<>(); + skuList.add(sku_id); + SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder(); + params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP); + billingClient.querySkuDetailsAsync(params.build(), + new SkuDetailsResponseListener() { + @Override + public void onSkuDetailsResponse(BillingResult billingResult, + List skuDetailsList) { + Log.d(TAG, "getGoogleSkuDetail onSkuDetailsResponse:" + billingResult + ";skuDetailsList:" + skuDetailsList); + // Process the result. + if (billingResult == null) { + Log.d(TAG, "getGoogleSkuDetail onSkuDetailsResponse: billingResult ==== null"); + return; + } + + int responseCode = billingResult.getResponseCode(); + String debugMessage = billingResult.getDebugMessage(); + switch (responseCode) { + case BillingClient.BillingResponseCode.OK: + if (skuDetailsList == null) { + Log.d(TAG, "getGoogleSkuDetail onSkuDetailsResponse: skuDetailsList ==== null"); + } else { + SkuDetails curr_skuDetails = null; + for (SkuDetails skuDetails : skuDetailsList) { + if (null != skuDetails) { + curr_skuDetails = skuDetails; + if (!skuDetailsMap.containsKey(skuDetails.getSku())) { + skuDetailsMap.put(skuDetails.getSku(), skuDetails); + } + } + } + if (curr_skuDetails != null) { + Log.d(TAG, "getGoogleSkuDetail onSkuDetailsResponse => curr_skuDetails: ..." + curr_skuDetails); + launchGooglePay(curr_skuDetails, order_Id, o_uid); + } + } + break; + case BillingClient.BillingResponseCode.SERVICE_DISCONNECTED: + case BillingClient.BillingResponseCode.SERVICE_UNAVAILABLE: + case BillingClient.BillingResponseCode.BILLING_UNAVAILABLE: + case BillingClient.BillingResponseCode.ITEM_UNAVAILABLE: + case BillingClient.BillingResponseCode.DEVELOPER_ERROR: + case BillingClient.BillingResponseCode.ERROR: + Log.d(TAG, "getGoogleSkuDetail onSkuDetailsResponse: " + responseCode + " " + debugMessage); + googlePaySuccess(-3, "", sku_id, "", pay_type); + break; + case BillingClient.BillingResponseCode.USER_CANCELED: + Log.d(TAG, "getGoogleSkuDetail onSkuDetailsResponse: " + responseCode + " " + debugMessage); + googlePaySuccess(-1, "", sku_id, "", pay_type); + break; + // These response codes are not expected. + case BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED: + case BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED: + case BillingClient.BillingResponseCode.ITEM_NOT_OWNED: + default: + Log.d(TAG, "getGoogleSkuDetail onSkuDetailsResponse: " + responseCode + " " + debugMessage); + googlePaySuccess(-2, "", sku_id, "", pay_type); + } + } + }); + } + + /** + * 拉起Google支付 + */ + private void launchGooglePay(SkuDetails curr_skuDetails, String order_Id, String o_uid) { + // if (!billingClient.isReady()) { + // Log.e(TAG, "launchBillingFlow: BillingClient is not ready"); + // } + + BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder() + .setSkuDetails(curr_skuDetails) //商品信息 + .setObfuscatedProfileId(order_Id) //订单号 + .setObfuscatedAccountId(MD5Util.MD5Encode(o_uid, "")) //系统对应的ID + .build(); + + BillingResult billingResult = billingClient.launchBillingFlow(mActivity, billingFlowParams); + int responseCode = billingResult.getResponseCode(); + String debugMessage = billingResult.getDebugMessage(); + Log.d(TAG, "launchGooglePay: BillingResponse " + responseCode + " " + debugMessage); + } + + /** + * 查询掉单订单信息、供LUA调用 + */ + private void queryGoogleHistoryOrder() { + Log.d(TAG, "queryGoogleHistoryOrder: "); + if (billingClient != null) { + billingClient.queryPurchasesAsync(BillingClient.SkuType.INAPP, new PurchasesResponseListener() { + @Override + public void onQueryPurchasesResponse(@NonNull @NotNull BillingResult billingResult, @NonNull @NotNull List purchases) { + if (purchases != null) { + for (Purchase purchase : purchases) { + if (purchase.isAcknowledged()) + consumeProduct(purchase.getPurchaseToken()); + else { + googlePaySuccess(1, purchase.getPurchaseToken(), purchase.getSkus().get(0), purchase.getPackageName(), 2); + } + } + } + } + }); + } + } + + /** + * 处理Google支付结果 + * + * @param billingResult + * @param purchases + */ + private void onPurchasesUpdatedFun(BillingResult billingResult, List purchases, String sku_id, int pay_type) { + if (billingResult == null) { + Log.d(TAG, "onPurchasesUpdatedFun: null BillingResult"); + googlePaySuccess(-3, "", sku_id, "", pay_type); + return; + } + int responseCode = billingResult.getResponseCode(); + String debugMessage = billingResult.getDebugMessage(); + Log.d(TAG, "onPurchasesUpdatedFun: " + responseCode + " " + debugMessage); + switch (responseCode) { + case BillingClient.BillingResponseCode.OK: + if (purchases == null) { + Log.d(TAG, "onPurchasesUpdatedFun: null purchase list"); +// processPurchases(null); + googlePaySuccess(-3, "", sku_id, "", pay_type); + } else { + for (Purchase purchase : purchases) { +// handlePurchase(purchase); + googlePaySuccess(1, purchase.getPurchaseToken(), purchase.getSkus().get(0), purchase.getPackageName(), pay_type); + } + } + break; + case BillingClient.BillingResponseCode.USER_CANCELED: + Log.d(TAG, "onPurchasesUpdatedFun: User canceled the purchase"); + googlePaySuccess(-1, "", sku_id, "", pay_type); + break; + case BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED: + Log.d(TAG, "onPurchasesUpdatedFun: The user already owns this item"); + if (IsUseProductDetails()) + queryProductGoogleHistoryOrder(); + else + queryGoogleHistoryOrder(); + break; + case BillingClient.BillingResponseCode.DEVELOPER_ERROR: + googlePaySuccess(-1, "", sku_id, "", pay_type); + Log.d(TAG, "onPurchasesUpdatedFun: Developer error means that Google Play " + + "does not recognize the configuration. If you are just getting started, " + + "make sure you have configured the application correctly in the " + + "Google Play Console. The SKU product ID must match and the APK you " + + "are using must be signed with release keys." + ); + break; + default: + googlePaySuccess(-1, "", sku_id, "", pay_type); + break; + } + } + + /** + * google支付完成、回传Lua + * + * @param state 状态:-1.用户取消支付、1.支付成功、-2.支付异常、-3.不支持此设备、-4.交易未完成 + * @param purchaseToken 购买Token、服务端用于校验并进行发放道具的校验码 + * @param sku_id 商品sku_id + * @param packgeName 包名 + * @param pay_Type 操作方式:1.支付、2.查询掉单 + */ + private void googlePaySuccess(int state, String purchaseToken, String sku_id, String packgeName, int pay_Type) { + String reStr = state + "|" + " " + "|" + " " + "|" + " "; + if (state == 1) { + reStr = state + "|" + sku_id + "|" + packgeName + "|" + purchaseToken; + Log.d(TAG, "googlePaySuccess:handleSignInResult:" + reStr); + //SendToJs(EventNameUtil.Android_Pay, reStr); + EventToScriptUtil.getInstance().dispatchEventToScript(EventNameUtil.Android_Pay, reStr); + } else { + EventToScriptUtil.getInstance().dispatchEventToScript(EventNameUtil.Android_Pay, reStr); + //SendToJs(EventNameUtil.Android_Pay, reStr); +// if (pay_Type == 1) { +// String toast_msg = ""; +// if (state == -1) +// toast_msg = "Kullanıcı ödemeyi iptal etti";//用户取消支付 +// else if (state == -2) +// toast_msg = "Ödeme istisnası";//支付异常 +// else if (state == -3) +// toast_msg = "Bu cihaz desteklenmiyor";//不支持此设备 +// else +// toast_msg = "İşlem tamamlanmadı";//交易未完成 +// Toast.makeText(mActivity, toast_msg, Toast.LENGTH_SHORT).show(); +// } + } + } + + /** + * 消耗商品 + * + * @param purchaseToken 商品token + */ + private void consumeProduct(String purchaseToken) { + Log.d(TAG, "consumeProduct:::" + purchaseToken); + if (billingClient != null && billingClient.isReady()) { + ConsumeParams consumeParams = + ConsumeParams.newBuilder() + .setPurchaseToken(purchaseToken) + .build(); + ConsumeResponseListener listener = new ConsumeResponseListener() { + @Override + public void onConsumeResponse(BillingResult billingResult, String purchaseToken) { + if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) { + // Handle the success of the consume operation. + } + } + }; + billingClient.consumeAsync(consumeParams, listener); + } + } + //#endregion + + //#region 6.0支付方法 + + /** + * 判断ProductDetails是否可用、不可用请使用SkuDetails + * + * @return + */ + private boolean IsUseProductDetails() { + //在极少数情况下,某些设备无法支持 ProductDetails 和 queryProductDetailsAsync(),这通常是因为 Google Play 服务版本已过时。 + //为了确保对此场景提供适当的支持,请先针对 PRODUCT_DETAILS 功能调用 isFeatureSupported(),然后再调用 queryProductDetailsAsync。 + //如果响应为 OK,表示设备支持该功能,您可以继续调用 queryProductDetailsAsync()。 + //如果响应为 FEATURE_NOT_SUPPORTED,您可以改用 querySkuDetailsAsync() 请求可用的向后兼容产品列表。 + //如需详细了解如何使用向后兼容功能,请参阅 2022 年 5 月的订阅功能指南。 + BillingResult _billingResult = billingClient.isFeatureSupported(BillingClient.FeatureType.PRODUCT_DETAILS); + return _billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK; + } + + /** + * 获取Google待支付商品信息 + */ + private void getGoogleProductDetail(String product_id, String order_Id, int pay_type, String o_uid) { + // 检查是否在缓存中 + if (productDetailsMap != null && productDetailsMap.containsKey(product_id)) { + ProductDetails curr_productDetails = productDetailsMap.get(product_id); + Log.d(TAG, "getGoogleProductDetail productDetailsMap => curr_skuDetails: ..." + curr_productDetails); + if (curr_productDetails != null) { + launchProductGooglePay(curr_productDetails, order_Id, o_uid); + } + return; + } + + List productList = new ArrayList<>(); + productList.add(QueryProductDetailsParams.Product.newBuilder() + .setProductId(product_id) + .setProductType(BillingClient.ProductType.INAPP) + .build()); + QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder() + .setProductList(productList) + .build(); + + billingClient.queryProductDetailsAsync( + params, + new ProductDetailsResponseListener() { + public void onProductDetailsResponse(BillingResult billingResult, List productDetailsList) { + // Process the result + Log.d(TAG, "getGoogleProductDetail onProductDetailsResponse:" + billingResult + ";productDetailsList:" + productDetailsList); + if (billingResult == null) { + Log.d(TAG, "getGoogleProductDetail onProductDetailsResponse: billingResult ==== null"); + return; + } + + int responseCode = billingResult.getResponseCode(); + String debugMessage = billingResult.getDebugMessage(); + switch (responseCode) { + case BillingClient.BillingResponseCode.OK: + if (productDetailsList == null) { + Log.d(TAG, "getGoogleProductDetail onProductDetailsResponse: productDetailsList ==== null"); + } else { + ProductDetails curr_productDetails = null; + for (ProductDetails productDetails : productDetailsList) { + if (null != productDetails) { + curr_productDetails = productDetails; + if (!productDetailsMap.containsKey(productDetails.getProductId())) { + productDetailsMap.put(productDetails.getProductId(), productDetails); + } + } + } + if (curr_productDetails != null) { + Log.d(TAG, "getGoogleProductDetail onProductDetailsResponse => curr_productDetails: ..." + curr_productDetails); + launchProductGooglePay(curr_productDetails, order_Id, o_uid); + } + } + break; + case BillingClient.BillingResponseCode.SERVICE_DISCONNECTED: + case BillingClient.BillingResponseCode.SERVICE_UNAVAILABLE: + case BillingClient.BillingResponseCode.BILLING_UNAVAILABLE: + case BillingClient.BillingResponseCode.ITEM_UNAVAILABLE: + case BillingClient.BillingResponseCode.DEVELOPER_ERROR: + case BillingClient.BillingResponseCode.ERROR: + Log.d(TAG, "getGoogleProductDetail onProductDetailsResponse: " + responseCode + " " + debugMessage); + googlePaySuccess(-3, "", product_id, "", pay_type); + break; + case BillingClient.BillingResponseCode.USER_CANCELED: + Log.d(TAG, "getGoogleProductDetail onProductDetailsResponse: " + responseCode + " " + debugMessage); + googlePaySuccess(-1, "", product_id, "", pay_type); + break; + // These response codes are not expected. + case BillingClient.BillingResponseCode.FEATURE_NOT_SUPPORTED: + case BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED: + case BillingClient.BillingResponseCode.ITEM_NOT_OWNED: + default: + Log.d(TAG, "getGoogleProductDetail onProductDetailsResponse: " + responseCode + " " + debugMessage); + googlePaySuccess(-2, "", product_id, "", pay_type); + } + } + } + ); + } + + /** + * 拉起Google支付 + */ + private void launchProductGooglePay(ProductDetails curr_productDetails, String order_Id, String o_uid) { + //if (!billingClient.isReady()) { + // Log.e(TAG, "launchBillingFlow: BillingClient is not ready"); + //} + + // 获取优惠信息 + // Retrieve a value for "productDetails" by calling queryProductDetailsAsync() + // Get the offerToken of the selected offer + // String offerToken = curr_productDetails + // .getSubscriptionOfferDetails() + // .get(selectedOfferIndex) + // .getOfferToken(); + + // 支付信息组合 + // Set the parameters for the offer that will be presented + // in the billing flow creating separate productDetailsParamsList variable + List productDetailsParamsList = new ArrayList<>(); + productDetailsParamsList.add( + BillingFlowParams.ProductDetailsParams.newBuilder() + .setProductDetails(curr_productDetails) + // .setOfferToken(offerToken) //优惠信息 + .build() + ); + BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder() + .setProductDetailsParamsList(productDetailsParamsList) + .setObfuscatedProfileId(order_Id) //订单号 + .setObfuscatedAccountId(MD5Util.MD5Encode(o_uid, "")) //系统对应的ID + .build(); + + // Launch the billing flow 拉起支付 + BillingResult billingResult = billingClient.launchBillingFlow(mActivity, billingFlowParams); + int responseCode = billingResult.getResponseCode(); + String debugMessage = billingResult.getDebugMessage(); + Log.d(TAG, "launchProductGooglePay: BillingResponse " + responseCode + " " + debugMessage); + } + + /** + * 查询掉单订单信息、供LUA调用 + */ + private void queryProductGoogleHistoryOrder() { + Log.d(TAG, "queryProductGoogleHistoryOrder: "); + if (billingClient != null) { + billingClient.queryPurchasesAsync( + QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.INAPP).build(), + new PurchasesResponseListener() { + public void onQueryPurchasesResponse( + BillingResult billingResult, + List purchases) { + // Process the result + for (Purchase purchase : purchases) { + if (purchase.isAcknowledged()) + consumeProduct(purchase.getPurchaseToken()); + else { + googlePaySuccess(1, purchase.getPurchaseToken(), purchase.getProducts().get(0), purchase.getPackageName(), 2); + } + } + } + } + ); + } + } + //#endregion + +// private void SendToJs(String eventName, String params) { +//// mActivity.runOnGLThread(new Runnable() { +//// @Override +//// public void run() { +//// Log.d(TAG, "SendToJs: " + eventName + " " + params); +//// GetJsbBridgeWrapper().dispatchEventToScript(eventName, params); +//// } +//// }); +// try { +// Log.d(TAG, "SendToJs: " + eventName + " " + params); +// GetJsbBridgeWrapper().dispatchEventToScript(eventName, params); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } +} diff --git a/android/app/src/com/cocos/game/ImageLoaderUtils.java b/android/app/src/com/cocos/game/ImageLoaderUtils.java new file mode 100644 index 0000000..ae947ce --- /dev/null +++ b/android/app/src/com/cocos/game/ImageLoaderUtils.java @@ -0,0 +1,28 @@ +package com.cocos.game; + +import android.app.Activity; +import android.content.Context; +import android.content.ContextWrapper; + +public class ImageLoaderUtils { + public static boolean assertValidRequest(Context context) { + if (context instanceof Activity) { + Activity activity = (Activity) context; + return !isDestroy(activity); + } else if (context instanceof ContextWrapper) { + ContextWrapper contextWrapper = (ContextWrapper) context; + if (contextWrapper.getBaseContext() instanceof Activity) { + Activity activity = (Activity) contextWrapper.getBaseContext(); + return !isDestroy(activity); + } + } + return true; + } + + private static boolean isDestroy(Activity activity) { + if (activity == null) { + return true; + } + return activity.isFinishing() || activity.isDestroyed(); + } +} diff --git a/android/app/src/com/cocos/game/JSCallJava.java b/android/app/src/com/cocos/game/JSCallJava.java new file mode 100644 index 0000000..2ce7597 --- /dev/null +++ b/android/app/src/com/cocos/game/JSCallJava.java @@ -0,0 +1,268 @@ +package com.cocos.game; + +import android.Manifest; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.os.Build; +import android.os.VibrationEffect; +import android.os.Vibrator; +import android.os.VibratorManager; +import android.provider.Settings; +import android.telephony.TelephonyManager; +import android.util.Log; + +import com.adjust.sdk.Adjust; +import com.adjust.sdk.AdjustEvent; + +import java.io.BufferedReader; +import java.io.InputStreamReader; + +public class JSCallJava { + private static final String TAG = "JSCallJava"; + + public static void setOrientationPortrait() { + AppActivity activity = AppActivity.nAppActivity; + if (activity == null) return; + activity.runOnUiThread(() -> + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) + ); + } + + public static void setOrientationLandscape() { + AppActivity activity = AppActivity.nAppActivity; + if (activity == null) return; + activity.runOnUiThread(() -> + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) + ); + } + + public static void setClipboardData(String data) { + AppActivity activity = AppActivity.nAppActivity; + if (activity == null) return; + activity.runOnUiThread(() -> { + ClipboardManager cm = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE); + if (cm != null) { + cm.setPrimaryClip(ClipData.newPlainText("Label", data)); + } + }); + } + + public static String GetAppVersion() { + AppActivity activity = AppActivity.nAppActivity; + if (activity == null) return ""; + try { + return activity.getPackageManager() + .getPackageInfo(activity.getPackageName(), 0).versionName; + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } + + public static String GetAndroidId() { + AppActivity activity = AppActivity.nAppActivity; + if (activity == null) return ""; + return Settings.Secure.getString(activity.getContentResolver(), Settings.Secure.ANDROID_ID); + } + + public static long GetOperatorsID() { + AppActivity activity = AppActivity.nAppActivity; + if (activity == null) return 0; + try { + TelephonyManager tm = (TelephonyManager) activity.getSystemService(Context.TELEPHONY_SERVICE); + if (tm != null) { + String numeric = tm.getNetworkOperator(); + if (numeric != null && !numeric.isEmpty()) { + return Long.parseLong(numeric); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return 0; + } + + public static void SendADJustEvent(String eventToken) { + try { + AdjustEvent event = new AdjustEvent(eventToken); + Adjust.trackEvent(event); + Log.d(TAG, "SendADJustEvent: " + eventToken); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static String HasVpnTransport() { + AppActivity activity = AppActivity.nAppActivity; + if (activity == null) return "0"; + try { + ConnectivityManager cm = (ConnectivityManager) activity.getSystemService(Context.CONNECTIVITY_SERVICE); + if (cm != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + Network activeNetwork = cm.getActiveNetwork(); + if (activeNetwork != null) { + NetworkCapabilities caps = cm.getNetworkCapabilities(activeNetwork); + if (caps != null && caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) { + return "1"; + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return "0"; + } + + public static String getADId() { + try { + String adid = Adjust.getAdid(); + return adid != null ? adid : ""; + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } + + public static void ShakePhoneOnece(int duration) { + AppActivity activity = AppActivity.nAppActivity; + if (activity == null) return; + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + VibratorManager vm = (VibratorManager) activity.getSystemService(Context.VIBRATOR_MANAGER_SERVICE); + if (vm != null) { + vm.getDefaultVibrator().vibrate( + VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE)); + } + } else { + Vibrator vib = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE); + if (vib != null && vib.hasVibrator()) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + vib.vibrate(VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE)); + } else { + vib.vibrate(duration); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static String GetSysInfo(int sysState) { + AppActivity activity = AppActivity.nAppActivity; + if (activity == null) return ""; + try { + switch (sysState) { + case 0: // SYSSTATE_MACADDRESS + return getMacAddress(activity); + case 1: // SYSSTATE_BIOSID (Android ID) + return GetAndroidId(); + case 2: // SYSSTATE_CUPID (CPU hardware) + return getCpuInfo(); + case 3: // SYSSTATE_HDID (Device ID) + return getDeviceId(activity); + case 4: // SYSSTATE_VERSION + return GetAppVersion(); + case 5: // SYSSTATE_MODEL + return Build.MODEL; + case 6: // SYSSTATE_UUID + return GetAndroidId(); + case 7: // SYSSTATE_OAID + return getADId(); + default: + return ""; + } + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } + + public static String GetChannel() { + return "liuhaotest"; + } + + /** + * 从本地选择图片 + * tempDir: 保存目录 + * fileName: 文件名 + * writePath: 可写根目录 + * callbackEvent: 回调事件名 + */ + public static void AdChooseImage(String tempDir, String fileName, String writePath, String callbackEvent) { + AppActivity activity = AppActivity.nAppActivity; + if (activity == null) return; + Log.d(TAG, "AdChooseImage tempDir=" + tempDir + " fileName=" + fileName); + activity.runOnUiThread(() -> { + AppActivity.selectImageIndex = 0; + AppActivity.ChooseImageDialog(); + }); + } + + // ------------------------------------------------------------------------- + // Private helpers + // ------------------------------------------------------------------------- + + private static String getMacAddress(Context context) { + try { + WifiManager wifi = (WifiManager) context.getApplicationContext() + .getSystemService(Context.WIFI_SERVICE); + if (wifi != null) { + WifiInfo info = wifi.getConnectionInfo(); + return info.getMacAddress(); + } + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } + + private static String getDeviceId(Context context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + return Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); + } + try { + TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (context.checkSelfPermission(Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) { + return Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); + } + } + if (tm != null && tm.getDeviceId() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + return tm.getImei(); + } else { + return tm.getDeviceId(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); + } + + private static String getCpuInfo() { + try { + Process process = Runtime.getRuntime().exec("cat /proc/cpuinfo"); + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String line; + while ((line = reader.readLine()) != null) { + if (line.startsWith("Hardware")) { + String[] parts = line.split(":"); + if (parts.length > 1) return parts[1].trim(); + } + } + reader.close(); + } catch (Exception e) { + e.printStackTrace(); + } + return Build.HARDWARE; + } +} diff --git a/android/app/src/com/cocos/game/MD5Util.java b/android/app/src/com/cocos/game/MD5Util.java new file mode 100644 index 0000000..764b306 --- /dev/null +++ b/android/app/src/com/cocos/game/MD5Util.java @@ -0,0 +1,43 @@ +package com.cocos.game; + +import java.security.MessageDigest; + +public class MD5Util { + + private static String byteArrayToHexString(byte b[]) { + StringBuffer resultSb = new StringBuffer(); + for (int i = 0; i < b.length; i++) + resultSb.append(byteToHexString(b[i])); + + return resultSb.toString(); + } + + private static String byteToHexString(byte b) { + int n = b; + if (n < 0) + n += 256; + int d1 = n / 16; + int d2 = n % 16; + return hexDigits[d1] + hexDigits[d2]; + } + + public static String MD5Encode(String origin, String charsetname) { + String resultString = null; + try { + resultString = new String(origin); + MessageDigest md = MessageDigest.getInstance("MD5"); + if (charsetname == null || "".equals(charsetname)) + resultString = byteArrayToHexString(md.digest(resultString + .getBytes())); + else + resultString = byteArrayToHexString(md.digest(resultString + .getBytes(charsetname))); + } catch (Exception exception) { + } + return resultString; + } + + private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5", + "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"}; + +} \ No newline at end of file diff --git a/android/app/src/com/cocos/game/MyApplication.java b/android/app/src/com/cocos/game/MyApplication.java new file mode 100644 index 0000000..c5d0827 --- /dev/null +++ b/android/app/src/com/cocos/game/MyApplication.java @@ -0,0 +1,85 @@ +package com.cocos.game; + +import android.app.Activity; +import android.app.Application; +import android.os.Bundle; +import android.util.Log; + +import com.adjust.sdk.Adjust; +import com.adjust.sdk.AdjustAttribution; +import com.adjust.sdk.AdjustConfig; +import com.adjust.sdk.LogLevel; +import com.adjust.sdk.OnAttributionChangedListener; +import com.ddddd.one.R; + +public class MyApplication extends Application { + private static final String TAG = "MyApplication"; + + @Override + public void onCreate() { + super.onCreate(); + + // Configure adjust SDK. + String appToken = getString(R.string.Adjust_AppToken); + String environment = AdjustConfig.ENVIRONMENT_PRODUCTION; + AdjustConfig config = new AdjustConfig(this, appToken, environment); + + // Change the log level. 如果希望禁用所有日志,请将日志级别设为 suppress: + config.setLogLevel(LogLevel.VERBOSE); + + // Set attribution delegate. + config.setOnAttributionChangedListener(new OnAttributionChangedListener() { + @Override + public void onAttributionChanged(AdjustAttribution attribution) { + //Log.d("cocos1", "Attribution callback called!"); + Log.d(TAG, "Attribution: " + attribution.toString()); + if (null != attribution) { + String clickLabel = null == attribution.clickLabel ? "" : attribution.clickLabel; + String trackerName = null == attribution.trackerName ? "" : attribution.trackerName; + String network = null == attribution.network ? "" : attribution.network; + String trackerToken = null == attribution.trackerToken ? "" : attribution.trackerToken; + String adjustTT = trackerToken + "@" + trackerName + "@" + network + "@" + clickLabel; + + EventToScriptUtil.getInstance().appCallBack(adjustTT); + } + } + }); + + // Initialise the adjust SDK. + Adjust.onCreate(config); + registerActivityLifecycleCallbacks(new AdjustLifecycleCallbacks()); + } + + // You can use this class if your app is for Android 4.0 or higher + private static final class AdjustLifecycleCallbacks implements ActivityLifecycleCallbacks { + @Override + public void onActivityResumed(Activity activity) { + Adjust.onResume(); + } + + @Override + public void onActivityPaused(Activity activity) { + Adjust.onPause(); + } + + @Override + public void onActivityStopped(Activity activity) { + } + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle outState) { + } + + @Override + public void onActivityDestroyed(Activity activity) { + } + + @Override + public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + } + + @Override + public void onActivityStarted(Activity activity) { + } + } +} diff --git a/android/app/src/com/cocos/game/MyFirebaseMessagingService.java b/android/app/src/com/cocos/game/MyFirebaseMessagingService.java new file mode 100644 index 0000000..961fbfe --- /dev/null +++ b/android/app/src/com/cocos/game/MyFirebaseMessagingService.java @@ -0,0 +1,198 @@ + + +/** + * Copyright 2016 Google Inc. All Rights Reserved. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.cocos.game; + +//import android.app.NotificationChannel; +//import android.app.NotificationManager; +//import android.app.PendingIntent; +//import android.content.Context; +//import android.content.Intent; +//import android.media.RingtoneManager; +//import android.net.Uri; +//import android.os.Build; +//import androidx.core.app.NotificationCompat; + +import android.util.Log; + +import com.adjust.sdk.Adjust; +import com.google.android.gms.common.GoogleApiAvailability; +import com.google.firebase.messaging.FirebaseMessagingService; +import com.google.firebase.messaging.RemoteMessage; +//import com.google.firebase.quickstart.fcm.R; +// +//import androidx.work.OneTimeWorkRequest; +//import androidx.work.WorkManager; + +/** + * NOTE: There can only be one service in each app that receives FCM messages. If multiple + * are declared in the Manifest then the first one will be chosen. + *

+ * In order to make this Java sample functional, you must remove the following from the Kotlin messaging + * service in the AndroidManifest.xml: + *

+ * + * + * + */ +public class MyFirebaseMessagingService extends FirebaseMessagingService { + + private static final String TAG = "MyFirebaseMsgService"; + + /** + * Called when message is received. + * + * @param remoteMessage Object representing the message received from Firebase Cloud Messaging. + */ + // [START receive_message] + @Override + public void onMessageReceived(RemoteMessage remoteMessage) { + // [START_EXCLUDE] + // There are two types of messages data messages and notification messages. Data messages + // are handled + // here in onMessageReceived whether the app is in the foreground or background. Data + // messages are the type + // traditionally used with GCM. Notification messages are only received here in + // onMessageReceived when the app + // is in the foreground. When the app is in the background an automatically generated + // notification is displayed. + // When the user taps on the notification they are returned to the app. Messages + // containing both notification + // and data payloads are treated as notification messages. The Firebase console always + // sends notification + // messages. For more see: https://firebase.google.com/docs/cloud-messaging/concept-options + // [END_EXCLUDE] + + // TODO(developer): Handle FCM messages here. + // Not getting messages here? See why this may be: https://goo.gl/39bRNJ + Log.d(TAG, "From: " + remoteMessage.getFrom()); + +// // Check if message contains a data payload. +// if (remoteMessage.getData().size() > 0) { +// Log.d(TAG, "Message data payload: " + remoteMessage.getData()); +// +// if (/* Check if data needs to be processed by long running job */ true) { +// // For long-running tasks (10 seconds or more) use WorkManager. +// scheduleJob(); +// } else { +// // Handle message within 10 seconds +// handleNow(); +// } +// +// } + + // Check if message contains a notification payload. + if (remoteMessage.getNotification() != null) { + Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody()); + } + + // Also if you intend on generating your own notifications as a result of a received FCM + // message, here is where that should be initiated. See sendNotification method below. + } + // [END receive_message] + + + // [START on_new_token] + + /** + * There are two scenarios when onNewToken is called: + * 1) When a new token is generated on initial app startup + * 2) Whenever an existing token is changed + * Under #2, there are three scenarios when the existing token is changed: + * A) App is restored to a new device + * B) User uninstalls/reinstalls the app + * C) User clears app data + */ + @Override + public void onNewToken(String token) { + Log.d(TAG, "Refreshed token: " + token); + Adjust.setPushToken(token, getApplicationContext()); + + // If you want to send messages to this application instance or + // manage this apps subscriptions on the server side, send the + // FCM registration token to your app server. +// sendRegistrationToServer(token); + + } + // [END on_new_token] + +// /** +// * Schedule async work using WorkManager. +// */ +// private void scheduleJob() { +// // [START dispatch_job] +// OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(MyWorker.class) +// .build(); +// WorkManager.getInstance(this).beginWith(work).enqueue(); +// // [END dispatch_job] +// } + + /** + * Handle time allotted to BroadcastReceivers. + */ + private void handleNow() { + Log.d(TAG, "Short lived task is done."); + } + + /** + * Persist token to third-party servers. + *

+ * Modify this method to associate the user's FCM registration token with any + * server-side account maintained by your application. + * + * @param token The new token. + */ + private void sendRegistrationToServer(String token) { + // TODO: Implement this method to send token to your app server. + } + +// /** +// * Create and show a simple notification containing the received FCM message. +// * +// * @param messageBody FCM message body received. +// */ +// private void sendNotification(String messageBody) { +// Intent intent = new Intent(this, MainActivity.class); +// intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); +// PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent, +// PendingIntent.FLAG_ONE_SHOT); +// +// String channelId = getString(R.string.default_notification_channel_id); +// Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); +// NotificationCompat.Builder notificationBuilder = +// new NotificationCompat.Builder(this, channelId) +// .setSmallIcon(R.drawable.ic_stat_ic_notification) +// .setContentTitle(getString(R.string.fcm_message)) +// .setContentText(messageBody) +// .setAutoCancel(true) +// .setSound(defaultSoundUri) +// .setContentIntent(pendingIntent); +// +// NotificationManager notificationManager = +// (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); +// +// // Since android Oreo notification channel is needed. +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { +// NotificationChannel channel = new NotificationChannel(channelId, +// "Channel human readable title", +// NotificationManager.IMPORTANCE_DEFAULT); +// notificationManager.createNotificationChannel(channel); +// } +// +// notificationManager.notify(0 /* ID of notification */, notificationBuilder.build()); +// } +} \ No newline at end of file diff --git a/android/app/src/com/cocos/game/PermissionsUtil.java b/android/app/src/com/cocos/game/PermissionsUtil.java new file mode 100644 index 0000000..7be7ff4 --- /dev/null +++ b/android/app/src/com/cocos/game/PermissionsUtil.java @@ -0,0 +1,547 @@ +package com.cocos.game; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.Fragment; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +/** + * 动态申请权限工具类 + */ + +public class PermissionsUtil { + + private PermissionsUtil() { + } + + /** + * 危险权限,targetSdkVersion >=23必须动态申请 + */ + public static final class Permission { + private static final List sCalendars = new ArrayList(2); + private static final List sCameras = new ArrayList(1); + private static final List sContactes = new ArrayList(3); + private static final List sLocations = new ArrayList(2); + private static final List sMicrophones = new ArrayList(1); + private static final List sPhones = new ArrayList(7); + private static final List sSensorses = new ArrayList(1); + private static final List sSmses = new ArrayList(5); + private static final List sStorages = new ArrayList(2); + + static { + //Calendar + sCalendars.add(Calendar.READ_CALENDAR); + sCalendars.add(Calendar.WRITE_CALENDAR); + //Camera + sCameras.add(Camera.CAMERA); + //Contacts + sContactes.add(Contacts.WRITE_CONTACTS); + sContactes.add(Contacts.READ_CONTACTS); + sContactes.add(Contacts.GET_ACCOUNTS); + //Location + sLocations.add(Location.ACCESS_FINE_LOCATION); + sLocations.add(Location.ACCESS_COARSE_LOCATION); + //Microphone + sMicrophones.add(Microphone.RECORD_AUDIO); + //Phone + sPhones.add(Phone.READ_PHONE_STATE); + sPhones.add(Phone.CALL_PHONE); + sPhones.add(Phone.READ_CALL_LOG); + sPhones.add(Phone.WRITE_CALL_LOG); + sPhones.add(Phone.ADD_VOICEMAIL); + sPhones.add(Phone.USE_SIP); + sPhones.add(Phone.PROCESS_OUTGOING_CALLS); + //Sensors + sSensorses.add(Sensors.BODY_SENSORS); + //Sms + sSmses.add(Sms.SEND_SMS); + sSmses.add(Sms.RECEIVE_SMS); + sSmses.add(Sms.READ_SMS); + sSmses.add(Sms.RECEIVE_WAP_PUSH); + sSmses.add(Sms.RECEIVE_MMS); + //Storage + sStorages.add(Storage.READ_EXTERNAL_STORAGE); + sStorages.add(Storage.WRITE_EXTERNAL_STORAGE); + } + + public static final class Calendar { + public static final String READ_CALENDAR = Manifest.permission.READ_CALENDAR; + public static final String WRITE_CALENDAR = Manifest.permission.WRITE_CALENDAR; + private static final String MSG = "日历"; + } + + public static final class Camera { + public static final String CAMERA = Manifest.permission.CAMERA; + private static final String MSG = "kamera";//相机 + } + + public static final class Contacts { + public static final String READ_CONTACTS = Manifest.permission.READ_CONTACTS; + public static final String WRITE_CONTACTS = Manifest.permission.WRITE_CONTACTS; + public static final String GET_ACCOUNTS = Manifest.permission.GET_ACCOUNTS; + private static final String MSG = "联系人"; + } + + public static final class Location { + public static final String ACCESS_FINE_LOCATION = Manifest.permission.ACCESS_FINE_LOCATION; + public static final String ACCESS_COARSE_LOCATION = Manifest.permission.ACCESS_COARSE_LOCATION; + private static final String MSG = "定位"; + } + + public static final class Microphone { + public static final String RECORD_AUDIO = Manifest.permission.RECORD_AUDIO; + private static final String MSG = "mikrofon";//麦克风 + } + + public static final class Phone { + public static final String READ_PHONE_STATE = Manifest.permission.READ_PHONE_STATE; + public static final String CALL_PHONE = Manifest.permission.CALL_PHONE; + public static final String READ_CALL_LOG = Manifest.permission.READ_CALL_LOG; + public static final String WRITE_CALL_LOG = Manifest.permission.WRITE_CALL_LOG; + public static final String ADD_VOICEMAIL = Manifest.permission.ADD_VOICEMAIL; + public static final String USE_SIP = Manifest.permission.USE_SIP; + public static final String PROCESS_OUTGOING_CALLS = Manifest.permission.PROCESS_OUTGOING_CALLS; + private static final String MSG = "电话"; + } + + public static final class Sensors { + public static final String BODY_SENSORS = Manifest.permission.BODY_SENSORS; + private static final String MSG = "传感器"; + } + + + public static final class Sms { + public static final String SEND_SMS = Manifest.permission.SEND_SMS; + public static final String RECEIVE_SMS = Manifest.permission.RECEIVE_SMS; + public static final String READ_SMS = Manifest.permission.READ_SMS; + public static final String RECEIVE_WAP_PUSH = Manifest.permission.RECEIVE_WAP_PUSH; + public static final String RECEIVE_MMS = Manifest.permission.RECEIVE_MMS; + private static final String MSG = "短信"; + } + + public static final class Storage { + public static final String READ_EXTERNAL_STORAGE = Manifest.permission.READ_EXTERNAL_STORAGE; + public static final String WRITE_EXTERNAL_STORAGE = Manifest.permission.WRITE_EXTERNAL_STORAGE; + public static final String CAMERA = Manifest.permission.CAMERA; + private static final String MSG = "Hafıza";//存储 + } + } + + public static final int SETTINGS_REQ_CODE = 99; + private static final String TAG = "PermissionsUtil"; + private static final String KEY_DENIED_PERMISSIONS = "deniedPermissions"; + private static final String KEY_REQUEST_CODE = "requestCode"; + private static final int MSG_UI_HANDLER_DEAL_DENIED_PERMISSION = 100; + private static boolean sDebug = true; + private Object mObject; + private String[] mPermissions; + private int mRequestCode; + private String mRationaleTitle; + private String mPositiveText = "Emin ol";//确定 + private String mNegativeText = "İptal";//取消 + private int mRequestPermissionCount = 0;//请求授权次数 + private Handler mUiHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_UI_HANDLER_DEAL_DENIED_PERMISSION: + Bundle data = msg.getData(); + int requestCode = data.getInt(KEY_REQUEST_CODE); + String[] permissions = data.getStringArray(KEY_DENIED_PERMISSIONS); + Object object = msg.obj; + showAlertDialog(object, requestCode, permissions); + break; + } + } + }; + + + private PermissionsUtil(@NonNull Object object) { + if (isObjectIllegality(object)) + throw new IllegalArgumentException("Activity or Fragment must implements IPermissionsCallback"); + mObject = object; + } + + public static PermissionsUtil with(@NonNull Activity activity) { + return new PermissionsUtil(activity); + } + + public static PermissionsUtil with(@NonNull Fragment fragment) { + return new PermissionsUtil(fragment); + } + + public static PermissionsUtil with(@NonNull android.app.Fragment fragment) { + return new PermissionsUtil(fragment); + } + + public PermissionsUtil permissions(@NonNull String... permissions) { + if (hasEmpty(permissions)) + throw new IllegalArgumentException("permissions can't contain null"); + mPermissions = permissions; + return this; + } + + public PermissionsUtil requestCode(int requestCode) { + mRequestCode = requestCode; + return this; + } + + public PermissionsUtil rationaleTitle(String title) { + mRationaleTitle = title; + return this; + } + + public PermissionsUtil positiveText(String positiveText) { + mPositiveText = positiveText; + return this; + } + + public PermissionsUtil negativeText(String negativeText) { + mNegativeText = negativeText; + return this; + } + + public PermissionsUtil isDebug(boolean isDebug) { + sDebug = isDebug; + return this; + } + + public PermissionsUtil requestPermissionCount(int requestPermissionCount) { + mRequestPermissionCount = requestPermissionCount; + return this; + } + + public PermissionsUtil request() { + request(mObject, mRequestCode, mPermissions); + return this; + } + + public void request(Object object, int requestCode, String... permissions) { + if (needRequest() && notGrantedAllPermissions(getActivity(object), permissions)) { + if (mRequestPermissionCount > 2) { + dealDeniedPermissions(object, requestCode, permissions); + } else { + List unGrantedPermissionsList = createUnGrantedPermissionsList(object, permissions); + PLog.d("request---" + "requestCode : " + requestCode + "---unGrantedPermissionsList : " + unGrantedPermissionsList); + if (unGrantedPermissionsList.size() > 0) { + requestPermissions(object, requestCode, listToStringArray(unGrantedPermissionsList)); + unGrantedPermissionsList.clear(); + } else { + invokePermissionsGranted(object, requestCode, permissions); + } + unGrantedPermissionsList = null; + } + } else { + PLog.d("request---" + "requestCode : " + requestCode + "---permissionsGranted : " + stringArrayToList(permissions)); + invokePermissionsGranted(object, requestCode, permissions); + } + } + + private List createUnGrantedPermissionsList(Object object, String... permissions) { + List unGrantedPermissionsList = new ArrayList(); + for (String permission : permissions) { + if (notGrantedPermission(getActivity(object), permission)) { + unGrantedPermissionsList.add(permission); + } + } + return unGrantedPermissionsList; + } + + + @SuppressLint("NewApi") + private void requestPermissions(Object object, int requestCode, String... permissions) { + PLog.d("requestPermissions---" + "requestCode : " + requestCode + "---requestPermissions : " + stringArrayToList(permissions)); + if (object instanceof Activity) { + ActivityCompat.requestPermissions((Activity) object, permissions, requestCode); + } else if (object instanceof Fragment) { + ((Fragment) object).requestPermissions(permissions, requestCode); + } else if (object instanceof android.app.Fragment) { + ((android.app.Fragment) object).requestPermissions(permissions, requestCode); + } + } + + /** + * @param object + * @param requestCode + * @param deniedPermissions denied permissions + * @return + */ + private void showAlertDialog(final Object object, final int requestCode, final String... deniedPermissions) { + PLog.d("showAlertDialog --- " + "requestCode : " + requestCode + "--- deniedPermissions : " + stringArrayToList(deniedPermissions)); + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(mObject)); + if (!TextUtils.isEmpty(mRationaleTitle)) { + builder.setTitle(mRationaleTitle); + } + builder.setMessage(createRationaleMsg(getActivity(object), deniedPermissions)) + .setPositiveButton(mPositiveText, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + goSetting(object); + } + + + }) + .setNegativeButton(mNegativeText, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + invokePermissionsDenied(object, requestCode, deniedPermissions); + } + }) + .create() + .show(); + } + + private boolean needRequest() { + return Build.VERSION.SDK_INT >= 23; + } + + private Activity getActivity(Object object) { + Activity activity = null; + if (object instanceof Activity) { + activity = (Activity) object; + } else if (object instanceof Fragment) { + activity = ((Fragment) object).getActivity(); + } else if (object instanceof android.app.Fragment) { + activity = ((android.app.Fragment) object).getActivity(); + } + return activity; + } + + public boolean grantedPermission(Activity activity, String permission) { + return ContextCompat.checkSelfPermission(activity, permission) == PackageManager.PERMISSION_GRANTED; + } + + public boolean notGrantedPermission(Activity activity, String permission) { + return !grantedPermission(activity, permission); + } + + public boolean grantedAllPermissions(Activity activity, String... permissions) { + for (String permission : permissions) { + if (notGrantedPermission(activity, permission)) { + return false; + } + } + return true; + } + + public boolean notGrantedAllPermissions(Activity activity, String... permissions) { + return !grantedAllPermissions(activity, permissions); + } + + private void dealDeniedPermissions(Object object, int requestCode, String... deniedPermissions) { + PLog.d("dealDeniedPermissions --- " + "requestCode : " + requestCode + "--- deniedPermissions : " + stringArrayToList(deniedPermissions)); + Message message = mUiHandler.obtainMessage(); + Bundle bundle = new Bundle(); + bundle.putStringArray(KEY_DENIED_PERMISSIONS, deniedPermissions); + bundle.putInt(KEY_REQUEST_CODE, requestCode); + message.setData(bundle); + message.obj = object; + message.what = MSG_UI_HANDLER_DEAL_DENIED_PERMISSION; + mUiHandler.sendMessage(message); + } + + private boolean isObjectLegal(Object object) { + return (object instanceof IPermissionsCallback); + } + + private boolean isObjectIllegality(Object object) { + return !isObjectLegal(object); + } + + private boolean hasEmpty(String... strings) { + boolean hasEmpty = false; + if (strings != null && strings.length > 0) { + for (String s : strings) { + if (TextUtils.isEmpty(s)) { + hasEmpty = true; + break; + } + } + } else { + hasEmpty = true; + } + return hasEmpty; + } + + private String[] listToStringArray(List stringList) { + return stringList.toArray(new String[stringList.size()]); + } + + private List stringArrayToList(String[] strings) { + return Arrays.asList(strings); + } + + private String getAppName(Context context) { + String appName = ""; + PackageManager packageManager = context.getPackageManager(); + try { + ApplicationInfo applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), 0); + appName = (String) packageManager.getApplicationLabel(applicationInfo); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return appName; + } + + private String createRationaleMsg(Context context, String... permissions) { + String permissionsMsg = createPermissionsMsg(permissions); + permissionsMsg = permissionsMsg.substring(0, permissionsMsg.length() - 1); + // return getAppName(context) + "需要" + permissionsMsg + "权限,是否去设置"; + return getAppName(context) + permissionsMsg + " iznine ihtiyacınız var,ayarlanıp ayarlanmayacağı?"; + } + + private String createPermissionsMsg(String... permissions) { + StringBuilder builder = new StringBuilder(); + boolean grantedCalendar = false; + boolean grantedCamera = false; + boolean grantedContacts = false; + boolean grantedLocation = false; + boolean grantedMicrophone = false; + boolean grantedPhone = false; + boolean grantedSensors = false; + boolean grantedSms = false; + boolean grantedStorage = false; + for (String permission : permissions) { + if (!grantedCalendar && Permission.sCalendars.contains(permission)) { + builder.append(Permission.Calendar.MSG); + builder.append("、"); + grantedCalendar = true; + } + if (!grantedCamera && Permission.sCameras.contains(permission)) { + builder.append(Permission.Camera.MSG); + builder.append("、"); + grantedCamera = true; + } + if (!grantedContacts && Permission.sContactes.contains(permission)) { + builder.append(Permission.Contacts.MSG); + builder.append("、"); + grantedContacts = true; + } + if (!grantedLocation && Permission.sLocations.contains(permission)) { + builder.append(Permission.Location.MSG); + builder.append("、"); + grantedLocation = true; + } + if (!grantedMicrophone && Permission.sMicrophones.contains(permission)) { + builder.append(Permission.Microphone.MSG); + builder.append("、"); + grantedMicrophone = true; + } + if (!grantedPhone && Permission.sPhones.contains(permission)) { + builder.append(Permission.Phone.MSG); + builder.append("、"); + grantedPhone = true; + } + if (!grantedSensors && Permission.sSensorses.contains(permission)) { + builder.append(Permission.Sensors.MSG); + builder.append("、"); + grantedSensors = true; + } + if (!grantedSms && Permission.sSmses.contains(permission)) { + builder.append(Permission.Sms.MSG); + builder.append("、"); + grantedSms = true; + } + if (!grantedStorage && Permission.sStorages.contains(permission)) { + builder.append(Permission.Storage.MSG); + builder.append("、"); + grantedStorage = true; + } + } + return builder.toString(); + } + + private IPermissionsCallback getPermissionsCallback(Object object) { + return (IPermissionsCallback) object; + } + + private void invokePermissionsGranted(Object object, int requestCode, String... permissions) { + getPermissionsCallback(object).onPermissionsGranted(requestCode, permissions); + } + + private void invokePermissionsDenied(Object object, int requestCode, String... permissions) { + getPermissionsCallback(object).onPermissionsDenied(requestCode, permissions); + } + + public interface IPermissionsCallback { + void onPermissionsGranted(int requestCode, String... permission); + + void onPermissionsDenied(int requestCode, String... permission); + } + + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + if (requestCode == mRequestCode) { + List deniedPermissions = new ArrayList(); + for (int i = 0; i < grantResults.length; i++) { + if (grantResults[i] != PackageManager.PERMISSION_GRANTED) + deniedPermissions.add(permissions[i]); + } + PLog.d("onRequestPermissionsResult--- " + "requestCode : " + requestCode + "--- deniedPermissions : " + deniedPermissions); + if (deniedPermissions.size() > 0) { + dealDeniedPermissions(mObject, requestCode, listToStringArray(deniedPermissions)); + } else { + invokePermissionsGranted(mObject, requestCode, permissions); + } + } + } + + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == SETTINGS_REQ_CODE) { + List unGrantedPermissionsList = createUnGrantedPermissionsList(mObject, mPermissions); + PLog.d("onActivityResult --- " + "requestCode : " + requestCode + "---" + "unGrantedPermissionsList : " + unGrantedPermissionsList); + if (unGrantedPermissionsList.size() > 0) { + invokePermissionsDenied(mObject, mRequestCode, listToStringArray(unGrantedPermissionsList)); + } else { + invokePermissionsGranted(mObject, mRequestCode, mPermissions); + } + } + } + + private void goSetting(Object object) { + PLog.d("goSetting"); + Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + Uri uri = Uri.fromParts("package", getActivity(object).getPackageName(), null); + intent.setData(uri); + if (object instanceof Activity) { + ((Activity) object).startActivityForResult(intent, SETTINGS_REQ_CODE); + } else if (object instanceof Fragment) { + ((Fragment) object).startActivityForResult(intent, SETTINGS_REQ_CODE); + } else if (object instanceof android.app.Fragment) { + ((android.app.Fragment) object).startActivityForResult(intent, SETTINGS_REQ_CODE); + } + } + + static final class PLog { + private static void d(String msg) { + if (sDebug) + Log.d(TAG, msg); + } + + private static void e(String msg) { + if (sDebug) + Log.e(TAG, msg); + } + } +} diff --git a/android/app/src/com/cocos/game/PictureSelectlucksiege.java b/android/app/src/com/cocos/game/PictureSelectlucksiege.java new file mode 100644 index 0000000..fd0cbe0 --- /dev/null +++ b/android/app/src/com/cocos/game/PictureSelectlucksiege.java @@ -0,0 +1,406 @@ +package com.cocos.game; + +import android.Manifest; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.TextUtils; +import android.text.style.AbsoluteSizeSpan; +import android.text.style.ForegroundColorSpan; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.RelativeLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.constraintlayout.widget.ConstraintSet; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.Fragment; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.target.CustomTarget; +import com.bumptech.glide.request.transition.Transition; +import com.luck.lib.camerax.CameraImageEngine; +import com.luck.lib.camerax.SimpleCameraX; +import com.luck.picture.lib.config.PictureMimeType; +import com.luck.picture.lib.config.SelectLimitType; +import com.luck.picture.lib.config.SelectorConfig; +import com.luck.picture.lib.dialog.RemindDialog; +import com.luck.picture.lib.engine.CompressFileEngine; +import com.luck.picture.lib.engine.CropFileEngine; +import com.luck.picture.lib.engine.UriToFileTransformEngine; +import com.luck.picture.lib.entity.LocalMedia; +import com.luck.picture.lib.interfaces.OnCallbackListener; +import com.luck.picture.lib.interfaces.OnCameraInterceptListener; +import com.luck.picture.lib.interfaces.OnKeyValueResultCallbackListener; +import com.luck.picture.lib.interfaces.OnPermissionDeniedListener; +import com.luck.picture.lib.interfaces.OnPermissionDescriptionListener; +import com.luck.picture.lib.interfaces.OnSelectLimitTipsListener; +import com.luck.picture.lib.permissions.PermissionConfig; +import com.luck.picture.lib.permissions.PermissionUtil; +import com.luck.picture.lib.style.SelectMainStyle; +import com.luck.picture.lib.utils.DateUtils; +import com.luck.picture.lib.utils.DensityUtil; +import com.luck.picture.lib.utils.SandboxTransformUtils; +import com.luck.picture.lib.utils.ToastUtils; +import com.luck.picture.lib.widget.MediumBoldTextView; +import com.yalantis.ucrop.UCrop; +import com.yalantis.ucrop.UCropImageEngine; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; + +import top.zibin.luban.CompressionPredicate; +import top.zibin.luban.Luban; +import top.zibin.luban.OnNewCompressListener; +import top.zibin.luban.OnRenameListener; + +import com.ddddd.one.R; + +public class PictureSelectlucksiege { + + private final static String TAG_EXPLAIN_VIEW = "TAG_EXPLAIN_VIEW"; + + /** + * 自定义压缩 + */ + public static class ImageFileCompressEngine implements CompressFileEngine { + + @Override + public void onStartCompress(Context context, ArrayList source, OnKeyValueResultCallbackListener call) { + Luban.with(context).load(source).ignoreBy(100).setRenameListener(new OnRenameListener() { + @Override + public String rename(String filePath) { + int indexOf = filePath.lastIndexOf("."); + String postfix = indexOf != -1 ? filePath.substring(indexOf) : ".jpg"; + String path_ = DateUtils.getCreateFileName("CMP_") + postfix; + Log.i("onResult", "rename: " + path_); + return path_; + } + }).filter(new CompressionPredicate() { + @Override + public boolean apply(String path) { + if (PictureMimeType.isUrlHasImage(path) && !PictureMimeType.isHasHttp(path)) { + return true; + } + return !PictureMimeType.isUrlHasGif(path); + } + }).setCompressListener(new OnNewCompressListener() { + @Override + public void onStart() { + + } + + @Override + public void onSuccess(String source, File compressFile) { + if (call != null) { + Log.i("onResult", "onSuccess: xxx "); + call.onCallback(source, compressFile.getAbsolutePath()); + } + } + + @Override + public void onError(String source, Throwable e) { + if (call != null) { + Log.i("onResult", "onError: xxx "); + call.onCallback(source, null); + } + } + }).launch(); + + } + } + + /** + * 自定义裁剪 + */ + public static class ImageFileCropEngine implements CropFileEngine { + @Override + public void onStartCrop(Fragment fragment, Uri srcUri, Uri destinationUri, ArrayList dataSource, int requestCode) { + UCrop.Options options = PictureSelectlucksiege.buildOptions(); + UCrop uCrop = UCrop.of(srcUri, destinationUri, dataSource); + uCrop.withOptions(options); + uCrop.setImageEngine(new UCropImageEngine() { + @Override + public void loadImage(Context context, String url, ImageView imageView) { + if (!ImageLoaderUtils.assertValidRequest(context)) { + return; + } + Glide.with(context).load(url).override(240, 240).into(imageView); + } + + @Override + public void loadImage(Context context, Uri url, int maxWidth, int maxHeight, OnCallbackListener call) { + Glide.with(context).asBitmap().load(url).override(maxWidth, maxHeight).into(new CustomTarget() { + @Override + public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition transition) { + if (call != null) { + call.onCall(resource); + } + } + + @Override + public void onLoadCleared(@Nullable Drawable placeholder) { + if (call != null) { + call.onCall(null); + } + } + }); + } + }); + uCrop.start(fragment.requireActivity(), fragment, requestCode); + } + } + + /** + * 拦截自定义提示 + */ + public static class MeOnSelectLimitTipsListener implements OnSelectLimitTipsListener { + + @Override + public boolean onSelectLimitTips(Context context, @Nullable LocalMedia media, SelectorConfig config, int limitType) { + if (limitType == SelectLimitType.SELECT_MIN_SELECT_LIMIT) { + ToastUtils.showToast(context, "图片最少不能低于" + config.minSelectNum + "张"); + return true; + } else if (limitType == SelectLimitType.SELECT_MIN_VIDEO_SELECT_LIMIT) { + ToastUtils.showToast(context, "视频最少不能低于" + config.minVideoSelectNum + "个"); + return true; + } else if (limitType == SelectLimitType.SELECT_MIN_AUDIO_SELECT_LIMIT) { + ToastUtils.showToast(context, "音频最少不能低于" + config.minAudioSelectNum + "个"); + return true; + } + return false; + } + } + + /** + * 添加权限说明 + */ + public static class MeOnPermissionDescriptionListener implements OnPermissionDescriptionListener { + + @Override + public void onPermissionDescription(Fragment fragment, String[] permissionArray) { + View rootView = fragment.requireView(); + if (rootView instanceof ViewGroup) { + addPermissionDescription(false, (ViewGroup) rootView, permissionArray); + } + } + + @Override + public void onDismiss(Fragment fragment) { + removePermissionDescription((ViewGroup) fragment.requireView()); + } + } + + /** + * 权限拒绝后回调 + */ + public static class MeOnPermissionDeniedListener implements OnPermissionDeniedListener { + + @Override + public void onDenied(Fragment fragment, String[] permissionArray, + int requestCode, OnCallbackListener call) { + String tips; + if (TextUtils.equals(permissionArray[0], PermissionConfig.CAMERA[0])) { + tips = "Kamera erişimi yok\nKamera işlevinin kullanılamamasıyla sonuçlanacaktır.";// "缺少相机权限\n可能会导致不能使用摄像头功能"; + } else if (TextUtils.equals(permissionArray[0], Manifest.permission.RECORD_AUDIO)) { + tips = "Ses kayıt erİşimi yok\nCihazınızdaki ses medya içeriği ve dosyalara erişin";// "缺少录音权限\n访问您设备上的音频、媒体内容和文件"; + } else { + tips = "Depolama erişmi yok\nCihazınızdaki fotoğraf ortamı içeriğine ve dosyalara erişin";//"缺少存储权限\n访问您设备上的照片、媒体内容和文件"; + } + RemindDialog dialog = RemindDialog.buildDialog(fragment.getContext(), tips); + dialog.setButtonText("Ayarlara git");//去设置 + dialog.setButtonTextColor(0xFF7D7DFF); + dialog.setContentTextColor(0xFF333333); + dialog.setOnDialogClickListener(new RemindDialog.OnDialogClickListener() { + @Override + public void onClick(View view) { + PermissionUtil.goIntentSetting(fragment, requestCode); + dialog.dismiss(); + } + }); + dialog.show(); + } + } + + /** + * 自定义沙盒文件处理 + */ + public static class MeSandboxFileEngine implements UriToFileTransformEngine { + + @Override + public void onUriToFileAsyncTransform(Context context, String srcPath, String mineType, OnKeyValueResultCallbackListener call) { + if (call != null) { + call.onCallback(srcPath, SandboxTransformUtils.copyPathToSandbox(context, srcPath, mineType)); + } + } + } + + /** + * 自定义拍照 + */ + public static class MeOnCameraInterceptListener implements OnCameraInterceptListener { + @Override + public void openCamera(Fragment fragment, int cameraMode, int requestCode) { + SimpleCameraX camera = SimpleCameraX.of(); + camera.isAutoRotation(true);//是否自动纠偏 + camera.setCameraMode(cameraMode);//相机模式 + camera.setVideoFrameRate(25); + //camera.setVideoBitRate(3 * 1024 * 1024); + //camera.isDisplayRecordChangeTime(true); + //camera.isManualFocusCameraPreview(cb_camera_focus.isChecked()); + camera.isZoomCameraPreview(true);//是否可缩放相机 + camera.setOutputPathDir(getSandboxCameraOutputPath()); + //camera.setPermissionDeniedListener(getSimpleXPermissionDeniedListener()); + //camera.setPermissionDescriptionListener(getSimpleXPermissionDescriptionListener()); + camera.setImageEngine(new CameraImageEngine() { + @Override + public void loadImage(Context context, String url, ImageView imageView) { + Glide.with(context).load(url).into(imageView); + } + }); + camera.start(fragment.requireActivity(), fragment, requestCode); + } + } + + /** + * 添加权限说明 + * + * @param viewGroup + * @param permissionArray + */ + private static void addPermissionDescription(boolean isHasSimpleXCamera, ViewGroup viewGroup, String[] permissionArray) { + int dp10 = DensityUtil.dip2px(viewGroup.getContext(), 10); + int dp15 = DensityUtil.dip2px(viewGroup.getContext(), 15); + MediumBoldTextView view = new MediumBoldTextView(viewGroup.getContext()); + view.setTag(TAG_EXPLAIN_VIEW); + view.setTextSize(14); + view.setTextColor(Color.parseColor("#333333")); + view.setPadding(dp10, dp15, dp10, dp15); + + String title; + String explain; + + if (TextUtils.equals(permissionArray[0], PermissionConfig.CAMERA[0])) { + title = "Kamera izinlerini kullanma açıklaması";//"相机权限使用说明"; + explain = "Fotoğraf çekmek/Video kaydetmek için";//"相机权限使用说明\n用户app用于拍照/录视频"; + } else if (TextUtils.equals(permissionArray[0], Manifest.permission.RECORD_AUDIO)) { + if (isHasSimpleXCamera) { + title = "Mikrofon izinlerini kullanma açıklaması";//""麦克风权限使用说明"; + explain = "Video kaydederken ses toplama için";//""麦克风权限使用说明\n用户app用于录视频时采集声音"; + } else { + title = "Ses kayıt izinlerini kullanma açıklaması";//"录音权限使用说明"; + explain = "Ses toplama için";//"录音权限使用说明\n用户app用于采集声音"; + } + } else { + title = "Depolama izinlerini kullanma açıklaması";// "存储权限使用说明"; + explain = "yazma/indirme/kaydetme/okuma/değiştirme/video/dosya ve bilgiler ve resimleri silmek için kullanılır";//"存储权限使用说明\n用户app写入/下载/保存/读取/修改/删除图片、视频、文件等信息"; + } + int startIndex = 0; + int endOf = startIndex + title.length(); + SpannableStringBuilder builder = new SpannableStringBuilder(explain); + builder.setSpan(new AbsoluteSizeSpan(DensityUtil.dip2px(viewGroup.getContext(), 16)), startIndex, endOf, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); + builder.setSpan(new ForegroundColorSpan(0xFF333333), startIndex, endOf, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); + view.setText(builder); + view.setBackground(ContextCompat.getDrawable(viewGroup.getContext(), R.drawable.ps_permission)); + + if (isHasSimpleXCamera) { + RelativeLayout.LayoutParams layoutParams = + new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT); + layoutParams.topMargin = DensityUtil.getStatusBarHeight(viewGroup.getContext()); + layoutParams.leftMargin = dp10; + layoutParams.rightMargin = dp10; + viewGroup.addView(view, layoutParams); + } else { + ConstraintLayout.LayoutParams layoutParams = + new ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.MATCH_PARENT, ConstraintLayout.LayoutParams.WRAP_CONTENT); + layoutParams.topToBottom = R.id.title_bar; + layoutParams.leftToLeft = ConstraintSet.PARENT_ID; + layoutParams.leftMargin = dp10; + layoutParams.rightMargin = dp10; + viewGroup.addView(view, layoutParams); + } + } + + /** + * 移除权限说明 + * + * @param viewGroup + */ + private static void removePermissionDescription(ViewGroup viewGroup) { + View tagExplainView = viewGroup.findViewWithTag(TAG_EXPLAIN_VIEW); + viewGroup.removeView(tagExplainView); + } + + + /** + * 配制UCrop,可根据需求自我扩展 + * + * @return + */ + private static UCrop.Options buildOptions() { + UCrop.Options options = new UCrop.Options(); + options.setHideBottomControls(true);//设置为true以隐藏底部控件(默认显示) + options.setFreeStyleCropEnabled(true);//允许用户调整裁剪边界的大小 + options.setShowCropFrame(true);//图像顶部看到裁剪框矩形 + options.setShowCropGrid(true);//如果希望在图像顶部看到裁剪网格/指导线,请设置为true + options.setCircleDimmedLayer(true);//如果希望暗显层内部有一个圆圈,请将其设置为true + options.withAspectRatio(240, 240);//设置裁剪边界的纵横比。用户将看不到带有其他比例选项的菜单。 +// options.setCropOutputPathDir(getSandboxPath()); + options.isCropDragSmoothToCenter(true);//自动居中裁剪和拖动 +// options.setSkipCropMimeType(getNotSupportCrop());//Skip crop mimeType +// options.isForbidCropGifWebp(cb_not_gif.isChecked());//你需要支持剪切动态图形吗gif或webp +// options.isForbidSkipMultipleCrop(true);//切割多个图纸时禁止跳过 + options.setMaxScaleMultiplier(100);//该方法设置用于从最小图像比例计算最大图像比例的乘数 +// if (selectorStyle != null && selectorStyle.getSelectMainStyle().getStatusBarColor() != 0) { +// SelectMainStyle mainStyle = selectorStyle.getSelectMainStyle(); +// boolean isDarkStatusBarBlack = mainStyle.isDarkStatusBarBlack(); +// int statusBarColor = mainStyle.getStatusBarColor(); +// options.isDarkStatusBarBlack(isDarkStatusBarBlack); +// if (StyleUtils.checkStyleValidity(statusBarColor)) { +// options.setStatusBarColor(statusBarColor); +// options.setToolbarColor(statusBarColor); +// } else { +// options.setStatusBarColor(ContextCompat.getColor(getContext(), R.color.ps_color_grey)); +// options.setToolbarColor(ContextCompat.getColor(getContext(), R.color.ps_color_grey)); +// } +// TitleBarStyle titleBarStyle = selectorStyle.getTitleBarStyle(); +// if (StyleUtils.checkStyleValidity(titleBarStyle.getTitleTextColor())) { +// options.setToolbarWidgetColor(titleBarStyle.getTitleTextColor()); +// } else { +// options.setToolbarWidgetColor(ContextCompat.getColor(getContext(), R.color.ps_color_white)); +// } +// } else { +// options.setStatusBarColor(ContextCompat.getColor(getContext(), R.color.ps_color_grey)); +// options.setToolbarColor(ContextCompat.getColor(getContext(), R.color.ps_color_grey)); +// options.setToolbarWidgetColor(ContextCompat.getColor(getContext(), R.color.ps_color_white)); +// } + options.setStatusBarColor(ContextCompat.getColor(AppActivity.nAppActivity, R.color.ps_color_grey)); + options.setToolbarColor(ContextCompat.getColor(AppActivity.nAppActivity, R.color.ps_color_grey)); + options.setToolbarWidgetColor(ContextCompat.getColor(AppActivity.nAppActivity, R.color.ps_color_white)); + + return options; + } + + /** + * 创建相机自定义输出目录 + * + * @return + */ + private static String getSandboxCameraOutputPath() { + File externalFilesDir = AppActivity.nAppActivity.getExternalFilesDir(""); + File customFile = new File(externalFilesDir.getAbsolutePath(), "Sandbox"); + if (!customFile.exists()) { + customFile.mkdirs(); + } + return customFile.getAbsolutePath() + File.separator; + } +} diff --git a/android/app/src/com/cocos/game/ShakeDetector.java b/android/app/src/com/cocos/game/ShakeDetector.java new file mode 100644 index 0000000..7c9136a --- /dev/null +++ b/android/app/src/com/cocos/game/ShakeDetector.java @@ -0,0 +1,153 @@ +package com.cocos.game; + +import java.util.ArrayList; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; + +/** + * 用于检测手机摇晃 + * + * @author 郑智仁 + * @see Blog + */ +public class ShakeDetector implements SensorEventListener { + /** + * 检测的时间间隔 + */ + static final int UPDATE_INTERVAL = 100; + /** + * 上一次检测的时间 + */ + long mLastUpdateTime; + /** + * 上一次检测时,加速度在x、y、z方向上的分量,用于和当前加速度比较求差。 + */ + float mLastX, mLastY, mLastZ; + float[] poses = new float[3]; + Context mContext; + SensorManager mSensorManager; + ArrayList mListeners; + /** + * 摇晃检测阈值,决定了对摇晃的敏感程度,越小越敏感。 + */ + public int shakeThreshold = 300; + + public ShakeDetector(Context context) { + mContext = context; + mSensorManager = (SensorManager) context + .getSystemService(Context.SENSOR_SERVICE); + mListeners = new ArrayList(); + + } + + public float[] getposes() { + return poses; + } + + ; + + /** + * 当摇晃事件发生时,接收通知 + */ + public interface OnShakeListener { + /** + * 当手机摇晃时被调用 + */ + void onShake(); + } + + /** + * 注册OnShakeListener,当摇晃时接收通知 + * + * @param listener + */ + public void registerOnShakeListener(OnShakeListener listener) { + if (mListeners.contains(listener)) + return; + mListeners.add(listener); + } + + /** + * 移除已经注册的OnShakeListener + * + * @param listener + */ + public void unregisterOnShakeListener(OnShakeListener listener) { + mListeners.remove(listener); + } + + /** + * 启动摇晃检测 + */ + public void start() { + if (mSensorManager == null) { + throw new UnsupportedOperationException(); + } + Sensor sensor = mSensorManager + .getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + if (sensor == null) { + throw new UnsupportedOperationException(); + } + boolean success = mSensorManager.registerListener(this, sensor, + SensorManager.SENSOR_DELAY_GAME); + if (!success) { + throw new UnsupportedOperationException(); + } + } + + /** + * 停止摇晃检测 + */ + public void stop() { + if (mSensorManager != null) + mSensorManager.unregisterListener(this); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // TODO Auto-generated method stub + } + + @Override + public void onSensorChanged(SensorEvent event) { + long currentTime = System.currentTimeMillis(); + long diffTime = currentTime - mLastUpdateTime; + if (diffTime < UPDATE_INTERVAL) + return; + mLastUpdateTime = currentTime; + float x = event.values[0]; + float y = event.values[1]; + float z = event.values[2]; + float deltaX = x - mLastX; + float deltaY = y - mLastY; + float deltaZ = z - mLastZ; + mLastX = x; + mLastY = y; + mLastZ = z; + float delta = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ + * deltaZ); + + delta = delta * 10000 / diffTime; +// Log.d("delta", "解析delta,: " + delta + "diffTime :" + diffTime); + if (delta > shakeThreshold) { // 当加速度的差值大于指定的阈值,认为这是一个摇晃 + this.notifyListeners(); + } + + poses[0] = x; + poses[1] = y; + poses[2] = z; + } + + /** + * 当摇晃事件发生时,通知所有的listener + */ + private void notifyListeners() { + for (OnShakeListener listener : mListeners) { + listener.onShake(); + } + } +} \ No newline at end of file diff --git a/android/app/src/com/cocos/game/ZegoUtil.java b/android/app/src/com/cocos/game/ZegoUtil.java new file mode 100644 index 0000000..de57479 --- /dev/null +++ b/android/app/src/com/cocos/game/ZegoUtil.java @@ -0,0 +1,469 @@ +package com.cocos.game; + +import android.Manifest; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.Message; +import android.util.Log; + +import androidx.annotation.RequiresApi; + +import com.cocos.lib.JsbBridgeWrapper; +import com.ddddd.one.R; + +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import im.zego.zegoexpress.ZegoExpressEngine; +import im.zego.zegoexpress.callback.IZegoEventHandler; +import im.zego.zegoexpress.constants.ZegoPlayerState; +import im.zego.zegoexpress.constants.ZegoPublisherState; +import im.zego.zegoexpress.constants.ZegoRoomStateChangedReason; +import im.zego.zegoexpress.constants.ZegoScenario; +import im.zego.zegoexpress.constants.ZegoUpdateType; +import im.zego.zegoexpress.entity.ZegoEngineConfig; +import im.zego.zegoexpress.entity.ZegoEngineProfile; +import im.zego.zegoexpress.entity.ZegoStream; +import im.zego.zegoexpress.entity.ZegoUser; + +public class ZegoUtil { + private static final String TAG = "ZegoUtil"; + private static ZegoUtil sInstance = null; + + public static ZegoUtil getInstance() { + if (sInstance == null) { + sInstance = new ZegoUtil(); + } + return sInstance; + } + + // JAVA + private JsbBridgeWrapper jbw = null; + + public JsbBridgeWrapper GetJsbBridgeWrapper() { + if (jbw == null) { + jbw = JsbBridgeWrapper.getInstance(); + } + return jbw; + } + + private static Context mContext; + private static AppActivity mActivity; + + /** + * 初始化信息 + * + * @param activity + */ + private static void InitInfo(AppActivity activity, Context _context) { + if (mActivity == null) + mActivity = activity; + if (mContext == null) + mContext = _context; + } + + /** + * 定义 SDK 引擎对象 + */ + private ZegoExpressEngine zegoExpressEngine; + /** + * 加入频道json字符串 + */ + public static String ZegoJoinJsonStr = ""; + private static List ZegoStreamList = new ArrayList<>(); + private static List CloseZegoStreamList = new ArrayList<>(); + private static boolean isClosePublishingStream = true; + + public void zegoDestroyEngine() { + if (zegoExpressEngine != null) { + ZegoJoinJsonStr = ""; + ZegoStreamList.clear(); + CloseZegoStreamList.clear(); + isClosePublishingStream = true; + // 退出全部房间 + zegoExpressEngine.logoutRoom(); + /** 销毁 SDK */ + zegoExpressEngine.destroyEngine(null); + zegoExpressEngine = null; + } + } + + /** + * 调用 zego SDK 的方法初始化。 + */ + public void zegoInitializeEngine(AppActivity nAppActivity, Context _context) { + InitInfo(nAppActivity, _context); + + try { + String zego_id = mActivity.getResources().getString(R.string.zego_appID); + Log.d(TAG, "zegoInitializeEngine: => " + zego_id); + + ZegoEngineConfig engineConfig = new ZegoEngineConfig(); + engineConfig.advancedConfig.put("audio_device_mode", "1"); + ZegoExpressEngine.setEngineConfig(engineConfig); + + ZegoEngineProfile profile = new ZegoEngineProfile(); + /** 请通过官网注册获取,格式为 123456789L */ + profile.appID = Long.parseLong(zego_id.replace("zego_", ""));//mActivity.getResources().getInteger(R.integer.zego_appID); + /** 64个字符,请通过官网注册获取,格式为"0123456789012345678901234567890123456789012345678901234567890123" */ + profile.appSign = mActivity.getString(R.string.zego_appSign).replace("zego_", ""); + /** 通用场景接入 */ + profile.scenario = ZegoScenario.STANDARD_CHATROOM;//; + /** 设置app的application 对象 */ + profile.application = mActivity.getApplication(); + /** 创建引擎 */ + zegoExpressEngine = ZegoExpressEngine.createEngine(profile, zegoEventHandler); + + zegoLogin(nAppActivity, _context); + zegoLoginOut(nAppActivity, _context); + zegoStartPublishingStream(nAppActivity, _context); + zegoStopPublishingStream(nAppActivity, _context); + zegoMuteAllPlayStreamAudio(nAppActivity, _context); + zegoStartPlayingStream(nAppActivity, _context); + zegoStopPlayingStream(nAppActivity, _context); + } catch (Exception e) { + Log.d(TAG, "zegoInitializeEngine: error=> " + Log.getStackTraceString(e)); + } + } + + /** + * 加入频道:供LUA调用 + */ + public void zegoLogin(AppActivity nAppActivity, Context _context) { + InitInfo(nAppActivity, _context); + GetJsbBridgeWrapper().addScriptEventListener(EventNameUtil.Zg_Login, arg -> { + try { + Log.d(TAG, "zegoLogin: " + arg); + ZegoJoinJsonStr = arg; + + Message msg = new Message(); + msg.what = nAppActivity.HANDLER_ZEGOLOGIN; + msg.obj = arg; + nAppActivity.handler.sendMessage(msg); + + } catch (Exception exception) { + Log.d(TAG, "zegoLogin:=>error " + exception.toString()); + exception.printStackTrace(); + } + }); + } + + /** + * 加入频道:供LUA调用 + */ + public void zegoLoginFun(AppActivity nAppActivity, Context _context, String param) { + InitInfo(nAppActivity, _context); + try { + Log.d(TAG, "zegoLoginFun: " + param); + + JSONObject jo = new JSONObject(param); + String userId = jo.getString("userId"); + String roomId = jo.getString("roomId"); + + /** 创建用户 */ + ZegoUser user = new ZegoUser(userId); + /** 开始登陆房间 */ + zegoExpressEngine.loginRoom(roomId, user); + zegoExpressEngine.startPublishingStream(userId); + zegoExpressEngine.startSoundLevelMonitor(200);//启动声浪监控 + isClosePublishingStream = false; + + String reStr = "0"; + EventToScriptUtil.getInstance().dispatchEventToScript(EventNameUtil.Zg_Login, reStr); + } catch (Exception exception) { + Log.d(TAG, "zegoLoginFun:=>error " + exception.toString()); + exception.printStackTrace(); + } + } + + /** + * 离开频道:供LUA调用 + */ + public void zegoLoginOut(AppActivity nAppActivity, Context _context) { + InitInfo(nAppActivity, _context); + + GetJsbBridgeWrapper().addScriptEventListener(EventNameUtil.Zg_LoginOut, arg -> { + try { + CloseZegoStreamList.clear(); + zegoExpressEngine.logoutRoom(); + } catch (Exception exception) { + Log.d(TAG, "zegoLoginOut: =>error " + exception.toString()); + } + }); + } + + /** + * 取消或恢复发布本地音频流:供LUA调用 - 推流 + */ + public void zegoStartPublishingStream(AppActivity nAppActivity, Context _context) { + InitInfo(nAppActivity, _context); + GetJsbBridgeWrapper().addScriptEventListener(EventNameUtil.Zg_StartPublish, strParam -> { + try { + Log.i(TAG, "zegoStartPublishingStream:=>strParam " + strParam); + + JSONObject jo = new JSONObject(strParam); + String streamId = jo.getString("streamId"); + isClosePublishingStream = false; + zegoExpressEngine.startPublishingStream(streamId); + + EventToScriptUtil.getInstance().dispatchEventToScript(EventNameUtil.Zg_StartPublish, "0|false"); + } catch (Exception e) { + Log.i(TAG, "zegoStartPublishingStream: =>error " + e.toString()); + } + }); + } + + /** + * 取消或恢复发布本地音频流:供LUA调用 - 停推流 + */ + public void zegoStopPublishingStream(AppActivity nAppActivity, Context _context) { + InitInfo(nAppActivity, _context); + GetJsbBridgeWrapper().addScriptEventListener(EventNameUtil.Zg_StopPublish, strParam -> { + try { + Log.i(TAG, "zegoStopPublishingStream:=>strParam " + strParam); + isClosePublishingStream = true; + zegoExpressEngine.stopPublishingStream(); + + EventToScriptUtil.getInstance().dispatchEventToScript(EventNameUtil.Zg_StopPublish, "0|true"); + } catch (Exception e) { + Log.i(TAG, "zegoStopPublishingStream: =>error " + e.toString()); + } + }); + } + + /** + * 取消或恢复订阅所有远端用户的音频流:供LUA调用 + */ + public void zegoMuteAllPlayStreamAudio(AppActivity nAppActivity, Context _context) { + InitInfo(nAppActivity, _context); + GetJsbBridgeWrapper().addScriptEventListener(EventNameUtil.Zg_AllPlay, muteBean -> { + try { + Log.i(TAG, "zegoMuteAllPlayStreamAudio:=>strParam " + muteBean); + + JSONObject jo = new JSONObject(muteBean); + boolean muted = jo.getBoolean("muted"); + + zegoExpressEngine.muteAllPlayStreamAudio(muted); + + } catch (Exception e) { + Log.i(TAG, "zegoMuteAllPlayStreamAudio: =>error " + e.toString()); + } + }); + } + + /** + * 取消或恢复订阅指定远端用户的音频流:供LUA调用 - 拉流 + */ + public void zegoStartPlayingStream(AppActivity nAppActivity, Context _context) { + InitInfo(nAppActivity, _context); + GetJsbBridgeWrapper().addScriptEventListener(EventNameUtil.Zg_StartPlay, muteBean -> { + try { + Log.i(TAG, "zegoStartPlayingStream:=>strParam " + muteBean); + + JSONObject jo = new JSONObject(muteBean); + String streamId = jo.getString("streamId"); + + if (CloseZegoStreamList.contains(streamId)) + CloseZegoStreamList.remove(streamId); + + zegoExpressEngine.startPlayingStream(streamId); + + EventToScriptUtil.getInstance().dispatchEventToScript(EventNameUtil.Zg_StartPlay, "0|" + streamId + "|false"); + } catch (Exception e) { + Log.i(TAG, "zegoStartPlayingStream: =>error " + e.toString()); + } + }); + } + + /** + * 取消或恢复订阅指定远端用户的音频流:供LUA调用 - 停拉流 + */ + public void zegoStopPlayingStream(AppActivity nAppActivity, Context _context) { + InitInfo(nAppActivity, _context); + GetJsbBridgeWrapper().addScriptEventListener(EventNameUtil.Zg_StopPlay, muteBean -> { + try { + Log.i(TAG, "zegoStartPlayingStream:=>strParam " + muteBean); + + JSONObject jo = new JSONObject(muteBean); + String streamId = jo.getString("streamId"); + if (!CloseZegoStreamList.contains(streamId)) + CloseZegoStreamList.add(streamId); + + zegoExpressEngine.stopPlayingStream(streamId); + + EventToScriptUtil.getInstance().dispatchEventToScript(EventNameUtil.Zg_StopPlay, "0|" + streamId + "|true"); + } catch (Exception e) { + Log.i(TAG, "zegoStartPlayingStream: =>error " + e.toString()); + } + }); + } + + /** + * 判断是否有语音权限 + * + * @return + */ + @RequiresApi(api = Build.VERSION_CODES.M) + public boolean checkAudioSelfPermission() { + return mActivity.getApplicationContext().checkSelfPermission(Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED; + } + + private final IZegoEventHandler zegoEventHandler = new IZegoEventHandler() { + /** 房间状态更新回调 */ + @Override + public void onRoomStateChanged(String roomID, ZegoRoomStateChangedReason reason, int errorCode, JSONObject extendedData) { + super.onRoomStateChanged(roomID, reason, errorCode, extendedData); + Log.i(TAG, "onRoomStatZg_LogineUpdate: 🚩 🚪 Login room reason " + reason + " roomID:" + roomID + " errorCode:" + errorCode); + if (reason == ZegoRoomStateChangedReason.LOGINING) { + // 登录中 + Log.i(TAG, "onRoomStateChanged: 登录中"); + } else if (reason == ZegoRoomStateChangedReason.LOGINED) { + // 登录成功 + //只有当房间状态是登录成功或重连成功时,推流(startPublishingStream)、拉流(startPlayingStream)才能正常收发音视频 + //将自己的音视频流推送到 ZEGO 音视频云 + Log.i(TAG, "onRoomStateChanged: 登录成功"); + } else if (reason == ZegoRoomStateChangedReason.LOGIN_FAILED) { + // 登录失败 + Log.i(TAG, "onRoomStateChanged: 登录失败"); + } else if (reason == ZegoRoomStateChangedReason.RECONNECTING) { + // 重连中 + Log.i(TAG, "onRoomStateChanged: 重连中"); + } else if (reason == ZegoRoomStateChangedReason.RECONNECTED) { + // 重连成功 + Log.i(TAG, "onRoomStateChanged: 重连成功"); + } else if (reason == ZegoRoomStateChangedReason.RECONNECT_FAILED) { + // 重连失败 + Log.i(TAG, "onRoomStateChanged: 重连失败"); + } else if (reason == ZegoRoomStateChangedReason.KICK_OUT) { + // 被踢出房间 + Log.i(TAG, "onRoomStateChanged: 被踢出房间"); + } else if (reason == ZegoRoomStateChangedReason.LOGOUT) { + // 登出成功 + Log.i(TAG, "onRoomStateChanged: 登出成功"); + } else if (reason == ZegoRoomStateChangedReason.LOGOUT_FAILED) { + // 登出失败 + Log.i(TAG, "onRoomStateChanged: 登出失败"); + } + } +// /** 房间状态更新回调 */ +// @Override +// public void onRoomStateUpdate(String roomID, ZegoRoomState state, int errorCode, JSONObject extendedData) { +// /** 根据需要实现事件回调 */ +// if (state == ZegoRoomState.CONNECTED && errorCode == 0) { +// Log.i(TAG, "onRoomStatZg_LogineUpdate: 🚩 🚪 Login room success"); +// } +// if (errorCode != 0) { +// Log.i(TAG, "onRoomStateUpdate: 🚩 ❌ 🚪 Login room fail, errorCode: " + errorCode); +// } +// } + + /** 用户状态更新 */ + @Override + public void onRoomUserUpdate(String roomID, ZegoUpdateType updateType, ArrayList userList) { + super.onRoomUserUpdate(roomID, updateType, userList); + /** 根据需要实现事件回调 */ + } + + /** + * 流状态更新 + */ + @Override + public void onRoomStreamUpdate(String roomID, ZegoUpdateType updateType, ArrayList streamList, JSONObject extendedData) { + /** 根据需要实现事件回调 */ + super.onRoomStreamUpdate(roomID, updateType, streamList, extendedData); + // Log.i(Zego_TAG, "🚩 🌊 onRoomStreamUpdate: roomID" + roomID + ", updateType:" + updateType.value() + ", streamList: " + streamList); + + // Add the rendered view dynamically after pulling the stream here + if (updateType == ZegoUpdateType.ADD) { + for (ZegoStream zegoStream : streamList) { + if (!ZegoStreamList.contains(zegoStream)) { + ZegoStreamList.add(zegoStream); + } + + if (!CloseZegoStreamList.contains(zegoStream.streamID)) + ZegoExpressEngine.getEngine().startPlayingStream(zegoStream.streamID); + } + } else if (updateType == ZegoUpdateType.DELETE) { + for (ZegoStream zegoStream : streamList) { + if (ZegoStreamList.contains(zegoStream)) { + ZegoStreamList.remove(zegoStream); + } + + ZegoExpressEngine.getEngine().stopPlayingStream(zegoStream.streamID); + } + } + } + + /** 推流状态更新回调*/ + @Override + public void onPublisherStateUpdate(String streamID, ZegoPublisherState state, int errorCode, JSONObject extendedData) { + // Log.i(Zego_TAG, "onPublisherStateUpdate streamID:"+streamID+" state:"+state); + if (state == ZegoPublisherState.PUBLISHING && errorCode == 0) { + Log.i(TAG, "onPublisherStateUpdate:🚩 📤 Publishing stream success"); + } + if (errorCode != 0) { + Log.i(TAG, "onPublisherStateUpdate: 🚩 ❌ 📤 Publishing stream fail, errorCode: " + errorCode); + } + } + + /** 拉流状态更新回调*/ + @Override + public void onPlayerStateUpdate(String streamID, ZegoPlayerState state, int errorCode, JSONObject extendedData) { + // Log.i(Zego_TAG, "onPlayerStateUpdate streamID:"+streamID+" state:"+state); + if (state == ZegoPlayerState.PLAYING && errorCode == 0) { + Log.i(TAG, "onPlayerStateUpdate: 🚩 📥 Playing stream success"); + } + if (errorCode != 0) { + Log.i(TAG, "onPlayerStateUpdate: 🚩 ❌ 📥 Playing stream fail, errorCode: " + errorCode); + } + } + + /** 接收远端拉流音频声浪回调 */ + @Override + public void onRemoteSoundLevelUpdate(HashMap soundLevels) { + super.onRemoteSoundLevelUpdate(soundLevels); +// Log.i(Zego_TAG, "onRemoteSoundLevelUpdate:"+ soundLevels.size()); + for (HashMap.Entry entry : soundLevels.entrySet()) { + String streamid = entry.getKey(); + Float value = entry.getValue(); + if (value > 10 && !streamid.equals("")) { + String speakers_str = streamid + "|" + value; + EventToScriptUtil.getInstance().dispatchEventToScript(EventNameUtil.Zg_AudioVolume, speakers_str); + } + } + } + + /** 本地采集音频声浪回调 */ + @Override + public void onCapturedSoundLevelUpdate(float soundLevel) { + super.onCapturedSoundLevelUpdate(soundLevel); + // Log.v(TAG, "onCapturedSoundLevelUpdate:" + soundLevel); + if (!isClosePublishingStream && soundLevel > 10) { + String speakers_str = "0|" + soundLevel; + EventToScriptUtil.getInstance().dispatchEventToScript(EventNameUtil.Zg_AudioVolume, speakers_str); + } + } + +// @Override +// public void onCapturedSoundLevelInfoUpdate(ZegoSoundLevelInfo soundLevelInfo) { +// super.onCapturedSoundLevelInfoUpdate(soundLevelInfo); +// Log.v("ZegoAudioTalk", "onCapturedSoundLevelUpdate:" + soundLevelInfo.soundLevel); +//// if(soundLevelInfo.soundLevel > 0 && soundLevelInfo.vad == 1){ +//// +//// } +// final String speakers_str = "0|" + soundLevelInfo.soundLevel; +// nAppActivity.runOnGLThread(new Runnable() { +// @Override +// public void run() { +// // 用户的音量 > 0 回调到lua 对应的玩家显示正在说话的状态 +// Cocos2dxLuaJavaBridge.callLuaGlobalFunctionWithString( +// "Zego_OnAudioVolume", speakers_str + ""); +// } +// }); +// } + }; +} diff --git a/android/build.gradle b/android/build.gradle index 183885e..a6985ee 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -17,6 +17,7 @@ buildscript { allprojects { repositories { + maven { url 'https://storage.zego.im/maven' } google() mavenCentral() // jcenter() // keeped as anchor, will be removed soon diff --git a/android/res/drawable/ps_image_placeholder.xml b/android/res/drawable/ps_image_placeholder.xml new file mode 100644 index 0000000..bdf2627 --- /dev/null +++ b/android/res/drawable/ps_image_placeholder.xml @@ -0,0 +1,5 @@ + + + + diff --git a/android/res/drawable/ps_permission.png b/android/res/drawable/ps_permission.png new file mode 100644 index 0000000..d3ef347 Binary files /dev/null and b/android/res/drawable/ps_permission.png differ diff --git a/android/res/drawable/splash.png b/android/res/drawable/splash.png new file mode 100644 index 0000000..0977c40 Binary files /dev/null and b/android/res/drawable/splash.png differ diff --git a/android/res/mipmap-hdpi/ic_launcher.png b/android/res/mipmap-hdpi/ic_launcher.png index 888c0d5..b6e92ec 100644 Binary files a/android/res/mipmap-hdpi/ic_launcher.png and b/android/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/res/mipmap-mdpi/ic_launcher.png b/android/res/mipmap-mdpi/ic_launcher.png index d5e3c77..fa955dc 100644 Binary files a/android/res/mipmap-mdpi/ic_launcher.png and b/android/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/res/mipmap-xhdpi/ic_launcher.png b/android/res/mipmap-xhdpi/ic_launcher.png index 2b6f32a..a52c8be 100644 Binary files a/android/res/mipmap-xhdpi/ic_launcher.png and b/android/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/res/mipmap-xxhdpi/ic_launcher.png b/android/res/mipmap-xxhdpi/ic_launcher.png index 2f1e5a6..1aa1112 100644 Binary files a/android/res/mipmap-xxhdpi/ic_launcher.png and b/android/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/res/mipmap-xxxhdpi/ic_launcher.png b/android/res/mipmap-xxxhdpi/ic_launcher.png index a9ebc79..5683c85 100644 Binary files a/android/res/mipmap-xxxhdpi/ic_launcher.png and b/android/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/res/values/colors.xml b/android/res/values/colors.xml new file mode 100644 index 0000000..3010a63 --- /dev/null +++ b/android/res/values/colors.xml @@ -0,0 +1,5 @@ + + + #666666 + #FFFFFF + diff --git a/android/res/values/ids.xml b/android/res/values/ids.xml new file mode 100644 index 0000000..d6948b9 --- /dev/null +++ b/android/res/values/ids.xml @@ -0,0 +1,7 @@ + + + + 77777777777 + + + \ No newline at end of file diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml index 8542005..4fed8d4 100644 --- a/android/res/values/strings.xml +++ b/android/res/values/strings.xml @@ -1,2 +1,18 @@ + 101 Okey Neşeli + + + + 77777777777-pe7pidhpuc9ohkg5t8aqip7qboire346.apps.googleusercontent.com + + + 4fbx099pwqv4 + + 63f5a8b9d4 + 1.0.0 + happyokey + false + + zego_57566727 + zego_e7e8723ad58492924c5c26087f735ee2f8b748cbd30acff1194ef1b37810f389 diff --git a/build.gradle b/build.gradle index 899d584..a42ed4d 100644 --- a/build.gradle +++ b/build.gradle @@ -9,6 +9,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:8.10.1' + classpath 'com.google.gms:google-services:4.4.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt new file mode 100644 index 0000000..2a65780 --- /dev/null +++ b/common/CMakeLists.txt @@ -0,0 +1,57 @@ +enable_language(C ASM) +set(DEVELOPMENT_TEAM "" CACHE STRING "APPLE Developtment Team") +set(RES_DIR "" CACHE STRING "Resource path") +set(COCOS_X_PATH "" CACHE STRING "Path to engine/native/") + +set(TARGET_OSX_VERSION "10.14" CACHE STRING "Target MacOSX version" FORCE) +set(TARGET_IOS_VERSION "11.0" CACHE STRING "Target iOS version" FORCE) + +set(CMAKE_CXX_STANDARD 17) +option(CC_DEBUG_FORCE "Force enable CC_DEBUG in release mode" OFF) +option(USE_SE_V8 "Use V8 JavaScript Engine" ON) +option(USE_SE_JSVM "Use JSVM JavaScript Engine" OFF) +option(USE_SE_JSC "Use JavaScriptCore on MacOSX/iOS" OFF) +option(USE_V8_DEBUGGER "Compile v8 inspector ws server" ON) +option(USE_V8_DEBUGGER_FORCE "Force enable debugger in release mode" OFF) +option(USE_SOCKET "Enable WebSocket & SocketIO" ON) +option(USE_AUDIO "Enable Audio" ON) #Enable AudioEngine +option(USE_EDIT_BOX "Enable EditBox" ON) +option(USE_VIDEO "Enable VideoPlayer Component" ON) +option(USE_WEBVIEW "Enable WebView Component" ON) +option(USE_MIDDLEWARE "Enable Middleware" ON) +option(USE_DRAGONBONES "Enable Dragonbones" ON) +option(USE_SPINE_3_8 "Enable Spine 3.8" ON) +option(USE_SPINE_4_2 "Enable Spine 4.2" OFF) +option(USE_WEBSOCKET_SERVER "Enable WebSocket Server" OFF) +option(USE_JOB_SYSTEM_TASKFLOW "Use taskflow as job system backend" OFF) +option(USE_JOB_SYSTEM_TBB "Use tbb as job system backend" OFF) +option(USE_PHYSICS_PHYSX "Use PhysX Physics" ON) +option(USE_OCCLUSION_QUERY "Use Occlusion Query" ON) +option(USE_DEBUG_RENDERER "Use Debug Renderer" ON) +option(USE_GEOMETRY_RENDERER "Use Geometry Renderer" ON) +option(USE_WEBP "Use Webp" ON) + +if(NOT RES_DIR) + message(FATAL_ERROR "RES_DIR is not set!") +endif() + +include(${RES_DIR}/proj/cfg.cmake) + +if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/localCfg.cmake) + include(${CMAKE_CURRENT_LIST_DIR}/localCfg.cmake) +endif() + +if(NOT COCOS_X_PATH) + message(FATAL_ERROR "COCOS_X_PATH is not set!") +endif() + +if(USE_XR OR USE_AR_MODULE) + include(${CMAKE_CURRENT_LIST_DIR}/xr.cmake) +endif() + +include(${COCOS_X_PATH}/CMakeLists.txt) + +list(APPEND CC_COMMON_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/Classes/Game.h + ${CMAKE_CURRENT_LIST_DIR}/Classes/Game.cpp +) diff --git a/common/Classes/Game.cpp b/common/Classes/Game.cpp new file mode 100644 index 0000000..c15664c --- /dev/null +++ b/common/Classes/Game.cpp @@ -0,0 +1,65 @@ +/**************************************************************************** + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. + + http://www.cocos.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated engine source code (the "Software"), a limited, + worldwide, royalty-free, non-assignable, revocable and non-exclusive license + to use Cocos Creator solely to develop games on your target platforms. You + shall not use Cocos Creator software for developing other software or tools + that's used for developing games. You are not granted to publish, distribute, + sublicense, and/or sell copies of Cocos Creator. + + The software or tools in this License Agreement are licensed, not sold. + Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to + you. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ****************************************************************************/ +#include "Game.h" + +#ifndef GAME_NAME +#define GAME_NAME "CocosGame"; +#endif + +#ifndef SCRIPT_XXTEAKEY +#define SCRIPT_XXTEAKEY ""; +#endif + +Game::Game() = default; + +int Game::init() { + _windowInfo.title = GAME_NAME; + // configurate window size + // _windowInfo.height = 600; + // _windowInfo.width = 800; + +#if CC_DEBUG + _debuggerInfo.enabled = true; +#else + _debuggerInfo.enabled = false; +#endif + _debuggerInfo.port = 6086; + _debuggerInfo.address = "0.0.0.0"; + _debuggerInfo.pauseOnStart = false; + + _xxteaKey = SCRIPT_XXTEAKEY; + + BaseGame::init(); + return 0; +} + +void Game::onPause() { BaseGame::onPause(); } + +void Game::onResume() { BaseGame::onResume(); } + +void Game::onClose() { BaseGame::onClose(); } + +CC_REGISTER_APPLICATION(Game); diff --git a/common/Classes/Game.h b/common/Classes/Game.h new file mode 100644 index 0000000..521615e --- /dev/null +++ b/common/Classes/Game.h @@ -0,0 +1,44 @@ +/**************************************************************************** + Copyright (c) 2018 Xiamen Yaji Software Co., Ltd. + + http://www.cocos.com + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated engine source code (the "Software"), a limited, + worldwide, royalty-free, non-assignable, revocable and non-exclusive license + to use Cocos Creator solely to develop games on your target platforms. You + shall not use Cocos Creator software for developing other software or tools + that's used for developing games. You are not granted to publish, distribute, + sublicense, and/or sell copies of Cocos Creator. + + The software or tools in this License Agreement are licensed, not sold. + Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to + you. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ****************************************************************************/ +#pragma once + +#include "cocos/cocos.h" + +/** + @brief The cocos2d Application. + + The reason for implement as private inheritance is to hide some interface call + by Director. + */ +class Game : public cc::BaseGame { +public: + Game(); + int init() override; + // bool init() override; + void onPause() override; + void onResume() override; + void onClose() override; +}; diff --git a/common/cocos-version.json b/common/cocos-version.json new file mode 100644 index 0000000..6d8c4d7 --- /dev/null +++ b/common/cocos-version.json @@ -0,0 +1 @@ +{"version":"3.8.8","skipCheck":false} diff --git a/common/localCfg.cmake b/common/localCfg.cmake new file mode 100644 index 0000000..f17333e --- /dev/null +++ b/common/localCfg.cmake @@ -0,0 +1,3 @@ +## Add or overwrite options from cfg.cmake. +## This file is ignored from git. +# set(NODE_EXECUTABLE /opt/...) \ No newline at end of file diff --git a/common/xr.cmake b/common/xr.cmake new file mode 100644 index 0000000..e4d666d --- /dev/null +++ b/common/xr.cmake @@ -0,0 +1,20 @@ + +include(${COCOS_X_PATH}/cmake/predefine.cmake) + +if(NOT DEFINED XR_COMMON_SOURCES) + set(XR_COMMON_SOURCES) +endif() + +if(NOT DEFINED XR_LIBS) + set(XR_LIBS) +endif() + +if(NOT DEFINED XR_COMMON_PATH) + set(XR_COMMON_PATH ${CMAKE_CURRENT_LIST_DIR}/../../../extensions/xr-plugin/common) +endif() + +if(NOT DEFINED XR_LIBRARY_PATH) + set(XR_LIBRARY_PATH ${CMAKE_CURRENT_LIST_DIR}/../../../extensions/xr-plugin/platforms) +endif() + +include(${XR_COMMON_PATH}/xr.cmake) diff --git a/gradle.properties b/gradle.properties index c840969..0a78375 100644 --- a/gradle.properties +++ b/gradle.properties @@ -36,7 +36,7 @@ PROP_TARGET_SDK_VERSION=35 PROP_BUILD_TOOLS_VERSION=36.0.0 # Android Application Name -PROP_APP_NAME=magotest +PROP_APP_NAME=Klasik 101 Okey # Instant App PROP_ENABLE_INSTANT_APP=false @@ -44,24 +44,25 @@ PROP_ENABLE_INSTANT_APP=false # InputSDK PROP_ENABLE_INPUTSDK=false -PROP_NDK_VERSION=29.0.14206865 +PROP_NDK_VERSION=25.2.9519653 -PROP_ENABLE_COMPRESS_SO=true + +PROP_ENABLE_COMPRESS_SO=false # Build variant -PROP_IS_DEBUG=false +PROP_IS_DEBUG=true # Cocos Engine Path COCOS_ENGINE_PATH=C:/ProgramData/cocos/editors/Creator/3.8.8/resources/resources/3d/engine/native # Res path -RES_PATH=E:/project/okey101_native_sm/okey/build/android +RES_PATH=E:/project/okey_an_test # Native source dir -NATIVE_DIR=E:/project/okey101_native_sm/okey/native/engine/android +NATIVE_DIR=E:/project/okey_an_test/android # Application ID -APPLICATION_ID=top.android.test +APPLICATION_ID=com.ddddd.one # List of CPU Archtexture to build that application with # Available architextures (armeabi-v7a | arm64-v8a | x86 | x86_64) diff --git a/libservice/build/intermediates/aapt_friendly_merged_manifests/release/processReleaseManifest/aapt/AndroidManifest.xml b/libservice/build/intermediates/aapt_friendly_merged_manifests/release/processReleaseManifest/aapt/AndroidManifest.xml new file mode 100644 index 0000000..8f53dcc --- /dev/null +++ b/libservice/build/intermediates/aapt_friendly_merged_manifests/release/processReleaseManifest/aapt/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/libservice/build/intermediates/aapt_friendly_merged_manifests/release/processReleaseManifest/aapt/output-metadata.json b/libservice/build/intermediates/aapt_friendly_merged_manifests/release/processReleaseManifest/aapt/output-metadata.json new file mode 100644 index 0000000..a601f77 --- /dev/null +++ b/libservice/build/intermediates/aapt_friendly_merged_manifests/release/processReleaseManifest/aapt/output-metadata.json @@ -0,0 +1,18 @@ +{ + "version": 3, + "artifactType": { + "type": "AAPT_FRIENDLY_MERGED_MANIFESTS", + "kind": "Directory" + }, + "applicationId": "com.cocos.service", + "variantName": "release", + "elements": [ + { + "type": "SINGLE", + "filters": [], + "attributes": [], + "outputFile": "AndroidManifest.xml" + } + ], + "elementType": "File" +} \ No newline at end of file diff --git a/libservice/build/intermediates/aar_main_jar/release/syncReleaseLibJars/classes.jar b/libservice/build/intermediates/aar_main_jar/release/syncReleaseLibJars/classes.jar new file mode 100644 index 0000000..5da6a5d Binary files /dev/null and b/libservice/build/intermediates/aar_main_jar/release/syncReleaseLibJars/classes.jar differ diff --git a/libservice/build/intermediates/aar_metadata/release/writeReleaseAarMetadata/aar-metadata.properties b/libservice/build/intermediates/aar_metadata/release/writeReleaseAarMetadata/aar-metadata.properties new file mode 100644 index 0000000..1211b1e --- /dev/null +++ b/libservice/build/intermediates/aar_metadata/release/writeReleaseAarMetadata/aar-metadata.properties @@ -0,0 +1,6 @@ +aarFormatVersion=1.0 +aarMetadataVersion=1.0 +minCompileSdk=1 +minCompileSdkExtension=0 +minAndroidGradlePluginVersion=1.0.0 +coreLibraryDesugaringEnabled=false diff --git a/libservice/build/intermediates/annotation_processor_list/release/javaPreCompileRelease/annotationProcessors.json b/libservice/build/intermediates/annotation_processor_list/release/javaPreCompileRelease/annotationProcessors.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/libservice/build/intermediates/annotation_processor_list/release/javaPreCompileRelease/annotationProcessors.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/.gradle/8.11.1/gc.properties b/libservice/build/intermediates/annotations_typedef_file/release/extractReleaseAnnotations/typedefs.txt similarity index 100% rename from .gradle/8.11.1/gc.properties rename to libservice/build/intermediates/annotations_typedef_file/release/extractReleaseAnnotations/typedefs.txt diff --git a/libservice/build/intermediates/compile_library_classes_jar/release/bundleLibCompileToJarRelease/classes.jar b/libservice/build/intermediates/compile_library_classes_jar/release/bundleLibCompileToJarRelease/classes.jar new file mode 100644 index 0000000..af23911 Binary files /dev/null and b/libservice/build/intermediates/compile_library_classes_jar/release/bundleLibCompileToJarRelease/classes.jar differ diff --git a/libservice/build/intermediates/compile_r_class_jar/release/generateReleaseRFile/R.jar b/libservice/build/intermediates/compile_r_class_jar/release/generateReleaseRFile/R.jar new file mode 100644 index 0000000..e301975 Binary files /dev/null and b/libservice/build/intermediates/compile_r_class_jar/release/generateReleaseRFile/R.jar differ diff --git a/.gradle/vcs-1/gc.properties b/libservice/build/intermediates/compile_symbol_list/release/generateReleaseRFile/R.txt similarity index 100% rename from .gradle/vcs-1/gc.properties rename to libservice/build/intermediates/compile_symbol_list/release/generateReleaseRFile/R.txt diff --git a/libservice/build/intermediates/default_proguard_files/global/proguard-android-optimize.txt-8.10.1 b/libservice/build/intermediates/default_proguard_files/global/proguard-android-optimize.txt-8.10.1 new file mode 100644 index 0000000..5a3e3a5 --- /dev/null +++ b/libservice/build/intermediates/default_proguard_files/global/proguard-android-optimize.txt-8.10.1 @@ -0,0 +1,89 @@ +# This is a configuration file for ProGuard. +# http://proguard.sourceforge.net/index.html#manual/usage.html +# +# Starting with version 2.2 of the Android plugin for Gradle, this file is distributed together with +# the plugin and unpacked at build-time. The files in $ANDROID_HOME are no longer maintained and +# will be ignored by new version of the Android plugin for Gradle. + +# Optimizations: If you don't want to optimize, use the proguard-android.txt configuration file +# instead of this one, which turns off the optimization flags. +-allowaccessmodification + +# Preserve some attributes that may be required for reflection. +-keepattributes AnnotationDefault, + EnclosingMethod, + InnerClasses, + RuntimeVisibleAnnotations, + RuntimeVisibleParameterAnnotations, + RuntimeVisibleTypeAnnotations, + Signature + +-keep public class com.google.vending.licensing.ILicensingService +-keep public class com.android.vending.licensing.ILicensingService +-keep public class com.google.android.vending.licensing.ILicensingService +-dontnote com.android.vending.licensing.ILicensingService +-dontnote com.google.vending.licensing.ILicensingService +-dontnote com.google.android.vending.licensing.ILicensingService + +# For native methods, see https://www.guardsquare.com/manual/configuration/examples#native +-keepclasseswithmembernames,includedescriptorclasses class * { + native ; +} + +# Keep setters in Views so that animations can still work. +-keepclassmembers public class * extends android.view.View { + void set*(***); + *** get*(); +} + +# We want to keep methods in Activity that could be used in the XML attribute onClick. +-keepclassmembers class * extends android.app.Activity { + public void *(android.view.View); +} + +# For enumeration classes, see https://www.guardsquare.com/manual/configuration/examples#enumerations +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +-keepclassmembers class * implements android.os.Parcelable { + public static final ** CREATOR; +} + +# Preserve annotated Javascript interface methods. +-keepclassmembers class * { + @android.webkit.JavascriptInterface ; +} + +# The support libraries contains references to newer platform versions. +# Don't warn about those in case this app is linking against an older +# platform version. We know about them, and they are safe. +-dontnote android.support.** +-dontnote androidx.** +-dontwarn android.support.** +-dontwarn androidx.** + +# Understand the @Keep support annotation. +-keep class android.support.annotation.Keep + +-keep @android.support.annotation.Keep class * {*;} + +-keepclasseswithmembers class * { + @android.support.annotation.Keep ; +} + +-keepclasseswithmembers class * { + @android.support.annotation.Keep ; +} + +-keepclasseswithmembers class * { + @android.support.annotation.Keep (...); +} + +# These classes are duplicated between android.jar and org.apache.http.legacy.jar. +-dontnote org.apache.http.** +-dontnote android.net.http.** + +# These classes are duplicated between android.jar and core-lambda-stubs.jar. +-dontnote java.lang.invoke.** diff --git a/libservice/build/intermediates/default_proguard_files/global/proguard-android.txt-8.10.1 b/libservice/build/intermediates/default_proguard_files/global/proguard-android.txt-8.10.1 new file mode 100644 index 0000000..6f7e4ef --- /dev/null +++ b/libservice/build/intermediates/default_proguard_files/global/proguard-android.txt-8.10.1 @@ -0,0 +1,95 @@ +# This is a configuration file for ProGuard. +# http://proguard.sourceforge.net/index.html#manual/usage.html +# +# Starting with version 2.2 of the Android plugin for Gradle, this file is distributed together with +# the plugin and unpacked at build-time. The files in $ANDROID_HOME are no longer maintained and +# will be ignored by new version of the Android plugin for Gradle. + +# Optimization is turned off by default. Dex does not like code run +# through the ProGuard optimize steps (and performs some +# of these optimizations on its own). +# Note that if you want to enable optimization, you cannot just +# include optimization flags in your own project configuration file; +# instead you will need to point to the +# "proguard-android-optimize.txt" file instead of this one from your +# project.properties file. +-dontoptimize + +# Preserve some attributes that may be required for reflection. +-keepattributes AnnotationDefault, + EnclosingMethod, + InnerClasses, + RuntimeVisibleAnnotations, + RuntimeVisibleParameterAnnotations, + RuntimeVisibleTypeAnnotations, + Signature + +-keep public class com.google.vending.licensing.ILicensingService +-keep public class com.android.vending.licensing.ILicensingService +-keep public class com.google.android.vending.licensing.ILicensingService +-dontnote com.android.vending.licensing.ILicensingService +-dontnote com.google.vending.licensing.ILicensingService +-dontnote com.google.android.vending.licensing.ILicensingService + +# For native methods, see https://www.guardsquare.com/manual/configuration/examples#native +-keepclasseswithmembernames,includedescriptorclasses class * { + native ; +} + +# Keep setters in Views so that animations can still work. +-keepclassmembers public class * extends android.view.View { + void set*(***); + *** get*(); +} + +# We want to keep methods in Activity that could be used in the XML attribute onClick. +-keepclassmembers class * extends android.app.Activity { + public void *(android.view.View); +} + +# For enumeration classes, see https://www.guardsquare.com/manual/configuration/examples#enumerations +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +-keepclassmembers class * implements android.os.Parcelable { + public static final ** CREATOR; +} + +# Preserve annotated Javascript interface methods. +-keepclassmembers class * { + @android.webkit.JavascriptInterface ; +} + +# The support libraries contains references to newer platform versions. +# Don't warn about those in case this app is linking against an older +# platform version. We know about them, and they are safe. +-dontnote android.support.** +-dontnote androidx.** +-dontwarn android.support.** +-dontwarn androidx.** + +# Understand the @Keep support annotation. +-keep class android.support.annotation.Keep + +-keep @android.support.annotation.Keep class * {*;} + +-keepclasseswithmembers class * { + @android.support.annotation.Keep ; +} + +-keepclasseswithmembers class * { + @android.support.annotation.Keep ; +} + +-keepclasseswithmembers class * { + @android.support.annotation.Keep (...); +} + +# These classes are duplicated between android.jar and org.apache.http.legacy.jar. +-dontnote org.apache.http.** +-dontnote android.net.http.** + +# These classes are duplicated between android.jar and core-lambda-stubs.jar. +-dontnote java.lang.invoke.** diff --git a/libservice/build/intermediates/default_proguard_files/global/proguard-defaults.txt-8.10.1 b/libservice/build/intermediates/default_proguard_files/global/proguard-defaults.txt-8.10.1 new file mode 100644 index 0000000..7bbb228 --- /dev/null +++ b/libservice/build/intermediates/default_proguard_files/global/proguard-defaults.txt-8.10.1 @@ -0,0 +1,89 @@ +# This is a configuration file for ProGuard. +# http://proguard.sourceforge.net/index.html#manual/usage.html +# +# Starting with version 2.2 of the Android plugin for Gradle, this file is distributed together with +# the plugin and unpacked at build-time. The files in $ANDROID_HOME are no longer maintained and +# will be ignored by new version of the Android plugin for Gradle. + +# Optimizations can be turned on and off in the 'postProcessing' DSL block. +# The configuration below is applied if optimizations are enabled. +-allowaccessmodification + +# Preserve some attributes that may be required for reflection. +-keepattributes AnnotationDefault, + EnclosingMethod, + InnerClasses, + RuntimeVisibleAnnotations, + RuntimeVisibleParameterAnnotations, + RuntimeVisibleTypeAnnotations, + Signature + +-keep public class com.google.vending.licensing.ILicensingService +-keep public class com.android.vending.licensing.ILicensingService +-keep public class com.google.android.vending.licensing.ILicensingService +-dontnote com.android.vending.licensing.ILicensingService +-dontnote com.google.vending.licensing.ILicensingService +-dontnote com.google.android.vending.licensing.ILicensingService + +# For native methods, see https://www.guardsquare.com/manual/configuration/examples#native +-keepclasseswithmembernames,includedescriptorclasses class * { + native ; +} + +# Keep setters in Views so that animations can still work. +-keepclassmembers public class * extends android.view.View { + void set*(***); + *** get*(); +} + +# We want to keep methods in Activity that could be used in the XML attribute onClick. +-keepclassmembers class * extends android.app.Activity { + public void *(android.view.View); +} + +# For enumeration classes, see https://www.guardsquare.com/manual/configuration/examples#enumerations +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +-keepclassmembers class * implements android.os.Parcelable { + public static final ** CREATOR; +} + +# Preserve annotated Javascript interface methods. +-keepclassmembers class * { + @android.webkit.JavascriptInterface ; +} + +# The support libraries contains references to newer platform versions. +# Don't warn about those in case this app is linking against an older +# platform version. We know about them, and they are safe. +-dontnote android.support.** +-dontnote androidx.** +-dontwarn android.support.** +-dontwarn androidx.** + +# Understand the @Keep support annotation. +-keep class android.support.annotation.Keep + +-keep @android.support.annotation.Keep class * {*;} + +-keepclasseswithmembers class * { + @android.support.annotation.Keep ; +} + +-keepclasseswithmembers class * { + @android.support.annotation.Keep ; +} + +-keepclasseswithmembers class * { + @android.support.annotation.Keep (...); +} + +# These classes are duplicated between android.jar and org.apache.http.legacy.jar. +-dontnote org.apache.http.** +-dontnote android.net.http.** + +# These classes are duplicated between android.jar and core-lambda-stubs.jar. +-dontnote java.lang.invoke.** diff --git a/libservice/build/intermediates/full_jar/release/createFullJarRelease/full.jar b/libservice/build/intermediates/full_jar/release/createFullJarRelease/full.jar new file mode 100644 index 0000000..6fb8259 Binary files /dev/null and b/libservice/build/intermediates/full_jar/release/createFullJarRelease/full.jar differ diff --git a/libservice/build/intermediates/incremental/lintVitalAnalyzeRelease/module.xml b/libservice/build/intermediates/incremental/lintVitalAnalyzeRelease/module.xml new file mode 100644 index 0000000..51ce3aa --- /dev/null +++ b/libservice/build/intermediates/incremental/lintVitalAnalyzeRelease/module.xml @@ -0,0 +1,19 @@ + + + + diff --git a/libservice/build/intermediates/incremental/lintVitalAnalyzeRelease/release-artifact-dependencies.xml b/libservice/build/intermediates/incremental/lintVitalAnalyzeRelease/release-artifact-dependencies.xml new file mode 100644 index 0000000..75ba889 --- /dev/null +++ b/libservice/build/intermediates/incremental/lintVitalAnalyzeRelease/release-artifact-dependencies.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + diff --git a/libservice/build/intermediates/incremental/lintVitalAnalyzeRelease/release-artifact-libraries.xml b/libservice/build/intermediates/incremental/lintVitalAnalyzeRelease/release-artifact-libraries.xml new file mode 100644 index 0000000..3cc905f --- /dev/null +++ b/libservice/build/intermediates/incremental/lintVitalAnalyzeRelease/release-artifact-libraries.xml @@ -0,0 +1,31 @@ + + + + + + + diff --git a/libservice/build/intermediates/incremental/lintVitalAnalyzeRelease/release.xml b/libservice/build/intermediates/incremental/lintVitalAnalyzeRelease/release.xml new file mode 100644 index 0000000..a9638ca --- /dev/null +++ b/libservice/build/intermediates/incremental/lintVitalAnalyzeRelease/release.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + diff --git a/libservice/build/intermediates/incremental/mergeReleaseAssets/merger.xml b/libservice/build/intermediates/incremental/mergeReleaseAssets/merger.xml new file mode 100644 index 0000000..61d59c6 --- /dev/null +++ b/libservice/build/intermediates/incremental/mergeReleaseAssets/merger.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/libservice/build/intermediates/incremental/mergeReleaseJniLibFolders/merger.xml b/libservice/build/intermediates/incremental/mergeReleaseJniLibFolders/merger.xml new file mode 100644 index 0000000..14b7453 --- /dev/null +++ b/libservice/build/intermediates/incremental/mergeReleaseJniLibFolders/merger.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/libservice/build/intermediates/incremental/mergeReleaseShaders/merger.xml b/libservice/build/intermediates/incremental/mergeReleaseShaders/merger.xml new file mode 100644 index 0000000..e3ad3ed --- /dev/null +++ b/libservice/build/intermediates/incremental/mergeReleaseShaders/merger.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/libservice/build/intermediates/incremental/release-mergeJavaRes/merge-state b/libservice/build/intermediates/incremental/release-mergeJavaRes/merge-state new file mode 100644 index 0000000..1c983fc Binary files /dev/null and b/libservice/build/intermediates/incremental/release-mergeJavaRes/merge-state differ diff --git a/libservice/build/intermediates/incremental/release/packageReleaseResources/compile-file-map.properties b/libservice/build/intermediates/incremental/release/packageReleaseResources/compile-file-map.properties new file mode 100644 index 0000000..eb7653a --- /dev/null +++ b/libservice/build/intermediates/incremental/release/packageReleaseResources/compile-file-map.properties @@ -0,0 +1 @@ +#Tue Mar 10 16:09:39 CST 2026 diff --git a/libservice/build/intermediates/incremental/release/packageReleaseResources/merger.xml b/libservice/build/intermediates/incremental/release/packageReleaseResources/merger.xml new file mode 100644 index 0000000..6b49f2f --- /dev/null +++ b/libservice/build/intermediates/incremental/release/packageReleaseResources/merger.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/libservice/build/intermediates/javac/release/compileReleaseJavaWithJavac/classes/com/cocos/service/SDKWrapper$1.class b/libservice/build/intermediates/javac/release/compileReleaseJavaWithJavac/classes/com/cocos/service/SDKWrapper$1.class new file mode 100644 index 0000000..8421648 Binary files /dev/null and b/libservice/build/intermediates/javac/release/compileReleaseJavaWithJavac/classes/com/cocos/service/SDKWrapper$1.class differ diff --git a/libservice/build/intermediates/javac/release/compileReleaseJavaWithJavac/classes/com/cocos/service/SDKWrapper$SDKInterface.class b/libservice/build/intermediates/javac/release/compileReleaseJavaWithJavac/classes/com/cocos/service/SDKWrapper$SDKInterface.class new file mode 100644 index 0000000..3628038 Binary files /dev/null and b/libservice/build/intermediates/javac/release/compileReleaseJavaWithJavac/classes/com/cocos/service/SDKWrapper$SDKInterface.class differ diff --git a/libservice/build/intermediates/javac/release/compileReleaseJavaWithJavac/classes/com/cocos/service/SDKWrapper$SDKWrapperInstance.class b/libservice/build/intermediates/javac/release/compileReleaseJavaWithJavac/classes/com/cocos/service/SDKWrapper$SDKWrapperInstance.class new file mode 100644 index 0000000..3fa8a9b Binary files /dev/null and b/libservice/build/intermediates/javac/release/compileReleaseJavaWithJavac/classes/com/cocos/service/SDKWrapper$SDKWrapperInstance.class differ diff --git a/libservice/build/intermediates/javac/release/compileReleaseJavaWithJavac/classes/com/cocos/service/SDKWrapper.class b/libservice/build/intermediates/javac/release/compileReleaseJavaWithJavac/classes/com/cocos/service/SDKWrapper.class new file mode 100644 index 0000000..e04780d Binary files /dev/null and b/libservice/build/intermediates/javac/release/compileReleaseJavaWithJavac/classes/com/cocos/service/SDKWrapper.class differ diff --git a/libservice/build/intermediates/lint-cache/lintVitalAnalyzeRelease/lint-cache-version.txt b/libservice/build/intermediates/lint-cache/lintVitalAnalyzeRelease/lint-cache-version.txt new file mode 100644 index 0000000..0b53a2d --- /dev/null +++ b/libservice/build/intermediates/lint-cache/lintVitalAnalyzeRelease/lint-cache-version.txt @@ -0,0 +1 @@ +Cache for Android Lint31.10.1 diff --git a/libservice/build/intermediates/lint_model/release/generateReleaseLintModel/module.xml b/libservice/build/intermediates/lint_model/release/generateReleaseLintModel/module.xml new file mode 100644 index 0000000..51ce3aa --- /dev/null +++ b/libservice/build/intermediates/lint_model/release/generateReleaseLintModel/module.xml @@ -0,0 +1,19 @@ + + + + diff --git a/libservice/build/intermediates/lint_model/release/generateReleaseLintModel/release-artifact-dependencies.xml b/libservice/build/intermediates/lint_model/release/generateReleaseLintModel/release-artifact-dependencies.xml new file mode 100644 index 0000000..19a761e --- /dev/null +++ b/libservice/build/intermediates/lint_model/release/generateReleaseLintModel/release-artifact-dependencies.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + diff --git a/libservice/build/intermediates/lint_model/release/generateReleaseLintModel/release-artifact-libraries.xml b/libservice/build/intermediates/lint_model/release/generateReleaseLintModel/release-artifact-libraries.xml new file mode 100644 index 0000000..8094328 --- /dev/null +++ b/libservice/build/intermediates/lint_model/release/generateReleaseLintModel/release-artifact-libraries.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/libservice/build/intermediates/lint_model/release/generateReleaseLintModel/release.xml b/libservice/build/intermediates/lint_model/release/generateReleaseLintModel/release.xml new file mode 100644 index 0000000..d025e5b --- /dev/null +++ b/libservice/build/intermediates/lint_model/release/generateReleaseLintModel/release.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + diff --git a/libservice/build/intermediates/lint_model_metadata/release/writeReleaseLintModelMetadata/lint-model-metadata.properties b/libservice/build/intermediates/lint_model_metadata/release/writeReleaseLintModelMetadata/lint-model-metadata.properties new file mode 100644 index 0000000..cd8efb2 --- /dev/null +++ b/libservice/build/intermediates/lint_model_metadata/release/writeReleaseLintModelMetadata/lint-model-metadata.properties @@ -0,0 +1,3 @@ +mavenArtifactId=libservice +mavenGroupId=magotest +mavenVersion=unspecified \ No newline at end of file diff --git a/libservice/build/intermediates/lint_vital_lint_model/release/generateReleaseLintVitalModel/module.xml b/libservice/build/intermediates/lint_vital_lint_model/release/generateReleaseLintVitalModel/module.xml new file mode 100644 index 0000000..51ce3aa --- /dev/null +++ b/libservice/build/intermediates/lint_vital_lint_model/release/generateReleaseLintVitalModel/module.xml @@ -0,0 +1,19 @@ + + + + diff --git a/libservice/build/intermediates/lint_vital_lint_model/release/generateReleaseLintVitalModel/release-artifact-dependencies.xml b/libservice/build/intermediates/lint_vital_lint_model/release/generateReleaseLintVitalModel/release-artifact-dependencies.xml new file mode 100644 index 0000000..19a761e --- /dev/null +++ b/libservice/build/intermediates/lint_vital_lint_model/release/generateReleaseLintVitalModel/release-artifact-dependencies.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + diff --git a/libservice/build/intermediates/lint_vital_lint_model/release/generateReleaseLintVitalModel/release-artifact-libraries.xml b/libservice/build/intermediates/lint_vital_lint_model/release/generateReleaseLintVitalModel/release-artifact-libraries.xml new file mode 100644 index 0000000..8094328 --- /dev/null +++ b/libservice/build/intermediates/lint_vital_lint_model/release/generateReleaseLintVitalModel/release-artifact-libraries.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/libservice/build/intermediates/lint_vital_lint_model/release/generateReleaseLintVitalModel/release.xml b/libservice/build/intermediates/lint_vital_lint_model/release/generateReleaseLintVitalModel/release.xml new file mode 100644 index 0000000..abcad71 --- /dev/null +++ b/libservice/build/intermediates/lint_vital_lint_model/release/generateReleaseLintVitalModel/release.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + diff --git a/libservice/build/intermediates/lint_vital_partial_results/release/lintVitalAnalyzeRelease/out/lint-resources.xml b/libservice/build/intermediates/lint_vital_partial_results/release/lintVitalAnalyzeRelease/out/lint-resources.xml new file mode 100644 index 0000000..e69de29 diff --git a/libservice/build/intermediates/local_aar_for_lint/release/out.aar b/libservice/build/intermediates/local_aar_for_lint/release/out.aar new file mode 100644 index 0000000..b0e4fcf Binary files /dev/null and b/libservice/build/intermediates/local_aar_for_lint/release/out.aar differ diff --git a/libservice/build/intermediates/local_only_symbol_list/release/parseReleaseLocalResources/R-def.txt b/libservice/build/intermediates/local_only_symbol_list/release/parseReleaseLocalResources/R-def.txt new file mode 100644 index 0000000..78ac5b8 --- /dev/null +++ b/libservice/build/intermediates/local_only_symbol_list/release/parseReleaseLocalResources/R-def.txt @@ -0,0 +1,2 @@ +R_DEF: Internal format may change without notice +local diff --git a/libservice/build/intermediates/manifest_merge_blame_file/release/processReleaseManifest/manifest-merger-blame-release-report.txt b/libservice/build/intermediates/manifest_merge_blame_file/release/processReleaseManifest/manifest-merger-blame-release-report.txt new file mode 100644 index 0000000..1aa59dc --- /dev/null +++ b/libservice/build/intermediates/manifest_merge_blame_file/release/processReleaseManifest/manifest-merger-blame-release-report.txt @@ -0,0 +1,7 @@ +1 +2 +4 +5 +6 +7 diff --git a/libservice/build/intermediates/merged_java_res/release/mergeReleaseJavaResource/feature-libservice.jar b/libservice/build/intermediates/merged_java_res/release/mergeReleaseJavaResource/feature-libservice.jar new file mode 100644 index 0000000..15cb0ec Binary files /dev/null and b/libservice/build/intermediates/merged_java_res/release/mergeReleaseJavaResource/feature-libservice.jar differ diff --git a/libservice/build/intermediates/merged_manifest/release/processReleaseManifest/AndroidManifest.xml b/libservice/build/intermediates/merged_manifest/release/processReleaseManifest/AndroidManifest.xml new file mode 100644 index 0000000..8f53dcc --- /dev/null +++ b/libservice/build/intermediates/merged_manifest/release/processReleaseManifest/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/libservice/build/intermediates/navigation_json/release/extractDeepLinksRelease/navigation.json b/libservice/build/intermediates/navigation_json/release/extractDeepLinksRelease/navigation.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/libservice/build/intermediates/navigation_json/release/extractDeepLinksRelease/navigation.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/libservice/build/intermediates/nested_resources_validation_report/release/generateReleaseResources/nestedResourcesValidationReport.txt b/libservice/build/intermediates/nested_resources_validation_report/release/generateReleaseResources/nestedResourcesValidationReport.txt new file mode 100644 index 0000000..08f4ebe --- /dev/null +++ b/libservice/build/intermediates/nested_resources_validation_report/release/generateReleaseResources/nestedResourcesValidationReport.txt @@ -0,0 +1 @@ +0 Warning/Error \ No newline at end of file diff --git a/libservice/build/intermediates/runtime_library_classes_jar/release/bundleLibRuntimeToJarRelease/classes.jar b/libservice/build/intermediates/runtime_library_classes_jar/release/bundleLibRuntimeToJarRelease/classes.jar new file mode 100644 index 0000000..11fc624 Binary files /dev/null and b/libservice/build/intermediates/runtime_library_classes_jar/release/bundleLibRuntimeToJarRelease/classes.jar differ diff --git a/libservice/build/intermediates/symbol_list_with_package_name/release/generateReleaseRFile/package-aware-r.txt b/libservice/build/intermediates/symbol_list_with_package_name/release/generateReleaseRFile/package-aware-r.txt new file mode 100644 index 0000000..d98b5bc --- /dev/null +++ b/libservice/build/intermediates/symbol_list_with_package_name/release/generateReleaseRFile/package-aware-r.txt @@ -0,0 +1 @@ +com.cocos.service diff --git a/libservice/build/outputs/logs/manifest-merger-release-report.txt b/libservice/build/outputs/logs/manifest-merger-release-report.txt new file mode 100644 index 0000000..9e55c87 --- /dev/null +++ b/libservice/build/outputs/logs/manifest-merger-release-report.txt @@ -0,0 +1,16 @@ +-- Merging decision tree log --- +manifest +ADDED from E:\project\okey_an_test\libservice\AndroidManifest.xml:1:1-3:12 +INJECTED from E:\project\okey_an_test\libservice\AndroidManifest.xml:1:1-3:12 + package + INJECTED from E:\project\okey_an_test\libservice\AndroidManifest.xml + xmlns:android + ADDED from E:\project\okey_an_test\libservice\AndroidManifest.xml:1:11-69 +uses-sdk +INJECTED from E:\project\okey_an_test\libservice\AndroidManifest.xml reason: use-sdk injection requested +INJECTED from E:\project\okey_an_test\libservice\AndroidManifest.xml +INJECTED from E:\project\okey_an_test\libservice\AndroidManifest.xml + android:targetSdkVersion + INJECTED from E:\project\okey_an_test\libservice\AndroidManifest.xml + android:minSdkVersion + INJECTED from E:\project\okey_an_test\libservice\AndroidManifest.xml diff --git a/libservice/build/tmp/compileReleaseJavaWithJavac/previous-compilation-data.bin b/libservice/build/tmp/compileReleaseJavaWithJavac/previous-compilation-data.bin new file mode 100644 index 0000000..0bc14e7 Binary files /dev/null and b/libservice/build/tmp/compileReleaseJavaWithJavac/previous-compilation-data.bin differ