Compare commits

...

174 Commits
1.0.9 ... main

Author SHA1 Message Date
胡宇飞 58cc5a3b46 fix: 规范文件缩进 2024-08-15 16:09:45 +08:00
胡宇飞 1ef9e3ccec update: 确保 SDK 的兼容性, 添加获取 FirebaseUser 的属性(但标记即将废弃) 2024-08-15 13:53:44 +08:00
胡宇飞 88efffa89f update: 增加 Launcher 模版的 namespace 属性 2024-08-15 13:21:24 +08:00
胡宇飞 018fb2c35b update: 删除无用的属性 2024-08-11 21:31:25 +08:00
胡宇飞 0870e71cf9 Update: 更新sdk_info打点位置 2024-08-11 19:35:56 +08:00
胡宇飞 15785b92a2 update: 自动上报 b_play 和 b_level 的方式默认值改为 false 2024-08-11 18:43:13 +08:00
胡宇飞 a67ba09408 update: 更新上报 SDK Info 的时机 2024-08-11 17:16:35 +08:00
胡宇飞 6dbbd27eb7 update: 优化 FirebaseUser 的回调机制 2024-08-11 15:35:43 +08:00
胡宇飞 643bb23939 update: 更新 Crashlytics 上报接口 2024-08-11 15:03:13 +08:00
胡宇飞 b0670ccafc update: 添加 sdk_version 属性打点, CodeReview 2024-08-09 18:21:01 +08:00
胡宇飞 7214c447a6 Fix: 修复中台数据存储的 BUG。 调整写入策略 2024-08-09 14:29:21 +08:00
胡宇飞 49774c3281 update: 还原 GuruSDKModel 缓存key 值, 确保老版本升级后数据不会丢失 2024-08-09 10:02:26 +08:00
胡宇飞 c001fe09ac update: 重构用户属性打点代码, CodeReview 2024-08-09 02:40:36 +08:00
胡宇飞 a063c4866d update: 调整SDK执行时序 2024-08-08 20:54:55 +08:00
胡宇飞 3062467e84 update: 完善初始化属性 2024-08-08 00:01:10 +08:00
胡宇飞 d4f5342e7f update: 更新函数名称 2024-08-07 23:17:52 +08:00
胡宇飞 f099a99c95 update: 添加GuruSDK 清空数据的接口 2024-08-07 23:14:15 +08:00
胡宇飞 6b95c2ab2f fix: 修复上报BPlay字段错误 2024-08-07 22:36:07 +08:00
胡宇飞 06792d098f fix:补全对外的各种用户属性接口 2024-08-07 22:17:53 +08:00
胡宇飞 af1ec1e04f update: GuruSDK 内部不再调用 SetUserProperty 2024-08-07 22:10:22 +08:00
胡宇飞 2494bd2748 fix: 修复打点错误 2024-08-07 19:47:51 +08:00
胡宇飞 5c3be01eb3 update:更新Model 内 FirstOpenTime 的时间获取 2024-08-07 19:33:12 +08:00
胡宇飞 149bc438ac update: 更新打点 key 名称 2024-08-07 17:55:58 +08:00
胡宇飞 e814324e98 update: 更新 AdjustServices 的引用 2024-08-07 13:49:07 +08:00
胡宇飞 46cd1717fb update: 新增获取 自打点实验 GroupId 的值 2024-08-07 11:08:17 +08:00
胡宇飞 aec21f3789 update: 重构用户属性上报逻辑, CodeReview 2024-08-07 03:28:44 +08:00
胡宇飞 cc65e9173c update: 完善设置 UID 的调用逻辑 2024-08-05 22:37:15 +08:00
胡宇飞 2bde58e27e update: 完善 GuruInitConfig 的 Builder 写法 2024-08-02 11:40:05 +08:00
胡宇飞 b3c541cb84 update: 设置 Banner 的默认值底色 2024-08-01 17:40:52 +08:00
胡宇飞 ceeeeaf15f update: 添加 Adjust Signature V3 文件自动导入的指令
--story=1021227 --user=yufei.hu 【中台】【变现】添加 Adjust Signature V3 文件导入功能 https://www.tapd.cn/33527076/s/1161236
2024-08-01 15:40:37 +08:00
胡宇飞 257f9796a5 update: 自打点升级 1.1.1, 外层代码优化
--story=1021175 --user=yufei.hu 【中台】【自打点】自打点升级 1.1.1, 并添加云控数据获取逻辑 https://www.tapd.cn/33527076/s/1161159
2024-08-01 12:45:33 +08:00
胡宇飞 fd01939a5f update: 重构 networkstatus 方法和规范值 2024-08-01 00:17:01 +08:00
胡宇飞 48c5235335 update: 优化 GuruSDK 初始化配置。 添加 Builder 模式 2024-07-31 20:59:36 +08:00
胡宇飞 42c7302ea3 update: GuruSDK 回调优化, codereview 2024-07-31 12:56:01 +08:00
胡宇飞 4da06c8f96 update: deeplink 接口代码 Review
--story=1021116 --user=yufei.hu 【中台】【SDK】新增 Adjust Deeplink 接口 https://www.tapd.cn/33527076/s/1160715
2024-07-30 20:31:41 +08:00
胡宇飞 ecf615598c update: JsonBuilder 新增 FindMaster 配置 2024-07-30 16:12:12 +08:00
胡宇飞 0a2fca05ab update: 更新 proguard-user 配置 2024-07-30 14:58:46 +08:00
胡宇飞 555638e31e fix: 完善 Android项目自动修改 Unity Project 路径 2024-07-29 21:05:20 +08:00
胡宇飞 3555b70ea7 update: 更新 AndroidProjectMod 以修复打包时报错和各种问题
--story=1020956 --user=yufei.hu 【中台】【Android】构建插件 API 升级 34 验证 https://www.tapd.cn/33527076/s/1160300
2024-07-29 19:05:33 +08:00
胡宇飞 314aaaa88b fix: 优化用户属性打点逻辑 2024-07-26 19:18:00 +08:00
胡宇飞 4246251295 update: 补全默认的用户属性打点 添加 network 属性 2024-07-26 14:38:55 +08:00
胡宇飞 1e59e5efa6 fix: 修复 Notification 授权在 iOS 上自动触发的 BUG。 2024-07-26 11:53:21 +08:00
胡宇飞 4721e027b2 update: 用户属性上报添加 Network 属性
--story=1021014 --user=yufei.hu 【中台】【SDK】用户属性上报添加 Network 属性 https://www.tapd.cn/33527076/s/1159710
2024-07-26 11:08:31 +08:00
胡宇飞 b9291e7885 update: 打点初始化重构,添加日志缓存功能
--story=1021118 --user=yufei.hu 【中台】【打点】打点初始化重构,添加日志缓存功能 https://www.tapd.cn/33527076/s/1159704
2024-07-25 23:00:22 +08:00
胡宇飞 3050b67a31 Merge branch 'deeplink' into dev
--story=1021116 --user=yufei.hu 【中台】【SDK】新增 Adjust Deeplink 接口 https://www.tapd.cn/33527076/s/1159389
# Conflicts:
#	Runtime/Code/SDK/GuruSDK.cs
2024-07-25 11:27:53 +08:00
胡宇飞 9f0ba03957 update: 打点逻辑优化:初始化流程, 属性和打点缓存
--story=1021114 --user=yufei.hu 【中台】打点逻辑优化:初始化流程, 属性缓存 (QA 无需测试) https://www.tapd.cn/33527076/s/1159381
2024-07-25 11:17:34 +08:00
胡宇飞 3de35b69e5 update: 更新插件版本 1.1.0 , 更新项目Json 配置 2024-07-25 10:17:54 +08:00
胡宇飞 25f4d7e588 Merge branch 'hotfix/v1.0.13' into release/v1.0.14 2024-07-17 09:34:34 +08:00
胡宇飞 1841edcbf3 update: 新增赋值接口 2024-07-17 09:34:23 +08:00
胡宇飞 957f60e797 update: 更新 Adjust 的 DeepLink 功能
--story=1020917 --user=yufei.hu 【中台】【SDK】新增 Adjust Deeplink 接口 https://www.tapd.cn/33527076/s/1156644
2024-07-12 10:29:30 +08:00
胡宇飞 2fc6faf831 fix: 修复 ServiceBuilder 上的输出数据带有换行的 BUG 2024-07-11 13:55:56 +08:00
胡宇飞 4d86931605 update: 修复编译 bug 2024-07-11 13:55:43 +08:00
胡宇飞 804618b12b update: 完善 JsonBuilder 2024-07-11 13:55:36 +08:00
胡宇飞 f85eb8d2bb update: 更新 JsonBuilder 的数据源
# Conflicts:
#	Editor/GuruJsonBuilder/guru-project-settings.cfg
2024-07-11 13:55:23 +08:00
胡宇飞 31991a780b update: add json builder
# Conflicts:
#	Editor/GuruManager/Helper/GuruServiceJsonBuilder.cs
2024-07-11 13:54:34 +08:00
胡宇飞 331a845f02 update: 完善 JsonBuilder
# Conflicts:
#	Editor/GuruJsonBuilder/guru-project-settings.cfg
2024-07-11 13:52:21 +08:00
胡宇飞 d8967001bf fix: 修复 ServiceBuilder 上的输出数据带有换行的 BUG 2024-07-11 13:48:49 +08:00
胡宇飞 f33fc2c764 update: 修复编译 bug
# Conflicts:
#	Editor/GuruManager/Helper/GuruServiceConverterHelper.cs
2024-07-11 13:48:30 +08:00
胡宇飞 9d168cdd46 fix: 修复 GuruSDK.UID 初始化时为空值的 BUG 2024-07-11 12:57:06 +08:00
胡宇飞 6e5ede3958 fix: 修复 GuruSDK.UID 初始化时为空值的 BUG 2024-07-08 19:10:33 +08:00
胡宇飞 2724683377 fix: 修复 ServiceBuilder 上的输出数据带有换行的 BUG 2024-07-04 17:23:07 +08:00
胡宇飞 d89de67e44 update: 添加IV广告和 RV 广告的关闭回调事件 2024-07-02 12:36:15 +08:00
胡宇飞 3be05511e4 update: 修复编译 bug 2024-07-01 18:45:07 +08:00
胡宇飞 6fc0c24823 update: 完善 JsonBuilder 2024-07-01 18:41:22 +08:00
胡宇飞 52b6af9fd0 update: 更新 JsonBuilder 的数据源 2024-07-01 16:06:48 +08:00
胡宇飞 d574ec5757 update: add json builder 2024-06-27 21:13:21 +08:00
胡宇飞 855cc61936 fix: 去掉不需要的报错 2024-06-27 18:37:17 +08:00
胡宇飞 bcaed876f7 update: 更新经济打点接口参数 2024-06-27 17:41:05 +08:00
胡宇飞 42966759b3 update: 更新场景使用接口和名称 2024-06-27 11:28:34 +08:00
胡宇飞 4a39747d3d update:更新 SDK version -> 1.0.14 2024-06-26 14:31:31 +08:00
胡宇飞 d51759bbd1 update: 取消初始化锁限制 2024-06-24 14:35:30 +08:00
胡宇飞 68e90a4ed9 【中台】【SDK】完善经济打点接口
--story=1020672 --user=yufei.hu 【中台】【SDK】完善经济打点接口 https://www.tapd.cn/33527076/s/1152293
2024-06-24 10:17:26 +08:00
胡宇飞 3d8bc1c642 update: 更新推送状态接口和对应的log
--story=1020629 --user=yufei.hu 【中台】【SDK】加入消息弹框管理,中台 noti_perm 打点逻辑优化 https://www.tapd.cn/33527076/s/1152197
2024-06-21 16:52:11 +08:00
胡宇飞 338c886ef6 update: version 2024-06-21 14:39:55 +08:00
胡宇飞 fa3ec5adef update: 新增 Notification 接口 2024-06-20 18:30:37 +08:00
胡宇飞 cd7074f490 update: 更新 SDK 版本号 2024-06-19 19:52:09 +08:00
胡宇飞 9d907463ee update: 更新 Model 引用, 防止报空 2024-06-18 11:58:42 +08:00
胡宇飞 86742abe46 update: 更新 SDK 自定义的优先级事件 2024-06-18 10:24:43 +08:00
胡宇飞 5306922cca update: 更新 SDK 添加优先级设置接口
--story=1020598 --user=yufei.hu 【中台】【BI】升级自打点插件至 1.12.0, Native 接口添加 priority 参数和功能 https://www.tapd.cn/33527076/s/1151108
2024-06-18 10:14:33 +08:00
胡宇飞 67000cbc63 update: b_level, b_play 参数赋值解耦 2024-06-06 20:09:23 +08:00
胡宇飞 8acd6faf94 update: 为 GuruSDK.Remote 取值添加 defaultValue 参数 2024-06-06 20:09:03 +08:00
胡宇飞 1b97f8ec03 update: 更新广告Banner操作的预处理逻辑 (广告未初始化前) 2024-06-05 17:48:01 +08:00
胡宇飞 529e2ddeda update: IAP 赚取道具, 删除 productId 属性 2024-06-05 17:46:11 +08:00
胡宇飞 68161b82c3 fix: 使用 UUID 默认值设置为 false。 默认使用设备 ID 生成 UID 2024-06-04 19:01:09 +08:00
胡宇飞 0d06131677 fix: 修复文字错误 2024-06-03 17:25:05 +08:00
胡宇飞 051baed476 update: 更新 SDK 注入逻辑 2024-05-31 16:19:20 +08:00
胡宇飞 c3403a335f fix: 规范用户属性接口名称 2024-05-31 14:31:01 +08:00
胡宇飞 2d857fc6a6 update: 更新版本号 2024-05-31 13:41:13 +08:00
胡宇飞 31cb08b6df update: 完善标准化打点接口, 补全自定义事件封装接口
--story=1020273 --user=yufei.hu 【中台】【BI】 中台打点标准化, 更新原有的打点和用户属性上报逻辑 https://www.tapd.cn/33527076/s/1147868
2024-05-30 20:53:50 +08:00
胡宇飞 d70f9c3566 update: 更新版本号 2024-05-30 11:12:02 +08:00
胡宇飞 8b5f42706e update: 添加 Adjust preinstall Tracker
--story=1020232 --user=yufei.hu 【中台】【SDK】添加 Adjust Preinstall Tracker 功能 https://www.tapd.cn/33527076/s/1147510
2024-05-30 09:41:22 +08:00
胡宇飞 0c4d6b31e9 update: 更新打点参数和打点库逻辑
--story=1020273 --user=yufei.hu 【中台】【BI】 中台打点标准化, 更新原有的打点和用户属性上报逻辑 https://www.tapd.cn/33527076/s/1147487
2024-05-29 21:00:43 +08:00
胡宇飞 bc62aec38e update: 完善刷新用户属性逻辑
--story=1020273 --user=yufei.hu 【中台】【BI】 中台打点标准化, 更新原有的打点和用户属性上报逻辑 https://www.tapd.cn/33527076/s/1147487
2024-05-29 19:55:30 +08:00
胡宇飞 2ea2db79c8 Merge branch 'main' into dev 2024-05-28 11:39:09 +08:00
胡宇飞 d6ff9736d9 fix: 删除第一个上报SDK 初始化时间的无效打点 2024-05-28 11:38:28 +08:00
胡宇飞 8932d6893f fix: 删除第一个上报SDK 初始化时间的无效打点 2024-05-28 11:34:25 +08:00
胡宇飞 f2949347df fix: 规范打点参数, 上报IAP虚拟道具的 item_name 值为 sku 2024-05-27 14:33:38 +08:00
胡宇飞 c2be368962 fix: bugs 2024-05-24 16:48:23 +08:00
胡宇飞 8d1afddcd5 fix: 修复启动时自打点没有上报用户属性的 BUG 2024-05-24 16:45:33 +08:00
胡宇飞 5b1097464d update: 添加设置推送消息的接口 2024-05-24 14:48:53 +08:00
胡宇飞 3fdd02c7b6 update: 延迟设置 UID 和 UUID 2024-05-22 21:49:30 +08:00
胡宇飞 9d7fd12cf3 Revert "fix: 更新 IAP 启动的时机"
This reverts commit 093e309e52.
2024-05-22 21:30:05 +08:00
胡宇飞 093e309e52 fix: 更新 IAP 启动的时机 2024-05-22 20:01:23 +08:00
胡宇飞 aad0fd6d76 update: 中台取消打点 iap_clk, 由项目组自行实现 2024-05-22 18:09:17 +08:00
胡宇飞 b5a64eac4e fix: 修改 Unity 控制台入口的命令, 缩短命令长度到 31 之内 2024-05-22 12:54:24 +08:00
胡宇飞 c6d6a1f5d8 fix: 修复 Google API 初始化传uid值 2024-05-21 16:54:25 +08:00
胡宇飞 fe36a57a81 update: 补充一个 is_iap_user 的属性 2024-05-21 11:19:31 +08:00
胡宇飞 6779c6ef73 fix: 恢复金币总量上报数量 2024-05-21 10:22:18 +08:00
胡宇飞 049de5acd3 update: 添加SDK model内置的 no_ad 属性 2024-05-21 08:41:29 +08:00
胡宇飞 bc002d1501 update: 添加 UUID 导入逻辑 2024-05-18 23:00:09 +08:00
胡宇飞 5494e6c3f9 update: 替换支付接口的 UID -> UUID 2024-05-18 21:17:20 +08:00
胡宇飞 7fe982f5bc update: 添加支付数量日志 2024-05-17 20:21:45 +08:00
胡宇飞 852853629e update: 调整支付接口, 记录成功后的属性 2024-05-17 19:40:42 +08:00
胡宇飞 23a2616b95 update: 废弃老的 IAP 支付接口 2024-05-17 19:30:22 +08:00
胡宇飞 777ac87b18 update: 添加 IsIapUser 的属性 2024-05-17 10:28:35 +08:00
胡宇飞 e4f51978d1 fix: 修复 AndroidResMod 导入造成的 Bug 2024-05-16 19:02:12 +08:00
胡宇飞 4db35e10fc fix: 更新用户金币数接口 2024-05-16 18:58:29 +08:00
胡宇飞 a6f0d8d884 fix: 修复注入工具即系 BUG 2024-05-15 17:34:11 +08:00
胡宇飞 5d7e32a9f1 update: 完善接口说明和文档 2024-05-15 09:58:22 +08:00
胡宇飞 8e005c9237 update: 更新接口参数名 2024-05-14 09:54:53 +08:00
胡宇飞 20938c5678 update: 更新 IAP 打点参数, 更新参数名称 2024-05-13 17:20:45 +08:00
胡宇飞 7883d1f0e2 fix: 修复 namespace 问题 2024-05-11 16:27:14 +08:00
胡宇飞 41fcd7746e update: 删除废弃库引用 2024-05-11 15:02:14 +08:00
胡宇飞 0e4a0a68cb update: 为公用打点接口 暴露 extra 属性方便项目扩展 2024-05-11 14:19:11 +08:00
胡宇飞 4cd2e5ff19 update: 升级 CDN Host 属性 2024-05-11 11:34:51 +08:00
胡宇飞 cd3c53d674 + Add First open time user-property 2024-05-11 10:28:58 +08:00
胡宇飞 2cc9d66aa1 + add new iap API method for subscraption. 2024-05-09 20:35:47 +08:00
胡宇飞 ad47a9696b update: 添加 Android Target 33 在 Consent 结束后, 弹出 Push Notification 权限的接口 2024-05-07 21:51:11 +08:00
胡宇飞 e11b61fa88 update: move GuruDebugger to SDKCore. Fix all API Errors. 2024-05-07 20:37:57 +08:00
胡宇飞 50b99d00a2 update: optimization Android permission Mod list (Both added & removed) 2024-04-25 20:53:39 +08:00
胡宇飞 f69245a8c4 update: 添加 Android 权限移除工具 2024-04-23 15:06:36 +08:00
胡宇飞 3097635271 update: 更新打开隐私和服务条款页面的接口 2024-04-23 14:09:35 +08:00
胡宇飞 14340e4f8b update: 更新版本号 2024-04-17 22:55:54 +08:00
胡宇飞 92a05cf9ee update: 更新SDK 信息打点 2024-04-17 21:56:46 +08:00
胡宇飞 5089d82d66 update: 优化 Banner 刷新逻辑 2024-04-17 21:44:09 +08:00
胡宇飞 85ef81ff5e update: 更新广告生命周期的回调 2024-04-17 19:57:11 +08:00
胡宇飞 4743fa298f update: 修复 LevelEndSuccess 打点数据, item_id 上报不准确的问题 2024-04-15 20:22:59 +08:00
胡宇飞 54ef66a97d update: fix Debugger issue 2024-04-15 13:36:14 +08:00
胡宇飞 cdf379be10 update: 更新 经济调用接口 2024-04-10 16:01:00 +08:00
胡宇飞 af54ea3864 update: 更新Debugger 路径和接口 2024-04-10 16:00:41 +08:00
胡宇飞 eba0171628 update: 新增经济打点的接口 2024-04-10 14:08:49 +08:00
胡宇飞 6ae05aad01 update: 优化内置工具接口和生成文档的路径 2024-04-09 21:00:18 +08:00
胡宇飞 75abc82500 update: 修复中台自打点启动点位信息 2024-04-09 18:54:37 +08:00
胡宇飞 860d353b9a update: 添加初始化组件操作 2024-04-09 17:24:41 +08:00
胡宇飞 baae2a6eb4 update: 更新线程控制 2024-04-09 16:58:08 +08:00
胡宇飞 ef3a11c382 update: 添加测试用例 2024-04-09 08:28:19 +08:00
胡宇飞 78f9e3b330 update: 优化 GuruService 导入, 实时更新配置数据 2024-04-09 08:19:56 +08:00
胡宇飞 7b6e8c7cbe update: 添加开启 ErrorLog 的云控标志位 2024-04-07 16:39:07 +08:00
胡宇飞 75909ccc1b update: 更新 GuruSDK 的用户属性默认值赋值 2024-04-07 16:17:46 +08:00
胡宇飞 faa8692667 fix: 启动默认上报用户的 is_iap_user 属性 2024-04-03 12:47:25 +08:00
胡宇飞 93935f87c4 fix: 修复 b_level 累加的问题, 目前完成关卡只会计算最高的 LevelId 作为 b_level 的数据 2024-04-02 15:27:34 +08:00
胡宇飞 311b53ea5f udpate: 初始化参数添加 去广告 和 banner 背景色 2024-03-31 18:36:47 +08:00
胡宇飞 fec486e8a6 update: 更新回调接口, 新增广告初始化默认配置接口 2024-03-31 18:08:26 +08:00
胡宇飞 b2fa553fb7 update: 更新 SDK 授权状态回调, 更新广告背景设置接口 2024-03-31 17:18:43 +08:00
胡宇飞 2217ab8e46 update: 隐藏 KeyStore 配置界面 2024-03-31 15:53:36 +08:00
胡宇飞 d3cf9a1b63 update: 添加云控解析标志位 2024-03-31 14:01:35 +08:00
胡宇飞 ac3f88422a update: 新增设置 KeyStore 功能 2024-03-31 13:59:59 +08:00
胡宇飞 17ea49120a update: 完善 AndroidManifest 文档管理和导入功能 2024-03-31 11:31:50 +08:00
胡宇飞 7bbbf34b60 update: 更新脚本 2024-03-31 10:08:12 +08:00
胡宇飞 0ecf957902 update: 更新推送消息组件助手 2024-03-30 22:54:54 +08:00
胡宇飞 9d08938d77 update: 完善push图标生成工具 2024-03-30 10:21:08 +08:00
胡宇飞 794673c272 Merge branch 'feature/test_suit' into feature/1.10.0 2024-03-27 09:13:37 +08:00
胡宇飞 6d17f595b2 update: 更新 Debugger 接口, 回调 2024-03-22 13:20:54 +08:00
胡宇飞 87ff0521d3 fix: 删除重复接口 2024-03-21 13:42:59 +08:00
胡宇飞 b5b7c46a46 Merge branch 'feature/guru_analytics' into feature/1.10.0
# Conflicts:
#	Runtime/Code/Config/GuruServicesConfig.cs
#	Runtime/Code/SDK/GuruSDK.Ads.cs
#	Runtime/Code/SDK/GuruSDK.IAP.cs
#	Runtime/Code/SDK/GuruSDK.cs
2024-03-21 13:37:56 +08:00
胡宇飞 fb492af633 update: 新增 Crashlytics 上报接口 2024-03-20 15:40:55 +08:00
胡宇飞 407e242b8c update: 新增一个 Firebase 服务初始化的标志位 2024-03-20 14:30:00 +08:00
胡宇飞 01008ca8c7 update: 延迟启动广告服务 2024-03-18 15:38:11 +08:00
胡宇飞 a962f7bb8c update: 初始化广告及对应的日志 2024-03-18 15:03:22 +08:00
胡宇飞 16a99087d9 update: 添加启动广告的快捷方式, 并且添加去广告标志位 2024-03-18 13:00:27 +08:00
胡宇飞 4fadb2b469 update: 添加广告启动初始化配置对象 2024-03-18 13:00:21 +08:00
胡宇飞 1f71522fa6 update: 新增打点和 Firebase 回调逻辑 2024-03-15 12:59:20 +08:00
胡宇飞 19be8b5d48 update: 更新打点和生命周期的日志 2024-03-15 12:27:27 +08:00
胡宇飞 b1bf57cbed update: 更新生命周期和对应的逻辑 2024-03-13 17:26:20 +08:00
78 changed files with 5635 additions and 1241 deletions

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f9c48f29655a4b8bb90520de5a7c2fa4
timeCreated: 1719552474

View File

@ -1,3 +1,4 @@
#if GURU_SDK_DEV
namespace Guru.Editor
{
@ -5,37 +6,69 @@ namespace Guru.Editor
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.Networking;
using System.Collections.Generic;
using System.Linq;
public class GuruServiceConverterHelper
public class GuruServiceJsonBuilder: EditorWindow
{
const string K_APP_SETTINGS = "app_settings";
const string K_ADJUST_SETTINGS = "adjust_settings";
const string K_FB_SETTINGS = "fb_settings";
const string K_AD_SETTINGS = "ad_settings";
const string K_IAP_SETTINGS = "iap_settings";
const char K_SPLITTER_TAB = '\t';
const char K_SPLITTER_COMMA = ',';
const string GuruProjectSettingsName = "guru-project-settings.cfg";
// Tool Version
public const string Version = "0.2.0";
private const string K_APP_SETTINGS = "app_settings";
private const string K_ADJUST_SETTINGS = "adjust_settings";
private const string K_FB_SETTINGS = "fb_settings";
private const string K_AD_SETTINGS = "ad_settings";
private const string K_IAP_SETTINGS = "iap_settings";
private const char K_SPLITTER_TAB = '\t';
private const char K_SPLITTER_COMMA = ',';
private const string NoSelectionName = "------";
private const string STATE_IDLE = "s_idle";
private const string STATE_LOADING = "s_loading";
private const string STATE_SUCCESS = "s_success";
private static GuruServiceJsonBuilder _instance;
private List<string> _projectNames;
private int _selectedProjectIndex = 0;
private static Dictionary<string, string> _publishLinks;
public static Dictionary<string, string> PublishLinks
{
get
{
if (_publishLinks == null) _publishLinks = LoadProjectSettingsCfg();
return _publishLinks;
}
}
#region Link & Keys
private const string TSVLink = "https://docs.google.com/spreadsheets/d/e/{0}/pub?gid=0&single=true&output=tsv";
#endregion
#region Export JSON
/// <summary>
/// 从 TSV 文件进行转化
/// </summary>
/// <param name="tsvPath"></param>
/// <param name="tsv"></param>
/// <param name="savePath"></param>
public static void ConvertFromTSV(string tsvPath, string savePath = "")
public static void ConvertFromTSV(string tsv, string savePath = "")
{
if (!File.Exists(tsvPath))
if (string.IsNullOrEmpty(tsv))
{
EditorUtility.DisplayDialog("FILE NOT FOUND!", $"File not exist:\n{tsvPath}", "OK");
EditorUtility.DisplayDialog("空文件!", $"文件格式错误!\n{tsv}", "OK");
return;
}
var guru_service = EditorGuruServiceIO.CreateEmpty();
var lines = File.ReadAllLines(tsvPath);
var lines = tsv.Split('\n');
string line = "";
for (int index = 0; index < lines.Length; index++)
{
@ -81,16 +114,45 @@ namespace Guru.Editor
}
}
guru_service.version = GetFileVersionByDate();
if (string.IsNullOrEmpty(savePath))
{
var dir = Path.GetFullPath($"{Application.dataPath}/../output");
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
savePath = $"{dir}/guru-services-{guru_service.app_settings.app_id.ToLower()}.json";
}
var arr = savePath.Split('/');
var fileName = arr[arr.Length - 1];
EditorGuruServiceIO.SourceServiceFilePath = savePath;
EditorGuruServiceIO.SaveConfig(guru_service, savePath);
if (EditorUtility.DisplayDialog("CONVERT SUCCESS!", $"Export Json to:\n{savePath}", "OK"))
if (EditorUtility.DisplayDialog("CONVERT SUCCESS!", $"Export Json File\n{fileName}\nto:\n{savePath}", "OK"))
{
GuruEditorHelper.OpenPath(Directory.GetParent(savePath)?.FullName ?? Application.dataPath);
}
}
public static void ConvertFromTsvFile(string tsvPath, string savePath = "")
{
if (!File.Exists(tsvPath))
{
EditorUtility.DisplayDialog("FILE NOT FOUND!", $"File not exist:\n{tsvPath}", "OK");
return;
}
var tsvString = File.ReadAllText(tsvPath);
if (!string.IsNullOrEmpty(tsvString))
{
ConvertFromTSV(tsvString, savePath);
}
}
/// <summary>
/// AppSettings 填充
/// </summary>
@ -100,10 +162,12 @@ namespace Guru.Editor
{
// 对于空行和空值直接跳过
if (IsInvalidLine(line)) return;
string value = "";
if(settings.app_settings == null) settings.app_settings = new GuruAppSettings();
if (settings.app_settings == null) settings.app_settings = new GuruAppSettings();
if (settings.parameters == null) settings.parameters = new GuruParameters();
//------------------- AppSettings -------------------------------
// 拾取值和注入
if (GetValue(line, "app_id", out value))
{
@ -153,9 +217,33 @@ namespace Guru.Editor
{
settings.app_settings.enable_iap = GetBool(value);
}
else if (GetValue(line, "custom_keystore", out value))
{
settings.app_settings.custom_keystore = GetBool(value);
}
//------------------- Parameters -------------------------------
else if (GetValue(line, "tch_020", out value))
{
settings.parameters.tch_020 = GetDouble(value);
}
else if (GetValue(line, "using_uuid", out value))
{
settings.parameters.using_uuid = GetBool(value);
}
else if (GetValue(line, "cdn_host", out value))
{
settings.parameters.cdn_host = value;
}
else if (GetValue(line, "enable_errorlog", out value))
{
settings.parameters.enable_errorlog = GetBool(value);
}
else if (GetValue(line, "level_end_success_num", out value))
{
settings.parameters.level_end_success_num = GetInt(value);
}
}
/// <summary>
/// AdjustSettings 填充
/// </summary>
@ -193,7 +281,14 @@ namespace Guru.Editor
settings.adjust_settings.events = events.ToArray();
index--;
}
private static long GetFileVersionByDate()
{
var startDt = new DateTime(1970,1,1,0,0,0);
return (long) (DateTime.UtcNow.Ticks - startDt.Ticks) / 10000;
}
private static void FillFacebookSettings(GuruServicesConfig settings, string[] lines, ref int index)
{
string value = "";
@ -384,15 +479,12 @@ namespace Guru.Editor
arr = GetStringArray(line, 0, 7);
if(string.IsNullOrEmpty(arr[5])) arr[5] = "Store";
if(string.IsNullOrEmpty(arr[6])) arr[6] = "0";
iaps.Add(string.Join("," , arr));
iaps.Add(string.Join(",", arr).Replace("\r", ""));
index++;
}
settings.products = iaps.ToArray();
index--;
}
#endregion
@ -468,19 +560,304 @@ namespace Guru.Editor
private static void ExportJsonFile()
{
string saveDir = Path.GetFullPath($"{Application.dataPath}/../output");
string saveFile = Path.Combine(saveDir,$"guru-service____{DateTime.Now:yyyy-M-d-HH-mm}.json");
string saveFile = Path.Combine(saveDir,$"guru-services____{DateTime.Now:yyyy-M-d-HH-mm}.json");
if(!Directory.Exists(saveDir)) Directory.CreateDirectory(saveDir);
string searchPath = "~/Downloads/";
string tsv = EditorUtility.OpenFilePanel("Load Guru Service TSV", searchPath, ".tsv");
if (!string.IsNullOrEmpty(tsv))
string tsvPath = EditorUtility.OpenFilePanel("Load Guru Service TSV", searchPath, ".tsv");
if (!string.IsNullOrEmpty(tsvPath))
{
GuruServiceConverterHelper.ConvertFromTSV(tsv, saveFile);
ConvertFromTsvFile(tsvPath, saveFile);
}
}
[MenuItem("Guru/Guru-Service Json Builder...", false, 0)]
private static void OpenWindow()
{
if(_instance != null ) _instance.Close();
_instance = GetWindow<GuruServiceJsonBuilder>();
_instance.Show();
}
#endregion
#region Settings
private static string GetRelativeDir()
{
var guids = AssetDatabase.FindAssets(nameof(GuruServiceJsonBuilder));
if (guids.Length > 0)
{
var path = AssetDatabase.GUIDToAssetPath(guids[0]);
var rpath = Directory.GetParent(path).FullName;
return rpath;
}
return Path.GetFullPath($"Assets/../Packages/Editor/GuruJsonBuilder");
}
private static Dictionary<string, string> LoadProjectSettingsCfg()
{
var cfgPath = $"{GetRelativeDir()}/{GuruProjectSettingsName}";
if (File.Exists(cfgPath))
{
var lines = File.ReadAllLines(cfgPath);
int len = lines?.Length ?? -1;
if (len > 0)
{
Dictionary<string, string> dict = new Dictionary<string, string>(lines.Length);
int i = 0;
string[] raw;
string line, key, value;
while (i < len)
{
line = lines[i];
if(string.IsNullOrEmpty(line)) continue;
raw = lines[i].Split(',');
value = "";
key = raw[0];
if(string.IsNullOrEmpty(key)) continue;
if(raw.Length > 1) value = raw[1];
dict[key] = value;
i++;
}
return dict;
}
}
return null;
}
#endregion
#region Window
private void Awake()
{
Debug.Log($"------- Awake -------");
this.titleContent = new GUIContent("Json Builder");
_projectNames = new List<string>(20);
string[] names = PublishLinks.Keys.ToArray();
string name = "";
for(int i = 0; i < names.Length; i++)
{
name = names[i];
if (name == "Default")
{
_projectNames.Insert(0, NoSelectionName);
}
else
{
_projectNames.Add(name);
}
}
_selectedProjectIndex = 0;
_state = STATE_IDLE;
var json = LoadProjectSettingsCfg();
Debug.Log(json);
}
private void OnEnable()
{
// Debug.Log($"------- OnEnable -------");
}
private void OnGUI()
{
GUI_Title();
switch (_state)
{
case STATE_IDLE:
GUI_Projects();
break;
case STATE_LOADING:
GUI_Loading();
break;
}
}
#endregion
#region GUI
private string _state = "";
private void GUI_Title()
{
var s = new GUIStyle("box")
{
fontSize = 20,
fontStyle = FontStyle.Bold,
stretchWidth = true,
alignment = TextAnchor.MiddleCenter,
padding = new RectOffset(4, 4, 4, 4),
};
GUILayout.Label("Guru-Service Json Builder", s, GUILayout.Height(60));
s.fontSize = 14;
GUILayout.Label($"Version: {Version}", s);
GUILayout.Space(10);
}
private void GUI_Loading()
{
var s = new GUIStyle("box")
{
fontSize = 16,
alignment = TextAnchor.MiddleCenter,
stretchWidth = true,
padding = new RectOffset(4, 4, 4, 4),
};
GUILayout.Label("Loading...", s);
GUILayout.Space(10);
}
private void GUI_Projects()
{
_selectedProjectIndex = EditorGUILayout.Popup("选择生成项目", _selectedProjectIndex, _projectNames.ToArray());
GUILayout.Space(5);
if (GUILayout.Button("生成 guru-services.json", GUILayout.Height(40)))
{
var id = _projectNames[_selectedProjectIndex];
if (id == NoSelectionName)
{
ShowDialog("选择错误", "请选择一个存在的项目");
}
else
{
DownloadTsvAndBuild(_selectedProjectIndex, (success, txt) =>
{
if (success)
{
ConvertFromTSV(txt);
}
else
{
ShowDialog("网络错误", txt);
}
_state = STATE_IDLE;
});
_state = STATE_LOADING;
}
}
}
private void ShowDialog(string title, string content, string okName = "OK", Action onOKCallback = null,
string cancelName = "", Action onCancelCallback = null)
{
if (EditorUtility.DisplayDialog(title, content, okName, cancelName))
{
onOKCallback?.Invoke();
}
else
{
onCancelCallback?.Invoke();
}
}
#endregion
#region Networking
private static string GetProjectTSVUrl(string pid)
{
if (PublishLinks.TryGetValue(pid, out var id))
{
string url = string.Format(TSVLink, id);
return url;
}
return "";
}
private void DownloadTsvAndBuild(int projIndex, Action<bool, string> loadCompleted = null)
{
var pid = _projectNames[projIndex];
var id = GetProjectTSVUrl(pid);
string title, msg;
if(string.IsNullOrEmpty(id))
{
title = "参数错误";
msg = $"项目 {pid} 不正确, 请重新选择...";
ShowDialog(title, msg);
Debug.LogError($"{title}\n{msg}");
return;
}
var www = UnityEngine.Networking.UnityWebRequest.Get(id);
www.SendWebRequest().completed += ap =>
{
if (www.result == UnityWebRequest.Result.Success)
{
Debug.Log($"<color=#088ff00>--- Load Success ---</color>");
// Debug.Log(www.downloadHandler.text);
loadCompleted?.Invoke(true, www.downloadHandler.text);
}
else
{
msg = $"Result {www.result}: {www.responseCode}\n\r{www.error}";
Debug.LogError(msg);
loadCompleted?.Invoke(false, msg);
}
};
}
#endregion
#region TEST
#if GURU_SDK_DEV
// [MenuItem("Tools/Test/Fetch Config File", false, 1)]
#endif
private static void Test_FetchConfigFile()
{
var pid = "FindOut";
var url = GetProjectTSVUrl(pid);
if(string.IsNullOrEmpty(url))
{
Debug.LogError($"Wrong ProjectId: {pid}");
return;
}
var www = UnityEngine.Networking.UnityWebRequest.Get(url);
www.SendWebRequest().completed += ap =>
{
if (www.result == UnityWebRequest.Result.Success)
{
Debug.Log($"<color=#088ff00>--- Load Success ---</color>");
Debug.Log(www.downloadHandler.text);
}
else
{
Debug.LogError($"Loading Failed: {www.error} : {www.result} : {www.responseCode}");
}
};
}
#endregion
}
}
}
#endif

View File

@ -0,0 +1,22 @@
Default
BallSort,2PACX-1vSPus7415l66-zScY1B1JOgLfSkm0yRPnRDG-BW0JgtnVZJziGDzvtBZr8D9oEZL2x478SdCS2yh0S_
BallSort2,2PACX-1vSZIZYbFuURK_ZMeMHV9ruL0SWBGMPA13er_J_DLRPVw5HBnU8_6c4mvek5UBFo1Ebbk63TMr-rsY6y
WaterSort,2PACX-1vTPxWbpP7KnT-e2xg9Uz-nukbLmqYc8SjwNL9MgycIhInNgmAjOmRnmyH6PWm3-hsEugKTJHKDcoKdI
WaterSort2,2PACX-1vQnZNtE7ZpT6eYagQDt686Be9Jr0tg22sRFg5cGiqFIsCVhWLu6jxDUg7qmyfIrX3iL5awat8FFnN7B
BallSortPlus,2PACX-1vTa324nQIwNmXNtHC5sZZXYBdpUryKXdb8rFbTAwBa4KVlQ5ZXnErx2IvifkRxD8qKcNupfO1Kv5pJw
TileBurst,2PACX-1vS5wvSZ5eSlK8TmYwVvgScJstaqN8cAB7Uxlq_nsbQwQ4VeD1BkhQuDbRdcO9ncuOCtoKjzSnviHVmc
Parking3D,2PACX-1vQY18hIrYjYNOqQNE12BcPtF4gtFMhBbfDrakBcIVQKoStmfPBY7C4O_Efj1Y8sNbytbKZr-0lPmBml
TileConnect,2PACX-1vRfSd9UbrLilele8Fw1BafFjlvHG5EMnODfbppPiUaacr7wZ62IvgM65SYSjGEXJmU9g9AczollaFCf
DOF,2PACX-1vSDpYZEUrCioCBRSkoZLra5nWk53Ks1f180TD1g2dnKcm-MZtAYvFSXDieAF4xromZCmxIoZuIfGyCJ
D2,2PACX-1vS1V35WTTPx2Jdeu6sVjPbaqFpjVwFu1Rn9tZkvxm8aEHbuRWibxt2pxPkLdDzwmCmrGBtU-PilABJm
FindOut,2PACX-1vQA3R66yWRHmUn6sneeIUU1qXEiaXv6h9QdzYzVRTEOg-yZf7WCJ6tuvXMWzwcOgGihiSnr9shMX__d
FindIt,2PACX-1vQs75JLsmTv1PESzOP6ANRqVk4zA3Y7PD5OV0yQbA5MM3wmp-hu4qE9gztZy7ETCP2nHJgDziwVpcFE
AP,2PACX-1vSvXTqbTcNCuHZYbKGA-fPKQj8XuixYUB9UXWqTQXz5QLXNurCtmBlziMbUxte_eqsGO0tB2GXe-sEC
Arrow,2PACX-1vS5w8rNcJycK-VYgum7gkrLAx2Ln_7wykXLBX4EulI8XNGnlPsVlENQ8LupDdtiIu-JlngJaTw5dIzR
ApFindDifference,2PACX-1vSmodKOvKjxmiNoYbrXFI6n1XC0aqvlnbYkEGyabLKqS6-C3Yi3nstmC89Hc31cdIzCk1FAl54beW2P
GoodsSort,2PACX-1vQrg6Ss2bNYAI7A250t3_zp10UJtbbQXb8I4LtDJ2Q3uopUG_PUadoJy7T4w0jlf4xcTqot2NWsfp1m
CookingBlitz,2PACX-1vShqtSSpwYQ8CvK-BVr-wQ5ygGeKKLdpaaWysSN_QumoDZYATemayAQIOdnFRzMP69nEwFYSx51oey8
WoodNuts,2PACX-1vQhtRg354eThBpWGCEk5f_2cLVbz1clQXv0n6w4Cyip0Knl6EQ4XwWMlcCec-legZdHU3E0-_cqKipc
MahjongSolitaire,2PACX-1vQpcaj8CpO__K1KGl-mg_940WOOIXBVzi0lmcjYTt1sqBI2PtK37s29McLTGU2I6N3fWM0ZepChedq7
NutsSort,2PACX-1vShdEGGEeMp51fMhg0UnMRHA51xfP8OkCdaD3gu-GTZCcJ6NvveP9Q0ahWDwSi1HS4Okyabn3gnLoLU
FindMaster,2PACX-1vRXB42qVcqE0nhWxPScs_qtaUkkVz_BXmdMiEZm580VC4a3I6Av8MrhOEhixVzg2NcPhQh_8HIHt7wk

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 88694fb6aaa64ea192e0bffb4cb5b744
timeCreated: 1719552889

View File

@ -29,7 +29,7 @@ namespace Guru.Editor
var a = AssetDatabase.FindAssets($"*{SourceConfigFileName}* t:TextAsset", new []{"Assets"});
if (a == null || a.Length == 0)
{
Debug.Log($"<color=orange>--- Can't find guru-services file</color>");
UnityEngine.Debug.Log($"<color=orange>--- Can't find guru-services file</color>");
}
else
{
@ -37,7 +37,7 @@ namespace Guru.Editor
var fp = Path.GetFullPath(p);
if (File.Exists(fp)) SourceServiceFilePath = fp;
var t = AssetDatabase.LoadAssetAtPath<TextAsset>(p);
// Debug.Log($"<color=#88ff00>--- find services file:{p} \n{t.text}</color>");
// UnityEngine.Debug.Log($"<color=#88ff00>--- find services file:{p} \n{t.text}</color>");
return JsonMapper.ToObject<GuruServicesConfig>(t.text);
}
return null;
@ -65,7 +65,7 @@ namespace Guru.Editor
if (string.IsNullOrEmpty(savePath)) savePath = DefaultFilePath;
File.WriteAllText(savePath, json);
Debug.Log($"Save config to {savePath}");
UnityEngine.Debug.Log($"Save config to {savePath}");
}
/// <summary>
@ -109,7 +109,7 @@ namespace Guru.Editor
if (null != config)
{
if (File.Exists(path)) File.Delete(path);
Debug.Log($"<color=#88ff00> --- setup {GuruSDK.ServicesConfigKey} to local resources.</color>");
UnityEngine.Debug.Log($"<color=#88ff00> --- setup {GuruSDK.ServicesConfigKey} to local resources.</color>");
File.Copy(from, path);
}
}

View File

@ -0,0 +1,31 @@
allprojects {
buildscript {
repositories {**ARTIFACTORYREPOSITORY**
google()
jcenter()
mavenCentral()
}
dependencies {
// If you are changing the Android Gradle Plugin version, make sure it is compatible with the Gradle version preinstalled with Unity
// See which Gradle version is preinstalled with Unity here https://docs.unity3d.com/Manual/android-gradle-overview.html
// See official Gradle and Android Gradle Plugin compatibility table here https://developer.android.com/studio/releases/gradle-plugin#updating-gradle
// To specify a custom Gradle version in Unity, go do "Preferences > External Tools", uncheck "Gradle Installed with Unity (recommended)" and specify a path to a custom Gradle version
// classpath 'com.android.tools.build:gradle:4.0.1'
classpath 'com.android.tools.build:gradle:7.4.2'
**BUILD_SCRIPT_DEPS**
}
}
repositories {**ARTIFACTORYREPOSITORY**
google()
jcenter()
flatDir {
dirs "${project(':unityLibrary').projectDir}/libs"
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 210888b84fe449e88319129ae097514d
timeCreated: 1722245010

View File

@ -2,7 +2,6 @@ org.gradle.jvmargs=-Xmx**JVM_HEAP_SIZE**M
org.gradle.parallel=true
org.gradle.daemon=true
org.gradle.caching=true
android.enableR8=**MINIFY_WITH_R_EIGHT**
unityStreamingAssets=**STREAMING_ASSETS**
# Android Resolver Properties Start
android.useAndroidX=true

View File

@ -5,7 +5,8 @@ dependencies {
}
android {
compileSdkVersion **APIVERSION**
namespace "**NAMESPACE**"
compileSdkVersion **TARGETSDKVERSION**
buildToolsVersion '**BUILDTOOLS**'
compileOptions {
@ -22,6 +23,7 @@ android {
}
versionCode **VERSIONCODE**
versionName '**VERSIONNAME**'
multiDexEnabled true
}
aaptOptions {

View File

@ -0,0 +1,41 @@
apply plugin: 'com.android.library'
**APPLY_PLUGINS**
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
**DEPS**}
android {
namespace "com.unity3d.player"
ndkPath "**NDKPATH**"
compileSdkVersion **APIVERSION**
buildToolsVersion '**BUILDTOOLS**'
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
defaultConfig {
minSdkVersion **MINSDKVERSION**
targetSdkVersion **TARGETSDKVERSION**
ndk {
abiFilters **ABIFILTERS**
}
versionCode **VERSIONCODE**
versionName '**VERSIONNAME**'
consumerProguardFiles 'proguard-unity.txt'**USER_PROGUARD**
}
lintOptions {
abortOnError false
}
aaptOptions {
noCompress = **BUILTIN_NOCOMPRESS** + unityStreamingAssets.tokenize(', ')
ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~"
}**PACKAGING_OPTIONS**
}
**IL_CPP_BUILD_SETUP**
**SOURCE_BUILD_SETUP**
**EXTERNAL_SOURCES**

View File

@ -0,0 +1,134 @@
-keep class com.unity3d.plugin.* { *; }
#proguard-adjust.pro
-keep public class com.adjust.sdk.** { *; }
-keep class com.amazon.device.ads.** { *; }
-keep class com.amazon.aps.** { *; }
-keep class com.google.android.gms.ads.identifier.AdvertisingIdClient {
com.google.android.gms.ads.identifier.AdvertisingIdClient$Info getAdvertisingIdInfo(android.content.Context);
}
-keep class com.google.android.gms.ads.identifier.AdvertisingIdClient$Info {
java.lang.String getId();
boolean isLimitAdTrackingEnabled();
}
-keep public class com.android.installreferrer.** { *; }
-keepclassmembers class com.ironsource.sdk.controller.IronSourceWebView$JSInterface {
public *;
}
-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
-keep public class com.google.android.gms.ads.** {
public *;
}
-keep class com.ironsource.adapters.** { *; }
-dontwarn com.ironsource.mediationsdk.**
-dontwarn com.ironsource.adapters.**
-keepattributes JavascriptInterface
-keepclassmembers class * {
@android.webkit.JavascriptInterface <methods>;
}
-keep class dalvik.system.VMRuntime {
java.lang.String getRuntime();
}
-keep class android.os.Build {
java.lang.String[] SUPPORTED_ABIS;
java.lang.String CPU_ABI;
}
-keep class android.content.res.Configuration {
android.os.LocaleList getLocales();
java.util.Locale locale;
}
-keep class android.os.LocaledList {
java.util.Locale get(int);
}
#proguard-facebook.pro
-keep class com.facebook.** { *; }
-keepattributes Signature
-keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip
-keep @com.facebook.common.internal.DoNotStrip class *
-keep class com.facebook.stetho.** { *; }
-keepclassmembers class * {
@com.facebook.common.internal.DoNotStrip *;
}
-keepclassmembers class * {
native <methods>;
}
-dontwarn okio.**
-dontwarn javax.annotation.**
-dontwarn com.android.volley.toolbox.**
-keep class com.google.firebase.** { *; }
-dontwarn com.google.firebase.**
-keep class com.bytedance.sdk.** { *; }
-keep class com.pgl.sys.ces.* { *; }
-keep class com.facebook.** { *; }
-keep class com.google.android.play.core.** { *; }
-keep class com.google.games.bridge.** { *; }
-keep class com.google.android.gms.** { *; }
-keep class com.google.android.gms.games.leaderboard.** { *; }
-keep class com.google.android.gms.games.snapshot.** { *; }
-keep class com.google.android.gms.games.achievement.** { *; }
-keep class com.google.android.gms.games.event.** { *; }
-keep class com.google.android.gms.games.stats.** { *; }
-keep class com.google.android.gms.games.video.** { *; }
-keep class com.google.android.gms.games.* { *; }
-keep class com.google.android.gms.common.api.ResultCallback { *; }
-keep class com.google.android.gms.signin.** { *; }
-keep class com.google.android.gms.dynamic.** { *; }
-keep class com.google.android.gms.dynamite.** { *; }
-keep class com.google.android.gms.tasks.** { *; }
-keep class com.google.android.gms.security.** { *; }
-keep class com.google.android.gms.base.** { *; }
-keep class com.google.android.gms.actions.** { *; }
-keep class com.google.android.gms.common.ConnectionResult { *; }
-keep class com.google.android.gms.common.GooglePlayServicesUtil { *; }
-keep class com.google.android.gms.common.api.** { *; }
-keep class com.google.android.gms.common.data.DataBufferUtils { *; }
-keep class com.google.android.gms.games.quest.** { *; }
-keep class com.google.android.gms.nearby.** { *; }
-keep class com.google.android.gms.ads.** { *; }
-keep class com.pubmatic.sdk.** { *; }
-keep class com.applovin.** { *; }
-keep class com.chartboost.** { *; }
-keep class com.guru.** { *; }
-keep class guru.core.** { *; }
-keep class com.onevcat.uniwebview.* { *; }
-keep class com.iab.omid.* { *; }
-keep public class com.tradplus.** { *; }
-keep class com.tradplus.ads.** { *; }
-keep class com.applovin.mediation.adapters.** { *; }
-keep public class com.applovin.sdk.AppLovinSdk{ *; }
-keep public class com.applovin.sdk.AppLovin* {
public protected *;
}
-keep public class com.applovin.nativeAds.AppLovin* {
public protected *;
}
-keep public class com.applovin.adview.* {
public protected *;
}
-keep public class com.applovin.mediation.* {
public protected *;
}
-keep public class com.applovin.mediation.ads.* {
public protected *;
}
-keep public class com.applovin.impl.*.AppLovin {
public protected *;
}
-keep public class com.applovin.impl.**.*Impl {
public protected *;
}
-keepclassmembers class com.applovin.sdk.AppLovinSdkSettings {
private java.util.Map localSettings;
}
-keep class com.applovin.mediation.adapters.** { *; }
-keep class com.applovin.mediation.adapter.** { *; }
-keep class com.applovin.mediation.unity.** { *; }

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a08608d11bf7483fa4c49df80ebab657
timeCreated: 1722245439

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 11c5c61708cec45b3886f7c93951d083
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.guru.unity.sdk.android.res.network.security"
android:versionCode="1"
android:versionName="1.0">
</manifest>

View File

@ -1,2 +0,0 @@
target=android-9
android.library=true

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: c6c2e979b6e21424a8c8807754828741
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 29db8462008d4d8d9220c83bf5dcd6e0
timeCreated: 1706057866

View File

@ -0,0 +1,60 @@
pluginManagement {
repositories {
**ARTIFACTORYREPOSITORY**
gradlePluginPortal()
google()
mavenCentral()
}
}
include ':launcher', ':unityLibrary'
**INCLUDES**
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
repositories {
**ARTIFACTORYREPOSITORY**
google()
mavenCentral()
// Android Resolver Repos Start
def unityProjectPath = $/file:///**DIR_UNITYPROJECT**/$.replace("\\", "/")
maven {
url "https://verve.jfrog.io/artifactory/verve-gradle-release" // Packages/com.guru.unity.max/Mediation/Verve/Editor/Dependencies.xml:7
}
maven {
url (unityProjectPath + "/Assets/GeneratedLocalRepo/Firebase/m2repository") // Packages/com.google.firebase.firestore/Firebase/Editor/FirestoreDependencies.xml:20, Packages/com.google.firebase.app/Firebase/Editor/AppDependencies.xml:22, Packages/com.google.firebase.auth/Firebase/Editor/AuthDependencies.xml:20, Packages/com.google.firebase.messaging/Firebase/Editor/MessagingDependencies.xml:24, Packages/com.google.firebase.crashlytics/Firebase/Editor/CrashlyticsDependencies.xml:20, Packages/com.google.firebase.dynamic-links/Firebase/Editor/DynamicLinksDependencies.xml:20, Packages/com.google.firebase.installations/Firebase/Editor/InstallationsDependencies.xml:20, Packages/com.google.firebase.remote-config/Firebase/Editor/RemoteConfigDependencies.xml:20, Packages/com.google.firebase.analytics/Firebase/Editor/AnalyticsDependencies.xml:18
}
maven {
url "https://aws.oss.sonatype.org/content/repositories/releases/" // Packages/com.guru.unity.max/Amazon/Scripts/Editor/AmazonDependencies.xml:10
}
maven {
url "https://artifactory.bidmachine.io/bidmachine" // Packages/com.guru.unity.max/Mediation/BidMachine/Editor/Dependencies.xml:8
}
maven {
url "https://artifact.bytedance.com/repository/pangle" // Packages/com.guru.unity.max/Mediation/ByteDance/Editor/Dependencies.xml:8
}
maven {
url "https://cboost.jfrog.io/artifactory/chartboost-ads/" // Packages/com.guru.unity.max/Mediation/Chartboost/Editor/Dependencies.xml:8
}
maven {
url "https://android-sdk.is.com/" // Packages/com.guru.unity.max/Mediation/IronSource/Editor/Dependencies.xml:8
}
maven {
url "https://dl-maven-android.mintegral.com/repository/mbridge_android_sdk_oversea" // Packages/com.guru.unity.max/Mediation/Mintegral/Editor/Dependencies.xml:8
}
maven {
url "https://maven.ogury.co" // Packages/com.guru.unity.max/Mediation/OguryPresage/Editor/Dependencies.xml:8
}
maven {
url "https://s3.amazonaws.com/smaato-sdk-releases/" // Packages/com.guru.unity.max/Mediation/Smaato/Editor/Dependencies.xml:8
}
maven {
url "https://repo.pubmatic.com/artifactory/public-repos" // Packages/com.guru.unity.max/OpenWrapSDK/Editor/OpenWrapSDKDependencies.xml:18, Packages/com.guru.unity.max/OpenWrapSDK/Mediation/AppLovinMAX/Editor/ALOpenWrapMediationDependencies.xml:7
}
mavenLocal()
// Android Resolver Repos End
flatDir {
dirs "${project(':unityLibrary').projectDir}/libs"
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4f7213bbbc8741bd89b3bd17a7bea43e
timeCreated: 1722240718

View File

@ -0,0 +1,57 @@
namespace Guru.Editor
{
using System;
using System.IO;
using UnityEngine;
/// <summary>
/// Create androidlib assets
/// </summary>
public class AndroidLibHelper
{
private static readonly string PluginsRoot = "Plugins/Android";
private static readonly string Extends = "androidlib";
private static readonly string ProjectPropertiesName = "project.properties";
private static readonly string ProjectPropertiesContent= "target=android-9\nandroid.library=true";
private static readonly string AndroidManifestName = "AndroidManifest.xml";
private static readonly string AndroidManifestContent = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"{0}\"\n android:versionCode=\"1\"\n android:versionName=\"1.0\">\n</manifest>";
public static bool IsEmbeddedAndroidLibExists(string fileName)
{
string dir = Path.GetFullPath($"{Application.dataPath}/{PluginsRoot}/{fileName}.{Extends}");
return Directory.Exists(dir);
}
public static string CreateLibRoot(string packageName, string fileName = "")
{
if (string.IsNullOrEmpty(packageName)) return "";
if(string.IsNullOrEmpty(fileName)) fileName = packageName;
string dir = Path.GetFullPath($"{Application.dataPath}/{PluginsRoot}/{fileName}.{Extends}");
if (Directory.Exists(dir))
{
return dir;
}
Directory.CreateDirectory(dir);
string path = "";
string content = "";
//------ Create project.properties ------
content = ProjectPropertiesContent;
path = $"{dir}/{ProjectPropertiesName}";
File.WriteAllText(path, content);
// ------ Create AndroidManifest.xml ------
content = AndroidManifestContent.Replace("{0}", packageName);
path = $"{dir}/{AndroidManifestName}";
File.WriteAllText(path, content);
return dir;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ee3d47ab5d544373ae6a43fdd36d96fb
timeCreated: 1711761343

View File

@ -0,0 +1,389 @@
namespace Guru.Editor
{
using System.Xml;
using System.IO;
using UnityEngine;
using System.Collections.Generic;
/// <summary>
/// Android 配置修改器
/// </summary>
public class AndroidManifestDoc
{
private const string TargetPath = "Plugins/Android/AndroidManifest.xml";
private const string XmlnsAndroid = "xmlns:android";
private const string NamespaceAndroid = "http://schemas.android.com/apk/res/android";
private const string XmlnsTools= "xmlns:tools";
private const string NamespaceTools = "http://schemas.android.com/tools";
private const string UserPermission = "uses-permission";
private const string MetaData = "meta-data";
private const string KName = "name";
private XmlDocument _doc;
public XmlDocument Doc => _doc;
private string _docPath;
private bool _isReady = false;
private XmlElement _manifestNode;
private XmlElement _applicationNode;
private XmlElement _queriesNode;
#region Initiallize
/// <summary>
/// 加载文件
/// </summary>
/// <param name="docPath"></param>
/// <returns></returns>
public static AndroidManifestDoc Load(string docPath = "")
{
if (string.IsNullOrEmpty(docPath))
{
docPath = Path.GetFullPath(Path.Combine(Application.dataPath, TargetPath));
}
if (!File.Exists(docPath))
{
Debug.LogError($"--- File not found: {docPath}");
return null;
}
var mod = new AndroidManifestDoc();
mod.ReadFromPath(docPath);
return mod;
}
public static AndroidManifestDoc Read(string xmlStr, string docPath = "")
{
var mod = new AndroidManifestDoc();
mod.ReadFromXml(xmlStr, docPath);
return mod;
}
/// <summary>
/// 从文件路径读取
/// </summary>
/// <param name="docPath"></param>
public void ReadFromPath(string docPath)
{
_isReady = false;
if (File.Exists(docPath))
{
var xmlStr = File.ReadAllText(docPath);
ReadFromXml(xmlStr, docPath);
}
else
{
Debug.LogError($"--- File not found: {docPath}");
}
}
public void ReadFromXml(string xmlStr, string docPath = "")
{
_doc = new XmlDocument();
_doc.LoadXml(xmlStr);
if(!string.IsNullOrEmpty(docPath)) _docPath = docPath;
Init();
}
/// <summary>
/// Initializes the Doc
/// </summary>
private void Init()
{
// --- Root Nodes ---
_manifestNode = _doc.SelectSingleNode("manifest") as XmlElement;
_applicationNode = _doc.SelectSingleNode("manifest/application") as XmlElement;
_queriesNode = _doc.SelectSingleNode("manifest/queries") as XmlElement;
AddXmlnsAndroid();
AddXmlnsTools();
_isReady = true;
}
/// <summary>
/// Save Doc
/// </summary>
public void Save(string docPath = "")
{
if (_isReady)
{
if (!string.IsNullOrEmpty(docPath)) _docPath = docPath;
if (!string.IsNullOrEmpty(_docPath))
{
var dir = Directory.GetParent(_docPath);
if(!dir.Exists) dir.Create();
_doc.Save(_docPath);
}
}
}
#endregion
#region Node Opreation
public static bool AddAttribute(XmlElement node, string key, string value)
{
if (node != null)
{
if (node.HasAttribute(key))
{
node.Attributes[key].Value = value;
}
else
{
node.SetAttribute(key, value);
}
return true;
}
return false;
}
#endregion
#region API
public bool AddXmlnsAndroid()
{
return AddAttribute(_manifestNode, XmlnsAndroid, NamespaceAndroid);
}
public bool AddXmlnsTools()
{
return AddAttribute(_manifestNode, XmlnsTools, NamespaceTools);
}
/// <summary>
/// Add Replace Item
/// </summary>
/// <param name="item"></param>
public void AddApplicationReplaceItem(string item)
{
if (_applicationNode != null)
{
List<string> items = new List<string>(5);
if (_applicationNode.HasAttribute("replace", NamespaceTools))
{
var arr = _applicationNode.GetAttribute("replace",NamespaceTools).Split(',');
if(arr != null && arr.Length > 0)
{
items.AddRange(arr);
}
}
if (!items.Contains(item)) items.Add(item);
_applicationNode.SetAttribute("replace", NamespaceTools, string.Join(",", items));
}
}
public void SetApplicationAttribute(string key, string value)
{
if (_applicationNode != null)
{
_applicationNode.SetAttribute(key, NamespaceAndroid, value);
}
}
/// <summary>
/// Set metadata
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="valueName"></param>
/// <param name="keyName"></param>
public void SetMetadata(string key, string value, string valueName = "value", string keyName = KName)
{
if (_doc == null || !_isReady) return;
XmlElement node = null;
if (!TryGetMetadata(key, out node, keyName))
{
node = _doc.CreateElement(MetaData);
_applicationNode?.AppendChild(node);
}
node.SetAttribute(keyName, NamespaceAndroid, key);
node.SetAttribute(valueName, NamespaceAndroid, value);
}
/// <summary>
/// 添加权限
/// </summary>
/// <param name="key"></param>
/// <param name="keyName"></param>
public void AddPermission(string key, string keyName = KName)
{
if (_doc == null || !_isReady) return;
XmlElement node = null;
if(!TryGetPermission(key, out node, keyName))
{
node = _doc.CreateElement(UserPermission);
_manifestNode?.AppendChild(node);
}
node.SetAttribute(keyName, NamespaceAndroid, key);
}
/// <summary>
/// 删除 Permission
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="keyName"></param>
/// <param name="valueName"></param>
public void RemovePermission(string key, string value = "remove", string keyName = "name", string valueName = "node")
{
if (_doc == null || !_isReady) return;
XmlElement node = null;
if(!TryGetPermission(key, out node, keyName))
{
node = _doc.CreateElement(UserPermission);
_manifestNode?.AppendChild(node);
}
node.SetAttribute(keyName, NamespaceAndroid, key);
node.SetAttribute(valueName, NamespaceTools, value);
}
public bool SetPackageName(string packageName)
{
if (_manifestNode != null)
{
_manifestNode.Attributes["package"].Value = packageName;
return true;
}
return false;
}
/// <summary>
/// 添加 Queries Intent
/// </summary>
/// <param name="value"></param>
/// <param name="keyName"></param>
/// <returns></returns>
public void AddQueriesIntent(string value, string keyName = "name")
{
if (_queriesNode == null)
{
_queriesNode = _doc.CreateElement("queries");
_manifestNode?.AppendChild(_queriesNode);
}
var intentList = _queriesNode.SelectNodes("intent");
if (intentList != null)
{
foreach (XmlElement intent in intentList)
{
var action = intent?.SelectSingleNode("action") as XmlElement;
if (action != null
&& action.GetAttribute(keyName, NamespaceAndroid) == value)
{
return; // Has injectedskip ...
}
}
}
// Inject new intent node
XmlElement intentNode = _doc.CreateElement("intent");
_queriesNode.AppendChild(intentNode);
XmlElement actionNode = _doc.CreateElement("action");
intentNode.AppendChild(actionNode);
actionNode.SetAttribute(keyName, NamespaceAndroid, value);
}
#endregion
#region Data Opration
public bool TryGetMetadata(string name, out XmlElement node, string keyName = KName)
{
node = null;
if(_applicationNode != null)
{
var list = _applicationNode.SelectNodes(MetaData);
if (list != null)
{
foreach (XmlElement e in list)
{
if (e.GetAttribute(keyName, NamespaceAndroid) == name)
{
node = e;
return true;
}
}
}
}
return false;
}
public bool TryGetPermission(string name, out XmlElement node, string keyName = KName)
{
node = null;
if(_manifestNode != null)
{
var list = _manifestNode.SelectNodes(UserPermission);
if (list != null)
{
foreach (XmlElement e in list)
{
if (e.GetAttribute(keyName, NamespaceAndroid) == name)
{
node = e;
return true;
}
}
}
}
return false;
}
public bool TryRootNode(string nodeName, out XmlElement node)
{
node = null;
if (_doc != null)
{
node = _doc.SelectSingleNode(nodeName) as XmlElement;
return node != null;
}
return false;
}
#endregion
#region Output
public override string ToString()
{
if (_doc != null) return _doc.InnerXml;
return this.ToString();
}
#endregion
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 89a1c7f77fcf4982adbbaf6dc61bd62d
timeCreated: 1711505949

View File

@ -1,5 +1,3 @@
using System.Collections;
using Unity.EditorCoroutines.Editor;
namespace Guru.Editor
{
@ -15,10 +13,35 @@ namespace Guru.Editor
private const string TargetPath = "Plugins/Android/AndroidManifest.xml";
private const string ValOptimizeInitialization = "com.google.android.gms.ads.flag.OPTIMIZE_INITIALIZATION";
private const string ValOptimizeAdLoading = "com.google.android.gms.ads.flag.OPTIMIZE_AD_LOADING";
private const string NamespaceAndroid = "http://schemas.android.com/apk/res/android";
private const string NamespaceTools = "http://schemas.android.com/tools";
private const string PermissionReadPostNotifications = "android.permission.POST_NOTIFICATIONS";
private const string PermissionReadPhoneState = "android.permission.READ_PHONE_STATE";
private const string PermissionAccessCoarseLocation = "android.permission.ACCESS_COARSE_LOCATION";
private const string PermissionAccessFineLocation = "android.permission.ACCESS_FINE_LOCATION";
private const string PermissionReadExternalStorage = "android.permission.READ_EXTERNAL_STORAGE";
private const string PermissionReadLogs = "android.permission.READ_LOGS";
private const string NetworkSecurityConfig = "networkSecurityConfig";
private const string NetworkSecurityConfigValue = "@xml/network_security_config";
private const string PermissionAdjustReadPermission = "com.adjust.preinstall.READ_PERMISSION"; // Adjust permission
private const string AdjustQueriesActionValue = "com.attribution.REFERRAL_PROVIDER"; // Adjust action
// Add Permissions
private static string[] addPermissions = new[]
{
PermissionReadPostNotifications,
PermissionAdjustReadPermission,
};
// Remove Permissions
private static string[] removePermissions = new[]
{
PermissionReadPhoneState,
PermissionAccessCoarseLocation,
PermissionAccessFineLocation,
PermissionReadExternalStorage,
PermissionReadLogs,
};
private static string TargetFullPath = Path.Combine(Application.dataPath, TargetPath);
@ -31,126 +54,53 @@ namespace Guru.Editor
CopyManifest();
}
var doc = new XmlDocument();
doc.Load(TargetFullPath);
SetApplicationMod(doc);
SetPermissionMod(doc);
doc.Save(TargetFullPath);
FixAndroidManifest();
}
/// <summary>
/// Fix Android Manifest
/// </summary>
private static void FixAndroidManifest()
{
var doc = AndroidManifestDoc.Load(TargetFullPath);
// --- network_security_config ---
doc.SetApplicationAttribute(NetworkSecurityConfig, NetworkSecurityConfigValue);
doc.AddApplicationReplaceItem($"android:{NetworkSecurityConfig}");
// ---- Metadata ---
doc.SetMetadata(ValOptimizeInitialization, "true");
doc.SetMetadata(ValOptimizeAdLoading, "true");
// ---- Permission ---
// Add needed permissions
foreach (var p in addPermissions)
{
doc.AddPermission(p);
}
// Remove sensitive permissions
foreach (var p in removePermissions)
{
doc.RemovePermission(p);
}
// --- Bundle Id ---
doc.SetPackageName(PlayerSettings.applicationIdentifier);
// --- Adjust Preinstall (Content provider) ---
doc.AddQueriesIntent(AdjustQueriesActionValue);
doc.Save();
}
/// <summary>
/// Fix all Elements in <Applicaiton>
/// 拷贝 AndroidManifest
/// </summary>
/// <param name="doc"></param>
private static void SetApplicationMod(XmlDocument doc)
{
string rootName = "manifest/application";
var rootNode = doc.SelectSingleNode(rootName);
int item1 = 0;
int item2 = 0;
if (rootNode == null)
{
Debug.LogError($"Can't find root with name {rootName} ...");
return;
}
XmlNodeList metadatas = rootNode.SelectNodes("meta-data");
if (metadatas != null && metadatas.Count > 0)
{
bool isDirty = false;
foreach (XmlElement e in metadatas)
{
if (e != null)
{
if (e.HasAttribute("android:name"))
{
if (e.Attributes["android:name"].Value == ValOptimizeInitialization) item1 = 1;
if (e.Attributes["android:name"].Value == ValOptimizeAdLoading) item2 = 1;
}
}
}
}
if (item1 == 0)
{
var e = doc.CreateElement("meta-data");
e.SetAttribute("name",NamespaceAndroid, ValOptimizeInitialization);
e.SetAttribute("value",NamespaceAndroid, "true");
rootNode.AppendChild(e);
}
if (item2 == 0)
{
var e = doc.CreateElement("meta-data");
e.SetAttribute("name",NamespaceAndroid,ValOptimizeAdLoading);
e.SetAttribute("value",NamespaceAndroid, "true");
rootNode.AppendChild(e);
}
var rootE = doc.SelectSingleNode("manifest") as XmlElement;
if (rootE != null)
{
rootE.Attributes["package"].Value = PlayerSettings.applicationIdentifier; // 写入包名
}
}
/// <summary>
/// Fix all permissions
/// </summary>
/// <param name="doc"></param>
private static void SetPermissionMod(XmlDocument doc)
{
string attName = "uses-permission";
string rootName = "manifest";
bool isBuild = false;
var rootNode = doc.SelectSingleNode(rootName);
XmlElement item1 = null;
if (rootNode == null)
{
Debug.LogError($"Can't find root with name {rootName} ...");
return;
}
XmlNodeList permissions = rootNode.SelectNodes(attName);
if (permissions != null && permissions.Count > 0)
{
foreach (XmlElement e in permissions)
{
if (e != null)
{
if (e.HasAttribute("android:name"))
{
if (e.Attributes["android:name"].Value == PermissionReadPhoneState) item1 = e;
}
}
}
}
isBuild = false;
if (item1 == null)
{
isBuild = true;
item1 = doc.CreateElement(attName);
}
item1.SetAttribute("name",NamespaceAndroid, PermissionReadPhoneState);
item1.SetAttribute("node",NamespaceTools, "remove");
if (isBuild) rootNode.AppendChild(item1);
}
private static void CopyManifest()
{
if (File.Exists(TargetFullPath)) return;
var path = GuruEditorHelper.GetFilePath($"{nameof(AndroidManifestMod)} t:Script");
var path = GuruEditorHelper.GetAssetPath(nameof(AndroidManifestMod), "Script", true);
if (!string.IsNullOrEmpty(path))
{
var from = Path.GetFullPath($"{path}/../../Files/AndroidManifest.txt");
@ -160,10 +110,7 @@ namespace Guru.Editor
}
}
}
#region Testing
[Test]

View File

@ -0,0 +1,178 @@
using System.Collections;
using Unity.EditorCoroutines.Editor;
namespace Guru.Editor
{
using NUnit.Framework;
using UnityEditor;
using UnityEngine;
using System;
using System.IO;
using System.Xml;
public static class AndroidManifestMod
{
private const string TargetPath = "Plugins/Android/AndroidManifest.xml";
private const string ValOptimizeInitialization = "com.google.android.gms.ads.flag.OPTIMIZE_INITIALIZATION";
private const string ValOptimizeAdLoading = "com.google.android.gms.ads.flag.OPTIMIZE_AD_LOADING";
private const string NamespaceAndroid = "http://schemas.android.com/apk/res/android";
private const string NamespaceTools = "http://schemas.android.com/tools";
private const string PermissionReadPhoneState = "android.permission.READ_PHONE_STATE";
private static string TargetFullPath = Path.Combine(Application.dataPath, TargetPath);
public static bool IsManifestExist() => File.Exists(TargetFullPath);
public static void Apply()
{
if (!IsManifestExist())
{
CopyManifest();
}
var doc = new XmlDocument();
doc.Load(TargetFullPath);
SetApplicationMod(doc);
SetPermissionMod(doc);
doc.Save(TargetFullPath);
}
/// <summary>
/// Fix all Elements in <Applicaiton>
/// </summary>
/// <param name="doc"></param>
private static void SetApplicationMod(XmlDocument doc)
{
string rootName = "manifest/application";
var rootNode = doc.SelectSingleNode(rootName);
int item1 = 0;
int item2 = 0;
if (rootNode == null)
{
Debug.LogError($"Can't find root with name {rootName} ...");
return;
}
XmlNodeList metadatas = rootNode.SelectNodes("meta-data");
if (metadatas != null && metadatas.Count > 0)
{
bool isDirty = false;
foreach (XmlElement e in metadatas)
{
if (e != null)
{
if (e.HasAttribute("name", NamespaceAndroid))
{
if (e.Attributes["android:name"].Value == ValOptimizeInitialization) item1 = 1;
if (e.Attributes["android:name"].Value == ValOptimizeAdLoading) item2 = 1;
}
}
}
}
if (item1 == 0)
{
var e = doc.CreateElement("meta-data");
e.SetAttribute("name",NamespaceAndroid, ValOptimizeInitialization);
e.SetAttribute("value",NamespaceAndroid, "true");
rootNode.AppendChild(e);
}
if (item2 == 0)
{
var e = doc.CreateElement("meta-data");
e.SetAttribute("name",NamespaceAndroid,ValOptimizeAdLoading);
e.SetAttribute("value",NamespaceAndroid, "true");
rootNode.AppendChild(e);
}
var rootE = doc.SelectSingleNode("manifest") as XmlElement;
if (rootE != null)
{
rootE.Attributes["package"].Value = PlayerSettings.applicationIdentifier; // 写入包名
}
}
/// <summary>
/// Fix all permissions
/// </summary>
/// <param name="doc"></param>
private static void SetPermissionMod(XmlDocument doc)
{
string attName = "uses-permission";
string rootName = "manifest";
bool isBuild = false;
var rootNode = doc.SelectSingleNode(rootName);
XmlElement item1 = null;
if (rootNode == null)
{
Debug.LogError($"Can't find root with name {rootName} ...");
return;
}
XmlNodeList permissions = rootNode.SelectNodes(attName);
if (permissions != null && permissions.Count > 0)
{
foreach (XmlElement e in permissions)
{
if (e != null)
{
if (e.HasAttribute("android:name"))
{
if (e.Attributes["android:name"].Value == PermissionReadPhoneState) item1 = e;
}
}
}
}
isBuild = false;
if (item1 == null)
{
isBuild = true;
item1 = doc.CreateElement(attName);
}
item1.SetAttribute("name",NamespaceAndroid, PermissionReadPhoneState);
item1.SetAttribute("node",NamespaceTools, "remove");
if (isBuild) rootNode.AppendChild(item1);
}
private static void CopyManifest()
{
if (File.Exists(TargetFullPath)) return;
var path = GuruEditorHelper.GetFilePath($"{nameof(AndroidManifestMod)} t:Script");
if (!string.IsNullOrEmpty(path))
{
var from = Path.GetFullPath($"{path}/../../Files/AndroidManifest.txt");
if (File.Exists(from))
{
File.Copy(from, TargetFullPath);
}
}
}
#region Testing
[Test]
public static void Test_Injection()
{
Apply();
}
#endregion
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 3a35ac1e970714224ba4adaae5756f10
guid: f4547abfcddf84bc6b61e884ebfb30e2
DefaultImporter:
externalObjects: {}
userData:

View File

@ -10,27 +10,42 @@ namespace Guru.Editor
public class AndroidProjectMod
{
private static readonly int TargetSDKVersion = 33;
private const int TargetSDKVersion = 34;
private const string K_ANDROID_PLUGINS_NAME = "Plugins/Android";
private static readonly string LauncherName = "launcherTemplate";
private static string LauncherFullPath = Path.Combine(Application.dataPath, $"Plugins/Android/{LauncherName}.gradle");
private const string LauncherName = "launcherTemplate";
private static readonly string LauncherFullPath = Path.Combine(Application.dataPath, $"{K_ANDROID_PLUGINS_NAME}/{LauncherName}.gradle");
private static readonly string MainName = "mainTemplate";
private static string MainFullPath = Path.Combine(Application.dataPath, $"Plugins/Android/{MainName}.gradle");
private const string MainName = "mainTemplate";
private static readonly string MainFullPath = Path.Combine(Application.dataPath, $"{K_ANDROID_PLUGINS_NAME}/{MainName}.gradle");
private static readonly string PropertiesName = "gradleTemplate";
private static string PropertiesFullPath = Path.Combine(Application.dataPath, $"Plugins/Android/{PropertiesName}.properties");
private const string BaseProjectName = "baseProjectTemplate";
private static readonly string BaseProjectFullPath = Path.Combine(Application.dataPath, $"{K_ANDROID_PLUGINS_NAME}/{BaseProjectName}.gradle");
private const string PropertiesName = "gradleTemplate";
private const string K_ENABLE_R8 = "android.enableR8";
private static readonly string PropertiesFullPath = Path.Combine(Application.dataPath, $"{K_ANDROID_PLUGINS_NAME}/{PropertiesName}.properties");
private const string SettingsName = "settingsTemplate";
private static readonly string SettingsFullPath = Path.Combine(Application.dataPath, $"Plugins/Android/{SettingsName}.gradle");
private const string K_LINE_UNITY_PROJECT = "def unityProjectPath";
private const string ProguardUserName = "proguard-user";
private static readonly string ProguardUserFullPath = Path.Combine(Application.dataPath, $"{K_ANDROID_PLUGINS_NAME}/{ProguardUserName}.txt");
public static void Apply()
{
FixLauncher();
FixMain();
FixProperties();
CheckTargetSDKVersion();
ApplyLauncher();
ApplyBaseProjectTemplates();
ApplyMainTemplates();
ApplyGradleTemplate();
ApplySettings();
ApplyProguardUser();
CheckTargetSDKVersion(); // 强制修复构建版本号
}
private static void FixLauncher()
private static void ApplyLauncher()
{
if (!File.Exists(LauncherFullPath))
{
@ -43,10 +58,9 @@ namespace Guru.Editor
var ptn2 = "abortOnError false";
var lines = File.ReadAllLines(LauncherFullPath);
string line = "";
for (int i = 0; i < lines.Length; i++)
{
line = lines[i];
var line = lines[i];
if (line.Contains(ptn1))
{
lines[i] = line.Replace(ptn1, "\n\n\tpackagingOptions {\n\t\texclude(\"META-INF/*.kotlin_module\")\n\t}\n\n");
@ -64,7 +78,7 @@ namespace Guru.Editor
File.WriteAllLines(LauncherFullPath, lines);
}
private static void FixMain()
private static void ApplyMainTemplates()
{
if (!File.Exists(MainFullPath))
{
@ -72,13 +86,100 @@ namespace Guru.Editor
CopyFile($"{MainName}.txt", MainFullPath);
}
}
private static void FixProperties()
private static void ApplyBaseProjectTemplates()
{
if (!File.Exists(BaseProjectFullPath))
{
Debug.Log($"[MOD] --- Copy file to: {BaseProjectFullPath}");
CopyFile($"{BaseProjectName}.txt", BaseProjectFullPath);
}
}
private static void ApplyGradleTemplate()
{
if (!File.Exists(PropertiesFullPath))
{
Debug.Log($"[MOD] --- Copy file to: {PropertiesFullPath}");
CopyFile($"{PropertiesName}.txt", PropertiesFullPath);
}
if (TargetSDKVersion > 33)
{
FixGradleTemplate(PropertiesFullPath);
}
}
/// <summary>
/// 该版本中不再使用 R8
/// </summary>
/// <param name="filePath"></param>
private static void FixGradleTemplate(string filePath)
{
if (File.Exists(filePath))
{
bool isDirty = false;
var lines = File.ReadAllLines(filePath);
for (int i = 0; i < lines.Length; i++)
{
if (lines[i].Contains(K_ENABLE_R8))
{
lines[i] = $"# {lines[i]}"; // 禁用R8
isDirty = true;
break;
}
}
if (isDirty) File.WriteAllLines(filePath, lines);
}
}
/// <summary>
/// 写入 settings.gradle 配置文件
/// </summary>
private static void ApplySettings()
{
if (!File.Exists(SettingsFullPath))
{
CopyFile($"{SettingsName}.txt", SettingsFullPath);
}
FixProjectPathInSettings(SettingsFullPath);
}
private static void FixProjectPathInSettings(string settingsPath)
{
bool isDirty = false;
if (File.Exists(settingsPath))
{
string projectPath = Path.GetFullPath($"{Application.dataPath}/../").Replace("\\", "/");
var lines = File.ReadAllLines(settingsPath);
for (int i = 0; i < lines.Length; i++)
{
if (lines[i].Contains(K_LINE_UNITY_PROJECT))
{
lines[i] = $" def unityProjectPath = $/file:////{projectPath}/$.replace(\"\\\\\", \"/\")";
isDirty = true;
break;
}
}
if(isDirty)
File.WriteAllLines(settingsPath, lines);
}
}
/// <summary>
/// 写入所有的配置文件
/// </summary>
private static void ApplyProguardUser()
{
if (!File.Exists(ProguardUserFullPath))
{
CopyFile($"{ProguardUserName}.txt", ProguardUserFullPath);
}
}
private static void CheckTargetSDKVersion()
@ -95,7 +196,7 @@ namespace Guru.Editor
private static string GetMoveFilePath(string fileName)
{
var path = GuruEditorHelper.GetFilePath($"{nameof(AndroidProjectMod)} t:Script");
var path = GuruEditorHelper.GetAssetPath(nameof(AndroidProjectMod), "Script", true);
var files = Path.GetFullPath($"{path}/../../Files");
return $"{files}/{fileName}";
}

View File

@ -0,0 +1,159 @@
using System.Net;
using UnityEditor;
namespace Guru.Editor
{
using UnityEngine;
using System.IO;
public class AndroidPushIconHelper
{
public static readonly int targetWidth = 96; // 目标宽度
public static readonly int targetHeight = 96;
private static readonly string LibName = "SDKRes";
private static readonly string PackageName = "com.guru.unity.res";
private static readonly string IconName = "ic_notification.png";
private static readonly string ColorContent =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <color name=\"colorAccent\">#{0}</color>\n</resources>";
private static readonly string ValueContent =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <string name=\"default_notification_channel_id\" translatable=\"false\">{0}</string>\n</resources>";
private static readonly string[] iconNames = new string[]
{
"drawable-mdpi",
"drawable-hdpi",
"drawable-xhdpi",
"drawable-xxhdpi",
"drawable-xxxhdpi"
};
private static string FcmChannelId = "fcm_default_channel";
/// <summary>
/// 设置推送图标
/// </summary>
/// <param name="source"></param>
/// <param name="color"></param>
public static bool SetPushIconAssets(Texture2D source, Color color = default(Color))
{
if (source == null)
{
Debug.LogError($"=== No Texture2D found ===");
return false;
}
return DeployAllIcons(source, color);
}
private static Texture2D ResizeTexture(Texture2D source, int newWidth, int newHeight)
{
MakeTextureReadable(source);
// Texture2D result = new Texture2D(newWidth, newHeight);
// Color[] newColors = new Color[newWidth * newHeight];
//
// for (int y = 0; y < newHeight; y++)
// {
// for (int x = 0; x < newWidth; x++)
// {
// // 应用一些缩放逻辑来获取新的颜色值
// newColors[x + y * newWidth] = source.GetPixelBilinear((float)x / newWidth * source.width, (float)y / newHeight * source.height);
// }
// }
//
// result.SetPixels(newColors);
// result.Apply();
// return result;
RenderTexture rt=new RenderTexture(newWidth, newHeight,24);
RenderTexture.active = rt;
Graphics.Blit(source,rt);
Texture2D result=new Texture2D(newWidth,newHeight);
result.ReadPixels(new Rect(0,0,newWidth,newHeight),0,0);
result.Apply();
return result;
}
private static void MakeTextureReadable(Texture2D source)
{
if (source.isReadable) return;
var path = AssetDatabase.GetAssetPath(source);
TextureImporter ti = (TextureImporter)AssetImporter.GetAtPath(path);
if (!ti.isReadable)
{
ti.isReadable = true;
ti.SaveAndReimport();
}
}
private static string ColorToHex(Color color)
{
return string.Format("{0:X2}{1:X2}{2:X2}", (int)(color.r * 255), (int)(color.g * 255), (int)(color.b * 255));
}
private static bool DeployAllIcons(Texture2D source, Color color)
{
var dir = AndroidLibHelper.CreateLibRoot(PackageName, LibName);
string path = "";
string content = "";
var result = ResizeTexture(source, targetWidth, targetHeight);
byte[] bytes = result.EncodeToPNG();
var resPath = $"{dir}/res";
if (!Directory.Exists(resPath))
{
Directory.CreateDirectory(resPath);
}
File.WriteAllBytes($"{resPath}/{IconName}", bytes); // Base Icon;
// ----- Build all Icons ------
foreach (var iconName in iconNames)
{
var iconPath = $"{resPath}/{iconName}";
if (!Directory.Exists(iconPath))
{
Directory.CreateDirectory(iconPath);
}
File.WriteAllBytes($"{iconPath}/{IconName}", bytes);
}
var valuesPath = $"{resPath}/values";
if (!Directory.Exists(valuesPath)) Directory.CreateDirectory(valuesPath);
// ----- Build colors.xml ------
path = $"{valuesPath}/colors.xml";
content = ColorContent.Replace("{0}", ColorToHex(color));
File.WriteAllText(path, content);
// ----- Build strings.xml ------
path = $"{valuesPath}/strings.xml";
content = ValueContent.Replace("{0}", FcmChannelId);
File.WriteAllText(path, content);
// ----- Inject AndroidManifest.xml ------
var doc = AndroidManifestDoc.Load();
if (doc != null)
{
doc.SetMetadata("com.google.firebase.messaging.default_notification_icon", "@drawable/ic_notification", valueName:"resource");
doc.SetMetadata("com.google.firebase.messaging.default_notification_color", "@color/colorAccent", valueName:"resource");
doc.SetMetadata("com.google.firebase.messaging.default_notification_channel_id", "@string/default_notification_channel_id");
doc.Save();
Debug.Log("<color=#88ff00> --- Push Icon Build Success --- </color>");
AssetDatabase.Refresh();
return true;
}
Debug.LogError("AndroidManifest.xml not found ...");
return false;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 39d65f42b9694641b0c3509262aca1d4
timeCreated: 1711724404

View File

@ -6,38 +6,43 @@ namespace Guru.Editor
public class AndroidResMod
{
private static readonly string ResName = "GuruNetworkSecurity";
private static string ResFullPath = Path.Combine(Application.dataPath, $"Plugins/Android/{ResName}.androidlib");
private static string OldSecurityXml = Path.Combine(Application.dataPath, "Plugins/Android/res/xml/network_security_config.xml");
private static readonly string NetworkSecurityXmlName = "network_security_config.xml";
private static readonly string LibNetworkSecurity = "GuruNetworkSecurity";
private static readonly string LibNetworkSecurityPackageName = "com.guru.unity.sdk.android.res.network.security";
private static string OldSecurityXml = Path.Combine(Application.dataPath, $"Plugins/Android/res/xml/{NetworkSecurityXmlName}");
/// <summary>
/// 应用补丁
/// </summary>
public static void Apply()
{
DeployAndroidRes();
DeployNetworkSecurity();
}
private static void DeployAndroidRes()
/// <summary>
/// 部署网络安全配置
/// </summary>
private static void DeployNetworkSecurity()
{
if(File.Exists(OldSecurityXml)) File.Delete(OldSecurityXml);
var spath = GuruEditorHelper.GetFilePath($"{nameof(AndroidResMod)} t:Script");
if (!string.IsNullOrEmpty(spath))
{
var from = Path.GetFullPath($"{spath}/../../Files/sdk_res");
if (Directory.Exists(from))
{
var to = ResFullPath;
if (Directory.Exists(to))
{
FileUtil.DeleteFileOrDirectory(ResFullPath);
}
FileUtil.CopyFileOrDirectory(from, to);
}
}
if(File.Exists(OldSecurityXml)) File.Delete(OldSecurityXml); // 清理旧文件
if (!AndroidLibHelper.IsEmbeddedAndroidLibExists(LibNetworkSecurity))
{
string dir = AndroidLibHelper.CreateLibRoot(LibNetworkSecurityPackageName, LibNetworkSecurity);
var d = GuruEditorHelper.GetAssetPath(nameof(AndroidResMod), "Script", true);
if (!string.IsNullOrEmpty(d))
{
var from = $"{Directory.GetParent(d)?.FullName ?? ""}/../Files/{NetworkSecurityXmlName}";
if (File.Exists(from))
{
string toDir = $"{dir}/res/xml";
if(!Directory.Exists(toDir))Directory.CreateDirectory(toDir);
string to = $"{toDir}/{NetworkSecurityXmlName}";
FileUtil.CopyFileOrDirectory(from, to);
}
}
}
}

View File

@ -2,19 +2,34 @@ namespace Guru.Editor
{
using UnityEditor;
using UnityEngine;
using System;
using System.IO;
public class GuruEditorHelper
{
public static string GetFilePath(string filter)
public static string GetAssetPath(string filter, bool useFullPath = false)
{
var guids = AssetDatabase.FindAssets(filter);
string path = "";
string fullPath = "";
if (guids != null && guids.Length > 0)
{
var path = AssetDatabase.GUIDToAssetPath(guids[0]);
return path;
path = AssetDatabase.GUIDToAssetPath(guids[0]);
fullPath = path.Replace("Assets", Application.dataPath);
if (File.Exists(fullPath))
{
return useFullPath? fullPath : path;
}
}
return "";
}
public static string GetAssetPath(string fileName, string typeName = "", bool useFullPath = false)
{
var filter = fileName;
if(!string.IsNullOrEmpty(typeName)) filter = $"{fileName} t:{typeName}";
return GetAssetPath(filter, useFullPath);
}
public static void OpenPath(string path)
{

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7f9d1111a9d94187bf583ae71d9192f0
timeCreated: 1711858346

View File

@ -0,0 +1,116 @@
namespace Guru.Editor
{
using System;
using System.Collections.Generic;
using System.IO;
public class EaseConfigFile
{
private Dictionary<string, string> _dataDict;
private string _filePath;
protected bool ReadFile(string path)
{
_filePath = path;
if (File.Exists(path))
{
var lines = File.ReadAllLines(path);
int len = lines.Length;
_dataDict = new Dictionary<string, string>(len);
string key = "";
string value = "";
for (int i=0; i< len; i++)
{
var line = lines[i];
if (line.Contains("="))
{
key = "";
value = "";
var kv = line.Split('=');
if(kv.Length > 0) key = kv[0];
if(kv.Length > 1) value = kv[1];
if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value))
{
_dataDict[key] = value;
}
}
}
return true;
}
_dataDict = new Dictionary<string, string>(10);
var dir = Directory.GetParent(path);
if(dir is { Exists: false }) dir.Create();
return false;
}
public void Save()
{
if (_dataDict == null || _dataDict.Count < 1) return;
List<string> lines = new List<string>(_dataDict.Count);
foreach (var key in _dataDict.Keys)
{
lines.Add($"{key}={_dataDict[key].ToString()}");
}
if(!string.IsNullOrEmpty(_filePath))
File.WriteAllLines(_filePath, lines);
}
public void Set(string key, object value)
{
if (_dataDict == null) _dataDict = new Dictionary<string, string>(10);
_dataDict[key] = value.ToString();
Save();
}
public string Get(string key) => _dataDict.ContainsKey(key) ? _dataDict[key] : "";
public bool TryGet(string key, out string value)
{
value = "";
return _dataDict?.TryGetValue(key, out value) ?? false;
}
public bool GetBool(string key, bool defaultVal = false)
{
if (TryGet(key, out var str))
{
return (str.ToLower() == "true" || str == "1");
}
return defaultVal;
}
public int GetInt(string key, int defaultVal = 0)
{
if (TryGet(key, out var str))
{
var inVal = 0;
if (int.TryParse(str, out inVal))
{
return inVal;
}
}
return defaultVal;
}
public float GetFloat(string key, float defaultVal = 0)
{
if (TryGet(key, out var str))
{
float val = 0;
if (float.TryParse(str, out val))
{
return val;
}
}
return defaultVal;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 58ea378c89d742008a18f019afd54a27
timeCreated: 1711858357

View File

@ -6,7 +6,9 @@
"GuruSDK",
"Guru.LitJson",
"Guru.Runtime",
"MaxSdk.Scripts.IntegrationManager.Editor"
"MaxSdk.Scripts.IntegrationManager.Editor",
"Guru.Editor",
"GuruAdjust.Editor"
],
"includePlatforms": [
"Editor"

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1d20da1a87484f37bc53360cc2414030
timeCreated: 1712622721

View File

@ -0,0 +1,23 @@
namespace Guru
{
/// <summary>
/// 更新器
/// </summary>
public interface IUpdater
{
UpdaterState State { get; }
void Start();
void OnUpdate();
void Pause(bool pause);
void Kill();
}
public enum UpdaterState
{
Prepare,
Running,
Pause,
Kill,
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6ab7dd209c3a494eab0dfbedce0700d8
timeCreated: 1712622736

View File

@ -0,0 +1,82 @@
namespace Guru
{
using System;
using System.Collections;
using System.Collections.Generic;
public class ThreadHandler: IUpdater
{
private Queue<Action> _actions;
public Queue<Action> Actions
{
get
{
if(_actions == null)
_actions = new Queue<Action>(10);
return _actions;
}
set
{
if (value != null) _actions = value;
}
}
private UpdaterState _state;
public UpdaterState State => _state;
/// <summary>
/// 启动 Updater
/// </summary>
public void Start()
{
_state = UpdaterState.Running;
}
/// <summary>
/// 执行方案
/// </summary>
public void OnUpdate()
{
if (Actions.Count > 0)
{
// 消耗对垒
while (Actions.Count > 0)
{
Actions.Dequeue()?.Invoke();
}
}
}
public void Pause(bool pause = true)
{
_state = pause ? UpdaterState.Pause : UpdaterState.Running;
}
/// <summary>
/// 删除对象
/// </summary>
public void Kill()
{
_state = UpdaterState.Kill;
}
public void Dispose()
{
_actions.Clear();
_state = UpdaterState.Kill;
}
public void AddAction(Action action)
{
if (action == null) return;
Actions.Enqueue(action);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: dc05f1b1ff5947c19ce6af1db301398d
timeCreated: 1712624042

View File

@ -1,12 +1,17 @@
namespace Guru
{
using System.Collections.Generic;
using System.Text;
using System;
/// <summary>
/// 启动参数配置
/// </summary>
public partial class GuruSDKInitConfig
public class GuruSDKInitConfig
{
#region Properties
/// <summary>
/// 使用自定义的ConsentFlow启动流程
/// </summary>
@ -20,82 +25,189 @@ namespace Guru
/// </summary>
public bool IAPEnabled = true;
/// <summary>
/// 自动申请推送授权信息
/// </summary>
public bool AutoNotificationPermission = true;
/// <summary>
/// 自动记录完成的关卡
/// </summary>
public bool AutoRecordFinishedLevels = true;
[Obsolete("Will be removed from InitConfig in next version. Use the <b_level> and <b_play> data from the GameUserData from game itself instead!")]
public bool AutoRecordFinishedLevels = false;
/// <summary>
/// 自定义 Service 云控 Key
/// </summary>
public string CustomServiceKey = "";
/// <summary>
/// Banner 背景颜色 Hex 值
/// </summary>
public string BannerBackgroundColor = "#00000040";
/// <summary>
/// 已购买去广告道具
/// </summary>
public bool IsBuyNoAds = false;
/// <summary>
/// Debug模式默认关闭
/// </summary>
public bool DebugMode = false;
/// <summary>
/// Debug模式下开启打点默认关闭
/// </summary>
public bool EnableDebugLogEvent = false;
private Dictionary<string, object> _defaultRemoteData = new Dictionary<string, object>();
/// <summary>
/// 云控参数的默认配置
/// </summary>
/// <returns></returns>
public Dictionary<string, object> DefaultRemoteData = new Dictionary<string, object>();
public Dictionary<string, object> DefaultRemoteData
{
set
{
if (value != null)
{
_defaultRemoteData = value;
}
}
get => _defaultRemoteData;
}
/// <summary>
/// 启用 AdjustDeeplink
/// </summary>
public Action<string> OnAdjustDeeplinkCallback = null;
/// <summary>
/// 支付初始化Keys
/// </summary>
public byte[] GoogleKeys;
public byte[] AppleRootCerts;
#region Initialization
/// <summary>
/// 构建启动配置
/// </summary>
/// <returns></returns>
public static GuruSDKInitConfig Build(
bool useCustomConsent = false,
bool autoLoadAds = true,
bool iapEnabled = true,
bool autoRecordFinishedLevels = true,
bool debugMode = false,
Dictionary<string, object> defaultRemoteData = null,
byte[] googleKeys = null,
byte[] appleRootCerts = null)
{
// 创建启动用参数
GuruSDKInitConfig config = new GuruSDKInitConfig()
{
UseCustomConsent = useCustomConsent,
AutoLoadWhenAdsReady = autoLoadAds,
IAPEnabled = iapEnabled,
AutoRecordFinishedLevels = autoRecordFinishedLevels,
DebugMode = debugMode,
GoogleKeys = googleKeys,
AppleRootCerts = appleRootCerts,
DefaultRemoteData = defaultRemoteData ?? new Dictionary<string, object>(),
};
#if UNITY_EDITOR
config.DebugMode = true;
#endif
return config;
}
public byte[] GoogleKeys; // 数据取自 GooglePlayTangle.Data();
public byte[] AppleRootCerts; // 数据取自 AppleTangle.Data();
#endregion
#region Print
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($"------- Custom init Config -------");
sb.AppendLine($"\tUseCustomConsent: {UseCustomConsent}");
sb.AppendLine($"\tAutoLoadWhenAdsReady: {AutoLoadWhenAdsReady}");
sb.AppendLine($"\tIAPEnabled: {IAPEnabled}");
sb.AppendLine($"\tShowDebugLog: {DebugMode}");
sb.AppendLine($"------- Custom init Config -------");
sb.AppendLine($"------- Custom InitConfig -------");
sb.AppendLine($"\t UseCustomConsent: {UseCustomConsent}");
sb.AppendLine($"\t AutoLoadWhenAdsReady: {AutoLoadWhenAdsReady}");
sb.AppendLine($"\t IAPEnabled: {IAPEnabled}");
sb.AppendLine($"\t AutoNotificationPermission: {AutoNotificationPermission}");
sb.AppendLine($"\t AutoRecordFinishedLevels: {AutoRecordFinishedLevels}");
sb.AppendLine($"\t CustomServiceKey: {CustomServiceKey}");
sb.AppendLine($"\t BannerBackgroundColor: {BannerBackgroundColor}");
sb.AppendLine($"\t IsBuyNoAds: {IsBuyNoAds}");
sb.AppendLine($"\t DebugMode: {DebugMode}");
sb.AppendLine($"\t DefaultRemote: Count: {DefaultRemoteData.Count}");
sb.AppendLine($"------- Custom InitConfig -------");
return sb.ToString();
}
#endregion
#region Builder
/// <summary>
/// 构造器
/// </summary>
/// <returns></returns>
public static GuruSDKInitConfigBuilder Builder() => new GuruSDKInitConfigBuilder();
#endregion
}
/// <summary>
/// 构建器
/// </summary>
public class GuruSDKInitConfigBuilder
{
private GuruSDKInitConfig _config = new GuruSDKInitConfig();
/// <summary>
/// 构建配置
/// </summary>
/// <returns></returns>
public GuruSDKInitConfig Build()
{
return _config;
}
public GuruSDKInitConfigBuilder SetUseCustomConsent(bool value)
{
_config.UseCustomConsent = value;
return this;
}
public GuruSDKInitConfigBuilder SetAutoLoadWhenAdsReady(bool value)
{
_config.AutoLoadWhenAdsReady = value;
return this;
}
public GuruSDKInitConfigBuilder SetIAPEnabled(bool value)
{
_config.IAPEnabled = value;
return this;
}
public GuruSDKInitConfigBuilder SetAutoRecordFinishedLevels(bool value)
{
_config.AutoRecordFinishedLevels = value;
return this;
}
public GuruSDKInitConfigBuilder SetIsBuyNoAds(bool value)
{
_config.IsBuyNoAds = value;
return this;
}
public GuruSDKInitConfigBuilder SetBannerBackgroundColor(string value)
{
_config.BannerBackgroundColor = value;
return this;
}
public GuruSDKInitConfigBuilder SetDebugMode(bool value)
{
_config.DebugMode = value;
return this;
}
public GuruSDKInitConfigBuilder SetOnAdjustDeeplinkCallback(Action<string> callback)
{
_config.OnAdjustDeeplinkCallback = callback;
return this;
}
public GuruSDKInitConfigBuilder SetGoogleKeys(byte[] value)
{
_config.GoogleKeys = value;
return this;
}
public GuruSDKInitConfigBuilder SetAppleRootCerts(byte[] value)
{
_config.AppleRootCerts = value;
return this;
}
public GuruSDKInitConfigBuilder SetDefaultRemoteData(Dictionary<string, object> value)
{
_config.DefaultRemoteData = value;
return this;
}
public GuruSDKInitConfigBuilder SetEnableDebugLogEvent(bool value)
{
_config.EnableDebugLogEvent = value;
return this;
}
public GuruSDKInitConfigBuilder SetCustomServiceKey(string value)
{
_config.CustomServiceKey = value;
return this;
}
public GuruSDKInitConfigBuilder SetAutoNotificationPermission(bool value)
{
_config.AutoNotificationPermission = value;
return this;
}
}
}

View File

@ -1,8 +1,9 @@
using System;
using System.Collections.Generic;
namespace Guru
{
using System;
using UnityEngine.Serialization;
[Serializable]
public class GuruServicesConfig
@ -42,6 +43,7 @@ namespace Guru
ad_settings.tradplus_ids_ios.Length > 0;
public bool IsIAPEnabled() => app_settings != null && app_settings.enable_iap
&& products != null && products.Length > 0;
public bool UseCustomKeystore() => app_settings?.custom_keystore ?? false;
public bool IsFirebaseEnabled() => app_settings?.enable_firebase ?? true;
public bool IsFacebookEnabled() => app_settings?.enable_facebook ?? true;
@ -54,17 +56,19 @@ namespace Guru
//-------------------------------- Parameters --------------------------------
public double Tch02Value() => parameters?.tch_020 ?? 0;
public bool IsAppReview() => parameters?.apple_review ?? false;
public bool EnableErrorLog() => parameters?.enable_errorlog ?? false;
public bool EnableAnaErrorLog() => parameters?.enable_errorlog ?? false;
public bool IsAdsCompliance() => parameters?.ads_compliance ?? false;
public bool DMACountryCheck() => parameters?.dma_country_check ?? false;
public string DMAMapRule() => parameters?.dma_map_rule ?? "";
public bool UseUUID() => parameters?.using_uuid ?? false;
public bool KeywordsEnabled() => parameters?.enable_keywords ?? false;
public int TokenValidTime() => parameters?.token_vaild_time ?? 604800;
public int TokenValidTime() => parameters?.token_valid_time ?? 604800;
public int LevelEndSuccessNum() => parameters?.level_end_success_num ?? 50;
public string CdnHost() => parameters?.cdn_host ?? "";
public bool UsingUUID() => parameters?.using_uuid ?? true;
//-------------------------------- Parameters --------------------------------
}
[Serializable]
@ -82,21 +86,23 @@ namespace Guru
public bool enable_facebook = true;
public bool enable_adjust = true;
public bool enable_iap = false;
public bool custom_keystore = false;
}
[Serializable]
public class GuruParameters
{
public int token_vaild_time = 604800;
public int token_valid_time = 604800;
public int level_end_success_num = 50;
public bool enable_keywords = false;
public double tch_020 = 0;
public bool using_uuid = true;
public bool using_uuid = false;
public string dma_map_rule = "";
public bool dma_country_check = false;
public bool apple_review = false; // 苹果审核标志位
public bool enable_errorlog = false;
public bool ads_compliance = false;
public string cdn_host = "";
}
[Serializable]

View File

@ -7,14 +7,17 @@ namespace Guru
/// 获取BLevel
/// </summary>
/// <returns></returns>
protected override int GetBLevel() => GuruSDKModel.Instance.SuccessLevelCount; // BLevel
protected override int GetBLevel() => Model.BLevel; // BLevel
private GuruSDKModel Model => GuruSDKModel.Instance;
protected override void OnPurchaseOver(bool success, string productName)
{
if (success)
{
GuruSDKModel.Instance.PurchasedCount++; // 记录成功购买次数
}
}
public void ClearData()
{
_model.ClearData();
}
}
}

View File

@ -12,27 +12,17 @@ namespace Guru
get => _value;
set
{
if (_value.Equals(value)) return;
_value = value;
OnValueChanged?.Invoke(value);
}
}
public event Action<T> OnValueChanged;
public BindableProperty()
{
}
public BindableProperty(Action<T> onChanged)
{
OnValueChanged = onChanged;
}
public BindableProperty(T initValue, Action<T> onChanged)
public BindableProperty(T initValue)
{
_value = initValue;
OnValueChanged = onChanged;
}
}
}

View File

@ -1,19 +1,46 @@
using System.Collections.Generic;
using System.Linq;
namespace Guru
{
using System;
using UnityEngine;
using System.IO;
using System.Collections.Generic;
using System.Linq;
[Serializable]
class PurchasedProduct
{
public string productName;
public string productId;
public string receipt;
public bool appleProductIsRestored;
}
[Serializable]
class GuruSDKSerializedModel
{
//-------------- data ---------------
public string uid = "";
public int b_level = 0;
public int b_play = 0;
public bool no_ads = false;
public List<PurchasedProduct> purchased = new List<PurchasedProduct>(10);
//-------------- data ---------------
}
[Serializable]
internal class GuruSDKModel
{
private const float SaveInterval = 3;
private const string SaveKey = "com.guru.sdk.model.save";
private DateTime _lastSavedTime = new DateTime(1970,1,1);
private bool _noAds = false;
private int _bLevel;
private int _bPlay;
private string _uid;
private List<PurchasedProduct> _purchased;
private static GuruSDKModel _instance;
@ -21,97 +48,77 @@ namespace Guru
{
get
{
if (null == _instance) _instance = Load();
if (null == _instance) _instance = new GuruSDKModel();
return _instance;
}
}
//-------------- data ---------------
public string uid = "";
public int b_level = 0;
public int b_play = 0;
public int buy_count = 0;
public List<PurchasedProduct> purchased;
//-------------- data ---------------
private float _lastSavedTime = 0;
public GuruSDKModel()
{
// 读取内存值
GuruSDKSerializedModel model = LoadModel();
_uid = model.uid;
_noAds = model.no_ads;
_bLevel = model.b_level;
_bPlay = model.b_play;
_purchased = model.purchased;
}
public int SuccessLevelCount
public int BLevel
{
get
{
if(_successLevel == null) InitProperties();
return _successLevel.Value;
}
get => _bLevel;
set
{
if(_successLevel == null) InitProperties();
_successLevel.Value = value;
if (value < _bLevel)
{
// b_level 必须比上一次的值大
Debug.LogWarning($"[SDK] :: Set b_level [{value}] should not be less than original value [{_bLevel}]");
return;
}
_bLevel = value;
Save();
}
}
public int TotalPlayedCount
public int BPlay
{
get
{
if(_totalPlayed == null) InitProperties();
return _totalPlayed.Value;
}
get => _bPlay;
set
{
if(_totalPlayed == null) InitProperties();
_totalPlayed.Value = value;
}
}
public int PurchasedCount
{
get
{
if(_purchasedCount == null) InitProperties();
return _purchasedCount.Value;
}
set
{
if(_purchasedCount == null) InitProperties();
_purchasedCount.Value = value;
_bPlay = value;
Save();
}
}
public string UserId
{
get
{
if(_uid == null) InitProperties();
return _uid.Value;
}
get => _uid;
set
{
if(_uid == null) InitProperties();
_uid.Value = value;
_uid = value;
Save();
}
}
public bool IsIAPUser => PurchasedCount > 0;
public bool IsIapUser => _purchased.Count > 0;
private BindableProperty<int> _successLevel;
private BindableProperty<int> _totalPlayed;
private BindableProperty<int> _purchasedCount;
private BindableProperty<string> _uid;
public bool IsNoAds
{
get => _noAds;
set
{
_noAds = value;
Save();
}
}
public BindableProperty<int> PropBLevel => _successLevel;
public BindableProperty<int> PropBPlay => _totalPlayed;
public BindableProperty<int> PropBuyCount => _purchasedCount;
public BindableProperty<string> Uid => _uid;
#region 初始化
public static GuruSDKModel Load()
private GuruSDKSerializedModel LoadModel()
{
GuruSDKModel model = null;
GuruSDKSerializedModel model = null;
if (PlayerPrefs.HasKey(SaveKey))
{
var json = PlayerPrefs.GetString(SaveKey, "");
@ -119,7 +126,7 @@ namespace Guru
{
try
{
model = JsonUtility.FromJson<GuruSDKModel>(json);
model = JsonUtility.FromJson<GuruSDKSerializedModel>(json);
}
catch (Exception e)
{
@ -127,101 +134,54 @@ namespace Guru
}
}
}
if(model == null) model = new GuruSDKModel();
model.InitProperties();
if(model == null) model = new GuruSDKSerializedModel();
return model;
}
/// <summary>
/// 保存至数据
/// 保存至 PlayerPrefs 数据
/// </summary>
private void SaveToPlayerPrefs()
private void SetToMemory()
{
var json = JsonUtility.ToJson(this);
PlayerPrefs.SetString(SaveKey, json);
}
public void InitProperties()
{
_successLevel = new BindableProperty<int>(b_level, OnLevelChanged);
_totalPlayed = new BindableProperty<int>(b_play, OnPlayedChanged);
_purchasedCount = new BindableProperty<int>(buy_count, OnPurchasedNumChanged);
_uid = new BindableProperty<string>(uid, OnUidChanged);
purchased = new List<PurchasedProduct>(20);
var model = new GuruSDKSerializedModel()
{
uid = _uid,
b_level = _bLevel,
b_play = _bPlay,
no_ads = _noAds,
purchased = _purchased,
};
var json = JsonUtility.ToJson(model);
if (!string.IsNullOrEmpty(json))
{
PlayerPrefs.SetString(SaveKey, json);
}
}
/// <summary>
/// 保存数据
/// </summary>
/// <param name="force"></param>
public void Save(bool force = false)
/// <param name="forceSave"></param>
public void Save(bool forceSave = false)
{
bool save = force || (Time.realtimeSinceStartup - _lastSavedTime>= SaveInterval);
if (save)
{
_lastSavedTime = Time.realtimeSinceStartup;
SaveToPlayerPrefs();
}
}
SetToMemory(); // 每次保存都要设置到 PlayerPrefs 内
#endregion
#region 数据绑定变化
private void OnLevelChanged(int value)
{
b_level = value;
Save();
}
private void OnPlayedChanged(int value)
{
b_play = value;
Save();
}
private void OnPurchasedNumChanged(int value)
{
buy_count = value;
Save();
bool shouldWriteToDisk = forceSave || (DateTime.Now - _lastSavedTime)>= TimeSpan.FromSeconds(SaveInterval);
if (!shouldWriteToDisk) return;
_lastSavedTime = DateTime.Now; // 更新最后保存时间
PlayerPrefs.Save(); // 写入到磁盘
}
private void OnUidChanged(string value)
{
uid = value;
Save();
}
#endregion
#region 启动配置
/// <summary>
/// 从 Streaming 加载 AppServices 配置
/// </summary>
/// <returns></returns>
public string LoadDefaltServicesConfigJson()
{
try
{
var txt = Resources.Load<TextAsset>(GuruSDK.ServicesConfigKey);
return txt?.text ?? "";
}
catch (Exception e)
{
Log.Exception(e);
}
return "";
}
#endregion
#region 订单记录
public bool HasPurchasedProduct(string receipt)
{
if(purchased == null || purchased.Count == 0) return false;
return purchased.Exists(p => p.receipt == receipt);
if(_purchased.Count == 0) return false;
return _purchased.Exists(p => p.receipt == receipt);
}
/// <summary>
@ -230,12 +190,12 @@ namespace Guru
/// <param name="receipt"></param>
/// <param name="productName"></param>
/// <param name="productId"></param>
/// <param name="appleProductIsRestored"></param>
public void AddReceipt(string receipt, string productName, string productId, bool appleProductIsRestored = false)
{
if (purchased == null) purchased = new List<PurchasedProduct>(20);
if (!HasPurchasedProduct(receipt))
{
purchased.Add(new PurchasedProduct()
_purchased.Add(new PurchasedProduct()
{
receipt = receipt,
productName = productName,
@ -248,22 +208,16 @@ namespace Guru
public string[] GetReceipts(string productName)
{
int count = purchased?.Count ?? 0;
if (count == 0) count = 20;
if (purchased == null) purchased = new List<PurchasedProduct>(count);
var receipts = new List<string>(count);
receipts.AddRange(from purchasedProduct in purchased where purchasedProduct.productName == productName select purchasedProduct.receipt);
var receipts = new List<string>();
receipts.AddRange(from purchasedProduct in _purchased where purchasedProduct.productName == productName select purchasedProduct.receipt);
return receipts.ToArray();
}
public string[] GetReceiptsById(string productId)
{
int count = purchased?.Count ?? 0;
if (count == 0) count = 20;
if (purchased == null) purchased = new List<PurchasedProduct>(count);
var receipts = new List<string>(count);
receipts.AddRange(from purchasedProduct in purchased where purchasedProduct.productId == productId select purchasedProduct.receipt);
var receipts = new List<string>();
receipts.AddRange(from purchasedProduct in _purchased where purchasedProduct.productId == productId select purchasedProduct.receipt);
return receipts.ToArray();
}
@ -271,17 +225,18 @@ namespace Guru
#endregion
#region 清除数据
public void ClearData()
{
PlayerPrefs.DeleteKey(SaveKey);
}
#endregion
}
[Serializable]
internal class PurchasedProduct
{
public string productName;
public string productId;
public string receipt;
public bool appleProductIsRestored;
}

View File

@ -1,14 +1,13 @@
namespace Guru
{
using UnityEngine;
using System;
using Guru.Notification;
public partial class GuruSDK
{
private const float CONSENT_FLOW_TIMEOUT = 10; // Consent Flow 超时时间(秒)
private static AdsInitSpec _adInitSpec;
/// <summary>
@ -53,7 +52,7 @@ namespace Guru
if (userAllow)
{
#if UNITY_IOS
CheckAttStatus();
Instance.CheckAttStatus();
#else
StartAdService(spec);
#endif
@ -81,6 +80,8 @@ namespace Guru
private bool _hasConsentCalled = false;
private bool _adServiceHasStarted = false;
private string _notiStatue = "";
private bool _hasNotiGranted = false;
/// <summary>
/// 启动Consent流程
@ -88,10 +89,12 @@ namespace Guru
/// </summary>
private void StartConsentFlow()
{
LogI($"#4.5 --- StartConsentFlow ---");
float time = 1;
if (!_adServiceHasStarted && _appServicesConfig != null)
{
time = _appServicesConfig.IsAdsCompliance() ? 10 : 1f; // 启动合规判定后, 延迟最多 10 秒后启动广告
time = _appServicesConfig.IsAdsCompliance() ? CONSENT_FLOW_TIMEOUT : 1f; // 启动合规判定后, 延迟最多 10 秒后启动广告
}
Delay(time, AdServiceHandler); // 广告延迟启动
@ -110,7 +113,7 @@ namespace Guru
#if UNITY_IOS
InitAttStatus(); // Consent 启动前记录 ATT 初始值
#endif
Debug.Log($"{Tag} --- Call:StartConsentFlow ---");
UnityEngine.Debug.Log($"{Tag} --- Call:StartConsentFlow ---");
GuruConsent.StartConsent(OnGuruConsentOver, dmaMapRule:dmaMapRule, enableCountryCheck:enableCountryCheck);
}
@ -125,10 +128,12 @@ namespace Guru
AdServiceHandler();
// 调用回调
Callbacks.ConsentFlow._onConsentResult?.Invoke(code);
Callbacks.ConsentFlow.InvokeOnConsentResult(code);
#if UNITY_IOS
CheckAttStatus(); // [iOS] Consent 启动后检查 ATT 初始值
#elif UNITY_ANDROID
CheckNotiPermission(); // Consent 回调后检查 Notification 权限
#endif
// 内部处理后继逻辑
@ -152,6 +157,8 @@ namespace Guru
StartAdService(_adInitSpec);
}
#endregion
#region IOS ATT 广告授权流程
@ -165,7 +172,7 @@ namespace Guru
/// <summary>
/// 显示系统的 ATT 弹窗
/// </summary>
public static void RequestAttDialog()
public void RequestAttDialog()
{
LogI($"RequestATTDialog");
ATTManager.RequestATTDailog(ReportAttStatus);
@ -178,13 +185,13 @@ namespace Guru
{
_attType = InitConfig.UseCustomConsent ? ATTManager.GUIDE_TYPE_CUSTOM : ATTManager.GUIDE_TYPE_ADMOB; // 点位属性确定
_initialAttStatus = ATTManager.GetStatus();
SetUserProperty(Analytics.ParameterATTStatus, _initialAttStatus); // 上报一个初始的状态
SetATTStatus(_initialAttStatus); // 上报一个初始的状态
}
/// <summary>
/// iOS 平台检查 ATT 状态
/// </summary>
private static void CheckAttStatus(bool autoReCall = false)
private void CheckAttStatus(bool autoReCall = false)
{
_autoReCallAtt = autoReCall;
@ -192,17 +199,18 @@ namespace Guru
Delay(1, () => ATTManager.CheckStatus(ReportAttStatus));
}
private static void ReportAttStatus(string status)
private void ReportAttStatus(string status)
{
LogI($"{Tag} --- Get Att status:{status} att Type:{_attType} recall:{_autoReCallAtt}");
SetUserProperty(Analytics.ParameterATTStatus, status); // 当前的状态
SetATTStatus(_initialAttStatus); // 上报一个初始的状态
// SetUserProperty(Analytics.ParameterATTStatus, status); // 当前的状态
if (!string.IsNullOrEmpty(status)
&& status != _initialAttStatus
&& status != ATTManager.ATT_STATUS_NOT_DETERMINED)
{
// 上报点位:
Analytics.AttResult(status, _attType);
SetATTStatus(_initialAttStatus);
}
switch(status)
@ -219,21 +227,111 @@ namespace Guru
// ATT 状态已授权
break;
}
CheckNotiPermission(); // Consent 回调后检查 Notification 权限
}
#endif
#endregion
#endregion
#region Notification Permission Check
/// <summary>
/// 初始化 Noti Service
/// </summary>
private void InitNotiPermission()
{
// bool hasNotiGranted = false;
_notiStatue = "no_determined";
NotificationService.Initialize(); // 初始化 Noti 服务
Analytics.SetNotiPerm(NotificationService.GetStatus());
}
/// <summary>
/// 检查 Noti 状态
/// </summary>
private void CheckNotiPermission()
{
var status = NotificationService.GetStatus();
// 如果未启用自动 Noti 授权,则直接上报状态
if (!_initConfig.AutoNotificationPermission)
{
Debug.LogWarning($"[SDK] ---- AutoNotificationPermission is OFF, Project should request permission own.");
return;
}
bool isGranted = NotificationService.IsPermissionGranted();
Debug.Log($"[SDK] ---- Check Noti Permission: {isGranted}");
if (isGranted)
{
Debug.Log($"[SDK] ---- Set Notification Permission: {status}");
Analytics.SetNotiPerm(status);
return;
}
RequestNotificationPermission(); // 请求授权
}
/// <summary>
/// 请求推送授权
/// </summary>
/// <param name="callback"></param>
public static void RequestNotificationPermission(Action<string> callback = null)
{
FirebaseUtil.StartFetchFcmToken();
Debug.Log($"[SDK] ---- RequestNotificationPermission");
NotificationService.RequestPermission(status =>
{
Debug.Log($"[SDK] ---- Set Notification Permission: {status}");
if(!string.IsNullOrEmpty(status)) Analytics.SetNotiPerm(status);
callback?.Invoke(status);
});
}
/// <summary>
/// 获取 Notification 状态值
/// </summary>
/// <returns></returns>
public static string GetNotificationStatus()
{
return NotificationService.GetStatus();
}
/// <summary>
/// 用户是否已经获取了 Notification 授权了
/// </summary>
/// <returns></returns>
public static bool IsNotificationPermissionGranted()
{
return NotificationService.IsPermissionGranted();
}
#endregion
#region Ad Services
private static bool _initAdsCompleted = false;
public static bool IsAdsReady => _initAdsCompleted;
private static int _preBannerAction = 0;
public static AdsInitSpec GetDefaultAdsSpec()
{
return AdsInitSpec.BuildDefault(InitConfig.AutoLoadWhenAdsReady, IsDebugMode);
}
/// <summary>
/// 启动广告服务
/// </summary>
internal static void StartAdService(AdsInitSpec spec = null)
private static void StartAdService(AdsInitSpec spec = null)
{
//---------- Using InitConfig ----------
if (InitConfig is { IsBuyNoAds: true }) SetBuyNoAds(true);
LogI($"StartAdService");
if (spec == null)
{
@ -243,38 +341,89 @@ namespace Guru
spec = AdsInitSpec.BuildWithNoAds(InitConfig.AutoLoadWhenAdsReady, IsDebugMode);
}
}
if(InitConfig != null && !string.IsNullOrEmpty(InitConfig.BannerBackgroundColor))
spec.bannerColorHex = InitConfig.BannerBackgroundColor;
//--------- Add Callbacks -----------
// BADS
ADService.Instance.OnBannerStartLoad = OnBannerStartLoad;
ADService.Instance.OnBannerLoaded = OnBannerLoaded;
// IADS
ADService.Instance.OnInterstitialStartLoad = OnInterstitialStartLoad;
ADService.Instance.OnInterstitialLoaded = OnInterstitialLoaded;
ADService.Instance.OnInterstitialFailed = OnInterstitialFailed;
ADService.Instance.OnInterstitialClosed = OnInterstitialClosed;
// RADS
ADService.Instance.OnRewardedStartLoad = OnRewardStartLoad;
ADService.Instance.OnRewardLoaded = OnRewardLoaded;
ADService.Instance.OnRewardFailed = OnRewardFailed;
ADService.Instance.OnRewardClosed = OnRewardClosed;
// ---------- Start Services ----------
ADService.Instance.StartService(OnAdsInitComplete, spec);
//--------- Callbacks -----------
ADService.OnBannerLoaded = OnBannerLoaded;
ADService.OnInterstitialLoaded = OnInterstitialLoaded;
ADService.OnInterstitialFailed = OnInterstitialFailed;
ADService.OnRewardLoaded = OnRewardLoaded;
ADService.OnRewardFailed = OnRewardFailed;
// ---------- Life Cycle ----------
Callbacks.App.OnAppPaused += OnAppPaused;
}
/// <summary>
/// 生命周期回调
/// </summary>
/// <param name="paused"></param>
private static void OnAppPaused(bool paused)
{
if(ADService.Instance.IsInitialized)
ADService.Instance.OnAppPaused(paused);
}
private static void OnBannerStartLoad(string adUnitId)
=> Callbacks.Ads.InvokeOnBannerADStartLoad(adUnitId);
private static void OnBannerLoaded()
=> Callbacks.Ads._onBannerADLoaded?.Invoke();
=> Callbacks.Ads.InvokeOnBannerADLoaded();
private static void OnInterstitialStartLoad(string adUnitId)
=> Callbacks.Ads.InvokeOnInterstitialADStartLoad(adUnitId);
private static void OnInterstitialLoaded()
=> Callbacks.Ads._onInterstitialADLoaded?.Invoke();
=> Callbacks.Ads.InvokeOnInterstitialADLoaded();
private static void OnInterstitialFailed()
=> Callbacks.Ads._onInterstitialADFailed?.Invoke();
=> Callbacks.Ads.InvokeOnInterstitialADFailed();
private static void OnInterstitialClosed()
=> Callbacks.Ads.InvokeOnInterstitialADClosed();
private static void OnRewardStartLoad(string adUnitId)
=> Callbacks.Ads.InvokeOnRewardedADStartLoad(adUnitId);
private static void OnRewardLoaded()
=> Callbacks.Ads._onRewardedADLoaded?.Invoke();
=> Callbacks.Ads.InvokeOnRewardedADLoaded();
private static void OnRewardFailed()
=> Callbacks.Ads._onRewardADFailed?.Invoke();
=> Callbacks.Ads.InvokeOnRewardADFailed();
private static void OnRewardClosed()
=> Callbacks.Ads.InvokeOnRewardADClosed();
private static void OnAdsInitComplete()
{
_initAdsCompleted = true;
Callbacks.Ads._onAdsInitComplete?.Invoke();
if (_adInitSpec != null && _adInitSpec.loadBanner)
{
// 预制动作处理
if (_preBannerAction == 1)
{
_preBannerAction = 0;
ShowBanner();
}
else if (_preBannerAction == -1)
{
_preBannerAction = 0;
HideBanner();
}
}
Callbacks.Ads.InvokeOnAdsInitComplete();
}
private static bool CheckAdsReady()
{
if (!_initAdsCompleted)
if (!IsAdsReady)
{
LogE("Ads is not ready. Call <GuruSDk.StartAdService> first.");
LogW("[SDK] Ads is not ready. Call <GuruSDk.StartAdService> first.");
return false;
}
return true;
@ -286,15 +435,40 @@ namespace Guru
/// <param name="placement"></param>
public static void ShowBanner(string placement = "")
{
if (!CheckAdsReady()) return;
if (!CheckAdsReady())
{
_preBannerAction = 1;
return;
}
ADService.Instance.ShowBanner(placement);
}
/// <summary>
/// 设置 Banner 背景颜色
/// </summary>
/// <param name="color"></param>
public static void SetBannerBackgroundColor(Color color)
{
// if (!CheckAdsReady()) return;
ADService.Instance.SetBannerBackgroundColor(color);
}
public static void SetBannerAutoRefresh(bool value = true)
{
if (!CheckAdsReady()) return;
ADService.Instance.SetBannerAutoRefresh(value);
}
/// <summary>
/// 隐藏Banner广告
/// </summary>
public static void HideBanner()
{
if (!CheckAdsReady()) return;
if (!CheckAdsReady())
{
_preBannerAction = -1;
return;
}
ADService.Instance.HideBanner();
}
@ -373,6 +547,7 @@ namespace Guru
}
#endregion
}

File diff suppressed because it is too large Load Diff

View File

@ -1,199 +0,0 @@
namespace Guru
{
using System;
using UnityEngine;
public partial class GuruSDK
{
/// <summary>
/// 回调参数类
/// </summary>
public class Callbacks
{
/// <summary>
/// APP 事件
/// </summary>
public static class App
{
internal static Action<bool> _onAppPaused;
public static event Action<bool> OnAppPaused
{
add => _onAppPaused += value;
remove => _onAppPaused -= value;
}
internal static Action _onAppQuit;
public static event Action OnAppQuit
{
add => _onAppQuit += value;
remove => _onAppQuit -= value;
}
}
/// <summary>
/// GDPR Consent
/// </summary>
public static class ConsentFlow
{
/// <summary>
/// 当Consent启动结束后返回状态码
/// </summary>
public static event Action<int> OnConsentResult
{
add => _onConsentResult += value;
remove => _onConsentResult -= value;
}
internal static Action<int> _onConsentResult;
/// <summary>
/// ATT 状态返回
/// </summary>
public static event Action<int> OnAttResult
{
add => _onAttResult += value;
remove => _onAttResult -= value;
}
internal static Action<int> _onAttResult;
}
/// <summary>
/// 广告回调
/// </summary>
public static class Ads
{
internal static Action _onAdsInitComplete;
public static event Action OnAdsInitComplete
{
add => _onAdsInitComplete += value;
remove => _onAdsInitComplete -= value;
}
internal static Action _onBannerADLoaded;
public static event Action OnBannerADLoaded
{
add => _onBannerADLoaded += value;
remove => _onBannerADLoaded -= value;
}
internal static Action _onInterstitialADLoaded;
public static event Action OnInterstitialADLoaded
{
add => _onInterstitialADLoaded += value;
remove => _onInterstitialADLoaded -= value;
}
internal static Action _onInterstitialADFailed;
public static event Action OnInterstitialADFailed
{
add => _onInterstitialADFailed += value;
remove => _onInterstitialADFailed -= value;
}
internal static Action _onRewardedADLoaded;
public static event Action OnRewardedADLoaded
{
add => _onRewardedADLoaded += value;
remove => _onRewardedADLoaded -= value;
}
internal static Action _onRewardADFailed;
public static event Action OnRewardADFailed
{
add => _onRewardADFailed += value;
remove => _onRewardADFailed -= value;
}
}
/// <summary>
/// 云控参数
/// </summary>
public static class Remote
{
internal static Action<bool> _onRemoteFetchComplete;
public static event Action<bool> OnRemoteFetchComplete
{
add => _onRemoteFetchComplete += value;
remove => _onRemoteFetchComplete -= value;
}
}
/// <summary>
/// 支付回调
/// </summary>
public static class IAP
{
internal static Action _onIAPInitStart;
public static event Action OnIAPInitStart
{
add => _onIAPInitStart += value;
remove => _onIAPInitStart -= value;
}
internal static Action<bool> _onIAPInitComplete;
public static event Action<bool> OnIAPInitComplete
{
add => _onIAPInitComplete += value;
remove => _onIAPInitComplete -= value;
}
internal static Action<string> _onPurchaseStart;
public static event Action<string> OnPurchaseStart
{
add => _onPurchaseStart += value;
remove => _onPurchaseStart -= value;
}
internal static Action<string, bool> _onPurchaseEnd;
public static event Action<string, bool> OnPurchaseEnd
{
add => _onPurchaseEnd += value;
remove => _onPurchaseEnd -= value;
}
internal static Action<string, string> _onPurchaseFailed;
public static event Action<string, string> OnPurchaseFailed
{
add => _onPurchaseFailed += value;
remove => _onPurchaseFailed -= value;
}
internal static Action<bool, string> _onIAPRestored;
public static event Action<bool, string> OnIAPRestored
{
add => _onIAPRestored += value;
remove => _onIAPRestored -= value;
}
}
public static class SDK
{
internal static Action<bool> _onFirebaseReady;
public static event Action<bool> OnFirebaseReady
{
add => _onFirebaseReady += value;
remove => _onFirebaseReady -= value;
}
internal static Action _onGuruServiceReady;
public static event Action OnGuruServiceReady
{
add => _onGuruServiceReady += value;
remove => _onGuruServiceReady -= value;
}
}
}
}
}

View File

@ -0,0 +1,378 @@
namespace Guru
{
using System;
public partial class GuruSDK
{
/// <summary>
/// 回调参数类
/// </summary>
public class Callbacks
{
/// <summary>
/// APP 事件
/// </summary>
public static class App
{
private static Action<bool> _onAppPaused;
public static event Action<bool> OnAppPaused
{
add => _onAppPaused += value;
remove => _onAppPaused -= value;
}
internal static void InvokeOnAppPaused(bool isPaused)
{
_onAppPaused?.Invoke(isPaused);
}
private static Action _onAppQuit;
public static event Action OnAppQuit
{
add => _onAppQuit += value;
remove => _onAppQuit -= value;
}
internal static void InvokeOnAppQuit()
{
_onAppQuit?.Invoke();
}
}
/// <summary>
/// GDPR Consent
/// </summary>
public static class ConsentFlow
{
/// <summary>
/// 当Consent启动结束后返回状态码
/// </summary>
public static event Action<int> OnConsentResult
{
add => _onConsentResult += value;
remove => _onConsentResult -= value;
}
private static Action<int> _onConsentResult;
internal static void InvokeOnConsentResult(int code)
{
_onConsentResult?.Invoke(code);
}
/// <summary>
/// ATT 状态返回
/// </summary>
public static event Action<int> OnAttResult
{
add => _onAttResult += value;
remove => _onAttResult -= value;
}
private static Action<int> _onAttResult;
internal static void InvokeOnAttResultCallback(int code)
{
_onAttResult?.Invoke(code);
}
}
/// <summary>
/// 广告回调
/// </summary>
public static class Ads
{
private static Action _onAdsInitComplete;
public static event Action OnAdsInitComplete
{
add => _onAdsInitComplete += value;
remove => _onAdsInitComplete -= value;
}
internal static void InvokeOnAdsInitComplete()
{
_onAdsInitComplete?.Invoke();
}
//------------ BANNER -----------------
private static Action<string> _onBannerADStartLoad;
public static event Action<string> OnBannerADStartLoad
{
add => _onBannerADStartLoad += value;
remove => _onBannerADStartLoad -= value;
}
internal static void InvokeOnBannerADStartLoad(string adUnitId)
{
_onBannerADStartLoad?.Invoke(adUnitId);
}
private static Action _onBannerADLoaded;
public static event Action OnBannerADLoaded
{
add => _onBannerADLoaded += value;
remove => _onBannerADLoaded -= value;
}
internal static void InvokeOnBannerADLoaded()
{
_onBannerADLoaded?.Invoke();
}
//------------ INTER -----------------
private static Action<string> _onInterstitialADStartLoad;
public static event Action<string> OnInterstitialADStartLoad
{
add => _onInterstitialADStartLoad += value;
remove => _onInterstitialADStartLoad -= value;
}
internal static void InvokeOnInterstitialADStartLoad(string adUnitId)
{
_onInterstitialADStartLoad?.Invoke(adUnitId);
}
private static Action _onInterstitialADLoaded;
public static event Action OnInterstitialADLoaded
{
add => _onInterstitialADLoaded += value;
remove => _onInterstitialADLoaded -= value;
}
internal static void InvokeOnInterstitialADLoaded()
{
_onInterstitialADLoaded?.Invoke();
}
private static Action _onInterstitialADFailed;
public static event Action OnInterstitialADFailed
{
add => _onInterstitialADFailed += value;
remove => _onInterstitialADFailed -= value;
}
internal static void InvokeOnInterstitialADFailed()
{
_onInterstitialADFailed?.Invoke();
}
private static Action _onInterstitialADClosed;
public static event Action OnInterstitialADClosed
{
add => _onInterstitialADClosed += value;
remove => _onInterstitialADClosed -= value;
}
internal static void InvokeOnInterstitialADClosed()
{
_onInterstitialADClosed?.Invoke();
}
//------------ REWARD -----------------
private static Action<string> _onRewardedADStartLoad;
public static event Action<string> OnRewardedADStartLoad
{
add => _onRewardedADStartLoad += value;
remove => _onRewardedADStartLoad -= value;
}
internal static void InvokeOnRewardedADStartLoad(string adUnitId)
{
_onRewardedADStartLoad?.Invoke(adUnitId);
}
private static Action _onRewardedADLoaded;
public static event Action OnRewardedADLoaded
{
add => _onRewardedADLoaded += value;
remove => _onRewardedADLoaded -= value;
}
internal static void InvokeOnRewardedADLoaded()
{
_onRewardedADLoaded?.Invoke();
}
private static Action _onRewardADClosed;
public static event Action OnRewardedADClosed
{
add => _onRewardADClosed += value;
remove => _onRewardADClosed -= value;
}
internal static void InvokeOnRewardADClosed()
{
_onRewardADClosed?.Invoke();
}
private static Action _onRewardADFailed;
public static event Action OnRewardADFailed
{
add => _onRewardADFailed += value;
remove => _onRewardADFailed -= value;
}
internal static void InvokeOnRewardADFailed()
{
_onRewardADFailed?.Invoke();
}
}
/// <summary>
/// 云控参数
/// </summary>
public static class Remote
{
private static Action<bool> _onRemoteFetchComplete;
public static event Action<bool> OnRemoteFetchComplete
{
add => _onRemoteFetchComplete += value;
remove => _onRemoteFetchComplete -= value;
}
internal static void InvokeOnRemoteFetchComplete(bool success)
{
_onRemoteFetchComplete?.Invoke(success);
}
}
/// <summary>
/// 支付回调
/// </summary>
public static class IAP
{
private static Action _onIAPInitStart;
public static event Action OnIAPInitStart
{
add => _onIAPInitStart += value;
remove => _onIAPInitStart -= value;
}
internal static void InvokeOnIAPInitStart()
{
_onIAPInitStart?.Invoke();
}
private static Action<bool> _onIAPInitComplete;
public static event Action<bool> OnIAPInitComplete
{
add => _onIAPInitComplete += value;
remove => _onIAPInitComplete -= value;
}
internal static void InvokeOnIAPInitComplete(bool success)
{
_onIAPInitComplete?.Invoke(success);
}
private static Action<string> _onPurchaseStart;
public static event Action<string> OnPurchaseStart
{
add => _onPurchaseStart += value;
remove => _onPurchaseStart -= value;
}
internal static void InvokeOnPurchaseStart(string productId)
{
_onPurchaseStart?.Invoke(productId);
}
private static Action<string, bool> _onPurchaseEnd;
public static event Action<string, bool> OnPurchaseEnd
{
add => _onPurchaseEnd += value;
remove => _onPurchaseEnd -= value;
}
internal static void InvokeOnPurchaseEnd(string productId, bool success)
{
_onPurchaseEnd?.Invoke(productId, success);
}
private static Action<string, string> _onPurchaseFailed;
public static event Action<string, string> OnPurchaseFailed
{
add => _onPurchaseFailed += value;
remove => _onPurchaseFailed -= value;
}
internal static void InvokeOnPurchaseFailed(string productId, string error)
{
_onPurchaseFailed?.Invoke(productId, error);
}
private static Action<bool, string> _onIAPRestored;
public static event Action<bool, string> OnIAPRestored
{
add => _onIAPRestored += value;
remove => _onIAPRestored -= value;
}
internal static void InvokeOnIAPRestored(bool success, string productId)
{
_onIAPRestored?.Invoke(success, productId);
}
}
public static class SDK
{
private static Action<bool> _onFirebaseReady;
public static event Action<bool> OnFirebaseReady
{
add => _onFirebaseReady += value;
remove => _onFirebaseReady -= value;
}
internal static void InvokeOnFirebaseReady(bool success)
{
_onFirebaseReady?.Invoke(success);
}
private static Action _onGuruServiceReady;
public static event Action OnGuruServiceReady
{
add => _onGuruServiceReady += value;
remove => _onGuruServiceReady -= value;
}
internal static void InvokeOnGuruServiceReady()
{
_onGuruServiceReady?.Invoke();
}
private static Action<bool> _onDebuggerDisplayed;
public static event Action<bool> OnDisplayDebugger
{
add => _onDebuggerDisplayed += value;
remove => _onDebuggerDisplayed -= value;
}
internal static void InvokeOnDebuggerDisplayed(bool success)
{
_onDebuggerDisplayed?.Invoke(success);
}
private static Action<bool> _onUserAuthResult;
public static event Action<bool> OnGuruUserAuthResult
{
add => _onUserAuthResult += value;
remove => _onUserAuthResult -= value;
}
internal static void InvokeOnGuruUserAuthResult(bool success)
{
_onUserAuthResult?.Invoke(success);
}
// DeepLink 回调
private static Action<string> _onDeeplinkCallback;
public static event Action<string> OnDeeplinkCallback
{
add => _onDeeplinkCallback += value;
remove => _onDeeplinkCallback -= value;
}
internal static void InvokeDeeplinkCallback(string deeplink)
{
_onDeeplinkCallback?.Invoke(deeplink);
}
// TODO: 之后需要添加 define 宏来控制是否可用
// Firebase Auth 回调
private static Action<bool, Firebase.Auth.FirebaseUser> _onFirebaseUserAuthResult;
public static event Action<bool, Firebase.Auth.FirebaseUser> OnFirebaseUserAuthResult
{
add => _onFirebaseUserAuthResult += value;
remove => _onFirebaseUserAuthResult -= value;
}
internal static void InvokeOnFirebaseAuthResult(bool success, Firebase.Auth.FirebaseUser firebaseUser = null)
{
_onFirebaseUserAuthResult?.Invoke(success, firebaseUser);
}
}
}
}
}

View File

@ -2,6 +2,8 @@ namespace Guru
{
public partial class GuruSDK
{
/// <summary>
/// Consts values
/// </summary>
@ -22,6 +24,7 @@ namespace Guru
public const string EventJoinGroup = "join_group";
public const string EventLevelEnd = "level_end";
public const string EventLevelStart = "level_start";
public const string EventLevelEndSuccessPrefix = "level_end_success_";
public const string EventLevelUp = "level_up";
public const string EventLogin = "login";
public const string EventPostScore = "post_score";
@ -36,9 +39,26 @@ namespace Guru
public const string EventShare = "share";
public const string EventSignUp = "sign_up";
public const string EventSpendVirtualCurrency = "spend_virtual_currency";
public const string EventTutorialBegin = "tutorial_begin";
public const string EventTutorialComplete = "tutorial_complete";
public const string EventUnlockAchievement = "unlock_achievement";
public const string EventHpPoints = "hp_points";
public const string EventAttReguideImp = "att_reguide_imp";
public const string EventAttReguideClk = "att_reguide_clk";
public const string EventAttReguideResult = "att_reguide_result";
public const string EventTutorialBegin = "tutorial_begin";
public const string EventTutorialImp= "tutorial_{0}_imp";
public const string EventTutorialNextClick= "tutorial_{0}_next_clk";
public const string EventTutorialComplete= "tutorial_complete";
public const string EventTutorialClose = "tutorial_close";
public const string EventNotiPermImp = "noti_perm_imp";
public const string EventNotiPermResult = "noti_perm_result";
public const string EventNotiPermRationaleImp = "noti_perm_rationale_imp";
public const string EventNotiPermRationaleResult = "noti_perm_rationale_result";
public const string EventDevAudit = "dev_audit";
public const string EventViewCart = "view_cart";
public const string EventViewItem = "view_item";
public const string EventViewItemList = "view_item_list";
@ -190,7 +210,7 @@ namespace Guru
public const string ATTOptIn = "att_opt_in";
public const string ATTOpOut = "att_opt_out";
public const string ParameterATTStatus = "att_status";
public const string EventATTResult = "att_result";
public const string EventAttResult = "att_result";
// 用户属性
public const string PropertyFirstOpenTime = "first_open_time"; //用户第一次first_open的时间
@ -206,22 +226,56 @@ namespace Guru
public const string PropertyCoin = "coin"; //当前金币数
public const string PropertyExp = "exp"; // 经验值
public const string PropertyHp = "hp"; // 生命值/体力
public const string PropertyNetwork = "network"; // 网络状态
public const string PropertyAndroidID = "android_id"; // Android 平台 AndroidID
public const string PropertyIDFV = "idfv"; // iOS 平台 IDFV
public const string PropertyPicture = "picture"; // 玩家在主线的mapid
public const string PropertyNoAds = "no_ads"; // 玩家是否去广告
public const string PropertyATTStatus = "att_status"; // ATT 状态
public const string PropertyNotiPerm = "noti_perm"; // Notification Permission 状态
public const string PropertyAdjustId = "adjust_id"; // AdjustId
public const string PropertyGDPR = "gdpr"; // GDPR状态
// 经济相关
public const string ParameterBalance = "balance"; // 用于余额
public const string ParameterSku = "sku"; // sku
public const string ParameterDefaultScene = "in_game"; // 货币消费默认场景
public const string ParameterVirtualCurrencyName = "virtual_currency_name"; // 虚拟货币名称
public const string CurrencyNameProps = "props"; // props
public const string CurrencyCategoryReward = "reward"; // common, ads
public const string CurrencyCategoryIAP = "iap_buy"; // In app purchase
public const string CurrencyCategoryBonus = "bonus"; // ads+items, gift box, item group
public const string CurrencyCategoryIGC = "igc"; // In game currency
public const string CurrencyCategoryIGB = "igb"; // In game barter
public const string CurrencyCategoryProp = "prop"; // prop
public const string CurrencyCategoryProps = "props"; // props
public const string CurrencyCategoryBundle = "bundle"; // prop groups
public const string CurrencyCategoryBoost = "boost"; // boost
// SDK
public const string EventSDKInfo = "sdk_info";
//----------------- 关卡开始类型 ---------------------
public const string EventLevelStartModePlay = "play";
public const string EventLevelStartModeReplay = "replay";
public const string EventLevelStartModeContinue= "continue";
//----------------- 关卡结束类型 ---------------------
public const string EventLevelEndSuccess = "success";
public const string EventLevelEndFail = "fail";
public const string EventLevelEndExit = "exit";
public const string EventLevelEndTimeout = "timeout";
/// <summary>
/// 主线关卡类型
/// 只有传入此类型时才会进行 Blevel 的累加
/// </summary>
public const string LevelTypeMain = "main";
#endregion
}
}

View File

@ -0,0 +1,60 @@
namespace Guru
{
public partial class GuruSDK
{
private const string K_CMD_NAME_DEBUGGER = "gurusdk.unity.dbg";
private const string K_CMD_NAME_WATERMARK = "gurusdk.unity.wm";
private const string K_CMD_NAME_CONSOLE = "gurusdk.unity.con";
#region Android 测试入口
/// <summary>
/// 启动 Android 测试配置
/// </summary>
private void StartAndroidDebugCmds()
{
if (string.IsNullOrEmpty(AppBundleId))
{
UnityEngine.Debug.LogError("--- App Bundle Id is empty, please set it first. ---");
return;
}
string val;
string key;
key = K_CMD_NAME_DEBUGGER;
val = AndroidSystemPropertiesHelper.Get(key);
if (val == AppBundleId)
{
// 显示应用调试状态栏
Debugger.ShowAdStatus();
}
key = K_CMD_NAME_WATERMARK;
val = AndroidSystemPropertiesHelper.Get(key);
if (val == AppBundleId)
{
// 显示应用水印
// TODO
}
key = K_CMD_NAME_CONSOLE;
val = AndroidSystemPropertiesHelper.Get(key);
if (val == AppBundleId)
{
// 显示控制台
}
}
private bool IsCmdAvailable(string value)
{
return value == "1" || value == "true";
}
#endregion
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 36f2d28a30ff46568d6adc0b728a3268
timeCreated: 1714354376

View File

@ -0,0 +1,129 @@
namespace Guru
{
using UnityEngine;
using System;
public partial class GuruSDK
{
private static readonly bool _useBaseOptions = true;
private static GuruDebugger _debugger;
public static GuruDebugger Debugger
{
get
{
if (_debugger == null)
{
_debugger = GuruDebugger.Instance;
if (_useBaseOptions)
{
InitDebuggerLayout();
}
}
return _debugger;
}
}
/// <summary>
/// 显示广告状态
/// </summary>
public static bool ShowAdStatus()
{
if (!IsServiceReady) return false;
Debugger.ShowAdStatus();
return true;
}
/// <summary>
/// 显示 Debugger
/// </summary>
/// <returns></returns>
public static bool ShowDebugger()
{
if (!IsServiceReady) return false;
Debugger.ShowPage(); // 显示 Debugger 界面
return true;
}
private static void InitDebuggerLayout()
{
var settings = GuruSettings.Instance;
var v = GuruAppVersion.Load();
var app_version = (v == null ? $"{Application.version} (unknown)" : $"{v.version} ({v.code})");
var uid = (string.IsNullOrEmpty(UID) ? "NULL" : UID);
var device_id = (string.IsNullOrEmpty(DeviceId) ? "NULL" : DeviceId);
var push_token = (string.IsNullOrEmpty(PushToken) ? "NULL" : PushToken);
var auth_token = (string.IsNullOrEmpty(AuthToken) ? "NULL" : AuthToken);
var fid = (string.IsNullOrEmpty(FirebaseId) ? "NULL" : FirebaseId);
var adjust_id = (string.IsNullOrEmpty(AdjustId) ? "NULL" : AdjustId);
var idfa = (string.IsNullOrEmpty(IDFA) ? "NULL" : IDFA);
var gsid = (string.IsNullOrEmpty(GSADID) ? "NULL" : GSADID);
// ------------ Info Page --------------------
Debugger.AddOption("Info/Guru SDK", GuruSDK.Version);
Debugger.AddOption("Info/Unity Version", Application.unityVersion);
Debugger.AddOption("Info/Name", settings.ProductName);
Debugger.AddOption("Info/Bundle Id", settings.GameIdentifier);
Debugger.AddOption("Info/Version", app_version);
Debugger.AddOption("Info/Uid", uid).AddCopyButton();
Debugger.AddOption("Info/Device ID", device_id).AddCopyButton();
Debugger.AddOption("Info/Push Token", push_token).AddCopyButton();
Debugger.AddOption("Info/Auth Token", auth_token).AddCopyButton();
Debugger.AddOption("Info/Firebase Id", fid).AddCopyButton();
Debugger.AddOption("Info/Adjust Id", adjust_id).AddCopyButton();
Debugger.AddOption("Info/IDFA", idfa).AddCopyButton();
Debugger.AddOption("Info/GSADID", gsid).AddCopyButton();
Debugger.AddOption("Info/Debug Mode", GuruSDK.IsDebugMode? "true" : "false");
Debugger.AddOption("Info/Screen size", $"{Screen.width} x {Screen.height}");
// ------------ Ads Page --------------------
Debugger.AddOption("Ads/Show Ads Debug Panel", "", ShowMaxDebugPanel);
var badsId = settings.ADSetting.GetBannerID();
var iadsId = settings.ADSetting.GetInterstitialID();
var radsId = settings.ADSetting.GetRewardedVideoID();
Debugger.AddOption("Ads/Banner Id", badsId);
Debugger.AddOption("Ads/Interstitial Id", iadsId);
Debugger.AddOption("Ads/Rewarded Id", radsId);
GuruDebugger.OnClosed -= OnDebuggerClosed;
GuruDebugger.OnClosed += OnDebuggerClosed;
Callbacks.SDK.InvokeOnDebuggerDisplayed(true);
}
private static void OnDebuggerClosed()
{
GuruDebugger.OnClosed -= OnDebuggerClosed;
Callbacks.SDK.InvokeOnDebuggerDisplayed(false);
}
/// <summary>
/// 显示 Debugger
/// </summary>
/// <param name="debugger"></param>
/// <returns></returns>
public static bool ShowDebuggerWithData(out GuruDebugger debugger)
{
debugger = null;
bool res = ShowDebugger();
if (res)
{
debugger = GuruDebugger.Instance;
}
return res;
}
public static GuruDebugger.OptionLayout AddOption(string uri, string content = "", Action clickHandler = null)
{
return Debugger.AddOption(uri, content, clickHandler);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 002605b9e408487bb69d27d07dda016c
timeCreated: 1711078536

View File

@ -1,6 +1,5 @@
namespace Guru
{
using UnityEngine;
using System;
using System.Linq;
@ -18,13 +17,12 @@ namespace Guru
public const string BuyFail_DuplicateTransaction = "DuplicateTransaction";
public const string BuyFail_Unknown = "Unknown";
#region Init
#region Start
/// <summary>
/// 初始化IAP 功能
/// </summary>
public static void InitIAP(byte[] googleKey, byte[] appleRootCerts)
public static void InitIAP(string uid, byte[] googleKey, byte[] appleRootCerts)
{
GuruIAP.Instance.OnInitResult += OnIAPInitResult;
GuruIAP.Instance.OnRestored += OnRestored;
@ -33,9 +31,9 @@ namespace Guru
GuruIAP.Instance.OnBuyFailed += OnBuyFailed;
GuruIAP.Instance.OnGetProductReceipt += OnGetReceipt;
Callbacks.IAP._onIAPInitStart?.Invoke(); // 初始化之前进行调用
Callbacks.IAP.InvokeOnIAPInitStart(); // 初始化之前进行调用
GuruIAP.Instance.InitWithKeys(googleKey, appleRootCerts, IsDebugMode);
GuruIAP.Instance.InitWithKeys(uid, googleKey, appleRootCerts, IsDebugMode);
}
/// <summary>
@ -45,9 +43,8 @@ namespace Guru
private static void OnIAPInitResult(bool success)
{
LogI($"IAP init result: {success}");
IsIAPReady = success;
Callbacks.IAP._onIAPInitComplete?.Invoke(success);
Callbacks.IAP.InvokeOnIAPInitComplete(success);
}
private static bool CheckIAPReady()
@ -72,7 +69,7 @@ namespace Guru
/// <returns></returns>
public static ProductInfo GetProductInfo(string productName)
{
return GuruIAP.Instance?.GetInfo(productName) ?? null;
return GuruIAP.Instance?.GetInfo(productName);
}
/// <summary>
@ -82,7 +79,7 @@ namespace Guru
/// <returns></returns>
public static ProductInfo GetProductInfoById(string productId)
{
return GuruIAP.Instance?.GetInfoById(productId) ?? null;
return GuruIAP.Instance?.GetInfoById(productId);
}
@ -129,7 +126,7 @@ namespace Guru
#region Purchase
private static Action<string, bool> _onPurchaseCallback;
private static Action<string, bool> InvokeOnPurchaseCallback;
/// <summary>
/// 老接口, 将会被废弃
@ -137,7 +134,7 @@ namespace Guru
/// <param name="productName"></param>
/// <param name="purchaseCallback"></param>
[Obsolete("Will be discarded in next version. Using Purchase(string productName, string category, Action<string, bool> purchaseCallback) instead.")]
public static void Purchase(string productName, Action<string, bool> purchaseCallback = null)
internal static void Purchase(string productName, Action<string, bool> purchaseCallback = null)
{
Purchase(productName, "", purchaseCallback);
}
@ -152,7 +149,7 @@ namespace Guru
{
if (CheckIAPReady())
{
_onPurchaseCallback = purchaseCallback;
InvokeOnPurchaseCallback = purchaseCallback;
GuruIAP.Instance.Buy(productName, category);
}
}
@ -183,8 +180,8 @@ namespace Guru
/// <param name="success"></param>
private static void OnBuyEnd(string productName, bool success)
{
_onPurchaseCallback?.Invoke(productName, success);
Callbacks.IAP._onPurchaseEnd?.Invoke(productName, success);
InvokeOnPurchaseCallback?.Invoke(productName, success);
Callbacks.IAP.InvokeOnPurchaseEnd(productName, success);
}
/// <summary>
@ -193,7 +190,7 @@ namespace Guru
/// <param name="productName"></param>
private static void OnBuyStart(string productName)
{
Callbacks.IAP._onPurchaseStart?.Invoke(productName);
Callbacks.IAP.InvokeOnPurchaseStart(productName);
}
/// <summary>
@ -203,7 +200,7 @@ namespace Guru
/// <param name="reason"></param>
private static void OnBuyFailed(string productName, string reason)
{
Callbacks.IAP._onPurchaseFailed?.Invoke(productName, reason);
Callbacks.IAP.InvokeOnPurchaseFailed(productName, reason);
}
#endregion
@ -224,7 +221,7 @@ namespace Guru
}
private static void OnRestored(bool success, string msg)
{
Callbacks.IAP._onIAPRestored?.Invoke(success, msg); // 更新回复购买回调
Callbacks.IAP.InvokeOnIAPRestored(success, msg); // 更新回复购买回调
}
#endregion
@ -246,6 +243,93 @@ namespace Guru
#endregion
#region Subscriptions
/// <summary>
/// 订阅是否被取消
/// </summary>
/// <param name="productName"></param>
/// <returns></returns>
public static bool IsSubscriptionCancelled(string productName)
{
return GuruIAP.Instance.IsSubscriptionCancelled(productName);
}
/// <summary>
/// 订阅是否可用
/// </summary>
/// <param name="productName"></param>
/// <returns></returns>
public static bool IsSubscriptionAvailable(string productName)
{
return GuruIAP.Instance.IsSubscriptionAvailable(productName);
}
/// <summary>
/// 订阅是否过期
/// </summary>
/// <param name="productName"></param>
/// <returns></returns>
public static bool IsSubscriptionExpired(string productName)
{
return GuruIAP.Instance.IsSubscriptionExpired(productName);
}
public static bool IsSubscriptionFreeTrail(string productName)
{
return GuruIAP.Instance.IsSubscriptionFreeTrail(productName);
}
public static bool IsSubscriptionAutoRenewing(string productName)
{
return GuruIAP.Instance.IsSubscriptionAutoRenewing(productName);
}
public static bool IsSubscriptionIntroductoryPricePeriod(string productName)
{
return GuruIAP.Instance.IsSubscriptionIntroductoryPricePeriod(productName);
}
public DateTime GetSubscriptionExpireDate(string productName)
{
return GuruIAP.Instance.GetSubscriptionExpireDate(productName);
}
public DateTime GetSubscriptionPurchaseDate(string productName)
{
return GuruIAP.Instance.GetSubscriptionPurchaseDate(productName);
}
public DateTime GetSubscriptionCancelDate(string productName)
{
return GuruIAP.Instance.GetSubscriptionCancelDate(productName);
}
public TimeSpan GetSubscriptionRemainingTime(string productName)
{
return GuruIAP.Instance.GetSubscriptionRemainingTime(productName);
}
public TimeSpan GetSubscriptionIntroductoryPricePeriod(string productName)
{
return GuruIAP.Instance.GetSubscriptionIntroductoryPricePeriod(productName);
}
public TimeSpan GetSubscriptionFreeTrialPeriod(string productName)
{
return GuruIAP.Instance.GetSubscriptionFreeTrialPeriod(productName);
}
public string GetSubscriptionInfoJsonString(string productName)
{
return GuruIAP.Instance.GetSubscriptionInfoJsonString(productName);
}
#endregion
}
}

View File

@ -1,13 +1,22 @@
namespace Guru
{
public partial class GuruSDK
{
public static string UID => _model?.UserId ?? IPMConfig.IPM_UID;
// UID
public static string UID
{
get
{
if(Model != null && !string.IsNullOrEmpty(Model.UserId))
return Model.UserId;
return IPMConfig.IPM_UID;
}
}
public static string UUID => IPMConfig.IPM_UUID ?? "";
public static string DeviceId => IPMConfig.IPM_DEVICE_ID ?? ""; // TODO: change it to _model member later.
public static string PushToken => IPMConfig.IPM_PUSH_TOKEN ?? ""; // TODO: change it to _model member later.
public static string AuthToken => IPMConfig.IPM_TOKEN ?? ""; // TODO: change it to _model member later.
public static string SupportEmail => GuruSettings.SupportEmail ?? "";
public static string StoreUrl
@ -26,14 +35,23 @@ namespace Guru
}
}
public static string AppVersion =>GuruAppVersion.version;
public static string PrivacyUrl => GuruSettings.PriacyUrl ?? "";
public static string TermsUrl => GuruSettings.TermsUrl ?? "";
public static string AppVersion => GuruAppVersion.version;
public static string AppVersionCode => GuruAppVersion.code;
public static string AppVersionString => GuruAppVersion.ToString();
public static bool IsNewUser => IPMConfig.IPM_NEWUSER;
public static string FirebaseId => IPMConfig.FIREBASE_ID;
public static string IDFA => IPMConfig.ADJUST_IDFA;
public static string AdjustId => IPMConfig.ADJUST_ID;
public static string GSADID => IPMConfig.ADJUST_ADID;
public static string CdnHost => _appServicesConfig?.CdnHost() ?? "";
private static GuruAppVersion _appVersion;
private static GuruAppVersion GuruAppVersion
@ -45,6 +63,10 @@ namespace Guru
}
}
private static string _appBundleId;
public static string AppBundleId => _appBundleId;
/// <summary>
/// 设置购买去广告道具的标志位
@ -52,28 +74,32 @@ namespace Guru
/// <param name="value"></param>
public static void SetBuyNoAds(bool value = true)
{
Model.IsNoAds = value;
ADService.Instance.IsBuyNoAds = value;
if (value)
{
Analytics.SetIsIapUser(true);
}
}
/// <summary>
/// 所有成功的主线关卡数量 (b_level)
/// </summary>
public static int SuccessLevelCount
public static int BLevel
{
get => GuruSDKModel.Instance.SuccessLevelCount;
set => GuruSDKModel.Instance.SuccessLevelCount = value;
get => GuruSDKModel.Instance.BLevel;
set => GuruSDKModel.Instance.BLevel = value;
}
/// <summary>
/// 成功关卡总计数量 (b_play)
/// </summary>
public static int TotalPlayedCount
public static int BPlay
{
get => GuruSDKModel.Instance.TotalPlayedCount;
set => GuruSDKModel.Instance.TotalPlayedCount = value;
get => GuruSDKModel.Instance.BPlay;
set => GuruSDKModel.Instance.BPlay = value;
}
}

View File

@ -30,27 +30,45 @@ namespace Guru
{
return RemoteConfigManager.GetConfig<T>(key);
}
public static string GetRemoteString(string key) => RemoteConfigManager.GetString(key);
public static int GetRemoteInt(string key) => RemoteConfigManager.GetInt(key);
public static long GetRemoteLong(string key) => RemoteConfigManager.GetLong(key);
public static double GetRemoteDouble(string key) => RemoteConfigManager.GetDouble(key);
public static float GetRemoteFloat(string key) => RemoteConfigManager.GetFloat(key);
public static bool GetRemoteBool(string key) => RemoteConfigManager.GetBool(key);
public static string GetRemoteString(string key, string defaultValue = "") => RemoteConfigManager.GetString(key, defaultValue);
public static int GetRemoteInt(string key, int defaultValue = 0) => RemoteConfigManager.GetInt(key, defaultValue);
public static long GetRemoteLong(string key, long defaultValue = 0 ) => RemoteConfigManager.GetLong(key, defaultValue);
public static double GetRemoteDouble(string key, double defaultValue = 0) => RemoteConfigManager.GetDouble(key, defaultValue);
public static float GetRemoteFloat(string key, float defaultValue = 0) => RemoteConfigManager.GetFloat(key, defaultValue);
public static bool GetRemoteBool(string key, bool defaultValue = false) => RemoteConfigManager.GetBool(key, defaultValue);
/// <summary>
/// 注册监听某个 Key 的变化
/// </summary>
/// <param name="key"></param>
/// <param name="onValueChanged"></param>
public static void RegisterOnValueChanged(string key, Action<string,string> onValueChanged)
{
RemoteConfigManager.RegisterOnValueChanged(key, onValueChanged);
}
/// <summary>
/// 注销监听某个 Key 的变化
/// </summary>
/// <param name="key"></param>
/// <param name="onValueChanged"></param>
public static void UnRegisterOnValueChanged(string key, Action<string,string> onValueChanged)
{
RemoteConfigManager.UnRegisterOnValueChanged(key, onValueChanged);
}
/// <summary>
/// 获取所有云控配置
/// </summary>
/// <returns></returns>
public static Dictionary<string, ConfigValue> GetRemoteAllValues() => RemoteConfigManager.GetAllValues();
/// <summary>
///
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static string GetRemoteStaticValue(string key) => RemoteConfigManager.GetStaticValue(key);
}

View File

@ -0,0 +1,97 @@
using System;
using UnityEngine;
namespace Guru
{
public partial class GuruSDK
{
#region System
/// <summary>
/// 打开页面
/// </summary>
/// <param name="url"></param>
public static void OpenURL(string url)
{
GuruWebview.OpenPage(url);
}
/// <summary>
/// 打开隐私协议页面
/// </summary>
public static void OpenPrivacyPage()
{
if (string.IsNullOrEmpty(PrivacyUrl))
{
LogE("PrivacyUrl is null");
return;
}
OpenURL(PrivacyUrl);
}
/// <summary>
/// 打开服务条款页面
/// </summary>
public static void OpenTermsPage()
{
if (string.IsNullOrEmpty(TermsUrl))
{
LogE("TermsUrl is null");
return;
}
OpenURL(TermsUrl);
}
#endregion
#region Android System
#if UNITY_ANDROID
/// <summary>
/// 获取 AndroidSDK 的系统版本号
/// </summary>
/// <returns></returns>
public static int GetAndroidSystemVersion()
{
try
{
// sdkInt 是 Android SDK 的整数版本号,例如 Android 10 对应 29。
// release 是 Android 版本的字符串表示,例如 "10"。
using (AndroidJavaClass jc = new AndroidJavaClass("android.os.Build$VERSION"))
{
int sdkInt = jc.GetStatic<int>("SDK_INT");
Debug.LogWarning($"[SDK] --- Android SDK Version:{sdkInt}");
return sdkInt;
}
}
catch (Exception ex)
{
Debug.LogError(ex);
}
return 0;
}
#endif
#endregion
#region Clear Data Cache
/// <summary>
/// 清除数据缓存
/// </summary>
public static void ClearData()
{
Model.ClearData();
GuruIAP.Instance.ClearData();
PlayerPrefs.DeleteAll();
PlayerPrefs.Save();
}
#endregion
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 80d35b4c8f0f4d23a7228fddfb06dd03
timeCreated: 1713238972

View File

@ -0,0 +1,29 @@
namespace Guru
{
using System;
using UnityEngine;
public partial class GuruSDK
{
private ThreadHandler _threadHandler;
private void InitThreadHandler()
{
_threadHandler = new ThreadHandler();
RegisterUpdater(_threadHandler);
}
private void AddActionToMainThread(Action action)
{
_threadHandler?.AddAction(action);
}
public static void RunOnMainThread(Action action)
{
Instance.AddActionToMainThread(action);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8a0605c6b4b64b37a0036b34cbadb9ca
timeCreated: 1712622534

View File

@ -4,12 +4,17 @@ namespace Guru
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using Debug = UnityEngine.Debug;
using Guru.Network;
using System.Linq;
public partial class GuruSDK: MonoBehaviour
{
public const string Version = "1.0.9";
public const string Tag = "[Guru]";
// SDK_VERSION
public const string Version = "1.1.0";
// Const
private const string Tag = "[Guru]";
public const string ServicesConfigKey = "guru_services";
private static GuruSDK _instance;
@ -30,12 +35,9 @@ namespace Guru
}
private GuruSDKInitConfig _initConfig;
private Action<bool> _onCompleteCallback;
private static GuruSDKModel _model;
internal static GuruSDKInitConfig InitConfig => Instance._initConfig;
internal static GuruSDKModel Model => GuruSDKModel.Instance;
private static GuruSDKInitConfig InitConfig => Instance._initConfig;
private static GuruSDKModel Model => GuruSDKModel.Instance;
private static GuruServicesConfig _appServicesConfig;
private static GuruSettings _guruSettings;
private static GuruSettings GuruSettings
@ -67,10 +69,21 @@ namespace Guru
/// 初始化成功标志位
/// </summary>
public static bool IsInitialSuccess { get; private set; } = false;
/// <summary>
/// Firebase 就绪标志位
/// </summary>
public static bool IsFirebaseReady { get; private set; } = false;
/// <summary>
/// 服务就绪标志位
/// </summary>
public static bool IsServiceReady { get; private set; } = false;
private Firebase.Auth.FirebaseUser _firebaseUser;
[Obsolete("获取 FirebaseUser 的属性接口即将废弃,请改用 <GuruSDK.Callbacks.SDK.OnFirebaseUserAuthResult += OnMyGetFirebaseUserCallback> 来异步获取该属性")]
public static Firebase.Auth.FirebaseUser FirebaseUser => Instance?._firebaseUser ?? null;
#region 初始化
private static GuruSDK CreateInstance()
@ -80,35 +93,23 @@ namespace Guru
_instance = go.AddComponent<GuruSDK>();
return _instance;
}
public static GuruSDKInitConfig BuildConfig(
bool useCustomConsent = false,
bool autoLoadAds = true,
bool iapEnabled = true,
bool autoRecordFinishedLevels = true,
bool debugMode = false,
Dictionary<string, object> defaultRemoteData = null,
byte[] googleKeys = null,
byte[] appleRootCerts = null)
{
var config = GuruSDKInitConfig.Build(useCustomConsent, autoLoadAds, iapEnabled,
autoRecordFinishedLevels, debugMode, defaultRemoteData, googleKeys, appleRootCerts);
return config;
}
// TODO : 下个版本需要将 整个 GuruSDK 做功能性的拆分
public static void Init(Action<bool> onComplete)
{
Init(GuruSDKInitConfig.Build(), onComplete);
Init(GuruSDKInitConfig.Builder().Build(), onComplete);
}
public static void Init(GuruSDKInitConfig config, Action<bool> onComplete)
{
_initTime = DateTime.Now.ToUniversalTime();
LogI($"#1 ---- Guru SDK init ----\n{config.ToString()}");
_initTime = DateTime.UtcNow;
// ----- First Open Time -----
// SetFirstOpenTime(GetFirstOpenTime()); // FirstOpenTime
LogI($"#1 ---- Guru SDK [{Version}] ----\n{config}");
Instance.StartWithConfig(config, onComplete);
}
/// <summary>
/// 启动SDK
/// </summary>
@ -116,72 +117,57 @@ namespace Guru
/// <param name="onComplete"></param>
private void StartWithConfig(GuruSDKInitConfig config, Action<bool> onComplete)
{
Model.PropBLevel.OnValueChanged += OnBLevelChanged;
Model.PropBPlay.OnValueChanged += OnBPlayChanged;
IsInitialSuccess = false;
_initConfig = config;
_onCompleteCallback = onComplete;
_isDebugEnabled = config.DebugMode;
if (config.EnableDebugLogEvent) Analytics.EnableDebugAnalytics = true; // 允许 Debug 模式下打点
if (!config.AutoNotificationPermission) FirebaseUtil.SetAutoFetchFcmToken(false); // 不允许自动启动获取 FCM Token
InitUpdaters(); // Updaters
InitThreadHandler(); // 初始化线程处理器
InitServices(); // 初始化所有的服务
InitNetworkMonitor(); // 网络状态
onComplete?.Invoke(true);
}
void Start()
private void InitServices()
{
//---- Init All tools ----
//---------- Start Analytics ------------
LogI($"#1.1 ---- Init Analytics ----");
// 初始化打点类
Analytics.Init();
// 从 Model 中注入打点属性初始值
Analytics.SetFirstOpenTime(IPMConfig.FIRST_OPEN_TIME);
Analytics.SetIsIapUser(Model.IsIapUser);
// Analytics.SetBLevel(Model.BLevel);
// Analytics.SetBPlay(Model.BPlay);
//---- Start All tools ----
LogI($"#2 --- InitFirebase ---");
//---------- Start Firebase ------------
FirebaseUtil.onInitComplete += OnFirebaseReady;
FirebaseUtil.InitFirebase(null); // 确保所有的逻辑提前被调用到
StartFirebaseService();
LogI($"#2.1 --- InitFacebook ---");
//---------- Start Facebook ------------
FBService.Instance.StartService();
FBService.Instance.StartService(Analytics.OnFBInitComplete);
LogI($"#2.2 --- Call SDK init complete -> callback: { (_onCompleteCallback == null ? "Null" : _onCompleteCallback.ToString()) } ---");
IsInitialSuccess = true;
_onCompleteCallback?.Invoke(true);
}
/// <summary>
/// 开始各种组件初始化
/// </summary>
private void OnFirebaseReady(bool success)
{
FirebaseUtil.onInitComplete -= OnFirebaseReady;
LogI($"#3 --- On FirebaseDeps:{success} ---");
IsFirebaseReady = success;
Callbacks.SDK._onFirebaseReady?.Invoke(success);
LogFirebaseDeps(success);
LogI($"#3.5 --- Call InitRemoteConfig ---");
// 开始Remote Manager初始化
RemoteConfigManager.Init(BuildDefaultRemoteData(_initConfig.DefaultRemoteData));
RemoteConfigManager.OnFetchCompleted += OnFetchRemoteCallback;
LogI($"#4 --- Apply remote services config ---");
// 根据缓存的云控配置来初始化参数
InitAllServices();
LogI($"#5 --- sync sdk time ---");
var sp = DateTime.Now.ToUniversalTime() - _initTime;
LogSDKInitTime(sp.TotalSeconds);
}
/// <summary>
/// 注入云控参数基础数据
/// </summary>
/// <param name="dict"></param>
/// <returns></returns>
private Dictionary<string, object> BuildDefaultRemoteData(Dictionary<string, object> dict)
private string LoadDefaultGuruServiceJson()
{
if (dict == null) dict = new Dictionary<string, object>(3);
// 注入默认的 Services 配置值
string json = Model.LoadDefaltServicesConfigJson();
if (!string.IsNullOrEmpty(json)) dict[ServicesConfigKey] = json;
return dict;
// 加载本地 Services 配置值
var txtAsset = Resources.Load<TextAsset>(ServicesConfigKey);
if (txtAsset != null)
{
return txtAsset.text;
}
return "";
}
/// <summary>
@ -192,9 +178,15 @@ namespace Guru
{
LogI($"#6 --- Remote fetch complete: {success} ---");
ABTestManager.Init(); // 启动AB测试解析器
Callbacks.Remote._onRemoteFetchComplete?.Invoke(success);
Callbacks.Remote.InvokeOnRemoteFetchComplete(success);
}
private void Update()
{
UpdateAllUpdates(); // 驱动所有的更新器
}
#endregion
#region App Remote Update
@ -202,12 +194,19 @@ namespace Guru
/// <summary>
/// Apply Cloud guru-service configs for sdk assets
/// </summary>
private void InitAllServices()
private void InitAllGuruServices()
{
// -------- Init Analytics ---------
SetSDKEventPriority();
// -------- Init Notification -----------
InitNotiPermission();
bool useKeywords = false;
bool useIAP = _initConfig.IAPEnabled;
bool appleReview = false;
// bool enableAnaErrorLog = false;
//----------- Set GuruServices ----------------
var services = GetRemoteServicesConfig();
if (services != null)
{
@ -215,15 +214,15 @@ namespace Guru
useKeywords = _appServicesConfig.KeywordsEnabled();
// 使用初始化配置中的 IAPEnable来联合限定, 如果本地写死关闭则不走云控开启
useIAP = _initConfig.IAPEnabled && _appServicesConfig.IsIAPEnabled();
// enableAnaErrorLog = _appServicesConfig.EnableAnaErrorLog();
Try(() =>
{
LogI($"#4.1 --- Init apply services ---");
LogI($"#4.1 --- Start apply services ---");
//----------------------------------------------------------------
// 自打点设置错误上报
// GuruAnalytics.EnableErrorLog = _appServicesConfig.EnableErrorLog();
// if(enableAnaErrorLog) GuruAnalytics.EnableErrorLog = true;
// adjust 事件设置
if (null != _appServicesConfig.adjust_settings && null != GuruSettings)
@ -236,7 +235,7 @@ namespace Guru
GuruSettings.UpdateAdjustEvents(_appServicesConfig.adjust_settings.events);
}
LogI($"#4.2 --- Init GuruSttings ---");
LogI($"#4.2 --- Start GuruSettings ---");
// GuruSettings 设置
if (null != _appServicesConfig.app_settings)
{
@ -267,7 +266,11 @@ namespace Guru
_appServicesConfig.app_settings.privacy_url,
_appServicesConfig.app_settings.terms_url,
_appServicesConfig.app_settings.android_store,
_appServicesConfig.app_settings.ios_store);
_appServicesConfig.app_settings.ios_store,
_appServicesConfig.parameters?.using_uuid ?? false,
_appServicesConfig.parameters?.cdn_host ?? "");
_appBundleId = _appServicesConfig.app_settings.bundle_id; // 配置预设的 BundleId
}
}
//---------------------------------
@ -278,28 +281,32 @@ namespace Guru
}
//----------- Set IAP ----------------
if (useIAP)
{
// InitIAP(_initConfig.GoogleKeys, _initConfig.AppleRootCerts); // 初始化IAP
Try(() =>
{
LogI($"#4.3 --- Init IAP ---");
InitIAP(_initConfig.GoogleKeys, _initConfig.AppleRootCerts); // 初始化IAP
LogI($"#4.3 --- Start IAP ---");
if (_initConfig.GoogleKeys == null || _initConfig.AppleRootCerts == null)
{
LogEx("[IAP] GoogleKeys is null when using IAPService! Integration failed. App will Exit");
}
InitIAP(UID, _initConfig.GoogleKeys, _initConfig.AppleRootCerts); // 初始化IAP
}, ex =>
{
Debug.LogError($"--- ERROR on useIAP: {ex.Message}");
});
}
//----------- Set Keywords ----------------
if (useKeywords)
{
// KeywordsManager.Install(Model.IsIAPUser, Model.SuccessLevelCount); // 启动Keyword管理器
// KeywordsManager.Install(Model.IsIAPUser, Model.SuccessLevelId); // 启动Keyword管理器
Try(() =>
{
LogI($"#4.4 --- Init Keywords ---");
KeywordsManager.Install(Model.IsIAPUser, Model.SuccessLevelCount); // 启动Keyword管理器
LogI($"#4.4 --- Start Keywords ---");
KeywordsManager.Install(Model.IsIapUser, Model.BLevel); // 启动Keyword管理器
}, ex =>
{
Debug.LogError($"--- ERROR on Keywords: {ex.Message}");
@ -312,7 +319,7 @@ namespace Guru
// StartAppleReviewFlow(); // 直接显示 ATT 弹窗, 跳过 Consent 流程
Try(() =>
{
LogI($"#4.5.0 --- StartAppleReviewFlow ---");
LogI($"#4.5 --- StartAppleReviewFlow ---");
StartAppleReviewFlow(); // 直接显示 ATT 弹窗, 跳过 Consent 流程
}, ex =>
{
@ -321,22 +328,26 @@ namespace Guru
return;
}
#endif
//----------- Set Consent ----------------
if (!InitConfig.UseCustomConsent && !appleReview)
{
// LogI($"--- #3 Start Consent Flow ---");
// StartConsentFlow();
Try(() =>
{
LogI($"#4.5 --- StartConsentFlow ---");
StartConsentFlow();
}, ex =>
LogI($"#4.6 --- Start Consent Flow ---");
Try(StartConsentFlow, ex =>
{
Debug.LogError($"--- ERROR on StartConsentFlow: {ex.Message}");
});
}
#if UNITY_ANDROID
LogI($"#5.1 --- Android StartAndroidDebug Cmd lines---");
// Android 命令行调试
StartAndroidDebugCmds();
#endif
IsServiceReady = true;
// 中台服务初始化结束
Callbacks.SDK._onGuruServiceReady?.Invoke();
Callbacks.SDK.InvokeOnGuruServiceReady();
}
/// <summary>
@ -383,7 +394,7 @@ namespace Guru
}
catch (Exception ex)
{
LogException(ex);
LogEx(ex);
// ignored
onException?.Invoke(ex);
}
@ -407,57 +418,30 @@ namespace Guru
#endif
#endregion
#region 数据
private void OnBLevelChanged(int blevel)
{
SetUserBLevel(blevel);
}
private void OnBPlayChanged(int bplay)
{
SetUserBPlay(bplay);
}
#endregion
#region Misc
/// <summary>
/// 打开页面
/// </summary>
/// <param name="url"></param>
public static void OpenURL(string url)
{
GuruWebview.OpenPage(url);
}
#endregion
#region Logging
internal static void LogI(object message)
private static void LogI(object message)
{
Debug.Log($"{Tag} {message}");
}
internal static void LogW(object message)
private static void LogW(object message)
{
Debug.LogWarning($"{Tag} {message}");
}
internal static void LogE(object message)
private static void LogE(object message)
{
Debug.LogError($"{Tag} {message}");
}
internal static void LogException(string message)
private static void LogEx(string message)
{
LogException( new Exception($"{Tag} {message}"));
LogEx( new Exception($"{Tag} {message}"));
}
internal static void LogException(Exception e)
private static void LogEx(Exception e)
{
Debug.LogException(e);
}
@ -500,7 +484,7 @@ namespace Guru
private void OnAppPauseHandler(bool paused)
{
if(paused) Model.Save(true); // 强制保存数据
Callbacks.App._onAppPaused?.Invoke(paused);
Callbacks.App.InvokeOnAppPaused(paused);
}
private void OnApplicationPause(bool paused)
@ -516,7 +500,7 @@ namespace Guru
private void OnApplicationQuit()
{
Model.Save(true);
Callbacks.App._onAppQuit?.Invoke();
Callbacks.App.InvokeOnAppQuit();
}
#endregion
@ -563,6 +547,297 @@ namespace Guru
}
#endregion
#region 帧更新 Updater
private List<IUpdater> _updaterRunningList;
private List<IUpdater> _updaterRemoveList;
private void InitUpdaters()
{
_updaterRunningList = new List<IUpdater>(20);
_updaterRemoveList = new List<IUpdater>(20);
}
private void UpdateAllUpdates()
{
int i = 0;
// ---- Updater Trigger ----
if (_updaterRunningList.Count > 0)
{
i = 0;
while (i < _updaterRunningList.Count)
{
var updater = _updaterRunningList[i];
if (updater != null)
{
if (updater.State == UpdaterState.Running)
{
updater.OnUpdate();
}
else if(updater.State == UpdaterState.Kill)
{
_updaterRemoveList.Add(updater);
}
}
else
{
_updaterRunningList.RemoveAt(i);
i--;
}
i++;
}
}
if (_updaterRemoveList.Count > 0)
{
i = 0;
while (i < _updaterRemoveList.Count)
{
RemoveUpdater(_updaterRemoveList[i]);
i++;
}
_updaterRemoveList.Clear();
}
}
/// <summary>
/// 注册帧更新器
/// </summary>
/// <param name="updater"></param>
public static void RegisterUpdater(IUpdater updater)
{
Instance.AddUpdater(updater);
updater.Start();
}
private void AddUpdater(IUpdater updater)
{
if (_updaterRunningList == null) _updaterRunningList = new List<IUpdater>(20);
_updaterRunningList.Add(updater);
}
private void RemoveUpdater(IUpdater updater)
{
if (_updaterRunningList != null && updater != null)
{
_updaterRunningList.Remove(updater);
}
}
#endregion
#region 中台推送管理
private static int _messageRetry = 0;
public static void SetPushNotificationEnabled(bool enabled)
{
DeviceInfoUploadRequest request = (DeviceInfoUploadRequest) new DeviceInfoUploadRequest()
.SetRetryTimes(1)
.SetSuccessCallBack(() =>
{
_messageRetry = 0;
Debug.Log($"[SDK] --- Set Push Enabled: {enabled} success");
})
.SetFailCallBack(() =>
{
double retryDelay = Math.Pow(2, _messageRetry);
_messageRetry++;
CoroutineHelper.Instance.StartDelayed((float)retryDelay, ()=> SetPushNotificationEnabled(enabled));
});
if (request == null) return;
request.SetPushEnabled(enabled);
request.Send();
}
#endregion
#region Deeplink
/// <summary>
/// 添加回调链接
/// </summary>
/// <param name="deeplink"></param>
private void OnDeeplinkCallback(string deeplink)
{
Callbacks.SDK.InvokeDeeplinkCallback(deeplink); // 尝试调用回调
}
#endregion
#region 网络状态上报
private NetworkStatusMonitor _networkStatusMonitor;
private string _lastNetworkStatus;
private void InitNetworkMonitor()
{
_networkStatusMonitor = new NetworkStatusMonitor(Analytics.SetNetworkStatus,
lastStatus =>
{
LogEvent("guru_offline", new Dictionary<string, dynamic>()
{
["from"] = lastStatus
});
});
}
/// <summary>
/// 获取当前的网络状态
/// </summary>
/// <returns></returns>
private string GetNetworkStatus() => _networkStatusMonitor.GetNetworkStatus();
#endregion
#region Firebase 服务
/// <summary>
/// 启动 Firebase 服务
/// </summary>
private void StartFirebaseService()
{
FirebaseUtil.Init(OnFirebaseDepsCheckResult,
OnGetFirebaseId,
OnGetGuruUID,
OnFirebaseLoginResult); // 确保所有的逻辑提前被调用到
}
private void OnGetGuruUID(bool success)
{
if (success)
{
Model.UserId = IPMConfig.IPM_UID;
if (GuruIAP.Instance != null)
{
GuruIAP.Instance.SetUID(UID);
GuruIAP.Instance.SetUUID(UUID);
}
// 自打点设置用户 ID
Analytics.SetUid(UID);
// Crashlytics 设置 uid
CrashlyticsAgent.SetUserId(UID);
// 上报所有的事件
Analytics.ShouldFlushGuruEvents();
}
Callbacks.SDK.InvokeOnGuruUserAuthResult(success);
}
private void OnGetFirebaseId(string fid)
{
// 初始化 Adjust 服务
InitAdjustService(fid, InitConfig.OnAdjustDeeplinkCallback);
// 初始化自打点
Analytics.InitGuruAnalyticService(fid);
//---------- Event SDK Info ------------
LogI($"#6.0 --- SDK is ready, report Info ---");
LogSDKInfo((DateTime.UtcNow - _initTime).TotalSeconds);
}
// TODO: 需要之后用宏隔离应用和实现
// Auth 登录认证
private void OnFirebaseLoginResult(bool success, Firebase.Auth.FirebaseUser firebaseUser)
{
_firebaseUser = firebaseUser;
Callbacks.SDK.InvokeOnFirebaseAuthResult(success, firebaseUser);
}
/// <summary>
/// 开始各种组件初始化
/// </summary>
private void OnFirebaseDepsCheckResult(bool success)
{
LogI($"#3 --- On FirebaseDeps: {success} ---");
IsFirebaseReady = success;
Callbacks.SDK.InvokeOnFirebaseReady(success);
Analytics.OnFirebaseInitCompleted();
LogI($"#3.5 --- Call InitRemoteConfig ---");
// 开始Remote Manager初始化
var defaultGuruServiceJson = LoadDefaultGuruServiceJson();
var dict = _initConfig.DefaultRemoteData.ToDictionary(
entry => entry.Key,
entry => entry.Value);
if (!string.IsNullOrEmpty(defaultGuruServiceJson))
{
dict[ServicesConfigKey] = defaultGuruServiceJson;
}
RemoteConfigManager.Init(dict);
RemoteConfigManager.OnFetchCompleted += OnFetchRemoteCallback;
LogI($"#4 --- Apply remote services config ---");
// 根据缓存的云控配置来初始化参数
InitAllGuruServices();
}
#endregion
#region Adjust服务
/// <summary>
/// 启动 Adjust 服务
/// </summary>
private static void InitAdjustService(string firebaseId, Action<string> onDeeplinkCallback = null)
{
// 启动 AdjustService
string appToken = GuruSettings.Instance.AdjustSetting?.GetAppToken() ?? "";
string fbAppId = GuruSettings.Instance.IPMSetting.FacebookAppId;
// if (!string.IsNullOrEmpty(IPMConfig.ADJUST_ID))
// Analytics.SetAdjustId(IPMConfig.ADJUST_ID); // 二次启动后,若有值则立即上报属性
AdjustService.Instance.Start(appToken, fbAppId, firebaseId, DeviceId,
OnAdjustInitComplete, onDeeplinkCallback ,OnGetGoogleAdId );
}
/// <summary>
/// Adjust 初始化结束
/// </summary>
/// <param name="adjustId"></param>
/// <param name="idfv"></param>
/// <param name="idfa"></param>
private static void OnAdjustInitComplete(string adjustId, string idfv, string idfa)
{
Debug.Log($"{Tag} --- OnAdjustInitComplete: adjustId:{adjustId} idfv:{idfv} idfa:{idfa}");
// 获取 ADID
if (string.IsNullOrEmpty(adjustId)) adjustId = "not_set";
if (string.IsNullOrEmpty(idfv)) idfv = "not_set";
if (string.IsNullOrEmpty(idfa)) idfa = "not_set";
IPMConfig.ADJUST_ID = adjustId;
IPMConfig.ADJUST_IDFV = idfv;
IPMConfig.ADJUST_IDFA = idfa;
Analytics.SetAdjustId(adjustId);
Analytics.SetIDFV(idfv);
Analytics.SetIDFA(idfa);
Analytics.OnAdjustInitComplete();
}
private static void OnGetGoogleAdId(string googleAdId)
{
Debug.Log($"{Tag} --- OnGetGoogleAdId: {googleAdId}");
// IPMConfig.ADJUST_GOOGLE_ADID = googleAdId;
Analytics.SetGoogleAdId(googleAdId);
}
#endregion
}
}

3
Runtime/Code/Utils.meta Normal file
View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 45ff6fac409a4698ada70e0d8d5e8269
timeCreated: 1714353187

View File

@ -0,0 +1,67 @@
namespace Guru
{
using System;
using UnityEngine;
/// <summary>
/// Android 系统属性获取器
/// </summary>
public class AndroidSystemPropertiesHelper
{
private static string _appBundleId;
public static string AppBundleId
{
get => _appBundleId;
set => _appBundleId = value;
}
/// <summary>
/// Get the system property value by key
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static string Get(string key)
{
#if UNITY_ANDROID
return GetPropValue(key);
#endif
return "";
}
#if UNITY_ANDROID
private static AndroidJavaClass _systemPropsCls;
private const string SYSTEM_PROPS_CLASS = "android.os.SystemProperties";
private static string GetPropValue(string key)
{
try
{
if (_systemPropsCls == null)
{
_systemPropsCls = new AndroidJavaClass(SYSTEM_PROPS_CLASS);
}
if (_systemPropsCls != null)
{
return _systemPropsCls.CallStatic<string>("get", key);
}
}
catch (Exception e)
{
Debug.LogException(e);
}
return "";
}
#endif
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 41272ed6104b4d80be5322135a811186
timeCreated: 1714353340

3
Tests.meta Normal file
View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 75760985db114e44bfcd901ed50af96c
timeCreated: 1712580213

View File

@ -0,0 +1,23 @@
{
"name": "GuruSDK.Tests",
"rootNamespace": "Guru.Tests",
"references": [
"UnityEngine.TestRunner",
"UnityEditor.TestRunner",
"Guru.Runtime",
"GuruSDK"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -1,6 +1,6 @@
fileFormatVersion: 2
guid: 038b314e2834b4b3f80537a3c7832481
DefaultImporter:
guid: 9eaf5abb3054d452ba2776169a31aa91
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:

51
Tests/Test_IAP.cs Normal file
View File

@ -0,0 +1,51 @@
namespace Guru.Tests
{
using UnityEditor;
using NUnit.Framework;
using UnityEngine;
using System;
public class Test_IAP
{
[Test]
public void Test__AppleOrders()
{
var model = IAPModel.Load();
int level = 1;
int orderType = 0;
for (int i = 0; i < 5; i++)
{
model.AddAppleOrder(new AppleOrderData(orderType,
$"i.iap.test.icon_{i}",
$"receipt_{i}",
$"order_id_{i}",
DateTime.Now.ToString("g"),
level,
"RMB", 6.99d, "Store"));
level++;
}
if (model.HasUnreportedAppleOrder)
{
int i = 0;
while (model.appleOrders.Count > 0
&& i < model.appleOrders.Count)
{
var o = model.appleOrders[i];
model.RemoveAppleOrder(o);
i++;
}
}
}
}
}

3
Tests/Test_IAP.cs.meta Normal file
View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7181a5508c604c439d3f4585ff49a5e0
timeCreated: 1712580254

49
Tests/Test_Threading.cs Normal file
View File

@ -0,0 +1,49 @@
namespace Guru.Tests
{
using NUnit.Framework;
using System.Threading;
using UnityEngine;
public class Test_Threading
{
private int TestCount
{
get => PlayerPrefs.GetInt(nameof(TestCount), 0);
set => PlayerPrefs.SetInt(nameof(TestCount), value);
}
[Test]
public void Test_ThreadingCall()
{
GuruSDK.Init(success =>
{
GuruSDK.Delay(0.1f, () =>
{
CallThreading();
});
});
}
private void CallThreading()
{
Debug.Log($"--------- CallThreading -------------");
var t = new Thread(() =>
{
Debug.Log($"--------- Thread Start -------------");
Thread.Sleep(2000);
GuruSDK.RunOnMainThread(() =>
{
TestCount++;
Debug.Log($">>>>> CallThreading: {TestCount}");
});
});
t.Start();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 67d9cfd9949c48f3a8f6e6821b733ff1
timeCreated: 1712649738

View File

@ -1,11 +1,11 @@
{
"name": "com.guru.unity.sdk",
"displayName": "Guru SDK",
"version": "1.0.9",
"version": "1.1.0",
"description": "Guru SDK for unity project",
"unity": "2020.3",
"author":{
"name": "Guru Games"
"name": "Guru Game"
},
"license": "MIT",
"category": "Game,Tool,Development",