Compare commits

...

242 Commits
1.0.0 ... 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
胡宇飞 1cc30c8a1f update: 更新版本号 2024-03-21 13:25:53 +08:00
胡宇飞 fb492af633 update: 新增 Crashlytics 上报接口 2024-03-20 15:40:55 +08:00
胡宇飞 e647f0ce6b update: 新增 Crashlytics 上报接口 2024-03-20 15:40:22 +08:00
胡宇飞 407e242b8c update: 新增一个 Firebase 服务初始化的标志位 2024-03-20 14:30:00 +08:00
胡宇飞 a692c4f592 update: 新增一个 Firebase 服务初始化的标志位 2024-03-20 14:29:43 +08:00
胡宇飞 d70c6eff8e fix: 修复 GuruServices 在使用 CustomKey 的时候, 由于首次加载无法获取报空的问题 2024-03-19 17:33:37 +08:00
胡宇飞 abf252360c update: 更新广告启动逻辑, 改为云控配置启动, 默认关闭合规性, 和 Consent 同时启动 2024-03-19 16:19:39 +08:00
胡宇飞 7f9263045b update: 更新 NewUser 属性 2024-03-19 10:02:31 +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
胡宇飞 04d1c6df21 update: 更新 IAP 初始化值, 回调值, IAP 接口参数公开, 生命周期回调, bPlay, bLevel 接口公开 2024-03-18 10:22:11 +08:00
胡宇飞 75745ceb11 update: 启动时 IAP 开关将由InitConfig 的 IAPEnabled 以及GuruService 内云控开关联合控制 2024-03-18 09:37:28 +08:00
胡宇飞 7f805c56de update: 更新 IAP 接口, 添加 Category 参数 2024-03-18 09:13:49 +08:00
胡宇飞 d2232e303b update: 添加启动广告的快捷方式, 并且添加去广告标志位 2024-03-15 17:12:36 +08:00
胡宇飞 b7051d3267 update: 添加广告启动初始化配置对象 2024-03-15 17:00:40 +08:00
胡宇飞 60e6ae0716 update: 新增打点和 Firebase 回调逻辑 2024-03-15 16:21:53 +08:00
胡宇飞 1f71522fa6 update: 新增打点和 Firebase 回调逻辑 2024-03-15 12:59:20 +08:00
胡宇飞 19be8b5d48 update: 更新打点和生命周期的日志 2024-03-15 12:27:27 +08:00
胡宇飞 e451155063 update: 更新打点和生命周期的日志 2024-03-15 12:26:34 +08:00
胡宇飞 53f543887e update: 更新 GuruSDK 的生命周期 2024-03-14 11:52:14 +08:00
胡宇飞 b1bf57cbed update: 更新生命周期和对应的逻辑 2024-03-13 17:26:20 +08:00
胡宇飞 bb4073aa2f update: 增加 AndroidManifestMode 的去 permission 功能 2024-03-12 10:35:30 +08:00
胡宇飞 3871841988 update: 添加 AppVersion 版本获取的功能 2024-03-12 10:35:00 +08:00
胡宇飞 d17a6e2eae update: add custom guru-service key 2024-03-11 18:51:49 +08:00
胡宇飞 6275afac9b update: 直接执行SDK 完成回调 2024-03-08 21:40:18 +08:00
胡宇飞 69edbeb6d9 update: 提前初始化自打点组件 2024-03-08 20:16:57 +08:00
胡宇飞 064edbd180 update: 完善初始化流程, 修复报空带来的异常 2024-03-08 19:27:55 +08:00
胡宇飞 ffff79d012 update: 增加打点测试SDK 初始化中断逻辑 2024-03-08 17:05:49 +08:00
胡宇飞 219ab130d1 update: 新增 GuruSDK 的常量合集 2024-03-07 18:00:57 +08:00
胡宇飞 748962528f update: 添加日志 2024-03-06 19:38:22 +08:00
胡宇飞 c7e255c95d update: 优化 Consent 及 ATT 上报逻辑 2024-03-06 12:36:41 +08:00
胡宇飞 c50c5d170e fix: 修复 Android 平台 ATT 引用报错的 BUG 2024-03-04 13:12:31 +08:00
胡宇飞 98c025a285 update: 更新SDK版本号-> 1.0.8, 修复ATT 点位上报的判定问题 2024-03-04 12:19:48 +08:00
胡宇飞 7044f87d3b update: 添加 GuruServices 配置参数, 预制 TradePlus 参数 2024-03-01 13:13:26 +08:00
胡宇飞 8e88cb88ec update: 更新版本号 2024-02-26 11:10:13 +08:00
胡宇飞 3995bba61a update: 更新异常上报接口 2024-02-04 21:10:25 +08:00
胡宇飞 ab0a27d835 fix: 修复 2020编译器报错 2024-02-04 21:09:56 +08:00
胡宇飞 e5ce48dc7f update: 更新版本号 2024-02-04 16:52:29 +08:00
胡宇飞 068f39a85d fix: 修复GuruServices 文档导入器解析报错的问题 2024-02-04 13:18:04 +08:00
胡宇飞 3673e3c594 update: 修复分隔符报错 2024-02-04 11:43:00 +08:00
胡宇飞 bc67eb6301 update: 新增 guru-services.json 生成工具 2024-02-02 19:06:32 +08:00
胡宇飞 017d7177f4 fix: 更新 pubmatic 配置导入方式 2024-02-01 20:48:09 +08:00
胡宇飞 784f9da3a0 update: 更新 Consent 授权回调, 一律开启广告服务 2024-02-01 17:53:45 +08:00
胡宇飞 0472bc2d98 update: 添加 using_uuid 和云控标记 2024-01-31 12:54:03 +08:00
胡宇飞 2845fc28a2 update: 更新 IAP 配置文件的导入机制 2024-01-30 14:24:32 +08:00
胡宇飞 a009a220e4 update: 完善 IOS ATT 打点和流程 2024-01-30 10:23:49 +08:00
胡宇飞 d26cc45962 fix: 更新插件包名错误 2024-01-29 16:35:01 +08:00
胡宇飞 cde02eaf1a update: 更新 Network Security 配置 2024-01-29 16:27:01 +08:00
胡宇飞 c1f4903a0e update: 更新 IAP 插件接口, 返回商品本地化价格字段 2024-01-29 14:37:43 +08:00
胡宇飞 6292994bbd update: 更新参数注入配置 2024-01-26 12:27:16 +08:00
胡宇飞 57d359a706 fix: 修复导入 Service 文件时, 解析报错. 分隔符改为辨识 ',' 2024-01-24 16:36:11 +08:00
胡宇飞 f99584b54a update: 更新版本号 2024-01-24 15:21:21 +08:00
胡宇飞 aa920208b5 update: 暂时关闭 GuruSDKRes 自动注入的 mod 2024-01-24 15:16:53 +08:00
胡宇飞 af06ddade2 update: 更新 Android Res 导入的Mod 工具 2024-01-24 10:08:49 +08:00
胡宇飞 636049d8c0 update: 更新 SDKRes 资源包, 添加 network_security_config.xml 2024-01-24 09:06:15 +08:00
胡宇飞 5b3f2844a6 update: 更新BeLevel 打点判定 2024-01-23 17:15:40 +08:00
胡宇飞 0bcda4f915 update: 添加Restore 回调 2024-01-23 13:57:09 +08:00
huyufei 09cf293d2e Update: 新增保存收据的功能 2024-01-19 20:45:56 +08:00
huyufei 9936809321 Update: 新增判断订单的收据接口 2024-01-19 18:59:18 +08:00
胡宇飞 91b0ba0346 update: 更新initconfig参数 2024-01-18 17:03:53 +08:00
胡宇飞 8b0e86a5f3 update: 更新 GuruSDK 的标记位 2024-01-17 20:46:23 +08:00
胡宇飞 fc53efbca3 update: 更新BannerAD Loaded 事件和回调 2024-01-17 17:51:08 +08:00
胡宇飞 2bbe1ccfd6 update: 更新版本号 2024-01-16 15:15:28 +08:00
胡宇飞 b93770ad29 update: 更新Pubmatic的导入格式。 2024-01-15 20:52:22 +08:00
胡宇飞 84ecdd1dfc update: 添加GuruSettings 的懒加载 2024-01-10 19:16:10 +08:00
胡宇飞 90b3aa4da6 update: 新增邮件和商店链接获取接口 2024-01-10 18:22:02 +08:00
胡宇飞 9ae151e818 update: 新增 GuruSettings 配置接口更新内容 2024-01-10 14:02:53 +08:00
胡宇飞 61910cbfc1 update: 更新fb配置参数, 暴露 fbAppId 读取接口 2024-01-10 13:34:21 +08:00
胡宇飞 790b2360d0 update: 更新太极020 打点值配置 2024-01-08 18:45:22 +08:00
胡宇飞 6a1ba3e38c update: 更新执行生命周 2024-01-08 15:01:04 +08:00
胡宇飞 de25d1ef2d fix: 修复扩展名称BUG 2024-01-08 11:47:35 +08:00
胡宇飞 058b65224c update: 修复services路径BUG 2024-01-08 11:45:48 +08:00
胡宇飞 a2b296423f update: 添加初始化保护逻辑, 未初始化时加入错误信息 2024-01-08 11:31:58 +08:00
胡宇飞 8881b2f442 fix: 修复IO接口问题, guru-services 改为内置 resources 存储 2024-01-08 10:22:56 +08:00
huyufei 74acbe841b update: 更新API 2024-01-07 22:59:02 +08:00
91 changed files with 7878 additions and 1624 deletions

View File

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

View File

@ -0,0 +1,863 @@
#if GURU_SDK_DEV
namespace Guru.Editor
{
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.Networking;
using System.Collections.Generic;
using System.Linq;
public class GuruServiceJsonBuilder: EditorWindow
{
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="tsv"></param>
/// <param name="savePath"></param>
public static void ConvertFromTSV(string tsv, string savePath = "")
{
if (string.IsNullOrEmpty(tsv))
{
EditorUtility.DisplayDialog("空文件!", $"文件格式错误!\n{tsv}", "OK");
return;
}
var guru_service = EditorGuruServiceIO.CreateEmpty();
var lines = tsv.Split('\n');
string line = "";
for (int index = 0; index < lines.Length; index++)
{
line = lines[index];
if (!IsInvalidLine(line))
{
//---------------- app_settings ----------------
if (line.StartsWith(K_APP_SETTINGS))
{
index++;
while (!line.StartsWith(K_ADJUST_SETTINGS))
{
line = lines[index];
FillAppSettings(guru_service, line);
index++;
line = lines[index];
}
}
//---------------- adjust_settings ----------------
if (line.StartsWith(K_ADJUST_SETTINGS))
{
index++;
FillAdjustSettings(guru_service, lines, ref index);
}
//---------------- fb_settings ----------------
if (line.StartsWith(K_FB_SETTINGS))
{
index++;
FillFacebookSettings(guru_service, lines, ref index);
}
//---------------- ad_settings ----------------
if (line.StartsWith(K_AD_SETTINGS))
{
index++;
FillAdSettings(guru_service, lines, ref index);
}
//---------------- iap_settings ----------------
if (line.StartsWith(K_IAP_SETTINGS))
{
index++;
FillProducts(guru_service, lines, ref index);
}
}
}
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 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>
/// <param name="settings"></param>
/// <param name="line"></param>
private static void FillAppSettings(GuruServicesConfig settings, string line)
{
// 对于空行和空值直接跳过
if (IsInvalidLine(line)) return;
string value = "";
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))
{
settings.app_settings.app_id = value;
}
else if (GetValue(line, "product_name", out value))
{
settings.app_settings.product_name = value;
}
else if (GetValue(line, "bundle_id", out value))
{
settings.app_settings.bundle_id = value;
}
else if (GetValue(line, "support_email", out value))
{
settings.app_settings.support_email = value;
}
else if (GetValue(line, "privacy_url", out value))
{
settings.app_settings.privacy_url = value;
}
else if (GetValue(line, "terms_url", out value))
{
settings.app_settings.terms_url = value;
}
else if (GetValue(line, "android_store", out value))
{
settings.app_settings.android_store = value;
}
else if (GetValue(line, "ios_store", out value))
{
settings.app_settings.ios_store = value;
}
else if (GetValue(line, "enable_firebase", out value))
{
settings.app_settings.enable_firebase = GetBool(value);
}
else if (GetValue(line, "enable_facebook", out value))
{
settings.app_settings.enable_facebook = GetBool(value);
}
else if (GetValue(line, "enable_adjust", out value))
{
settings.app_settings.enable_adjust = GetBool(value);
}
else if (GetValue(line, "enable_iap", out value))
{
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>
/// <param name="settings"></param>
/// <param name="line"></param>
private static void FillAdjustSettings(GuruServicesConfig settings, string[] lines, ref int index)
{
if(settings.adjust_settings == null) settings.adjust_settings = new GuruAdjustSettings();
string[] list = null;
string line = lines[index];
bool pass = false;
List<string> events = new List<string>(40);
while (!lines[index].StartsWith(K_FB_SETTINGS))
{
line = lines[index];
if (!IsInvalidLine(line))
{
if (line.StartsWith("app_token"))
{
list = GetStringArray(line, 1, 2);
settings.adjust_settings.app_token = list;
}
else
{
list = GetStringArray(line, 0, 3);
pass = list != null && !string.IsNullOrEmpty(list[0])
&& (!string.IsNullOrEmpty(list[1]) || !string.IsNullOrEmpty(list[2]));
if( pass) events.Add(string.Join(",", list));
}
}
index++;
}
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 = "";
if(settings.fb_settings == null) settings.fb_settings = new GuruFbSettings();
var line = "";
while (!lines[index].StartsWith(K_AD_SETTINGS))
{
line = lines[index];
if (!IsInvalidLine(line))
{
// 拾取值和注入
if (GetValue(line, "fb_app_id", out value))
{
settings.fb_settings.fb_app_id = value;
}
else if (GetValue(line, "fb_client_token", out value))
{
settings.fb_settings.fb_client_token = value;
}
}
index++;
}
index--;
}
private static void FillAdSettings(GuruServicesConfig settings, string[] lines, ref int index)
{
string value = "";
if(settings.ad_settings == null) settings.ad_settings = new GuruAdSettings();
var line = lines[index];
// SDK Key
string[] max_ids_android = new string[3];
string[] max_ids_ios = new string[3];
string[] amazon_ids_android = new string[4];
string[] amazon_ids_ios = new string[4];
string[] pubmatic_ids_android = new string[3];
string[] pubmatic_ids_ios = new string[3];
string[] moloco_ids_android = new string[3];
string[] moloco_ids_ios = new string[3];
string[] tradplus_ids_android = new string[3];
string[] tradplus_ids_ios = new string[3];
//------- 开始记录广告配置;
string[] arr;
while (!lines[index].StartsWith(K_IAP_SETTINGS))
{
line = lines[index];
if (GetValue(line, "sdk_key", out value))
{
settings.ad_settings.sdk_key = value;
}
else if (line.StartsWith("admob_app_id"))
{
settings.ad_settings.admob_app_id = GetStringArray(line, 1, 2);
}
else if (line.StartsWith("max_bads"))
{
arr = GetStringArray(line, 1, 2);
max_ids_android[0] = arr[0];
max_ids_ios[0] = arr[1];
}
else if (line.StartsWith("max_iads"))
{
arr = GetStringArray(line, 1, 2);
max_ids_android[1] = arr[0];
max_ids_ios[1] = arr[1];
}
else if (line.StartsWith("max_rads"))
{
arr = GetStringArray(line, 1, 2);
max_ids_android[2] = arr[0];
max_ids_ios[2] = arr[1];
}
else if (line.StartsWith("amazon_app_id"))
{
arr = GetStringArray(line, 1, 2);
amazon_ids_android[0] = arr[0];
amazon_ids_ios[0] = arr[1];
}
else if (line.StartsWith("amazon_bads"))
{
arr = GetStringArray(line, 1, 2);
amazon_ids_android[1] = arr[0];
amazon_ids_ios[1] = arr[1];
}
else if (line.StartsWith("amazon_iads"))
{
arr = GetStringArray(line, 1, 2);
amazon_ids_android[2] = arr[0];
amazon_ids_ios[2] = arr[1];
}
else if (line.StartsWith("amazon_rads"))
{
arr = GetStringArray(line, 1, 2);
amazon_ids_android[3] = arr[0];
amazon_ids_ios[3] = arr[1];
}
else if (line.StartsWith("pubmatic_bads"))
{
arr = GetStringArray(line, 1, 2);
pubmatic_ids_android[0] = arr[0];
pubmatic_ids_ios[0] = arr[1];
}
else if (line.StartsWith("pubmatic_iads"))
{
arr = GetStringArray(line, 1, 2);
pubmatic_ids_android[1] = arr[0];
pubmatic_ids_ios[1] = arr[1];
}
else if (line.StartsWith("pubmatic_rads"))
{
arr = GetStringArray(line, 1, 2);
pubmatic_ids_android[2] = arr[0];
pubmatic_ids_ios[2] = arr[1];
}
else if (line.StartsWith("moloco_bads"))
{
arr = GetStringArray(line, 1, 2);
moloco_ids_android[0] = arr[0];
moloco_ids_ios[0] = arr[1];
}
else if (line.StartsWith("moloco_iads"))
{
arr = GetStringArray(line, 1, 2);
moloco_ids_android[1] = arr[0];
moloco_ids_ios[1] = arr[1];
}
else if (line.StartsWith("moloco_rads"))
{
arr = GetStringArray(line, 1, 2);
moloco_ids_android[2] = arr[0];
moloco_ids_ios[2] = arr[1];
}
else if (line.StartsWith("tradplus_bads"))
{
arr = GetStringArray(line, 1, 2);
tradplus_ids_android[0] = arr[0];
tradplus_ids_ios[0] = arr[1];
}
else if (line.StartsWith("tradplus_iads"))
{
arr = GetStringArray(line, 1, 2);
tradplus_ids_android[1] = arr[0];
tradplus_ids_ios[1] = arr[1];
}
else if (line.StartsWith("tradplus_rads"))
{
arr = GetStringArray(line, 1, 2);
tradplus_ids_android[2] = arr[0];
tradplus_ids_ios[2] = arr[1];
}
index++;
}
//-------- Fill all data -----------
settings.ad_settings.max_ids_android = max_ids_android;
settings.ad_settings.max_ids_ios = max_ids_ios;
settings.ad_settings.amazon_ids_android = amazon_ids_android;
settings.ad_settings.amazon_ids_ios = amazon_ids_ios;
settings.ad_settings.pubmatic_ids_android = pubmatic_ids_android;
settings.ad_settings.pubmatic_ids_ios = pubmatic_ids_ios;
settings.ad_settings.moloco_ids_android = moloco_ids_android;
settings.ad_settings.moloco_ids_ios = moloco_ids_ios;
settings.ad_settings.tradplus_ids_android = tradplus_ids_android;
settings.ad_settings.tradplus_ids_ios = tradplus_ids_ios;
index--;
}
private static void FillProducts(GuruServicesConfig settings, string[] lines, ref int index)
{
string line = "";
List<string> iaps = new List<string>(30);
string[] arr;
while (index < lines.Length)
{
line = lines[index];
if(IsInvalidLine(line)) continue;
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).Replace("\r", ""));
index++;
}
settings.products = iaps.ToArray();
index--;
}
#endregion
#region Utils
private static bool GetBool(string value)
{
return value.ToLower() == "true" || value == "1";
}
private static int GetInt(string value)
{
int val = 0;
int.TryParse(value, out val);
return val;
}
private static double GetDouble(string value)
{
double val = 0;
double.TryParse(value, out val);
return val;
}
private static bool IsInvalidLine(string line)
{
return string.IsNullOrEmpty(line) || line.StartsWith(K_SPLITTER_TAB.ToString());
}
private static bool GetValue(string line, string key, out string value)
{
value = ""; // default
if (line.StartsWith(key))
{
value = line.Split(K_SPLITTER_TAB)[1];
if (string.IsNullOrEmpty(value)) value = "empty";
return true;
}
return false;
}
private static string[] GetStringArray(string line, int startIndex = 0, int length = 0, char spliter = K_SPLITTER_TAB)
{
if (IsInvalidLine(line)) return null;
var raw = line.Split(spliter);
if (length == 0) length = raw.Length;
var a = new List<string>(length);
for (int i = startIndex; i < length + startIndex; i++)
{
if (i < raw.Length)
{
a.Add(raw[i]);
}
else
{
a.Add("");
}
}
return a.ToArray();
}
#endregion
#region Menu Items
#if GURU_SDK_DEV
[MenuItem("Tools/Export Guru Service", false, 0 )]
#endif
private static void ExportJsonFile()
{
string saveDir = Path.GetFullPath($"{Application.dataPath}/../output");
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 tsvPath = EditorUtility.OpenFilePanel("Load Guru Service TSV", searchPath, ".tsv");
if (!string.IsNullOrEmpty(tsvPath))
{
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,3 @@
fileFormatVersion: 2
guid: 8c7dc826ea0848f0a3bf05009fab6377
timeCreated: 1706856053

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

@ -10,12 +10,15 @@ namespace Guru.Editor
public class EditorGuruServiceIO
{
private static readonly string DefaultFileName = "guru-service";
internal static readonly string SourceConfigFileName = "guru-service";
internal const string LocalServicesConfigPath = "Guru/Resources";
internal const string SourceConfigExtension = ".json";
internal const string LocalConfigExtension = ".txt";
private static string DefaultFilePath =
Path.GetFullPath(Path.Combine(Application.dataPath, $"{DefaultFileName}.json"));
internal static string DefaultFilePath =
Path.GetFullPath(Path.Combine(Application.dataPath, $"{SourceConfigFileName}{SourceConfigExtension}"));
internal static string EmbededServiceFilePath = "";
internal static string SourceServiceFilePath = "";
/// <summary>
/// 加载配置
@ -23,18 +26,18 @@ namespace Guru.Editor
/// <returns></returns>
public static GuruServicesConfig LoadConfig()
{
var a = AssetDatabase.FindAssets($"*{DefaultFileName}* t:TextAsset");
var a = AssetDatabase.FindAssets($"*{SourceConfigFileName}* t:TextAsset", new []{"Assets"});
if (a == null || a.Length == 0)
{
Debug.Log($"<color=orange>--- Can't ind guru-services file</color>");
UnityEngine.Debug.Log($"<color=orange>--- Can't find guru-services file</color>");
}
else
{
var p = AssetDatabase.GUIDToAssetPath(a[0]);
var fp = Path.GetFullPath(p);
if (File.Exists(fp)) EmbededServiceFilePath = fp;
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;
@ -44,7 +47,7 @@ namespace Guru.Editor
/// 保存配置
/// </summary>
/// <param name="config"></param>
internal static void SaveConfig(GuruServicesConfig config = null)
internal static void SaveConfig(GuruServicesConfig config = null, string savePath = "")
{
if (config == null)
{
@ -56,12 +59,13 @@ namespace Guru.Editor
PrettyPrint = true,
};
JsonMapper.ToJson(config, jw);
var json = jw.ToString();
if (string.IsNullOrEmpty(EmbededServiceFilePath)) EmbededServiceFilePath = DefaultFilePath;
File.WriteAllText(EmbededServiceFilePath, json);
Debug.Log($"Save config to {EmbededServiceFilePath}");
if (string.IsNullOrEmpty(savePath)) savePath = SourceServiceFilePath;
if (string.IsNullOrEmpty(savePath)) savePath = DefaultFilePath;
File.WriteAllText(savePath, json);
UnityEngine.Debug.Log($"Save config to {savePath}");
}
/// <summary>
@ -76,6 +80,7 @@ namespace Guru.Editor
cfg.ad_settings = new GuruAdSettings();
cfg.adjust_settings = new GuruAdjustSettings();
cfg.fb_settings = new GuruFbSettings();
cfg.parameters = new GuruParameters();
return cfg;
}
@ -88,14 +93,14 @@ namespace Guru.Editor
}
public static void DeployAppServiceFile()
public static void DeployLocalServiceFile()
{
var streamingPath = Application.streamingAssetsPath;
if(!Directory.Exists(streamingPath)) Directory.CreateDirectory(streamingPath);
var path = Path.Combine(streamingPath, $"{GuruSDK.ServicesConfigKey}.{GuruSDK.ServicesConfigExtension}");
var deployPath = Path.Combine(Application.dataPath, LocalServicesConfigPath);
if(!Directory.Exists(deployPath)) Directory.CreateDirectory(deployPath);
var path = Path.Combine(deployPath, $"{GuruSDK.ServicesConfigKey}{LocalConfigExtension}");
var config = LoadConfig();
var from = EmbededServiceFilePath;
var from = SourceServiceFilePath;
if (string.IsNullOrEmpty(from) || !File.Exists(from)) // 文件不存在
{
return;
@ -104,7 +109,7 @@ namespace Guru.Editor
if (null != config)
{
if (File.Exists(path)) File.Delete(path);
Debug.Log($"<color=#88ff00> --- setup {GuruSDK.ServicesConfigKey} file on streamingPath</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

@ -1,6 +1,7 @@
org.gradle.jvmargs=-Xmx**JVM_HEAP_SIZE**M
org.gradle.parallel=true
android.enableR8=**MINIFY_WITH_R_EIGHT**
org.gradle.daemon=true
org.gradle.caching=true
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 {
@ -33,6 +35,11 @@ android {
abortOnError false
checkReleaseBuilds false
}
dexOptions {
incremental true
javaMaxHeapSize "8g"
}
buildTypes {
debug {

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,43 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
<domain-config cleartextTrafficPermitted="true">
<!-- For Facebook(Meta) -->
<!-- In the Audience Network Android SDK,
we use 127.0.0.1 (localhost) as a caching
proxy to cache media files in the SDK.
Since Android P, cleartext traffic (unencrypted HTTP)
will be blocked by default, which will affect the
functionality of media caching of the SDK and could
affect user experience and ads revenue. -->
<domain includeSubdomains="true">127.0.0.1</domain>
<!-- For Amazon -->
<domain includeSubdomains="true">amazon-adsystem.com</domain>
<!-- For Unity -->
<domain includeSubdomains="true">cdn-creatives-akamai-prd.unityads.unity3d.com</domain>
<domain includeSubdomains="true">cdn-creatives-akamaistls-prd.unityads.unity3d.com</domain>
<domain includeSubdomains="true">cdn-creatives-akamaistls-re-prd.unityads.unity3d.com</domain>
<domain includeSubdomains="true">cdn-creatives-geocdn-prd.unityads.unity3d.com</domain>
<domain includeSubdomains="true">cdn-creatives-prd.unityads.unity3d.com</domain>
<domain includeSubdomains="true">cdn-creatives-highwinds-prd.unityads.unity3d.com</domain>
<domain includeSubdomains="true">cdn-creatives-tencent-prd.unityads.unitychina.cn</domain>
<domain includeSubdomains="true">cdn-store-icons-akamai-prd.unityads.unity3d.com</domain>
<domain includeSubdomains="true">cdn-store-icons-highwinds-prd.unityads.unity3d.com</domain>
<domain includeSubdomains="true">cdn-store-icons-tencent-prd.unityads.unitychina.cn</domain>
<domain includeSubdomains="true">cdn-creatives-akamaistls-prd.acquire.unity3dusercontent.com</domain>
<domain includeSubdomains="true">unityads.unity3d.com</domain>
<domain includeSubdomains="true">unityads.unitychina.cn</domain>
</domain-config>
<debug-overrides>
<trust-anchors>
<!-- Trust user added CAs while debuggable only -->
<certificates src="user" />
</trust-anchors>
</debug-overrides>
</network-security-config>

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e92a383bcfa548e2bdf0eeb8b0264205
timeCreated: 1630417512

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

@ -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
{
@ -16,6 +14,35 @@ namespace Guru.Editor
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 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);
public static bool IsManifestExist() => File.Exists(TargetFullPath);
@ -25,80 +52,65 @@ namespace Guru.Editor
if (!IsManifestExist())
{
CopyManifest();
return;
}
var doc = new XmlDocument();
doc.Load(TargetFullPath);
var rootNode = doc.SelectSingleNode("manifest/application");
int item1 = 0;
int item2 = 0;
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;
}
}
}
}
string androidSP = "http://schemas.android.com/apk/res/android";
if (item1 == 0)
{
var e = doc.CreateElement("meta-data");
e.SetAttribute("name",androidSP, ValOptimizeInitialization);
e.SetAttribute("value",androidSP, "true");
rootNode.AppendChild(e);
}
if (item2 == 0)
{
var e = doc.CreateElement("meta-data");
e.SetAttribute("name",androidSP,ValOptimizeAdLoading);
e.SetAttribute("value",androidSP, "true");
rootNode.AppendChild(e);
}
var rootE = doc.SelectSingleNode("manifest") as XmlElement;
if (rootE != null)
{
rootE.Attributes["package"].Value = PlayerSettings.applicationIdentifier; // 写入包名
}
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>
/// 拷贝 AndroidManifest
/// </summary>
private static void CopyManifest()
{
if (File.Exists(TargetFullPath)) return;
var path = GuruEditorHelper.GetFilePath($"{nameof(AndroidManifestMod)}.cs t:Script");
var path = GuruEditorHelper.GetAssetPath(nameof(AndroidManifestMod), "Script", true);
if (!string.IsNullOrEmpty(path))
{
var files = Path.GetFullPath($"{path}/../Files");
var from = $"{files}/AndroidManifest.txt";
var from = Path.GetFullPath($"{path}/../../Files/AndroidManifest.txt");
if (File.Exists(from))
{
File.Copy(from, TargetFullPath);
}
}
}
#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

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: f4547abfcddf84bc6b61e884ebfb30e2
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,4 +1,4 @@
using System.Linq;
namespace Guru.Editor
{
@ -6,29 +6,46 @@ namespace Guru.Editor
using UnityEngine;
using System;
using System.IO;
using System.Linq;
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))
{
@ -41,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");
@ -62,7 +78,7 @@ namespace Guru.Editor
File.WriteAllLines(LauncherFullPath, lines);
}
private static void FixMain()
private static void ApplyMainTemplates()
{
if (!File.Exists(MainFullPath))
{
@ -70,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()
@ -93,8 +196,8 @@ namespace Guru.Editor
private static string GetMoveFilePath(string fileName)
{
var path = GuruEditorHelper.GetFilePath($"{nameof(AndroidProjectMod)}.cs t:Script");
var files = Path.GetFullPath($"{path}/../Files");
var path = GuruEditorHelper.GetAssetPath(nameof(AndroidProjectMod), "Script", true);
var files = Path.GetFullPath($"{path}/../../Files");
return $"{files}/{fileName}";
}
private static void CopyFile(string fileName, string toPath)

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

@ -0,0 +1,51 @@
namespace Guru.Editor
{
using System.IO;
using UnityEditor;
using UnityEngine;
public class AndroidResMod
{
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()
{
DeployNetworkSecurity();
}
/// <summary>
/// 部署网络安全配置
/// </summary>
private static void DeployNetworkSecurity()
{
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

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c06b1730aac144929ca73fba2e481508
timeCreated: 1706058636

View File

@ -1,17 +1,46 @@
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)
{
#if UNITY_EDITOR_OSX
EditorUtility.RevealInFinder(path);
return;
#endif
Application.OpenURL($"file://{path}");
}
}
}

View File

@ -37,7 +37,7 @@ namespace Guru.Editor
{
static BoostOnLoad()
{
EditorGuruServiceIO.DeployAppServiceFile();
EditorGuruServiceIO.DeployLocalServiceFile();
}
}
}

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

@ -0,0 +1,213 @@
namespace Guru
{
using System.Collections.Generic;
using System.Text;
using System;
/// <summary>
/// 启动参数配置
/// </summary>
public class GuruSDKInitConfig
{
#region Properties
/// <summary>
/// 使用自定义的ConsentFlow启动流程
/// </summary>
public bool UseCustomConsent = false;
/// <summary>
/// SDK初始化完成后自动加载广告
/// </summary>
public bool AutoLoadWhenAdsReady = true;
/// <summary>
/// 使用IAP支付插件功能
/// </summary>
public bool IAPEnabled = true;
/// <summary>
/// 自动申请推送授权信息
/// </summary>
public bool AutoNotificationPermission = true;
/// <summary>
/// 自动记录完成的关卡
/// </summary>
[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
{
set
{
if (value != null)
{
_defaultRemoteData = value;
}
}
get => _defaultRemoteData;
}
/// <summary>
/// 启用 AdjustDeeplink
/// </summary>
public Action<string> OnAdjustDeeplinkCallback = null;
/// <summary>
/// 支付初始化Keys
/// </summary>
public byte[] GoogleKeys; // 数据取自 GooglePlayTangle.Data();
public byte[] AppleRootCerts; // 数据取自 AppleTangle.Data();
#endregion
#region Print
public override string ToString()
{
StringBuilder sb = new StringBuilder();
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,15 +1,16 @@
using System;
using System.Collections.Generic;
using UnityEngine.Serialization;
namespace Guru
{
using System;
using UnityEngine.Serialization;
[Serializable]
public class GuruServicesConfig
{
public long version = 0;
public GuruAppSettings app_settings;
public GuruParameters parameters;
public GuruAdjustSettings adjust_settings;
public GuruFbSettings fb_settings;
public GuruAdSettings ad_settings;
@ -34,10 +35,40 @@ namespace Guru
public bool IsMolocoIOSEnabled() => ad_settings != null &&
ad_settings.moloco_ids_ios != null &&
ad_settings.moloco_ids_ios.Length > 0;
public bool IsTradplusAndroidEnabled() => ad_settings != null &&
ad_settings.tradplus_ids_android != null &&
ad_settings.tradplus_ids_android.Length > 0;
public bool IsTradplusIOSEnabled() => ad_settings != null &&
ad_settings.tradplus_ids_ios != null &&
ad_settings.tradplus_ids_ios.Length > 0;
public bool IsIAPEnabled() => app_settings != null && app_settings.enable_iap
&& products != null && products.Length > 0;
public bool IsKeywordsEnabled() => app_settings != null && app_settings.enable_keywords;
public bool UseCustomKeystore() => app_settings?.custom_keystore ?? false;
public bool IsFirebaseEnabled() => app_settings?.enable_firebase ?? true;
public bool IsFacebookEnabled() => app_settings?.enable_facebook ?? true;
public bool IsAdjustEnabled() => app_settings?.enable_adjust ?? true;
//-------------------------------- 配置检测 -------------------------------
//-------------------------------- Parameters --------------------------------
public double Tch02Value() => parameters?.tch_020 ?? 0;
public bool IsAppReview() => parameters?.apple_review ?? 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_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]
@ -51,30 +82,44 @@ namespace Guru
public string terms_url;
public string android_store;
public string ios_store;
public int token_vaild_time = 604800;
public int level_end_success_num = 50;
public bool enable_keywords = true;
public bool enable_firebase = true;
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_valid_time = 604800;
public int level_end_success_num = 50;
public bool enable_keywords = false;
public double tch_020 = 0;
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]
public class GuruAdjustSettings
{
public string[] app_token;
public string[] events;
public string AndroidToken => app_token != null && app_token.Length > 0 ? app_token[0] : "";
public string iOSToken => app_token != null && app_token.Length > 1 ? app_token[1] : "";
public string AndroidToken() => app_token != null && app_token.Length > 0 ? app_token[0] : "";
public string iOSToken() => app_token != null && app_token.Length > 1 ? app_token[1] : "";
}
[Serializable]
public class GuruFbSettings
{
public string app_id;
public string client_token;
public string fb_app_id;
public string fb_client_token;
}
[Serializable]
@ -90,8 +135,8 @@ namespace Guru
public string[] pubmatic_ids_ios;
public string[] moloco_ids_android;
public string[] moloco_ids_ios;
public string[] tradplus_ids_android;
public string[] tradplus_ids_ios;
}
}

View File

@ -1,227 +0,0 @@
namespace Guru
{
using UnityEngine;
using System;
public partial class GuruSDK
{
/// <summary>
/// 启动广告服务
/// </summary>
public static void StartAds()
{
if (InitConfig.UseCustomConsent)
{
Debug.Log($"{Tag} --- Call <color=orange>StartAdsWithCustomConsent</color> when you use custom consent, and pass the result (boolean) to the method.");
}
else
{
// 默认的启动顺序是先启动Consent后, 根据用户回调的结果来启动广告
Instance.StartConsentFlow();
}
}
/// <summary>
/// 使用自定义的Consent, 获取用户授权后, 调用此方法
/// </summary>
/// <param name="userAllow"></param>
public static void StartAdsWithCustomConsent(bool userAllow = true)
{
if (userAllow)
{
StartAdService();
}
else
{
Debug.Log($"{Tag} --- User refuse to provide ads Id, Ads Service will be cancelled");
}
}
#region Guru Consent
/// <summary>
/// 启动Consent流程
/// </summary>
private void StartConsentFlow()
{
LogI($"StartConsentFlow");
GuruConsent.StartConsent(OnConsentOver);
}
private void OnConsentOver(int code)
{
Callbacks.ConsentFlow._onConsentResult?.Invoke(code);
switch(code)
{
case GuruConsent.StatusCode.OBTAINED:
case GuruConsent.StatusCode.NOT_AVAILABLE:
// 已获取授权, 或者地区不可用
#if UNITY_IOS
CheckATTStatus();
#else
StartAdService();
#endif
break;
}
}
#if UNITY_IOS
/// <summary>
/// iOS 平台检查 ATT 状态
/// </summary>
private void CheckATTStatus()
{
AttManager.Instance.CheckATTStatus(OnATTStatus);
}
#endif
#endregion
#region Ad Services
private static bool _initAdsCompleted = false;
/// <summary>
/// 启动广告服务
/// </summary>
public static void StartAdService()
{
LogI($"StartAdService");
ADService.Instance.StartService(OnAdsInitComplete,
InitConfig.AutoLoadWhenAdsReady, IsDebugMode);
//--------- Callbacks -----------
ADService.OnInterstitialLoaded = OnInterstitialLoaded;
ADService.OnInterstitialFailed = OnInterstitialFailed;
ADService.OnRewardLoaded = OnRewardLoaded;
ADService.OnRewardFailed = OnRewardFailed;
}
private static void OnInterstitialLoaded()
=> Callbacks.Ads._onInterstitialADLoaded?.Invoke();
private static void OnInterstitialFailed()
=> Callbacks.Ads._onInterstitialADFailed?.Invoke();
private static void OnRewardLoaded()
=> Callbacks.Ads._onRewardedADLoaded?.Invoke();
private static void OnRewardFailed()
=> Callbacks.Ads._onRewardADFailed?.Invoke();
private static void OnAdsInitComplete()
{
_initAdsCompleted = true;
Callbacks.Ads._onAdsInitComplete?.Invoke();
}
private static bool CheckAdsReady()
{
if (!_initAdsCompleted)
{
LogE("Ads is not ready. Call <GuruSDk.StartAdService> first.");
return false;
}
return true;
}
/// <summary>
/// 显示Banner广告
/// </summary>
/// <param name="placement"></param>
public static void ShowBanner(string placement = "")
{
if (!CheckAdsReady()) return;
ADService.Instance.ShowBanner(placement);
}
/// <summary>
/// 隐藏Banner广告
/// </summary>
public static void HideBanner()
{
if (!CheckAdsReady()) return;
ADService.Instance.HideBanner();
}
public static void LoadInterstitialAd()
{
if (!CheckAdsReady()) return;
ADService.Instance.RequestInterstitialAD();
}
public static bool IsInterstitialAdReady => ADService.Instance.IsInterstitialADReady();
/// <summary>
/// 显示插屏广告
/// </summary>
/// <param name="placement"></param>
/// <param name="onDismissed"></param>
public static void ShowInterstitialAd(string placement = "", Action onDismissed = null)
{
if (!CheckAdsReady()) return;
if (!ADService.Instance.IsInterstitialADReady())
{
LogE("Interstitial is not ready. Call <GuruSDk.ShowInterstitialAd> again.");
LoadInterstitialAd();
return;
}
ADService.Instance.ShowInterstitialAD(placement, onDismissed);
}
public static void LoadRewardAd()
{
if (!CheckAdsReady()) return;
ADService.Instance.RequestRewardedAD();
}
public static bool IsRewardAdReady => ADService.Instance.IsRewardedADReady();
/// <summary>
/// 显示激励视频广告
/// </summary>
/// <param name="placement"></param>
/// <param name="onRewarded"></param>
/// <param name="onFailed"></param>
public static void ShowRewardAd(string placement = "", Action onRewarded = null, Action<string> onFailed = null)
{
if (!CheckAdsReady()) return;
if (!ADService.Instance.IsRewardedADReady())
{
LogE("RewardAd is not ready. Call <GuruSDk.LoadRewardAd> again.");
LoadRewardAd();
return;
}
ADService.Instance.ShowRewardedAD(placement, onRewarded, onFailed);
}
#endregion
#region MaxServices
/// <summary>
/// 显示Max调试菜单
/// </summary>
public static void ShowMaxDebugPanel()
{
#if UNITY_EDITOR
LogI($"Can not show Max Debug Panel in Editor, skipped.");
return;
#endif
if (!ADService.Instance.IsInitialized)
{
LogI($"ADService is not initialized, call <GuruSDK.StartAds> first.");
return;
}
ADService.Instance.ShowMaxDebugPanel();
}
#endregion
}
}

View File

@ -1,216 +0,0 @@
namespace Guru
{
using System.Collections.Generic;
/// <summary>
/// 打点管理
/// </summary>
public partial class GuruSDK
{
//----------------- 关卡开始类型 ---------------------
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";
#region 通用接口
/// <summary>
/// 自定义事件打点
/// </summary>
/// <param name="eventName"></param>
/// <param name="data"></param>
public static void LogEvent(string eventName, Dictionary<string, dynamic> data = null)
=> Analytics.Track(eventName, data);
public static void SetScreen(string screen, string extra = "")
=> Analytics.SetCurrentScreen(screen, extra);
#endregion
#region 游戏打点
/// <summary>
/// 游戏启动打点
/// </summary>
/// <param name="level"></param>
/// <param name="levelName"></param>
/// <param name="levelCategory"></param>
/// <param name="levelID"></param>
/// <param name="startType"></param>
/// <param name="isReplay"></param>
public static void LogLevelStart(int level, string startType = EventLevelStartModePlay,
string levelCategory = "main", string levelName = "", string levelID = "",
bool isReplay = false)
{
Analytics.LogLevelStart(level, levelName, levelCategory, levelID, startType, isReplay);
}
/// <summary>
/// 游戏点击 Continue 重开始游戏
/// </summary>
/// <param name="level"></param>
/// <param name="levelCategory"></param>
/// <param name="levelName"></param>
/// <param name="levelID"></param>
public static void LogLevelContinue(int level, string levelCategory = "main",
string levelName = "", string levelID = "")
{
LogLevelStart(level, EventLevelStartModeContinue, levelCategory, levelName, levelID, true);
}
/// <summary>
/// 游戏点击 Continue 重开始游戏
/// </summary>
/// <param name="level"></param>
/// <param name="levelCategory"></param>
/// <param name="levelName"></param>
/// <param name="levelID"></param>
public static void LogLevelReplay(int level, string levelCategory = "main",
string levelName = "", string levelID = "")
{
LogLevelStart(level, EventLevelStartModeReplay,levelCategory, levelName, levelID, true);
}
/// <summary>
/// 游戏胜利打点
/// </summary>
public static void LogLevelEnd(int level, string result = EventLevelEndSuccess,
string levelCategory = "main", string levelName = "", string levelID = "",
int? duration = null, int? step = null, int? score = null )
{
if (InitConfig.AutoRecordFinishedLevels)
{
if(result == EventLevelEndSuccess) Model.SuccessLevelCount++; // 自动记录关卡完成次数
Model.TotalPlayedCount++; // 自动记录关卡总次数
Analytics.BLevel = Model.SuccessLevelCount; // 记录 BLevel
Analytics.BPlay = Model.TotalPlayedCount; // 记录 BPlay
}
Analytics.LogLevelEnd(level, result,
levelName, levelCategory, levelCategory,
duration, step, score);
}
/// <summary>
/// 游戏失败打点
/// 需要为游戏记录详细的失败原因
/// </summary>
public static void LogLevelFail(int level,
string levelCategory = "main",string levelName = "", string levelID = "",
int? duration = null, int? step = null, int? score = null )
{
LogLevelEnd(level, EventLevelEndFail, levelCategory, levelName, levelID, duration, step, score);
}
/// <summary>
/// 因退出关卡导致游戏失败
/// </summary>
public static void LogLevelFailExit(int level,
string levelCategory = "main", string levelName = "", string levelID = "",
int? duration = null, int? step = null, int? score = null)
{
LogLevelEnd(level, EventLevelEndExit, levelCategory, levelName, levelID, duration, step, score);
}
/// <summary>
/// 因关卡超时导致游戏失败
/// </summary>
public static void LogLevelFailTimeout(int level,
string levelCategory = "main", string levelName = "", string levelID = "",
int? duration = null, int? step = null, int? score = null)
{
LogLevelEnd(level, EventLevelEndTimeout, levelCategory, levelName, levelID, duration, step, score);
}
/// <summary>
/// 玩家(角色)升级事件
/// </summary>
/// <param name="playerLevel"></param>
/// <param name="playerName"></param>
public static void LogLevelUp(int playerLevel, string playerName)
{
Analytics.LevelUp(playerLevel, playerName);
}
/// <summary>
/// 玩家解锁成就
/// </summary>
/// <param name="achievementName"></param>
public static void LogAchievement(string achievementName)
{
Analytics.UnlockAchievement(achievementName);
}
#endregion
#region 用户属性
/// <summary>
/// 设置用户属性
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public static void SetUserProperty(string key, string value)
=> Analytics.SetUserProperty(key, value);
public static void SetUID(string uid)
{
SetUserProperty(Analytics.PropertyUserID, uid);
}
public static void SetUserBLevel(int blevel)
{
SetUserProperty(Analytics.PropertyLevel, $"{blevel}");
}
public static void SetUserBPlay(int bplay)
{
SetUserProperty(Analytics.PropertyPlay, $"{bplay}");
}
public static void SetUserTotalCoins(int totalCoins)
{
SetUserProperty(Analytics.PropertyCoin, $"{totalCoins}");
}
public static void SetUserCoins(int coins)
{
SetUserProperty(Analytics.PropertyNonIAPCoin, $"{coins}");
}
public static void SetUserPaidCoins(int paidCoins)
{
SetUserProperty(Analytics.PropertyIAPCoin, $"{paidCoins}");
}
public static void SetUserExp(int exp)
{
SetUserProperty(Analytics.PropertyExp, $"{exp}");
}
public static void SetUserHp(int hp)
{
SetUserProperty(Analytics.PropertyHp, $"{hp}");
}
public static void SetUserGrade(int grade)
{
SetUserProperty(Analytics.PropertyGrade, $"{grade}");
}
#endregion
}
}

View File

@ -1,157 +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 _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 _onRemoteInitComplete;
public static event Action OnRemoteInitComplete
{
add => _onRemoteInitComplete += value;
remove => _onRemoteInitComplete -= value;
}
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<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, string> _onPurchaseFailed;
public static event Action<string, string> OnPurchaseFailed
{
add => _onPurchaseFailed += value;
remove => _onPurchaseFailed -= value;
}
}
}
}
}

View File

@ -1,171 +0,0 @@
namespace Guru
{
using UnityEngine;
using System;
using System.Linq;
public partial class GuruSDK
{
public static bool IsIAPReady = false;
//---------- 支付失败原因 ----------
public const string BuyFail_PurchasingUnavailable = "PurchasingUnavailable";
public const string BuyFail_Pending = "ExistingPurchasePending";
public const string BuyFail_ProductUnavailable = "ProductUnavailable";
public const string BuyFail_SignatureInvalid = "SignatureInvalid";
public const string BuyFail_UserCancelled = "UserCancelled";
public const string BuyFail_PaymentDeclined = "PaymentDeclined";
public const string BuyFail_DuplicateTransaction = "DuplicateTransaction";
public const string BuyFail_Unknown = "Unknown";
/// <summary>
/// 初始化IAP 功能
/// </summary>
public static void InitIAP(byte[] googleKey, byte[] appleRootCerts)
{
GuruIAP.Instance.OnInitResult += OnIAPInitResult;
GuruIAP.Instance.OnRestored += OnRestored;
GuruIAP.Instance.OnBuyStart += OnBuyStart;
GuruIAP.Instance.OnBuyEnd += OnBuyEnd;
GuruIAP.Instance.OnBuyFailed += OnBuyFailed;
GuruIAP.Instance.InitWithKeys(googleKey, appleRootCerts, IsDebugMode);
}
/// <summary>
/// 初始化结果
/// </summary>
/// <param name="success"></param>
private static void OnIAPInitResult(bool success)
{
LogI($"IAP init result: {success}");
IsIAPReady = success;
Callbacks.IAP._onIAPInitComplete?.Invoke(success);
}
private static bool CheckIAPReady()
{
if (!IsIAPReady)
{
LogE("IAP is not ready, call <GuruSDK.InitIAP> first.");
return false;
}
return true;
}
#region Purchase
public static ProductSetting GetProductSettingById(string productId)
{
var products = GuruSettings.Instance.Products;
if (products != null && products.Length > 0)
{
return products.FirstOrDefault(p => p.ProductId == productId);
}
return null;
}
public static ProductSetting GetProductSetting(string productName)
{
var products = GuruSettings.Instance.Products;
if (products != null && products.Length > 0)
{
return products.FirstOrDefault(p => p.ProductName == productName);
}
return null;
}
private static Action<string, bool> _onPurchaseCallback;
/// <summary>
/// 购买商品, 通过商品Name
/// </summary>
/// <param name="productName"></param>
/// <param name="purchaseCallback"></param>
public static void Purchase(string productName, Action<string, bool> purchaseCallback = null)
{
if (CheckIAPReady())
{
_onPurchaseCallback = purchaseCallback;
GuruIAP.Instance.Buy(productName);
}
}
/// <summary>
/// 购买商品, 通过商品ID
/// </summary>
/// <param name="productId"></param>
/// <param name="purchaseCallback"></param>
public static bool PurchaseById(string productId, Action<string, bool> purchaseCallback = null)
{
var productName = GetProductSettingById(productId)?.ProductName ?? "";
if (CheckIAPReady() && !string.IsNullOrEmpty(productName))
{
Purchase(productName, purchaseCallback);
return true;
}
return false;
}
/// <summary>
/// 支付回调
/// </summary>
/// <param name="productId"></param>
/// <param name="success"></param>
private static void OnBuyEnd(string productName, bool success)
{
_onPurchaseCallback?.Invoke(productName, success);
}
/// <summary>
/// 支付开始
/// </summary>
/// <param name="productName"></param>
private static void OnBuyStart(string productName)
{
Callbacks.IAP._onPurchaseStart?.Invoke(productName);
}
/// <summary>
/// 支付失败
/// </summary>
/// <param name="productName"></param>
/// <param name="reason"></param>
private static void OnBuyFailed(string productName, string reason)
{
Callbacks.IAP._onPurchaseFailed?.Invoke(productName, reason);
}
#endregion
#region Restore Purchase
private static Action<bool> _onRestored;
/// <summary>
/// 恢复购买
/// </summary>
/// <param name="restoreCallback"></param>
public static void Restore(Action<bool> restoreCallback)
{
if (CheckIAPReady())
{
_onRestored = restoreCallback;
GuruIAP.Instance.Restore();
}
}
private static void OnRestored(bool success)
{
_onRestored?.Invoke(success);
}
#endregion
}
}

View File

@ -1,355 +0,0 @@
namespace Guru
{
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
public partial class GuruSDK: MonoBehaviour
{
public const string Version = "1.0.0";
public const string Tag = "[Guru]";
public const string ServicesConfigKey = "guru_services";
public const string ServicesConfigExtension = "cfg";
private static GuruSDK _instance;
/// <summary>
/// 单利引用
/// </summary>
public static GuruSDK Instance
{
get
{
if(null == _instance)
{
_instance = CreateInstance();
}
return _instance;
}
}
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 GuruServicesConfig _appServicesConfig;
/// <summary>
/// Debug Mode
/// </summary>
public static bool IsDebugMode
{
get
{
#if UNITY_EDITOR || DEBUG
return true;
#endif
return false;
}
}
#region 初始化
private static GuruSDK CreateInstance()
{
var go = new GameObject(nameof(GuruSDK));
DontDestroyOnLoad(go);
_instance = go.AddComponent<GuruSDK>();
return _instance;
}
public static GuruSDKInitConfig BuildConfig(
bool useCustomConsent = false,
bool autoLoadAds = true,
bool iapEnabled = true,
bool autoRecordFinishedLevels = true,
bool showDebugLog = false,
Dictionary<string, object> defaultRemoteData = null,
byte[] googleKeys = null,
byte[] appleRootCerts = null)
{
var config = GuruSDKInitConfig.Build(useCustomConsent, autoLoadAds, iapEnabled,
autoRecordFinishedLevels, showDebugLog, defaultRemoteData, googleKeys, appleRootCerts);
return config;
}
public static void Init(Action<bool> onComplete)
{
Init(GuruSDKInitConfig.Build(), onComplete);
}
public static void Init(GuruSDKInitConfig config, Action<bool> onComplete)
{
LogI($"---- Guru SDK init ----");
LogI(config.ToString());
Instance.StartWithConfig(config, onComplete);
}
/// <summary>
/// 启动SDK
/// </summary>
/// <param name="config"></param>
/// <param name="onComplete"></param>
private void StartWithConfig(GuruSDKInitConfig config, Action<bool> onComplete)
{
Model.PropBLevel.OnValueChanged += OnBLevelChanged;
Model.PropBPlay.OnValueChanged += OnBPlayChanged;
_initConfig = config;
_onCompleteCallback = onComplete;
//---------- Start Firebase ------------
FirebaseUtil.InitFirebase(OnFirebaseReady);
// FirebaseUtil.OnFetchRemoteSuccess+= OnFetchRemoteCallback;
//---------- Start Facebook ------------
FBService.Instance.StartService();
}
/// <summary>
/// 开始各种组件初始化
/// </summary>
private void OnFirebaseReady()
{
if (!InitConfig.UseCustomConsent)
{
// LogI($"--- #3 Start Consent Flow ---");
StartConsentFlow();
}
if(!string.IsNullOrEmpty(IPMConfig.IPM_UID)) SetUID(IPMConfig.IPM_UID);
// 开始Remote Manager初始化
RemoteConfigManager.Init(BuildDefaultRemoteData(_initConfig.DefaultRemoteData));
RemoteConfigManager.OnFetchCompleted += OnFetchRemoteCallback;
// 根据上次的云控配置来初始化参数
SetupServicesConfig();
_onCompleteCallback?.Invoke(true);
}
/// <summary>
/// 注入云控参数基础数据
/// </summary>
/// <param name="dict"></param>
/// <returns></returns>
private Dictionary<string, object> BuildDefaultRemoteData(Dictionary<string, object> dict)
{
string json = Model.LoadAppServicesConfigJson(); // 注入默认的Services 配置值
if (!string.IsNullOrEmpty(json))
{
if (dict == null) dict = new Dictionary<string, object>(3);
dict[ServicesConfigKey] = json;
return dict;
}
return null;
}
/// <summary>
/// 拉取云控参数完成
/// </summary>
/// <param name="success"></param>
private void OnFetchRemoteCallback(bool success)
{
LogI($"--- Remote fetch complete: {success} ---");
ABTestManager.Init(); // 启动AB测试解析器
Callbacks.Remote._onRemoteFetchComplete?.Invoke(success);
}
#endregion
#region App Remote Update
private void SetupServicesConfig()
{
bool useKeywords = true;
bool useIAP = true;
var guruSettings = GuruSettings.Instance;
var services = GetRemoteServicesConfig();
if (services != null)
{
_appServicesConfig = services;
useKeywords = _appServicesConfig.IsKeywordsEnabled();
if (null != guruSettings)
{
if(_appServicesConfig.adjust_settings != null)
{
// 更新 Adjust Tokens
guruSettings.UpdateAdjustTokens(_appServicesConfig.adjust_settings.AndroidToken
,_appServicesConfig.adjust_settings.iOSToken);
// 更新 Adjust Events
guruSettings.UpdateAdjustEvents(_appServicesConfig.adjust_settings.events);
}
}
useIAP = _appServicesConfig.IsIAPEnabled();
}
AdjustService.StartService();
if(useIAP) {
InitIAP(_initConfig.GoogleKeys, _initConfig.AppleRootCerts); // 初始化IAP
}
if(useKeywords) {
KeywordsManager.Install(Model.IsIAPUser, Model.SuccessLevelCount); // 启动Keyword管理器
}
}
private GuruServicesConfig GetRemoteServicesConfig()
{
var json = GetRemoteString(ServicesConfigKey);
if (!string.IsNullOrEmpty(json))
{
return JsonParser.ToObject<GuruServicesConfig>(json);
}
return null;
}
#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
public static void LogI(object message)
{
Debug.Log($"{Tag} {message}");
}
public static void LogW(object message)
{
Debug.LogWarning($"{Tag} {message}");
}
public static void LogE(object message)
{
Debug.LogError($"{Tag} {message}");
}
public static void LogException(string message)
{
LogException( new Exception($"{Tag} {message}"));
}
public static void LogException(Exception e)
{
Debug.LogException(e);
}
#endregion
#region 生命周期
/// <summary>
/// 暂停时处理
/// </summary>
/// <param name="paused"></param>
private void OnAppPauseHandler(bool paused)
{
if(paused) Model.Save(true); // 强制保存数据
Callbacks.App._onAppPaused?.Invoke(paused);
}
private void OnApplicationPause(bool paused)
{
OnAppPauseHandler(paused);
}
private void OnApplicationFocus(bool hasFocus)
{
OnAppPauseHandler(!hasFocus);
}
private void OnApplicationQuit()
{
Model.Save(true);
Callbacks.App._onAppQuit?.Invoke();
}
#endregion
#region 延迟处理
/// <summary>
/// 启动协程
/// </summary>
/// <param name="enumerator"></param>
/// <returns></returns>
public static Coroutine DoCoroutine(IEnumerator enumerator)
{
return Instance != null ? Instance.StartCoroutine(enumerator) : null;
}
public static void KillCoroutine(Coroutine coroutine)
{
if(coroutine != null)
Instance.StopCoroutine(coroutine);
}
/// <summary>
/// 延时执行
/// </summary>
/// <param name="seconds"></param>
/// <param name="callback"></param>
public static void Delay(float seconds, Action callback)
{
DoCoroutine(Instance.OnDelayCall(seconds, callback));
}
private IEnumerator OnDelayCall(float delay, Action callback)
{
if (delay > 0)
{
yield return new WaitForSeconds(delay);
}
else
{
yield return null;
}
callback?.Invoke();
}
#endregion
}
}

View File

@ -1,97 +0,0 @@
namespace Guru
{
using System.Collections.Generic;
using System.Text;
/// <summary>
/// 启动参数配置
/// </summary>
public partial class GuruSDKInitConfig
{
/// <summary>
/// 使用自定义的ConsentFlow启动流程
/// </summary>
public bool UseCustomConsent = false;
/// <summary>
/// SDK初始化完成后自动加载广告
/// </summary>
public bool AutoLoadWhenAdsReady = true;
/// <summary>
/// 使用IAP支付插件功能
/// </summary>
public bool IAPEnabled = true;
/// <summary>
/// 自动记录完成的关卡
/// </summary>
public bool AutoRecordFinishedLevels = true;
/// <summary>
/// 显示Debug日志
/// </summary>
public bool ShowDebugLog = false;
/// <summary>
/// 云控参数的默认配置
/// </summary>
/// <returns></returns>
public Dictionary<string, object> DefaultRemoteData = new Dictionary<string, object>();
/// <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 showDebugLog = false,
Dictionary<string, object> defaultRemoteData = null,
byte[] googleKeys = null,
byte[] appleRootCerts = null)
{
// 创建启动用参数
GuruSDKInitConfig config = new GuruSDKInitConfig()
{
UseCustomConsent = useCustomConsent,
AutoLoadWhenAdsReady = autoLoadAds,
IAPEnabled = iapEnabled,
AutoRecordFinishedLevels = autoRecordFinishedLevels,
ShowDebugLog = showDebugLog,
GoogleKeys = googleKeys,
AppleRootCerts = appleRootCerts,
DefaultRemoteData = defaultRemoteData ?? new Dictionary<string, object>(),
};
#if UNITY_EDITOR
config.ShowDebugLog = true;
#endif
return config;
}
#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: {ShowDebugLog}");
sb.AppendLine($"------- Custom init Config -------");
return sb.ToString();
}
#endregion
}
}

View File

@ -3,20 +3,21 @@ namespace Guru
internal class GuruIAP: IAPServiceBase<GuruIAP>
{
/// <summary>
/// 获取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,16 +1,46 @@
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;
@ -18,67 +48,77 @@ namespace Guru
{
get
{
if (null == _instance) _instance = Load();
if (null == _instance) _instance = new GuruSDKModel();
return _instance;
}
}
//-------------- data ---------------
public int b_level = 0;
public int b_play = 0;
public int buy_count = 0;
//-------------- data ---------------
private float _lastSavedTime = 0;
public int SuccessLevelCount
public GuruSDKModel()
{
get
// 读取内存值
GuruSDKSerializedModel model = LoadModel();
_uid = model.uid;
_noAds = model.no_ads;
_bLevel = model.b_level;
_bPlay = model.b_play;
_purchased = model.purchased;
}
public int BLevel
{
get => _bLevel;
set
{
if(_successLevel == null) InitProperties();
return _successLevel.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();
}
set => _successLevel.Value = value;
}
public int TotalPlayedCount
public int BPlay
{
get
get => _bPlay;
set
{
if(_totalPlayed == null) InitProperties();
return _totalPlayed.Value;
_bPlay = value;
Save();
}
set => _totalPlayed.Value = value;
}
public int PurchasedCount
public string UserId
{
get
get => _uid;
set
{
if(_purchasedCount == null) InitProperties();
return _purchasedCount.Value;
_uid = value;
Save();
}
set => _purchasedCount.Value = value;
}
public bool IsIAPUser => PurchasedCount > 0;
public bool IsIapUser => _purchased.Count > 0;
private BindableProperty<int> _successLevel;
private BindableProperty<int> _totalPlayed;
private BindableProperty<int> _purchasedCount;
public bool IsNoAds
{
get => _noAds;
set
{
_noAds = value;
Save();
}
}
public BindableProperty<int> PropBLevel => _successLevel;
public BindableProperty<int> PropBPlay => _totalPlayed;
public BindableProperty<int> PropBuyCount => _purchasedCount;
#region 初始化
public static GuruSDKModel Load()
private GuruSDKSerializedModel LoadModel()
{
GuruSDKModel model = null;
GuruSDKSerializedModel model = null;
if (PlayerPrefs.HasKey(SaveKey))
{
var json = PlayerPrefs.GetString(SaveKey, "");
@ -86,7 +126,7 @@ namespace Guru
{
try
{
model = JsonUtility.FromJson<GuruSDKModel>(json);
model = JsonUtility.FromJson<GuruSDKSerializedModel>(json);
}
catch (Exception e)
{
@ -94,98 +134,108 @@ 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);
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 内
bool shouldWriteToDisk = forceSave || (DateTime.Now - _lastSavedTime)>= TimeSpan.FromSeconds(SaveInterval);
if (!shouldWriteToDisk) return;
_lastSavedTime = DateTime.Now; // 更新最后保存时间
PlayerPrefs.Save(); // 写入到磁盘
}
#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();
}
#endregion
#region 启动配置
#endregion
#region 订单记录
public bool HasPurchasedProduct(string receipt)
{
if(_purchased.Count == 0) return false;
return _purchased.Exists(p => p.receipt == receipt);
}
/// <summary>
/// 从 Streaming 加载 AppServices 配置
/// 添加已支付订单
/// </summary>
/// <returns></returns>
public string LoadAppServicesConfigJson()
/// <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)
{
try
if (!HasPurchasedProduct(receipt))
{
string path = Path.Combine(Application.streamingAssetsPath, $"{GuruSDK.ServicesConfigKey}.{GuruSDK.ServicesConfigExtension}");
return File.ReadAllText(path);
_purchased.Add(new PurchasedProduct()
{
receipt = receipt,
productName = productName,
productId = productId,
appleProductIsRestored = appleProductIsRestored
});
Save();
}
catch (Exception e)
{
Log.Exception(e);
}
return "";
}
public string[] GetReceipts(string productName)
{
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)
{
var receipts = new List<string>();
receipts.AddRange(from purchasedProduct in _purchased where purchasedProduct.productId == productId select purchasedProduct.receipt);
return receipts.ToArray();
}
#endregion
#region 清除数据
public void ClearData()
{
PlayerPrefs.DeleteKey(SaveKey);
}
#endregion
}

View File

@ -0,0 +1,556 @@
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>
/// 启动广告服务
/// </summary>
public static void StartAds(AdsInitSpec spec = null)
{
_adInitSpec = spec;
if (InitConfig.UseCustomConsent)
{
Debug.Log($"{Tag} --- Call <color=orange>StartAdsWithCustomConsent</color> when you use custom consent, and pass the result (boolean) to the method.");
return;
}
// 默认的启动顺序是先启动Consent后, 根据用户回调的结果来启动广告
Instance.StartConsentFlow();
}
/// <summary>
/// 是否已经购买了去广告
/// </summary>
/// <param name="buyNoAds"></param>
public static void StartAds(bool buyNoAds = false)
{
StartAds(AdsInitSpec.BuildWithNoAds()); // 按照默认的去广告来生成广告启动配置
}
/// <summary>
/// 使用自定义的Consent, 获取用户授权后, 调用此方法
/// </summary>
/// <param name="userAllow"></param>
/// <param name="consentName">Consent 引导的类型, 如果使用了 MAX 的 consent 请填写 max </param>
/// <param name="spec">广告启动配置</param>
public static void StartAdsWithCustomConsent(bool userAllow = true,
string consentName = "custom", AdsInitSpec spec = null)
{
#if UNITY_IOS
_attType = consentName;
InitAttStatus();
#endif
if (userAllow)
{
#if UNITY_IOS
Instance.CheckAttStatus();
#else
StartAdService(spec);
#endif
}
else
{
Debug.Log($"{Tag} --- User refuse to provide ads Id, Ads Service will be cancelled");
}
}
/// <summary>
/// 使用自定义的Consent, 获取用户授权后, 调用此方法
/// </summary>
/// <param name="userAllow">自定义 Consent 的用户授权结果</param>
/// <param name="consentName">Consent引导名称</param>
/// <param name="buyNoAds">是否已经购买了去广告</param>
public static void StartAdsWithCustomConsent(bool userAllow = true, string consentName = "custom",
bool buyNoAds = false)
{
StartAdsWithCustomConsent(userAllow, consentName, AdsInitSpec.BuildWithNoAds());
}
#region Guru Consent
private bool _hasConsentCalled = false;
private bool _adServiceHasStarted = false;
private string _notiStatue = "";
private bool _hasNotiGranted = false;
/// <summary>
/// 启动Consent流程
/// 因为之后规划广告流程会放在 Consent 初始化之后, 因此请求广告的时候会需要先请求 Consent
/// </summary>
private void StartConsentFlow()
{
LogI($"#4.5 --- StartConsentFlow ---");
float time = 1;
if (!_adServiceHasStarted && _appServicesConfig != null)
{
time = _appServicesConfig.IsAdsCompliance() ? CONSENT_FLOW_TIMEOUT : 1f; // 启动合规判定后, 延迟最多 10 秒后启动广告
}
Delay(time, AdServiceHandler); // 广告延迟启动
if (_hasConsentCalled) return;
_hasConsentCalled = true;
bool enableCountryCheck = false;
string dmaMapRule = "";
if (_appServicesConfig != null && _appServicesConfig.parameters != null)
{
enableCountryCheck = _appServicesConfig.DMACountryCheck();
dmaMapRule = _appServicesConfig.DMAMapRule();
}
#if UNITY_IOS
InitAttStatus(); // Consent 启动前记录 ATT 初始值
#endif
UnityEngine.Debug.Log($"{Tag} --- Call:StartConsentFlow ---");
GuruConsent.StartConsent(OnGuruConsentOver, dmaMapRule:dmaMapRule, enableCountryCheck:enableCountryCheck);
}
/// <summary>
/// Guru Consent flow is Over
/// </summary>
/// <param name="code"></param>
private void OnGuruConsentOver(int code)
{
// 无论状态如何, 都在回调内启动广告初始化
AdServiceHandler();
// 调用回调
Callbacks.ConsentFlow.InvokeOnConsentResult(code);
#if UNITY_IOS
CheckAttStatus(); // [iOS] Consent 启动后检查 ATT 初始值
#elif UNITY_ANDROID
CheckNotiPermission(); // Consent 回调后检查 Notification 权限
#endif
// 内部处理后继逻辑
switch(code)
{
case GuruConsent.StatusCode.OBTAINED:
case GuruConsent.StatusCode.NOT_AVAILABLE:
// 已获取授权, 或者地区不可用, ATT 尚未启动
// TODO: 添加后继处理逻辑
break;
}
}
/// <summary>
/// 启动广告服务
/// </summary>
private void AdServiceHandler()
{
if (_adServiceHasStarted) return;
_adServiceHasStarted = true;
StartAdService(_adInitSpec);
}
#endregion
#region IOS ATT 广告授权流程
#if UNITY_IOS
private static string _initialAttStatus;
private static String _attType = "admob";
private static bool _autoReCallAtt = false;
/// <summary>
/// 显示系统的 ATT 弹窗
/// </summary>
public void RequestAttDialog()
{
LogI($"RequestATTDialog");
ATTManager.RequestATTDailog(ReportAttStatus);
}
/// <summary>
/// 初始化 ATT 状态
/// </summary>
public static void InitAttStatus()
{
_attType = InitConfig.UseCustomConsent ? ATTManager.GUIDE_TYPE_CUSTOM : ATTManager.GUIDE_TYPE_ADMOB; // 点位属性确定
_initialAttStatus = ATTManager.GetStatus();
SetATTStatus(_initialAttStatus); // 上报一个初始的状态
}
/// <summary>
/// iOS 平台检查 ATT 状态
/// </summary>
private void CheckAttStatus(bool autoReCall = false)
{
_autoReCallAtt = autoReCall;
// Delay 1s to get the user choice
Delay(1, () => ATTManager.CheckStatus(ReportAttStatus));
}
private void ReportAttStatus(string status)
{
LogI($"{Tag} --- Get Att status:{status} att Type:{_attType} recall:{_autoReCallAtt}");
SetATTStatus(_initialAttStatus); // 上报一个初始的状态
// SetUserProperty(Analytics.ParameterATTStatus, status); // 当前的状态
if (!string.IsNullOrEmpty(status)
&& status != _initialAttStatus
&& status != ATTManager.ATT_STATUS_NOT_DETERMINED)
{
// 上报点位:
SetATTStatus(_initialAttStatus);
}
switch(status)
{
case ATTManager.ATT_STATUS_NOT_DETERMINED:
// ATT 状态未知, 请求弹窗
if(_autoReCallAtt) RequestAttDialog();
break;
case ATTManager.ATT_STATUS_RESTRICTED:
case ATTManager.ATT_STATUS_DENIED:
// ATT 状态受限, 或者被拒绝
break;
case ATTManager.ATT_STATUS_AUTHORIZED:
// ATT 状态已授权
break;
}
CheckNotiPermission(); // Consent 回调后检查 Notification 权限
}
#endif
#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>
private static void StartAdService(AdsInitSpec spec = null)
{
//---------- Using InitConfig ----------
if (InitConfig is { IsBuyNoAds: true }) SetBuyNoAds(true);
LogI($"StartAdService");
if (spec == null)
{
spec = AdsInitSpec.BuildDefault(InitConfig.AutoLoadWhenAdsReady, IsDebugMode);
if (ADService.Instance.IsBuyNoAds)
{
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);
// ---------- 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.InvokeOnBannerADLoaded();
private static void OnInterstitialStartLoad(string adUnitId)
=> Callbacks.Ads.InvokeOnInterstitialADStartLoad(adUnitId);
private static void OnInterstitialLoaded()
=> Callbacks.Ads.InvokeOnInterstitialADLoaded();
private static void OnInterstitialFailed()
=> 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.InvokeOnRewardedADLoaded();
private static void OnRewardFailed()
=> Callbacks.Ads.InvokeOnRewardADFailed();
private static void OnRewardClosed()
=> Callbacks.Ads.InvokeOnRewardADClosed();
private static void OnAdsInitComplete()
{
_initAdsCompleted = true;
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 (!IsAdsReady)
{
LogW("[SDK] Ads is not ready. Call <GuruSDk.StartAdService> first.");
return false;
}
return true;
}
/// <summary>
/// 显示Banner广告
/// </summary>
/// <param name="placement"></param>
public static void ShowBanner(string placement = "")
{
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())
{
_preBannerAction = -1;
return;
}
ADService.Instance.HideBanner();
}
public static void LoadInterstitialAd()
{
if (!CheckAdsReady()) return;
ADService.Instance.RequestInterstitialAD();
}
public static bool IsInterstitialAdReady => ADService.Instance.IsInterstitialADReady();
/// <summary>
/// 显示插屏广告
/// </summary>
/// <param name="placement"></param>
/// <param name="onDismissed"></param>
public static void ShowInterstitialAd(string placement = "", Action onDismissed = null)
{
if (!CheckAdsReady()) return;
if (!ADService.Instance.IsInterstitialADReady())
{
LogE("Interstitial is not ready. Call <GuruSDk.ShowInterstitialAd> again.");
LoadInterstitialAd();
return;
}
ADService.Instance.ShowInterstitialAD(placement, onDismissed);
}
public static void LoadRewardAd()
{
if (!CheckAdsReady()) return;
ADService.Instance.RequestRewardedAD();
}
public static bool IsRewardAdReady => ADService.Instance.IsRewardedADReady();
/// <summary>
/// 显示激励视频广告
/// </summary>
/// <param name="placement"></param>
/// <param name="onRewarded"></param>
/// <param name="onFailed"></param>
public static void ShowRewardAd(string placement = "", Action onRewarded = null, Action<string> onFailed = null)
{
if (!CheckAdsReady()) return;
if (!ADService.Instance.IsRewardedADReady())
{
LogE("RewardAd is not ready. Call <GuruSDk.LoadRewardAd> again.");
LoadRewardAd();
return;
}
ADService.Instance.ShowRewardedAD(placement, onRewarded, onFailed);
}
#endregion
#region MaxServices
/// <summary>
/// 显示Max调试菜单
/// </summary>
public static void ShowMaxDebugPanel()
{
#if UNITY_EDITOR
LogI($"Can not show Max Debug Panel in Editor, skipped.");
return;
#endif
if (!ADService.Instance.IsInitialized)
{
LogI($"ADService is not initialized, call <GuruSDK.StartAds> first.");
return;
}
ADService.Instance.ShowMaxDebugPanel();
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,282 @@
namespace Guru
{
public partial class GuruSDK
{
/// <summary>
/// Consts values
/// </summary>
public static class Consts
{
#region Firebase Defines
public const string EventAdImpression = "ad_impression";
public const string EventAddPaymentInfo = "add_payment_info";
public const string EventAddShippingInfo = "add_shipping_info";
public const string EventAddToCart = "add_to_cart";
public const string EventAddToWishlist = "add_to_wishlist";
public const string EventAppOpen = "app_open";
public const string EventBeginCheckout = "begin_checkout";
public const string EventCampaignDetails = "campaign_details";
public const string EventEarnVirtualCurrency = "earn_virtual_currency";
public const string EventGenerateLead = "generate_lead";
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";
public const string EventPurchase = "purchase";
public const string EventRefund = "refund";
public const string EventRemoveFromCart = "remove_from_cart";
public const string EventScreenView = "screen_view";
public const string EventSearch = "search";
public const string EventSelectContent = "select_content";
public const string EventSelectItem = "select_item";
public const string EventSelectPromotion = "select_promotion";
public const string EventShare = "share";
public const string EventSignUp = "sign_up";
public const string EventSpendVirtualCurrency = "spend_virtual_currency";
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";
public const string EventViewPromotion = "view_promotion";
public const string EventViewSearchResults = "view_search_results";
public const string ParameterAchievementId = "achievement_id";
public const string ParameterAdFormat = "ad_format";
public const string ParameterAdNetworkClickID = "aclid";
public const string ParameterAdPlatform = "ad_platform";
public const string ParameterAdSource = "ad_source";
public const string ParameterAdUnitName = "ad_unit_name";
public const string ParameterAffiliation = "affiliation";
public const string ParameterCP1 = "cp1";
public const string ParameterCampaign = "campaign";
public const string ParameterCharacter = "character";
public const string ParameterContent = "content";
public const string ParameterContentType = "content_type";
public const string ParameterCoupon = "coupon";
public const string ParameterCreativeName = "creative_name";
public const string ParameterCreativeSlot = "creative_slot";
public const string ParameterCurrency = "currency";
public const string ParameterDestination = "destination";
public const string ParameterDiscount = "discount";
public const string ParameterEndDate = "end_date";
public const string ParameterExtendSession = "extend_session";
public const string ParameterFlightNumber = "flight_number";
public const string ParameterGroupId = "group_id";
public const string ParameterIndex = "index";
public const string ParameterItemBrand = "item_brand";
public const string ParameterItemCategory = "item_category";
public const string ParameterItemCategory2 = "item_category2";
public const string ParameterItemCategory3 = "item_category3";
public const string ParameterItemCategory4 = "item_category4";
public const string ParameterItemCategory5 = "item_category5";
public const string ParameterItemId = "item_id";
public const string ParameterItemList = "item_list";
public const string ParameterItemListID = "item_list_id";
public const string ParameterItemListName = "item_list_name";
public const string ParameterItemName = "item_name";
public const string ParameterLevel = "level";
public const string ParameterLevelName = "level_name";
public const string ParameterLocation = "location";
public const string ParameterLocationID = "location_id";
public const string ParameterMedium = "medium";
public const string ParameterMethod = "method";
public const string ParameterNumberOfNights = "number_of_nights";
public const string ParameterNumberOfPassengers = "number_of_passengers";
public const string ParameterNumberOfRooms = "number_of_rooms";
public const string ParameterOrigin = "origin";
public const string ParameterPaymentType = "payment_type";
public const string ParameterPrice = "price";
public const string ParameterPromotionID = "promotion_id";
public const string ParameterPromotionName = "promotion_name";
public const string ParameterQuantity = "quantity";
public const string ParameterScore = "score";
public const string ParameterScreenClass = "screen_class";
public const string ParameterScreenName = "screen_name";
public const string ParameterSearchTerm = "search_term";
public const string ParameterShipping = "shipping";
public const string ParameterShippingTier = "shipping_tier";
public const string ParameterSignUpMethod = "sign_up_method";
public const string ParameterSource = "source";
public const string ParameterStartDate = "start_date";
public const string ParameterSuccess = "success";
public const string ParameterTax = "tax";
public const string ParameterTerm = "term";
public const string ParameterTransactionId = "transaction_id";
public const string ParameterTravelClass = "travel_class";
public const string ParameterValue = "value";
#endregion
#region Guru BI Events & Parameters
public const string TAG = "Analytics";
// 美元符号
public const string USD = "USD";
// 广告平台
public const string AdMAX = "MAX";
//IAP打点事件
public const string EventIAPFirst = "first_iap";
public const string EventIAPImp = "iap_imp";
public const string EventIAPClose = "iap_close";
public const string EventIAPClick = "iap_clk";
public const string EventIAPReturnTrue = "iap_ret_true";
public const string EventIAPReturnFalse = "iap_ret_false";
//横幅广告打点事件
public const string EventBadsLoad = "bads_load";
public const string EventBadsLoaded = "bads_loaded";
public const string EventBadsFailed = "bads_failed";
public const string EventBadsClick = "bads_clk";
public const string EventBadsImp = "bads_imp";
//插屏广告打点事件
public const string EventIadsLoad = "iads_load";
public const string EventIadsLoaded = "iads_loaded";
public const string EventIadsFailed = "iads_failed";
public const string EventIadsImp = "iads_imp";
public const string EventIadsClick = "iads_clk";
public const string EventIadsClose = "iads_close";
//激励视频广告打点事件
public const string EventRadsLoad = "rads_load";
public const string EventRadsLoaded = "rads_loaded";
public const string EventRadsFailed = "rads_failed";
public const string EventRadsImp = "rads_imp";
public const string EventRadsRewarded = "rads_rewarded";
public const string EventRadsClick = "rads_clk";
public const string EventRadsClose = "rads_close";
public const string EventFirstRadsRewarded = "first_rads_rewarded";
//广告收益打点事件
public const string EventTchAdRev001Impression = "tch_ad_rev_roas_001";
public const string EventTchAdRev02Impression = "tch_ad_rev_roas_02";
public const string EventTchAdRevAbnormal = "tch_ad_rev_value_abnormal";
//内购成功事件上报
public const string EventIAPPurchase = "iap_purchase";
public const string EventSubPurchase = "sub_purchase";
public const string IAPStoreCategory = "Store";
public const string IAPTypeProduct = "product";
public const string IAPTypeSubscription = "subscription";
//打点参数名
public const string ParameterResult = "result";
public const string ParameterStep = "step";
public const string ParameterDuration = "duration";
public const string ParameterErrorCode = "error_code";
public const string ParameterProductId = "product_id";
public const string ParameterPlatform = "platform";
public const string ParameterStartType = "start_type"; // 游戏启动类型
public const string ParameterReplay = "replay"; // 游戏重玩
public const string ParameterContinue = "continue"; // 游戏继续
// 评价参数
public const string EventRateImp = "rate_imp"; // 评价弹窗展示
public const string EventRateNow = "rate_now"; // 点击评分引导弹窗中的评分
//打点内部执行错误
public static string ParameterEventError => "event_error";
//ios ATT打点
public const string ATTGuideShow = "att_guide_show";
public const string ATTGuideOK = "att_guide_ok";
public const string ATTWindowShow = "att_window_show";
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 PropertyFirstOpenTime = "first_open_time"; //用户第一次first_open的时间
public const string PropertyDeviceID = "device_id"; //用户的设备ID
public const string PropertyUserID = "user_id";
public const string PropertyLevel = "b_level"; //"每次完成通关上升一次显示用户完成的最大关卡数。只针对主关卡和主玩法的局数做累加初始值为0。"
public const string PropertyPlay = "b_play"; //每完成一局或者游戏触发,
public const string PropertyLastPlayedLevel = "last_played_level";
public const string PropertyGrade = "grade"; //当游戏玩家角色升级时触发
public const string PropertyIsIAPUser = "is_iap_user"; //付费成功后设置属性参数为true如果没有发生付费可以不用设置该属性
public const string PropertyIAPCoin = "iap_coin"; //付费所得的总金币数(iap获取累计值)\
public const string PropertyNonIAPCoin = "noniap_coin"; //非付费iap获取累计值
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 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,3 @@
fileFormatVersion: 2
guid: 35b764b09fe74efe8e2ebead9716279a
timeCreated: 1709802191

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

@ -0,0 +1,335 @@
namespace Guru
{
using System;
using System.Linq;
public partial class GuruSDK
{
public static bool IsIAPReady = false;
//---------- 支付失败原因 ----------
public const string BuyFail_PurchasingUnavailable = "PurchasingUnavailable";
public const string BuyFail_Pending = "ExistingPurchasePending";
public const string BuyFail_ProductUnavailable = "ProductUnavailable";
public const string BuyFail_SignatureInvalid = "SignatureInvalid";
public const string BuyFail_UserCancelled = "UserCancelled";
public const string BuyFail_PaymentDeclined = "PaymentDeclined";
public const string BuyFail_DuplicateTransaction = "DuplicateTransaction";
public const string BuyFail_Unknown = "Unknown";
#region Start
/// <summary>
/// 初始化IAP 功能
/// </summary>
public static void InitIAP(string uid, byte[] googleKey, byte[] appleRootCerts)
{
GuruIAP.Instance.OnInitResult += OnIAPInitResult;
GuruIAP.Instance.OnRestored += OnRestored;
GuruIAP.Instance.OnBuyStart += OnBuyStart;
GuruIAP.Instance.OnBuyEnd += OnBuyEnd;
GuruIAP.Instance.OnBuyFailed += OnBuyFailed;
GuruIAP.Instance.OnGetProductReceipt += OnGetReceipt;
Callbacks.IAP.InvokeOnIAPInitStart(); // 初始化之前进行调用
GuruIAP.Instance.InitWithKeys(uid, googleKey, appleRootCerts, IsDebugMode);
}
/// <summary>
/// 初始化结果
/// </summary>
/// <param name="success"></param>
private static void OnIAPInitResult(bool success)
{
LogI($"IAP init result: {success}");
IsIAPReady = success;
Callbacks.IAP.InvokeOnIAPInitComplete(success);
}
private static bool CheckIAPReady()
{
if (!IsIAPReady)
{
LogE("IAP is not ready, call <GuruSDK.InitIAP> first.");
return false;
}
return true;
}
#endregion
#region Data
/// <summary>
/// 获取商品信息
/// </summary>
/// <param name="productName"></param>
/// <returns></returns>
public static ProductInfo GetProductInfo(string productName)
{
return GuruIAP.Instance?.GetInfo(productName);
}
/// <summary>
/// 获取商品信息 (提供 ProductId)
/// </summary>
/// <param name="productId"></param>
/// <returns></returns>
public static ProductInfo GetProductInfoById(string productId)
{
return GuruIAP.Instance?.GetInfoById(productId);
}
[Obsolete("Will be discarded in next version. Using GetProductInfoById(string productId) instead.")]
public static ProductSetting GetProductSettingById(string productId)
{
var products = GuruSettings.Instance.Products;
if (products != null && products.Length > 0)
{
return products.FirstOrDefault(p => p.ProductId == productId);
}
return null;
}
[Obsolete("Will be discarded in next version. Using GetProductInfo(string productName) instead.")]
public static ProductSetting GetProductSetting(string productName)
{
var products = GuruSettings.Instance.Products;
if (products != null && products.Length > 0)
{
return products.FirstOrDefault(p => p.ProductName == productName);
}
return null;
}
/// <summary>
/// 查询某个商品是否已经包含订单了
/// </summary>
/// <param name="productName"></param>
/// <returns></returns>
public static bool IsProductHasReceipt(string productName)
{
return GuruIAP.Instance.IsProductHasReceipt(productName);
}
public static string GetProductLocalizedPriceString(string productName)
{
return GuruIAP.Instance.GetLocalizedPriceString(productName);
}
#endregion
#region Purchase
private static Action<string, bool> InvokeOnPurchaseCallback;
/// <summary>
/// 老接口, 将会被废弃
/// </summary>
/// <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.")]
internal static void Purchase(string productName, Action<string, bool> purchaseCallback = null)
{
Purchase(productName, "", purchaseCallback);
}
/// <summary>
/// 购买商品, 通过商品Name
/// </summary>
/// <param name="productName"></param>
/// <param name="category"></param>
/// <param name="purchaseCallback"></param>
public static void Purchase(string productName, string category = "", Action<string, bool> purchaseCallback = null)
{
if (CheckIAPReady())
{
InvokeOnPurchaseCallback = purchaseCallback;
GuruIAP.Instance.Buy(productName, category);
}
}
/// <summary>
/// 购买商品, 通过商品ID
/// </summary>
/// <param name="productId"></param>
/// <param name="category"></param>
/// <param name="purchaseCallback"></param>
public static bool PurchaseById(string productId, string category = "", Action<string, bool> purchaseCallback = null)
{
var productName = GetProductInfoById(productId)?.Name ?? "";
if (CheckIAPReady() && !string.IsNullOrEmpty(productName))
{
Purchase(productName, category, purchaseCallback);
return true;
}
return false;
}
/// <summary>
/// 支付回调
/// </summary>
/// <param name="productName"></param>
/// <param name="success"></param>
private static void OnBuyEnd(string productName, bool success)
{
InvokeOnPurchaseCallback?.Invoke(productName, success);
Callbacks.IAP.InvokeOnPurchaseEnd(productName, success);
}
/// <summary>
/// 支付开始
/// </summary>
/// <param name="productName"></param>
private static void OnBuyStart(string productName)
{
Callbacks.IAP.InvokeOnPurchaseStart(productName);
}
/// <summary>
/// 支付失败
/// </summary>
/// <param name="productName"></param>
/// <param name="reason"></param>
private static void OnBuyFailed(string productName, string reason)
{
Callbacks.IAP.InvokeOnPurchaseFailed(productName, reason);
}
#endregion
#region Restore Purchase
/// <summary>
/// 恢复购买
/// </summary>
/// <param name="restoreCallback"></param>
public static void Restore(Action<bool, string> restoreCallback = null)
{
if( restoreCallback != null) Callbacks.IAP.OnIAPRestored += restoreCallback;
if (CheckIAPReady())
{
GuruIAP.Instance.Restore();
}
}
private static void OnRestored(bool success, string msg)
{
Callbacks.IAP.InvokeOnIAPRestored(success, msg); // 更新回复购买回调
}
#endregion
#region Receipt
/// <summary>
/// 获取订单收据
/// </summary>
/// <param name="productId"></param>
/// <param name="receipt"></param>
/// <param name="appleProductIsRestored"></param>
private static void OnGetReceipt(string productId, string receipt, bool appleProductIsRestored = false)
{
var productName = GetProductInfoById(productId)?.Name ?? "";
if(!string.IsNullOrEmpty(productName))
Model.AddReceipt(receipt, productName, productId, appleProductIsRestored);
}
#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

@ -0,0 +1,108 @@
namespace Guru
{
public partial class GuruSDK
{
// 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
{
get
{
string url = "";
#if UNITY_EDITOR
url = "https://test@com.guru.ai";
#elif UNITY_ANDROID
url = GuruSettings?.AndroidStoreUrl ?? "";
#elif UNITY_IOS
url = GuruSettings?.IOSStoreUrl ?? "";
#endif
return url;
}
}
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
{
get
{
if(_appVersion == null) _appVersion = GuruAppVersion.Load();
return _appVersion;
}
}
private static string _appBundleId;
public static string AppBundleId => _appBundleId;
/// <summary>
/// 设置购买去广告道具的标志位
/// </summary>
/// <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 BLevel
{
get => GuruSDKModel.Instance.BLevel;
set => GuruSDKModel.Instance.BLevel = value;
}
/// <summary>
/// 成功关卡总计数量 (b_play)
/// </summary>
public static int BPlay
{
get => GuruSDKModel.Instance.BPlay;
set => GuruSDKModel.Instance.BPlay = value;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ab7d65b5c8774a12924f3fe72a6e4a38
timeCreated: 1710154459

View File

@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using Firebase.RemoteConfig;
namespace Guru
{
using System;
using System.Collections.Generic;
using Firebase.RemoteConfig;
public partial class GuruSDK
{
@ -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

843
Runtime/Code/SDK/GuruSDK.cs Normal file
View File

@ -0,0 +1,843 @@
namespace Guru
{
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using Debug = UnityEngine.Debug;
using Guru.Network;
using System.Linq;
public partial class GuruSDK: MonoBehaviour
{
// 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;
/// <summary>
/// 单利引用
/// </summary>
public static GuruSDK Instance
{
get
{
if(null == _instance)
{
_instance = CreateInstance();
}
return _instance;
}
}
private GuruSDKInitConfig _initConfig;
private static GuruSDKInitConfig InitConfig => Instance._initConfig;
private static GuruSDKModel Model => GuruSDKModel.Instance;
private static GuruServicesConfig _appServicesConfig;
private static GuruSettings _guruSettings;
private static GuruSettings GuruSettings
{
get
{
if (_guruSettings == null) _guruSettings = GuruSettings.Instance;
return _guruSettings;
}
}
private static DateTime _initTime;
private static bool _isDebugEnabled = false;
/// <summary>
/// Debug Mode
/// </summary>
public static bool IsDebugMode
{
get
{
#if UNITY_EDITOR || DEBUG
return true;
#endif
return _isDebugEnabled;
}
}
/// <summary>
/// 初始化成功标志位
/// </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()
{
var go = new GameObject(nameof(GuruSDK));
DontDestroyOnLoad(go);
_instance = go.AddComponent<GuruSDK>();
return _instance;
}
// TODO : 下个版本需要将 整个 GuruSDK 做功能性的拆分
public static void Init(Action<bool> onComplete)
{
Init(GuruSDKInitConfig.Builder().Build(), onComplete);
}
public static void Init(GuruSDKInitConfig config, Action<bool> onComplete)
{
_initTime = DateTime.UtcNow;
// ----- First Open Time -----
// SetFirstOpenTime(GetFirstOpenTime()); // FirstOpenTime
LogI($"#1 ---- Guru SDK [{Version}] ----\n{config}");
Instance.StartWithConfig(config, onComplete);
}
/// <summary>
/// 启动SDK
/// </summary>
/// <param name="config"></param>
/// <param name="onComplete"></param>
private void StartWithConfig(GuruSDKInitConfig config, Action<bool> onComplete)
{
IsInitialSuccess = false;
_initConfig = config;
_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);
}
private void InitServices()
{
//---------- 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 ------------
StartFirebaseService();
LogI($"#2.1 --- InitFacebook ---");
//---------- Start Facebook ------------
FBService.Instance.StartService(Analytics.OnFBInitComplete);
IsInitialSuccess = true;
}
/// <summary>
/// 注入云控参数基础数据
/// </summary>
/// <returns></returns>
private string LoadDefaultGuruServiceJson()
{
// 加载本地 Services 配置值
var txtAsset = Resources.Load<TextAsset>(ServicesConfigKey);
if (txtAsset != null)
{
return txtAsset.text;
}
return "";
}
/// <summary>
/// 拉取云控参数完成
/// </summary>
/// <param name="success"></param>
private void OnFetchRemoteCallback(bool success)
{
LogI($"#6 --- Remote fetch complete: {success} ---");
ABTestManager.Init(); // 启动AB测试解析器
Callbacks.Remote.InvokeOnRemoteFetchComplete(success);
}
private void Update()
{
UpdateAllUpdates(); // 驱动所有的更新器
}
#endregion
#region App Remote Update
/// <summary>
/// Apply Cloud guru-service configs for sdk assets
/// </summary>
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)
{
_appServicesConfig = services;
useKeywords = _appServicesConfig.KeywordsEnabled();
// 使用初始化配置中的 IAPEnable来联合限定, 如果本地写死关闭则不走云控开启
useIAP = _initConfig.IAPEnabled && _appServicesConfig.IsIAPEnabled();
// enableAnaErrorLog = _appServicesConfig.EnableAnaErrorLog();
Try(() =>
{
LogI($"#4.1 --- Start apply services ---");
//----------------------------------------------------------------
// 自打点设置错误上报
// if(enableAnaErrorLog) GuruAnalytics.EnableErrorLog = true;
// adjust 事件设置
if (null != _appServicesConfig.adjust_settings && null != GuruSettings)
{
// 更新 Adjust Tokens
GuruSettings.UpdateAdjustTokens(
_appServicesConfig.adjust_settings.AndroidToken(),
_appServicesConfig.adjust_settings.iOSToken());
// 更新 Adjust Events
GuruSettings.UpdateAdjustEvents(_appServicesConfig.adjust_settings.events);
}
LogI($"#4.2 --- Start GuruSettings ---");
// GuruSettings 设置
if (null != _appServicesConfig.app_settings)
{
if (_appServicesConfig.Tch02Value() > 0)
{
Analytics.EnableTch02Event = true;
Analytics.SetTch02TargetValue(_appServicesConfig.Tch02Value());
}
// 设置获取设备 UUID 的方法
if (_appServicesConfig.UseUUID())
{
IPMConfig.UsingUUID = true; // 开始使用 UUID 作为 DeviceID 标识
}
#if UNITY_IOS
// 苹果审核标志位
appleReview = _appServicesConfig.IsAppReview();
#endif
if (null != GuruSettings)
{
// 更新和升级 GuruSettings 对应的值
GuruSettings.UpdateAppSettings(
_appServicesConfig.app_settings.bundle_id,
_appServicesConfig.fb_settings?.fb_app_id ?? "",
_appServicesConfig.app_settings.support_email,
_appServicesConfig.app_settings.privacy_url,
_appServicesConfig.app_settings.terms_url,
_appServicesConfig.app_settings.android_store,
_appServicesConfig.app_settings.ios_store,
_appServicesConfig.parameters?.using_uuid ?? false,
_appServicesConfig.parameters?.cdn_host ?? "");
_appBundleId = _appServicesConfig.app_settings.bundle_id; // 配置预设的 BundleId
}
}
//---------------------------------
}, ex =>
{
Debug.LogError($"--- ERROR on apply services: {ex.Message}");
});
}
//----------- Set IAP ----------------
if (useIAP)
{
// InitIAP(_initConfig.GoogleKeys, _initConfig.AppleRootCerts); // 初始化IAP
Try(() =>
{
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.SuccessLevelId); // 启动Keyword管理器
Try(() =>
{
LogI($"#4.4 --- Start Keywords ---");
KeywordsManager.Install(Model.IsIapUser, Model.BLevel); // 启动Keyword管理器
}, ex =>
{
Debug.LogError($"--- ERROR on Keywords: {ex.Message}");
});
}
#if UNITY_IOS
if (appleReview)
{
// StartAppleReviewFlow(); // 直接显示 ATT 弹窗, 跳过 Consent 流程
Try(() =>
{
LogI($"#4.5 --- StartAppleReviewFlow ---");
StartAppleReviewFlow(); // 直接显示 ATT 弹窗, 跳过 Consent 流程
}, ex =>
{
Debug.LogError($"--- ERROR on StartAppleReviewFlow: {ex.Message}");
});
return;
}
#endif
//----------- Set Consent ----------------
if (!InitConfig.UseCustomConsent && !appleReview)
{
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.InvokeOnGuruServiceReady();
}
/// <summary>
/// Get the guru-service cloud config value;
/// User can fetch the cloud guru-service config by using Custom Service Key
/// </summary>
/// <returns></returns>
private GuruServicesConfig GetRemoteServicesConfig()
{
string defaultJson = GetRemoteString(ServicesConfigKey);
bool useCustomKey = false;
string key = ServicesConfigKey;
if (!string.IsNullOrEmpty(_initConfig.CustomServiceKey))
{
key = _initConfig.CustomServiceKey;
useCustomKey = true;
}
var json = GetRemoteString(key); // Cloud cached data
if (string.IsNullOrEmpty(json) && useCustomKey && !string.IsNullOrEmpty(defaultJson))
{
// No remote data fetched from cloud, should use default values
json = defaultJson;
Debug.Log($"{Tag} --- No remote data found with: {key} -> Using default key {ServicesConfigKey} and local data!!!");
}
if (!string.IsNullOrEmpty(json))
{
return JsonParser.ToObject<GuruServicesConfig>(json);
}
return null;
}
private void Try(Action method, Action<Exception> onException = null, Action onFinal = null)
{
if (method == null) return;
try
{
method.Invoke();
}
catch (Exception ex)
{
LogEx(ex);
// ignored
onException?.Invoke(ex);
}
finally
{
// Finally
onFinal?.Invoke();
}
}
#endregion
#region Apple 审核流程逻辑
#if UNITY_IOS
private void StartAppleReviewFlow()
{
CheckAttStatus();
}
#endif
#endregion
#region Logging
private static void LogI(object message)
{
Debug.Log($"{Tag} {message}");
}
private static void LogW(object message)
{
Debug.LogWarning($"{Tag} {message}");
}
private static void LogE(object message)
{
Debug.LogError($"{Tag} {message}");
}
private static void LogEx(string message)
{
LogEx( new Exception($"{Tag} {message}"));
}
private static void LogEx(Exception e)
{
Debug.LogException(e);
}
/// <summary>
/// 上报崩溃信息
/// </summary>
/// <param name="message"></param>
public static void Report(string message)
{
Analytics.LogCrashlytics(message, false);
}
/// <summary>
/// 上报异常
/// </summary>
/// <param name="message"></param>
public static void ReportException(string message)
{
Analytics.LogCrashlytics(message);
}
/// <summary>
/// 上报异常 Exception
/// </summary>
/// <param name="ex"></param>
public static void ReportException(Exception ex)
{
Analytics.LogCrashlytics(ex);
}
#endregion
#region 生命周期
/// <summary>
/// 暂停时处理
/// </summary>
/// <param name="paused"></param>
private void OnAppPauseHandler(bool paused)
{
if(paused) Model.Save(true); // 强制保存数据
Callbacks.App.InvokeOnAppPaused(paused);
}
private void OnApplicationPause(bool paused)
{
OnAppPauseHandler(paused);
}
private void OnApplicationFocus(bool hasFocus)
{
OnAppPauseHandler(!hasFocus);
}
private void OnApplicationQuit()
{
Model.Save(true);
Callbacks.App.InvokeOnAppQuit();
}
#endregion
#region 延迟处理
/// <summary>
/// 启动协程
/// </summary>
/// <param name="enumerator"></param>
/// <returns></returns>
public static Coroutine DoCoroutine(IEnumerator enumerator)
{
return Instance != null ? Instance.StartCoroutine(enumerator) : null;
}
public static void KillCoroutine(Coroutine coroutine)
{
if(coroutine != null)
Instance.StopCoroutine(coroutine);
}
/// <summary>
/// 延时执行
/// </summary>
/// <param name="seconds"></param>
/// <param name="callback"></param>
public static Coroutine Delay(float seconds, Action callback)
{
return DoCoroutine(Instance.OnDelayCall(seconds, callback));
}
private IEnumerator OnDelayCall(float delay, Action callback)
{
if (delay > 0)
{
yield return new WaitForSeconds(delay);
}
else
{
yield return null;
}
callback?.Invoke();
}
#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

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 9eaf5abb3054d452ba2776169a31aa91
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

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.0",
"version": "1.1.0",
"description": "Guru SDK for unity project",
"unity": "2020.3",
"author":{
"name": "Guru Fungames Studio"
"name": "Guru Game"
},
"license": "MIT",
"category": "Game,Tool,Development",