Compare commits
45 Commits
hotfix/v1.
...
main
| Author | SHA1 | Date |
|---|---|---|
|
|
5c1f73fc18 | |
|
|
8a81ed78b4 | |
|
|
76fc4f5c26 | |
|
|
72bf076537 | |
|
|
19a46fff1e | |
|
|
4989926a47 | |
|
|
fe691235d6 | |
|
|
dc47cec8bd | |
|
|
2075f676b9 | |
|
|
660303e45d | |
|
|
2a895b370e | |
|
|
c0c557b34e | |
|
|
1256880b22 | |
|
|
e77994d811 | |
|
|
85dc4a7ddc | |
|
|
b7aacb61e4 | |
|
|
8cc083410d | |
|
|
81f37625c1 | |
|
|
1a9481b094 | |
|
|
2174bcf1a3 | |
|
|
602662881c | |
|
|
e36f7483a3 | |
|
|
7cbc5ac148 | |
|
|
e8c17f4cf4 | |
|
|
e8b3112cc5 | |
|
|
e0e78da9a3 | |
|
|
9e7e94ef36 | |
|
|
80e38bf85d | |
|
|
cfe81b5583 | |
|
|
34ae9e3f0b | |
|
|
eba48e4a75 | |
|
|
a53c153338 | |
|
|
ffcb846a64 | |
|
|
dbb56e5a32 | |
|
|
b6e038a027 | |
|
|
4e24169b25 | |
|
|
9024b8171c | |
|
|
f5747977ec | |
|
|
d3028d9e3b | |
|
|
a9a438b288 | |
|
|
523115bdb9 | |
|
|
f360529552 | |
|
|
f96e506a19 | |
|
|
c59f76aead | |
|
|
ba90a32195 |
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"name": "Guru.Editor",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"Guru.Runtime"
|
||||
"Guru.Runtime",
|
||||
"Guru.Notification"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@ namespace Guru.Editor
|
|||
|
||||
|
||||
|
||||
|
||||
[PostProcessBuild(47)] // MAX POD Process Order
|
||||
// Firebase 10.20.0 fixed to 10.22.0. BUT higher version do not open this ATTRIBUTE !!
|
||||
// [PostProcessBuild(47)] // MAX POD Process Order
|
||||
public static void PostBuildFixPodDeps(BuildTarget target, string projPath)
|
||||
{
|
||||
if (target != BuildTarget.iOS) return;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ namespace Guru
|
|||
/// </summary>
|
||||
public class IOSPostBuildSwift
|
||||
{
|
||||
[PostProcessBuild(40)]
|
||||
[PostProcessBuild(2000)]
|
||||
public static void OnPostProcessBuild(BuildTarget target, string buildPath)
|
||||
{
|
||||
if (target != BuildTarget.iOS) return;
|
||||
|
|
@ -43,7 +43,7 @@ namespace Guru
|
|||
|
||||
// 设置主项目的SWIFT构建支持
|
||||
project.SetBuildProperty(mainTargetGuid, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", "YES");
|
||||
|
||||
project.SetBuildProperty(frameworkTargetGuid, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", "NO");
|
||||
|
||||
project.WriteToFile(projectPath);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
"MaxSdk",
|
||||
"MaxSdk.Scripts",
|
||||
"Amazon",
|
||||
"Amazon.Scripts",
|
||||
"OpenWrapSDK",
|
||||
"UniWebView-CSharp",
|
||||
"UnityEngine.Purchasing",
|
||||
|
|
@ -17,7 +18,9 @@
|
|||
"Google.Play.Review",
|
||||
"Google.Play.Common",
|
||||
"Guru.LitJson",
|
||||
"Unity.Advertisement.IosSupport"
|
||||
"Unity.Advertisement.IosSupport",
|
||||
"Unity.Notifications.Android",
|
||||
"Unity.Notifications.iOS"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
|
||||
|
||||
|
||||
namespace Guru
|
||||
{
|
||||
using UnityEngine;
|
||||
using com.adjust.sdk;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
public static class AdjustService
|
||||
public class AdjustService
|
||||
{
|
||||
public const string Version = "1.6.1";
|
||||
public const string AdjustVersion = "4.38.0"; // Adjust SDK Version
|
||||
|
|
@ -14,6 +17,8 @@ namespace Guru
|
|||
|
||||
public const string K_IAP_PURCHASE = "iap_purchase"; // 固定点位事件
|
||||
public const string K_SUB_PURCHASE = "sub_purchase"; // 固定点位事件
|
||||
|
||||
private static Action<string> _onSessionSuccessCallback;
|
||||
|
||||
|
||||
private static string _adId = "";
|
||||
|
|
@ -45,7 +50,7 @@ namespace Guru
|
|||
/// </summary>
|
||||
/// <param name="appToken"></param>
|
||||
/// <param name="fbAppId">MIR 追踪 AppID</param>
|
||||
public static void StartService(string appToken, string fbAppId = "")
|
||||
public static void StartService(string appToken, string fbAppId = "", Action<string> onSessionSuccess = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(appToken))
|
||||
{
|
||||
|
|
@ -53,6 +58,8 @@ namespace Guru
|
|||
return;
|
||||
}
|
||||
|
||||
_onSessionSuccessCallback = onSessionSuccess;
|
||||
|
||||
InstallEvent(IPMConfig.FIREBASE_ID, IPMConfig.IPM_DEVICE_ID); // 注入启动参数
|
||||
|
||||
AdjustEnvironment environment = GetAdjustEnvironment();
|
||||
|
|
@ -61,6 +68,7 @@ namespace Guru
|
|||
config.setDelayStart(DelayTime);
|
||||
|
||||
config.setPreinstallTrackingEnabled(true); // Adjust Preinstall
|
||||
config.setSessionSuccessDelegate(OnSessionSuccessCallback); // SessionSuccess
|
||||
|
||||
#if UNITY_ANDROID
|
||||
if (!string.IsNullOrEmpty(fbAppId)) config.setFbAppId(fbAppId); // 注入 MIR ID
|
||||
|
|
@ -71,7 +79,7 @@ namespace Guru
|
|||
config.setLogDelegate(log => LogI(LOG_TAG, log));
|
||||
config.setEventSuccessDelegate(OnEventSuccessCallback);
|
||||
config.setEventFailureDelegate(OnEventFailureCallback);
|
||||
config.setSessionSuccessDelegate(OnSessionSuccessCallback);
|
||||
|
||||
config.setSessionFailureDelegate(OnSessionFailureCallback);
|
||||
config.setAttributionChangedDelegate(OnAttributionChangedCallback);
|
||||
#endif
|
||||
|
|
@ -272,27 +280,10 @@ namespace Guru
|
|||
|
||||
private static void OnSessionSuccessCallback(AdjustSessionSuccess sessionSuccessData)
|
||||
{
|
||||
LogI(LOG_TAG,"Session tracked successfully!");
|
||||
LogI(LOG_TAG,$"{LOG_TAG} --- Session tracked successfully!");
|
||||
|
||||
if (sessionSuccessData.Message != null)
|
||||
{
|
||||
LogI(LOG_TAG,"Message: " + sessionSuccessData.Message);
|
||||
}
|
||||
|
||||
if (sessionSuccessData.Timestamp != null)
|
||||
{
|
||||
LogI(LOG_TAG,"Timestamp: " + sessionSuccessData.Timestamp);
|
||||
}
|
||||
|
||||
if (sessionSuccessData.Adid != null)
|
||||
{
|
||||
LogI(LOG_TAG, "Adid: " + sessionSuccessData.Adid);
|
||||
}
|
||||
|
||||
if (sessionSuccessData.JsonResponse != null)
|
||||
{
|
||||
LogI(LOG_TAG, "JsonResponse: " + sessionSuccessData.GetJsonResponse());
|
||||
}
|
||||
var adid = sessionSuccessData.Adid;
|
||||
_onSessionSuccessCallback?.Invoke(adid);
|
||||
}
|
||||
|
||||
private static void OnSessionFailureCallback(AdjustSessionFailure sessionFailureData)
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ namespace Guru
|
|||
/// <summary>
|
||||
/// 初始化平台
|
||||
/// </summary>
|
||||
public void Initialize()
|
||||
public void Initialize(bool isDebug = false)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"<color=orange>=== Amazon will not init on Editor ===</color>");
|
||||
|
|
@ -93,11 +93,9 @@ namespace Guru
|
|||
// 初始化Amazon
|
||||
Amazon.Initialize (AmazonAppID);
|
||||
Amazon.SetAdNetworkInfo(new AdNetworkInfo(DTBAdNetwork.MAX));
|
||||
#if UNITY_EDITOR || DEBUG
|
||||
Amazon.EnableTesting (true); // Make sure to take this off when going live.
|
||||
#else
|
||||
Amazon.EnableLogging (false);
|
||||
#endif
|
||||
Debug.Log($"[Ads] --- Amazon init start isDebug:{isDebug}, AmazonID:{AmazonAppID}");
|
||||
Amazon.EnableTesting (isDebug); // Make sure to take this off when going live.
|
||||
Amazon.EnableLogging (isDebug);
|
||||
|
||||
#if UNITY_IOS
|
||||
Amazon.SetAPSPublisherExtendedIdFeatureEnabled(true);
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ namespace Guru
|
|||
* before it can request an ad using OpenWrap SDK.
|
||||
* The storeURL is the URL where users can download your app from the App Store/Google Play Store.
|
||||
*/
|
||||
public void Initialize()
|
||||
public void Initialize(bool isDebug = false)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"<color=orange>=== PubMatic will not init on Editor ===</color>");
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 07cf2335bd298401b8015718fca55265
|
||||
guid: 32eda01e213614348899eefe856392d3
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
Binary file not shown.
|
|
@ -1,3 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: caf6ff09835a4a75bff1b4b068f664ef
|
||||
timeCreated: 1717117895
|
||||
Binary file not shown.
|
|
@ -0,0 +1,32 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 66c5f430ab9654ef4a2376e71aa04bca
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Android: Android
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -7,15 +7,29 @@ GuruAnalyticsLib 的 Unity 插件库
|
|||
- 插件库内的 .aar 通过 [guru_analytics](https://github.com/castbox/guru_analytics) 项目直接构建 ( 命令 `gradle publishToMavenLocal` )
|
||||
- 构建后请改名为 `guru-analytics-{version}.aar`
|
||||
- 请将 .aar 文件放置于 `./Runtime/GuruAnalytics/Plugins/Android` 目录下
|
||||
- **iOS**
|
||||
- 插件库内的文件 通过 [GuruAnalytics_iOS](https://github.com/castbox/GuruAnalytics_iOS) 项目
|
||||
- (1) 请将 repo 内的两个文件夹 `Assets` 和 `Classses` 拷贝至 `./Runtime/GuruAnalytics/Plugins/iOS/GuruAnalytics` 目录下:
|
||||
- (2) 请将部署到 Unity 内所有的 `.swift` 文件的 meta 属性内, 取消 iOS 文件属性. (因为打包时会按照 POD 导入)
|
||||
- 注意及时更新 `GuruAnalyticsLib.podspec`文件内的更新内容
|
||||
- **iOS**
|
||||
- 插件库内的文件 通过 [GuruAnalytics_iOS](https://github.com/castbox/GuruAnalytics_iOS) 项目
|
||||
- (1) 请将 repo 内的两个文件夹 `Assets` 和 `Classses` 拷贝至 `./Runtime/GuruAnalytics/Plugins/iOS/GuruAnalytics` 目录下:
|
||||
- (2) 请将部署到 Unity 内所有的 `.swift` 文件的 meta 属性内, 取消 iOS 文件属性. (因为打包时会按照 POD 导入)
|
||||
- 注意及时更新 `GuruAnalyticsLib.podspec`文件内的更新内容
|
||||
```ruby
|
||||
# 将 source 内的 git 属性删除, 只保留 tag 属性
|
||||
# s.source = { :git => 'git@github.com:castbox/GuruAnalytics_iOS.git', :tag => s.version.to_s }
|
||||
s.source = { :tag => s.version.to_s }
|
||||
```
|
||||
---
|
||||
|
||||
## Change Logs
|
||||
|
||||
### 1.12.0
|
||||
- Android 端对齐 `1.1.1`
|
||||
> Hash: bdb41ae118dcf438e8efe4f27d0ec856bc3147b0
|
||||
- iOS 端对齐 `0.3.6`
|
||||
> Hash: 0cd5ce7aa64e12caa7413c938a3164687b973843
|
||||
- Pod 库改为 本地文件引用 (配合外部发行项目)
|
||||
|
||||
|
||||
|
||||
### 1.11.0
|
||||
- Android 端对齐 `1.0.3`
|
||||
> Hash: 1978686dbcba38b7b0421d8b6b2bef111356366b
|
||||
|
|
|
|||
|
|
@ -14,10 +14,11 @@ namespace Guru
|
|||
public class GuruAnalytics
|
||||
{
|
||||
// Plugin Version
|
||||
public const string Version = "1.11.0";
|
||||
public const string Version = "1.10.5";
|
||||
|
||||
public static readonly string Tag = "[ANU]";
|
||||
private static readonly string ActionName = "logger_error";
|
||||
internal const int EventPriorityDefault = 10;
|
||||
|
||||
private static IAnalyticsAgent _agent;
|
||||
|
||||
|
|
@ -200,7 +201,8 @@ namespace Guru
|
|||
/// </summary>
|
||||
/// <param name="eventName">事件名称</param>
|
||||
/// <param name="data">INT类型的值</param>
|
||||
public static void LogEvent(string eventName, Dictionary<string, dynamic> data = null)
|
||||
/// <param name="priority"></param>
|
||||
public static void LogEvent(string eventName, Dictionary<string, dynamic> data = null, int priority = -1)
|
||||
{
|
||||
if(_autoSyncProperties)
|
||||
UpdateAllUserProperties(); // 每次打点更新用户属性
|
||||
|
|
@ -210,8 +212,9 @@ namespace Guru
|
|||
{
|
||||
raw = BuildParamsJson(data);
|
||||
}
|
||||
Debug.Log($"{Tag} event:{eventName} | raw: {raw}");
|
||||
Agent?.LogEvent(eventName, raw);
|
||||
if (priority < 0) priority = EventPriorityDefault;
|
||||
Debug.Log($"{Tag} event:{eventName} | raw: {raw} | priority: {priority}");
|
||||
Agent?.LogEvent(eventName, raw, priority);
|
||||
}
|
||||
|
||||
private static string BuildParamsString(Dictionary<string, dynamic> data)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ namespace Guru
|
|||
void SetUid(string uid);
|
||||
bool IsDebug { get; }
|
||||
bool EnableErrorLog { get; set; }
|
||||
void LogEvent(string eventName, string parameters);
|
||||
void LogEvent(string eventName, string parameters, int priority = -1);
|
||||
void ReportEventSuccessRate(); // 上报任务成功率
|
||||
void SetTch02Value(double value); // 设置太极02数值
|
||||
void InitCallback(string objName, string method); // 设置回调对象参数
|
||||
|
|
|
|||
|
|
@ -80,7 +80,8 @@ namespace Guru
|
|||
{
|
||||
_isDebug = isDebug;
|
||||
string bundleId = Application.identifier;
|
||||
CallStatic("init", appId, deviceInfo, bundleId, UseWorker, isDebug, UseCronet, BaseUrl); // 调用接口
|
||||
// public static void init(String appId, String deviceInfo, String bundleId, boolean isDebug, boolean useWorker, boolean useCronet, String baseUrl)
|
||||
CallStatic("init", appId, deviceInfo, bundleId, isDebug, UseWorker, UseCronet, BaseUrl); // 调用接口
|
||||
}
|
||||
|
||||
public void SetScreen(string screenName)
|
||||
|
|
@ -124,7 +125,10 @@ namespace Guru
|
|||
}
|
||||
|
||||
public bool IsDebug => CallStatic<bool>("isDebug");
|
||||
public void LogEvent(string eventName, string parameters) => CallStatic("logEvent", eventName, parameters);
|
||||
public void LogEvent(string eventName, string parameters, int priority = -1)
|
||||
{
|
||||
CallStatic("logEvent", eventName, parameters, priority);
|
||||
}
|
||||
public void ReportEventSuccessRate() => CallStatic("reportEventRate");
|
||||
public void SetTch02Value(double value) => CallStatic("setTch02Value", value);
|
||||
public void InitCallback(string objName, string method) => CallStatic("initCallback", objName, method);
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ namespace Guru
|
|||
|
||||
public bool IsDebug => _isDebug;
|
||||
|
||||
public void LogEvent(string eventName, string data)
|
||||
public void LogEvent(string eventName, string data, int priority = -1)
|
||||
{
|
||||
#if UNITY_IOS
|
||||
unityLogEvent(eventName, data);
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ namespace Guru
|
|||
public bool IsDebug => _isDebug;
|
||||
|
||||
|
||||
public void LogEvent(string eventName, string parameters)
|
||||
public void LogEvent(string eventName, string parameters, int priority = -1)
|
||||
{
|
||||
if (_isShowLog)
|
||||
{
|
||||
|
|
@ -127,7 +127,7 @@ namespace Guru
|
|||
}
|
||||
}
|
||||
|
||||
Debug.Log($"{TAG} LogEvent: event:<color=orange>{eventName}</color> Properties:\n{sb.ToString()}");
|
||||
Debug.Log($"{TAG} LogEvent: event:<color=orange>{eventName} ({priority})</color> Properties:\n{sb.ToString()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ namespace Guru
|
|||
protected override void InitService()
|
||||
{
|
||||
base.InitService();
|
||||
InitChannels(); // 启动各广告渠道代理
|
||||
InitChannels(_isDebug); // 启动各广告渠道代理
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
@ -37,14 +37,14 @@ namespace Guru
|
|||
/// <summary>
|
||||
/// 各渠道初始化
|
||||
/// </summary>
|
||||
private void InitChannels()
|
||||
private void InitChannels(bool isDebug)
|
||||
{
|
||||
_adChannels = new HashSet<IAdChannel>();
|
||||
IAdChannel channel = null;
|
||||
_asyncLoader = null;
|
||||
|
||||
_chanelMax = new AdChanelMax(); // 默认持有MAXChannel
|
||||
_chanelMax.Initialize();
|
||||
_chanelMax.Initialize(isDebug);
|
||||
if(_initSpec != null) _chanelMax.SetBannerBackColor(_initSpec.bannerColorHex);
|
||||
|
||||
//------------ 以下为扩展的广告渠道 ------------------
|
||||
|
|
@ -52,7 +52,7 @@ namespace Guru
|
|||
// 开启渠道需要添加对应的宏
|
||||
|
||||
channel = new AdChanelAmazon();
|
||||
channel.Initialize();
|
||||
channel.Initialize(isDebug);
|
||||
_adChannels.Add(channel); // Amazon
|
||||
_asyncLoader = channel as IAsyncRequestChannel;
|
||||
if (_asyncLoader != null)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Guru;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Guru
|
||||
{
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public abstract class ADServiceBase<T> : IADService where T : new()
|
||||
{
|
||||
// 单利定义
|
||||
|
|
@ -23,6 +22,8 @@ namespace Guru
|
|||
public bool IsInitialized => MaxSdk.IsInitialized() || _isServiceStarted;
|
||||
protected bool IsNetworkEnabled => Application.internetReachability != NetworkReachability.NotReachable;
|
||||
|
||||
private const int MAX_ADS_RELOAD_INTERVAL = 6; // 广告加载最高时间为 2 的 6 次方 = 64秒
|
||||
|
||||
private bool _isServiceStarted;
|
||||
|
||||
protected Action _onSdkInitReady;
|
||||
|
|
@ -33,10 +34,12 @@ namespace Guru
|
|||
public static Action<string> OnInterstitialStartLoad;
|
||||
public static Action OnInterstitialLoaded;
|
||||
public static Action OnInterstitialFailed;
|
||||
public static Action OnInterstitialClosed;
|
||||
|
||||
public static Action<string> OnRewardedStartLoad;
|
||||
public static Action OnRewardLoaded;
|
||||
public static Action OnRewardFailed;
|
||||
public static Action OnRewardClosed;
|
||||
|
||||
protected AdsModel _model;
|
||||
protected AdsInitSpec _initSpec = null;
|
||||
|
|
@ -50,6 +53,7 @@ namespace Guru
|
|||
}
|
||||
}
|
||||
|
||||
internal bool _isDebug = false;
|
||||
|
||||
/// <summary>
|
||||
/// 启动广告服务
|
||||
|
|
@ -62,11 +66,23 @@ namespace Guru
|
|||
if (IsInitialized) return; // 已经初始化后, 无需再次初始化
|
||||
|
||||
_initSpec = initSpec;
|
||||
if (_initSpec == null) _initSpec = AdsInitSpec.BuildDefault();
|
||||
|
||||
_isDebug = _initSpec.isDebug;
|
||||
_isServiceStarted = true;
|
||||
_onSdkInitReady = callback;
|
||||
if(_model == null) _model = AdsModel.Create();
|
||||
this.Log("AD SDK Start Init");
|
||||
|
||||
InitMaxCallbacks(); // 初始化 MAX 广告
|
||||
InitService(); // 内部继承接口
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 MAX 广告组件
|
||||
/// </summary>
|
||||
private void InitMaxCallbacks()
|
||||
{
|
||||
//-------------- 初始化回调 ------------------
|
||||
MaxSdkCallbacks.OnSdkInitializedEvent += OnMaxSdkInitializedCallBack;
|
||||
MaxSdkCallbacks.Interstitial.OnAdRevenuePaidEvent += OnAdRevenuePaidEvent;
|
||||
|
|
@ -94,10 +110,7 @@ namespace Guru
|
|||
MaxSdkCallbacks.Rewarded.OnAdReceivedRewardEvent += OnRewardedAdReceivedRewardEvent;
|
||||
|
||||
//-------------- SDK 初始化 -------------------
|
||||
if (_initSpec == null) _initSpec = AdsInitSpec.BuildDefault();
|
||||
MaxSdk.SetVerboseLogging(_initSpec.isDebug);
|
||||
|
||||
InitService(); // 内部继承接口
|
||||
MaxSdk.SetExtraParameter("enable_black_screen_fixes", "true"); // 修复黑屏
|
||||
}
|
||||
|
||||
protected virtual void InitService()
|
||||
|
|
@ -138,6 +151,13 @@ namespace Guru
|
|||
set => Model.BuyNoAds = value;
|
||||
}
|
||||
|
||||
private float GetRetryDelaySeconds(int retryCount)
|
||||
{
|
||||
// 最低 2^retryCount 秒
|
||||
// 最高 2^6 = 64 秒
|
||||
return (float)Math.Pow(2, Math.Min(MAX_ADS_RELOAD_INTERVAL, retryCount));
|
||||
}
|
||||
|
||||
#region Lifecycele
|
||||
|
||||
public void OnAppPaused(bool paused)
|
||||
|
|
@ -417,7 +437,7 @@ namespace Guru
|
|||
private string _iadsCategory = "main";
|
||||
private int _interstitialRetryAttempt;
|
||||
protected float _iadsLoadStartTime;
|
||||
private Action _interstitialDismissAction;
|
||||
private Action _interCloseAction;
|
||||
protected bool _isIadsLoading = false;
|
||||
public bool IsIadsLoading => _isIadsLoading;
|
||||
|
||||
|
|
@ -473,7 +493,7 @@ namespace Guru
|
|||
}
|
||||
|
||||
_iadsCategory = category;
|
||||
_interstitialDismissAction = dismissAction;
|
||||
_interCloseAction = dismissAction;
|
||||
MaxSdk.ShowInterstitial(GetInterstitialID());
|
||||
|
||||
// RequestInterstitialAD(); // 直接加载下一个广告
|
||||
|
|
@ -500,8 +520,8 @@ namespace Guru
|
|||
this.LogError(
|
||||
$"OnInterstitialFailedEvent AdLoadFailureInfo:{errorInfo.AdLoadFailureInfo}, Message: {errorInfo.Message}");
|
||||
_interstitialRetryAttempt++;
|
||||
double retryDelay = Math.Pow(2, Math.Min(3, _interstitialRetryAttempt));
|
||||
DelayCall((float)retryDelay, RequestInterstitialAD);
|
||||
float retryDelay = GetRetryDelaySeconds(_interstitialRetryAttempt);
|
||||
DelayCall(retryDelay, RequestInterstitialAD);
|
||||
// Analytics.ADIadsFailed(adUnitId, (int)errorInfo.Code, GetAdsLoadDuration(ref _iadsLoadStartTime), _iadsCategory);
|
||||
Analytics.ADIadsFailed(AdParams.Build(adUnitId,
|
||||
duration: GetAdsLoadDuration(ref _iadsLoadStartTime), category: _iadsCategory,
|
||||
|
|
@ -540,7 +560,8 @@ namespace Guru
|
|||
protected virtual void OnInterstitialDismissedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
|
||||
{
|
||||
// Interstitial ad is hidden. Pre-load the next ad
|
||||
_interstitialDismissAction?.Invoke();
|
||||
_interCloseAction?.Invoke();
|
||||
OnInterstitialClosed?.Invoke();
|
||||
// Analytics.ADIadsClose(adUnitId, _iadsCategory);
|
||||
Analytics.ADIadsClose(AdParams.Build(adUnitId, category: _iadsCategory));
|
||||
//延时加载下一个广告
|
||||
|
|
@ -554,9 +575,9 @@ namespace Guru
|
|||
private string _rewardCategory = "main";
|
||||
private int _rewardRetryAttempt;
|
||||
protected float _radsLoadStartTime;
|
||||
private Action _rewardAction;
|
||||
private Action<string> _failAction;
|
||||
private Action _dismissAction;
|
||||
private Action _rvRewardAction;
|
||||
private Action<string> _rvFailAction;
|
||||
private Action _rvDismissAction;
|
||||
protected bool _isRadsLoading = false;
|
||||
public bool IsRadsLoading => _isRadsLoading;
|
||||
|
||||
|
|
@ -620,9 +641,9 @@ namespace Guru
|
|||
}
|
||||
|
||||
_rewardCategory = category;
|
||||
_rewardAction = rewardAction;
|
||||
_failAction = failAction;
|
||||
_dismissAction = dismissAction;
|
||||
_rvRewardAction = rewardAction;
|
||||
_rvFailAction = failAction;
|
||||
_rvDismissAction = dismissAction;
|
||||
MaxSdk.ShowRewardedAd(GetRewardedID());
|
||||
|
||||
// RequestRewardedAD();
|
||||
|
|
@ -657,8 +678,8 @@ namespace Guru
|
|||
errorCode: (int)errorInfo.Code,
|
||||
waterfallName: errorInfo?.WaterfallInfo?.Name ?? ""));
|
||||
_rewardRetryAttempt++;
|
||||
double retryDelay = Math.Pow(2, Math.Min(3, _rewardRetryAttempt));
|
||||
DelayCall((float)retryDelay, RequestRewardedAD);
|
||||
float retryDelay = GetRetryDelaySeconds(_rewardRetryAttempt);
|
||||
DelayCall(retryDelay, RequestRewardedAD);
|
||||
|
||||
OnRewardFailed?.Invoke();
|
||||
}
|
||||
|
|
@ -674,7 +695,7 @@ namespace Guru
|
|||
duration: GetAdsLoadDuration(ref _radsLoadStartTime), category: _rewardCategory,
|
||||
errorCode: (int)errorInfo.Code,
|
||||
waterfallName: errorInfo?.WaterfallInfo?.Name ?? ""));
|
||||
_failAction?.Invoke("OnRewardedAdFailedToDisplayEvent");
|
||||
_rvFailAction?.Invoke("OnRewardedAdFailedToDisplayEvent");
|
||||
DelayCall(2.0f, RequestRewardedAD);
|
||||
|
||||
OnRewardFailed?.Invoke();
|
||||
|
|
@ -697,11 +718,17 @@ namespace Guru
|
|||
protected virtual void OnRewardedAdDismissedEvent(string adUnitId, MaxSdkBase.AdInfo adInfo)
|
||||
{
|
||||
this.Log("OnRewardedAdDismissedEvent");
|
||||
|
||||
_rvDismissAction?.Invoke();
|
||||
OnRewardClosed?.Invoke();
|
||||
|
||||
// Analytics.ADRadsClose(adUnitId, _rewardCategory);
|
||||
Analytics.ADRadsClose(AdParams.Build(adUnitId, category: _rewardCategory));
|
||||
_dismissAction?.Invoke();
|
||||
|
||||
//延时加载下一个广告
|
||||
DelayCall(2.0f, RequestRewardedAD);
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected virtual void OnRewardedAdReceivedRewardEvent(string adUnitId, MaxSdk.Reward reward,
|
||||
|
|
@ -711,7 +738,7 @@ namespace Guru
|
|||
// Analytics.ADRadsRewarded(adUnitId, _rewardCategory);
|
||||
Analytics.ADRadsRewarded(AdParams.Build(adUnitId, category: _rewardCategory));
|
||||
// Rewarded ad was displayed and user should receive the reward
|
||||
_rewardAction?.Invoke();
|
||||
_rvRewardAction?.Invoke();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
|||
|
|
@ -32,10 +32,11 @@ namespace Guru
|
|||
/// <summary>
|
||||
/// MAX 渠道初始化, 启动服务
|
||||
/// </summary>
|
||||
public void Initialize()
|
||||
public void Initialize(bool isDebug = false)
|
||||
{
|
||||
MaxSdk.SetSdkKey(GuruSettings.Instance.ADSetting.SDK_KEY);
|
||||
MaxSdk.SetUserId(IPMConfig.IPM_UID); // 上报用户ID
|
||||
MaxSdk.SetVerboseLogging(isDebug); // 设置调试数据
|
||||
MaxSdk.InitializeSdk();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ namespace Guru
|
|||
{
|
||||
// Action<string> OnRequestOver { get; set; }
|
||||
|
||||
void Initialize();
|
||||
void Initialize(bool isDebug = false);
|
||||
|
||||
string Name { get;}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ namespace Guru
|
|||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
UnityEngine.Debug.Log($"{TAG} --- Send Adjust Event: {eventName}({tokenID})");
|
||||
return new AdjustEvent(tokenID);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -224,17 +224,17 @@ namespace Guru
|
|||
/// </summary>
|
||||
/// <param name="status"></param>
|
||||
/// <param name="channel"></param>
|
||||
/// <param name="others"></param>
|
||||
public static void AttResult(string status, string type = "custom", string others = "")
|
||||
/// <param name="scene"></param>
|
||||
public static void AttResult(string status, string type = "custom", string scene = "")
|
||||
{
|
||||
SetAttProperty(status);
|
||||
Debug.Log($"{TAG} AttResult: {status} type:{type} others:{others}");
|
||||
Debug.Log($"{TAG} AttResult: {status} type:{type} others:{scene}");
|
||||
var dict = new Dictionary<string, dynamic>()
|
||||
{
|
||||
{ ParameterItemCategory, status },
|
||||
{ "type", type }
|
||||
};
|
||||
if(!string.IsNullOrEmpty(others)) dict[ParameterItemName] = others;
|
||||
if(!string.IsNullOrEmpty(scene)) dict[ParameterItemName] = scene;
|
||||
LogEvent(EventATTResult, dict);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ namespace Guru
|
|||
// 美元符号
|
||||
public static readonly string USD = "USD";
|
||||
// 广告平台
|
||||
public static readonly string AdMAX = "MAX";
|
||||
public static readonly string AdMAX = "MAX";
|
||||
|
||||
//IAP打点事件
|
||||
public static readonly string EventIAPFirst = "first_iap";
|
||||
|
|
|
|||
|
|
@ -309,12 +309,12 @@ namespace Guru
|
|||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="data"></param>
|
||||
private static void CustomLogEvent(string key, Dictionary<string, dynamic> data = null)
|
||||
private static void CustomLogEvent(string key, Dictionary<string, dynamic> data = null, int priority = -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (data == null) data = new Dictionary<string, dynamic>();
|
||||
GuruAnalytics.LogEvent(key, data);
|
||||
GuruAnalytics.LogEvent(key, data, priority);
|
||||
UpdateAllValues(); // 同步所有的ID
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
|
|||
|
|
@ -295,9 +295,11 @@ namespace Guru
|
|||
/// <param name="orderId"></param>
|
||||
/// <param name="orderType"></param>
|
||||
/// <param name="timestamp"></param>
|
||||
public static void Tch001IAPRev(double value, string productId, string orderId, string orderType, string timestamp)
|
||||
/// <param name="isTest"></param>
|
||||
public static void Tch001IAPRev(double value, string productId, string orderId, string orderType, string timestamp, bool isTest = false)
|
||||
{
|
||||
TchRevEvent(EventTchAdRev001Impression, IAPPlatform, value, orderType, productId, orderId, timestamp);
|
||||
string sandbox = isTest ? "true" : "false";
|
||||
TchRevEvent(EventTchAdRev001Impression, IAPPlatform, value, orderType, productId, orderId, timestamp, sandbox);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -309,11 +311,13 @@ namespace Guru
|
|||
/// <param name="orderId"></param>
|
||||
/// <param name="orderType"></param>
|
||||
/// <param name="timestamp"></param>
|
||||
/// <param name="isTest"></param>
|
||||
// public static void Tch02IAPRev(double value, string productId, string orderId, string orderType, string timestamp)
|
||||
public static void Tch02IAPRev(double value, string productId, string orderId, string orderType, string timestamp)
|
||||
public static void Tch02IAPRev(double value, string productId, string orderId, string orderType, string timestamp, bool isTest = false)
|
||||
{
|
||||
if (!EnableTch02Event) return;
|
||||
TchRevEvent(EventTchAdRev02Impression, IAPPlatform, value, orderType, productId, orderId, timestamp);
|
||||
string sandbox = isTest ? "true" : "false";
|
||||
TchRevEvent(EventTchAdRev02Impression, IAPPlatform, value, orderType, productId, orderId, timestamp, sandbox);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -362,8 +366,9 @@ namespace Guru
|
|||
/// <param name="productId"></param>
|
||||
/// <param name="orderId"></param>
|
||||
/// <param name="timestamp"></param>
|
||||
/// <param name="sandbox"></param>
|
||||
private static void TchRevEvent(string evtName, string platform, double value,
|
||||
string orderType = "", string productId = "", string orderId = "", string timestamp = "")
|
||||
string orderType = "", string productId = "", string orderId = "", string timestamp = "", string sandbox = "")
|
||||
{
|
||||
var data = new Dictionary<string, dynamic>()
|
||||
{
|
||||
|
|
@ -373,10 +378,13 @@ namespace Guru
|
|||
};
|
||||
|
||||
//--------- Extra data for IAP receipt ---------------
|
||||
|
||||
if(!string.IsNullOrEmpty(orderType)) data["order_type"] = orderType;
|
||||
if(!string.IsNullOrEmpty(productId)) data["product_id"] = productId;
|
||||
if(!string.IsNullOrEmpty(orderId)) data["order_id"] = orderId;
|
||||
if(!string.IsNullOrEmpty(timestamp)) data["trans_ts"] = timestamp;
|
||||
if(!string.IsNullOrEmpty(sandbox)) data["sandbox"] = sandbox;
|
||||
|
||||
//--------- Extra data for IAP receipt ---------------
|
||||
|
||||
LogEvent(evtName, data);
|
||||
|
|
@ -603,15 +611,19 @@ namespace Guru
|
|||
/// </summary>
|
||||
/// <param name="productId"></param>
|
||||
/// <param name="usdPrice"></param>
|
||||
/// <param name="userCurrency"></param>
|
||||
/// <param name="payPrice"></param>
|
||||
/// <param name="orderId"></param>
|
||||
/// <param name="orderType"></param>
|
||||
/// <param name="orderDate"></param>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="isFree"></param>
|
||||
public static void ReportIAPSuccessEvent(double usdPrice, string productId, BaseOrderData orderData)
|
||||
/// <param name="orderData"></param>
|
||||
/// <param name="isTest"></param>
|
||||
public static void ReportIAPSuccessEvent(BaseOrderData orderData, double usdPrice, bool isTest = false)
|
||||
{
|
||||
if (orderData == null) return;
|
||||
|
||||
if (!isTest && usdPrice == 0)
|
||||
{
|
||||
Debug.Log($"[SDK] --- Pruchase value is 0, skip report orders");
|
||||
return;
|
||||
}
|
||||
|
||||
string productId = orderData.productId;
|
||||
string userCurrency = orderData.userCurrency;
|
||||
double payPrice = orderData.payPrice;
|
||||
string orderType = orderData.OrderType();
|
||||
|
|
@ -621,7 +633,7 @@ namespace Guru
|
|||
string scene = orderData.scene;
|
||||
bool isFree = orderData.isFree;
|
||||
string offerId = orderData.offerId;
|
||||
|
||||
|
||||
string transactionId = "";
|
||||
string productToken = "";
|
||||
string receipt = "";
|
||||
|
|
@ -636,21 +648,23 @@ namespace Guru
|
|||
}
|
||||
|
||||
// TCH 001
|
||||
Tch001IAPRev(usdPrice, productId, orderId, orderType, orderDate);
|
||||
Tch001IAPRev(usdPrice, productId, orderId, orderType, orderDate, isTest);
|
||||
|
||||
// TCH 020
|
||||
Tch02IAPRev(usdPrice, productId, orderId, orderType, orderDate);
|
||||
Tch02IAPRev(usdPrice, productId, orderId, orderType, orderDate, isTest);
|
||||
|
||||
// Facebook Track IAP Purchase
|
||||
FBPurchase(usdPrice, USD, "iap", IAPPlatform);
|
||||
|
||||
if (orderData.orderType == 1)
|
||||
{
|
||||
// sub_pruchase : Firebase + Guru + Adjust
|
||||
SubPurchase(usdPrice, productId, orderId, orderDate, productToken, receipt);
|
||||
SubPurchase(usdPrice, productId, orderId, orderDate, productToken, receipt, isTest);
|
||||
}
|
||||
else
|
||||
{
|
||||
// iap_purchase : Firebase + Guru + Adjust
|
||||
IAPPurchase(usdPrice, productId, orderId, orderDate, productToken, receipt);
|
||||
IAPPurchase(usdPrice, productId, orderId, orderDate, productToken, receipt, isTest);
|
||||
}
|
||||
|
||||
// IAP Ret true : Firebase + Guru + Adjust
|
||||
|
|
@ -669,21 +683,33 @@ namespace Guru
|
|||
/// <param name="productId"></param>
|
||||
/// <param name="orderId"></param>
|
||||
/// <param name="orderDate"></param>
|
||||
public static void IAPPurchase(double value, string productId, string orderId, string orderDate,
|
||||
string purchaseToken = "", string receipt = "")
|
||||
/// <param name="purchaseToken"></param>
|
||||
/// <param name="receipt"></param>
|
||||
/// <param name="isSandbox"></param>
|
||||
public static void IAPPurchase(double value, string productId, string orderId, string orderDate,
|
||||
string purchaseToken = "", string receipt = "", bool isSandbox = false)
|
||||
{
|
||||
IAPPurchaseReport(EventIAPPurchase, value, productId, orderId, "IAP", orderDate, purchaseToken, receipt);
|
||||
IAPPurchaseReport(EventIAPPurchase, value, productId, orderId, "IAP", orderDate, purchaseToken, receipt, isSandbox);
|
||||
}
|
||||
|
||||
public static void SubPurchase(double value, string productId, string orderId, string orderDate,
|
||||
string purchaseToken = "", string receipt = "")
|
||||
{
|
||||
IAPPurchaseReport(EventSubPurchase, value, productId, orderId, "SUB", orderDate, purchaseToken, receipt);
|
||||
}
|
||||
|
||||
|
||||
private static void IAPPurchaseReport(string eventName, double value, string productId, string orderId, string orderType, string orderDate,
|
||||
string purchaseToken = "", string receipt = "")
|
||||
/// <summary>
|
||||
/// SUB 订阅上报
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="productId"></param>
|
||||
/// <param name="orderId"></param>
|
||||
/// <param name="orderDate"></param>
|
||||
/// <param name="purchaseToken"></param>
|
||||
/// <param name="receipt"></param>
|
||||
/// <param name="isSandbox"></param>
|
||||
public static void SubPurchase(double value, string productId, string orderId, string orderDate,
|
||||
string purchaseToken = "", string receipt = "", bool isSandbox = false)
|
||||
{
|
||||
IAPPurchaseReport(EventSubPurchase, value, productId, orderId, "SUB", orderDate, purchaseToken, receipt, isSandbox);
|
||||
}
|
||||
|
||||
private static void IAPPurchaseReport(string eventName, double value, string productId,
|
||||
string orderId, string orderType, string orderDate, string purchaseToken = "", string receipt = "", bool isSandbox = false)
|
||||
{
|
||||
|
||||
var dict = new Dictionary<string, dynamic>()
|
||||
|
|
@ -694,20 +720,17 @@ namespace Guru
|
|||
[ParameterProductId] = productId,
|
||||
["order_id"] = orderId,
|
||||
["order_type"] = orderType,
|
||||
["trans_ts"] = orderDate
|
||||
["trans_ts"] = orderDate,
|
||||
["sandbox"] = isSandbox? "true": "false"
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 上报Firebase + 自打点
|
||||
LogEvent(eventName, dict, new EventSetting() { EnableFirebaseAnalytics = true, });
|
||||
|
||||
|
||||
LogEvent(eventName, dict, new EventSetting() { EnableFirebaseAnalytics = true });
|
||||
|
||||
// 上报 Adjust 支付事件
|
||||
// if (value > 0)
|
||||
// {
|
||||
// 根据事件名称来获取对应的事件Token(iap_purchase/sub_purchase)
|
||||
LogAdjustRevenueEvent(eventName, value, productId, orderId, purchaseToken, receipt, dict);
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
|
||||
|
||||
using System.Collections;
|
||||
|
||||
namespace Guru
|
||||
{
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using com.adjust.sdk;
|
||||
using Facebook.Unity;
|
||||
using UnityEngine;
|
||||
using Firebase.Analytics;
|
||||
using Firebase.Crashlytics;
|
||||
|
||||
|
|
@ -148,10 +148,10 @@ namespace Guru
|
|||
/// </summary>
|
||||
/// <param name="eventName"></param>
|
||||
/// <param name="eventSetting"></param>
|
||||
internal static void LogEvent(string eventName, EventSetting eventSetting = null)
|
||||
internal static void LogEvent(string eventName, EventSetting eventSetting = null, int priority = -1)
|
||||
{
|
||||
Log.I(TAG, $"eventName:{eventName}");
|
||||
CustomLogEvent(eventName); // 自定义打点上报
|
||||
CustomLogEvent(eventName, null, priority); // 自定义打点上报
|
||||
CheckLogCache(eventName, null, eventSetting); // log缓存和消费
|
||||
|
||||
if (!IsEnable) return;
|
||||
|
|
@ -180,22 +180,24 @@ namespace Guru
|
|||
/// <param name="eventName"></param>
|
||||
/// <param name="extras"></param>
|
||||
/// <param name="eventSetting"></param>
|
||||
internal static void LogEvent(string eventName, Dictionary<string, dynamic> extras, EventSetting eventSetting = null)
|
||||
/// <param name="priority"></param>
|
||||
internal static void LogEvent(string eventName, Dictionary<string, dynamic> extras, EventSetting eventSetting = null, int priority = -1)
|
||||
{
|
||||
Log.I(TAG, $"eventName:{eventName}, params:{string.Join(",", extras)}");
|
||||
CustomLogEvent(eventName, extras); // 自定义打点上报
|
||||
|
||||
CustomLogEvent(eventName, extras, priority); // 自定义打点上报
|
||||
CheckLogCache(eventName, extras, eventSetting); // log缓存和消费
|
||||
|
||||
if (!IsEnable) return;
|
||||
|
||||
if (extras == null)
|
||||
{
|
||||
LogEvent(eventName, eventSetting); // 防空判定
|
||||
LogEvent(eventName, eventSetting, priority); // 防空判定
|
||||
return;
|
||||
}
|
||||
|
||||
string paramStr = string.Join(",", extras);
|
||||
Log.I(TAG, $"eventName:{eventName}, params:{paramStr}");
|
||||
|
||||
eventSetting ??= _defaultEventSetting;
|
||||
if (eventSetting == null) eventSetting = _defaultEventSetting;
|
||||
if (eventSetting.EnableFirebaseAnalytics)
|
||||
{
|
||||
List<Parameter> parameters = new List<Parameter>();
|
||||
|
|
@ -217,7 +219,6 @@ namespace Guru
|
|||
parameters.Add(new Parameter(kv.Key, decimal.ToDouble(decimalValue)));
|
||||
else
|
||||
parameters.Add(new Parameter(kv.Key, kv.Value.ToString()));
|
||||
|
||||
}
|
||||
|
||||
FirebaseAnalytics.LogEvent(eventName, parameters.ToArray());
|
||||
|
|
@ -296,15 +297,16 @@ namespace Guru
|
|||
/// <param name="key"></param>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="setting"></param>
|
||||
public static void Track(string key, Dictionary<string, dynamic> data = null, EventSetting setting = null)
|
||||
/// <param name="priority"></param>
|
||||
public static void Track(string key, Dictionary<string, dynamic> data = null, EventSetting setting = null, int priority = -1)
|
||||
{
|
||||
if (null != data)
|
||||
{
|
||||
LogEvent(key, data, setting);
|
||||
LogEvent(key, data, setting, priority);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogEvent(key, setting);
|
||||
LogEvent(key, setting, priority);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -350,7 +352,7 @@ namespace Guru
|
|||
}
|
||||
|
||||
|
||||
private static void CheckLogCache(string key, Dictionary<string, dynamic> data = null, EventSetting setting = null)
|
||||
private static void CheckLogCache(string key, Dictionary<string, dynamic> data = null, EventSetting setting = null, int priority = -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -358,7 +360,7 @@ namespace Guru
|
|||
{
|
||||
if (data == null) data = new Dictionary<string, dynamic>();
|
||||
data["log_stamp"] = TimeUtil.GetCurrentTimeStamp().ToString();
|
||||
SavedLogs.Enqueue(new SavedLog(key, data, setting));
|
||||
SavedLogs.Enqueue(new SavedLog(key, data, setting, priority));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -368,7 +370,7 @@ namespace Guru
|
|||
while (SavedLogs.Count > 0)
|
||||
{
|
||||
var log = SavedLogs.Dequeue();
|
||||
LogEvent(log.key, log.data, log.setting);
|
||||
LogEvent(log.key, log.data, log.setting, log.priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -387,18 +389,27 @@ namespace Guru
|
|||
internal class SavedLog
|
||||
{
|
||||
public string key;
|
||||
public int priority;
|
||||
public Dictionary<string, dynamic> data;
|
||||
public Analytics.EventSetting setting;
|
||||
|
||||
public SavedLog()
|
||||
{
|
||||
}
|
||||
|
||||
public SavedLog(string _key, Dictionary<string, dynamic> _data = null, Analytics.EventSetting _setting = null)
|
||||
|
||||
/// <summary>
|
||||
/// 保存打点信息
|
||||
/// </summary>
|
||||
/// <param name="_key"></param>
|
||||
/// <param name="_data"></param>
|
||||
/// <param name="_setting"></param>
|
||||
/// <param name="_priority"></param>
|
||||
public SavedLog(string _key, Dictionary<string, dynamic> _data = null, Analytics.EventSetting _setting = null, int _priority = -1)
|
||||
{
|
||||
key = _key;
|
||||
data = _data;
|
||||
setting = _setting;
|
||||
priority = _priority;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -52,7 +52,10 @@ namespace Guru
|
|||
try
|
||||
{
|
||||
// return JsonMapper.ToObject<T>(jsonStr);
|
||||
return JsonConvert.DeserializeObject<T>(jsonStr);
|
||||
return JsonConvert.DeserializeObject<T>(jsonStr, new JsonSerializerSettings()
|
||||
{
|
||||
ObjectCreationHandling = ObjectCreationHandling.Replace,
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Firebase;
|
||||
using Firebase.Analytics;
|
||||
using Firebase.Extensions;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Guru
|
||||
{
|
||||
|
|
@ -117,12 +117,36 @@ namespace Guru
|
|||
// 启动 AdjustService
|
||||
string appToken = GuruSettings.Instance.AdjustSetting?.GetAppToken() ?? "";
|
||||
string fbAppId = GuruSettings.Instance.IPMSetting.FacebookAppId;
|
||||
AdjustService.StartService(appToken, fbAppId);
|
||||
|
||||
if (!string.IsNullOrEmpty(IPMConfig.ADJUST_ID))
|
||||
{
|
||||
ReportAdjustId(IPMConfig.ADJUST_ID); // 二次启动后,若有值则立即上报属性
|
||||
}
|
||||
|
||||
AdjustService.StartService(appToken, fbAppId, adjustId =>
|
||||
{
|
||||
// 获取 ADID
|
||||
if (string.IsNullOrEmpty(adjustId))
|
||||
{
|
||||
adjustId = "not_set";
|
||||
}
|
||||
else
|
||||
{
|
||||
IPMConfig.ADJUST_ID = adjustId;
|
||||
}
|
||||
ReportAdjustId(adjustId);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static void ReportAdjustId(string adjustId)
|
||||
{
|
||||
FirebaseAnalytics.SetUserProperty("adjust_id", adjustId); // 仅上报 Firebase 用户属性
|
||||
Debug.Log($"[SDK] --- Firebase + Adjust ID: {adjustId}");
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ namespace Guru
|
|||
{
|
||||
GenDeviceId();
|
||||
}
|
||||
|
||||
return SavedDeviceId; // 优先使用缓存的 DeviceID
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ namespace Guru
|
|||
$"{nameof(language)}: {language}, {nameof(locale)}: {locale}, {nameof(deviceToken)}: {deviceToken}, {nameof(deviceType)}: {deviceType}, " +
|
||||
$"{nameof(pushType)}: {pushType}, {nameof(appIdentifier)}: {appIdentifier}, {nameof(appVersion)}: {appVersion}, {nameof(brand)}: {brand}, " +
|
||||
$"{nameof(model)}: {model}, {nameof(timezone)}: {timezone}, {nameof(pushNotificationEnable)}: {pushNotificationEnable}, " +
|
||||
$"{nameof(firebaseAppInstanceId)}: {firebaseAppInstanceId}, {nameof(idfa)}: {idfa}, {nameof(adid)}: {adid}, {nameof(gpsAdid)}: {gpsAdid}";
|
||||
$"{nameof(firebaseAppInstanceId)}: {firebaseAppInstanceId}, {nameof(idfa)}: {idfa}, {nameof(adid)}: {adid}, {nameof(gpsAdid)}: {gpsAdid}, {nameof(userUuid)}: {userUuid}";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,19 +34,14 @@ namespace Guru
|
|||
{
|
||||
try
|
||||
{
|
||||
Debug.Log($"[IAP] --- Apple Order Response: {response}");
|
||||
ResponseData<OrderResponse> responseData = JsonUtility.FromJson<ResponseData<OrderResponse>>(response);
|
||||
if (responseData != null && responseData.data != null)
|
||||
{
|
||||
double usdPrice = responseData.data.usdPrice;
|
||||
string productId = orderData.productId;
|
||||
{
|
||||
double usdPrice = responseData.data.usdPrice;
|
||||
bool isTest = responseData.data.test;
|
||||
|
||||
// Analytics.Tch001IAPRev(usdPrice, productId, orderData.orderId, orderData.OrderType(), orderData.payedDate);
|
||||
// Analytics.Tch02IAPRev(usdPrice);
|
||||
//
|
||||
// AdjustService.TrackSubPurchase(usdPrice, productId);
|
||||
// Analytics.SubPurchase(usdPrice, productId);
|
||||
|
||||
Analytics.ReportIAPSuccessEvent(usdPrice, productId, orderData);
|
||||
Analytics.ReportIAPSuccessEvent(orderData, usdPrice, isTest);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ namespace Guru
|
|||
{
|
||||
DeviceData deviceData = new DeviceData();
|
||||
deviceData.pushNotificationEnable = _isPushEnabled;
|
||||
this.Log($"send deviceData:{deviceData}");
|
||||
UnityEngine.Debug.Log($"[SDK] --- Send DeviceData:{deviceData}");
|
||||
var request = new UnityWebRequest(RequestURL, "POST");
|
||||
request.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(JsonUtility.ToJson(deviceData)));
|
||||
request.downloadHandler = new DownloadHandlerBuffer();
|
||||
|
|
@ -31,10 +31,10 @@ namespace Guru
|
|||
|
||||
protected override void RequestSuccessCallBack(string response)
|
||||
{
|
||||
this.Log("@@@ Send OK!");
|
||||
UnityEngine.Debug.Log("[SDK] --- Send DeviceData Success");
|
||||
IPMConfig.IS_UPLOAD_DEVICE_SUCCESS = true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 设置是否打开推送
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -31,21 +31,14 @@ namespace Guru
|
|||
{
|
||||
try
|
||||
{
|
||||
Debug.Log($"[IAP] --- Google Order Response: {response}");
|
||||
ResponseData<OrderResponse> responseData = JsonUtility.FromJson<ResponseData<OrderResponse>>(response);
|
||||
if (responseData != null && responseData.data != null)
|
||||
{
|
||||
double usdPrice = responseData.data.usdPrice;
|
||||
string productId = orderData.RealProductId;
|
||||
bool isTest = responseData.data.test;
|
||||
|
||||
// Analytics.Tch001IAPRev(usdPrice, productId, orderId, orderType, orderDate); // TCH 001
|
||||
// // Analytics.Tch02IAPRev(usdPrice, productId, orderId, orderTypeString, timestamp);
|
||||
// Analytics.Tch02IAPRev(usdPrice); // TCH 020
|
||||
//
|
||||
// // Adjust Track IAP Purchase
|
||||
// AdjustService.TrackIAPPurchase(usdPrice, productId); // 上报 IAP 支付事件
|
||||
// Analytics.IAPPurchase(usdPrice, productId);
|
||||
|
||||
Analytics.ReportIAPSuccessEvent(usdPrice, productId, orderData);
|
||||
Analytics.ReportIAPSuccessEvent(orderData, usdPrice, isTest);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
|
|||
|
|
@ -6,10 +6,12 @@ namespace Guru
|
|||
public class OrderResponse
|
||||
{
|
||||
public double usdPrice;
|
||||
public bool test;
|
||||
public string state;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{nameof(usdPrice)}: {usdPrice}";
|
||||
return $"{nameof(usdPrice)}: {usdPrice} {nameof(test)}: {test} {nameof(state)}: {state}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -99,22 +99,22 @@ namespace Guru
|
|||
UpdateView(); // 刷新视图
|
||||
}
|
||||
|
||||
|
||||
// 字段缓冲
|
||||
private StringBuilder _infoBuff;
|
||||
private string CreateMonitorInfo()
|
||||
{
|
||||
string msg = "";
|
||||
bool loaded = false;
|
||||
|
||||
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (!ADService.Instance.IsInitialized)
|
||||
{
|
||||
msg = ColoredText("AdService not initialized...", Consts.ColorRed);
|
||||
return msg;
|
||||
}
|
||||
|
||||
if (_infoBuff == null) _infoBuff = new StringBuilder();
|
||||
_infoBuff.Clear();
|
||||
|
||||
if (_curBadsInfo == null)
|
||||
{
|
||||
msg = $"BADS: {ColoredText("not ready", Consts.ColorRed)}\n";
|
||||
|
|
@ -137,7 +137,7 @@ namespace Guru
|
|||
msg = $"BADS: {ColoredText("loading...", Consts.ColorYellow)}\n\tformat: {_curBadsInfo.format}\n";
|
||||
break;
|
||||
case AdStatusType.Paid:
|
||||
msg = $"BADS: {ColoredText("display", Consts.ColorGreen)}\n\tnetwork: {_curIadsInfo.network}\n\trevenue: {_curBadsInfo.revenue}\n";
|
||||
msg = $"BADS: {ColoredText("display", Consts.ColorGreen)}\n\tnetwork: {_curBadsInfo.network}\n\trevenue: {_curBadsInfo.revenue}\n";
|
||||
break;
|
||||
case AdStatusType.NotReady:
|
||||
msg = $"BADS: {ColoredText("not ready", Consts.ColorGray)}\n\t{ColoredText("---", Consts.ColorGray)}\n";
|
||||
|
|
@ -147,7 +147,7 @@ namespace Guru
|
|||
break;
|
||||
}
|
||||
}
|
||||
sb.Append(msg);
|
||||
_infoBuff.Append(msg);
|
||||
|
||||
|
||||
if (_curIadsInfo == null)
|
||||
|
|
@ -159,7 +159,7 @@ namespace Guru
|
|||
switch (_curIadsInfo.status)
|
||||
{
|
||||
case AdStatusType.Loaded:
|
||||
msg = $"IADS: {ColoredText("loaded", Consts.ColorGreen)}\n\tnetwork: {_curIadsInfo.network}\n\twaterfall: {_curBadsInfo.waterfall}\n";
|
||||
msg = $"IADS: {ColoredText("loaded", Consts.ColorGreen)}\n\tnetwork: {_curIadsInfo.network}\n\twaterfall: {_curIadsInfo.waterfall}\n";
|
||||
break;
|
||||
case AdStatusType.LoadFailed:
|
||||
msg = $"IADS: {ColoredText("loading failed", Consts.ColorRed)}\n\tmessage: {_curIadsInfo.info}\n";
|
||||
|
|
@ -181,7 +181,7 @@ namespace Guru
|
|||
break;
|
||||
}
|
||||
}
|
||||
sb.Append(msg);
|
||||
_infoBuff.Append(msg);
|
||||
|
||||
|
||||
if (_curRadsInfo == null)
|
||||
|
|
@ -215,10 +215,11 @@ namespace Guru
|
|||
break;
|
||||
}
|
||||
}
|
||||
sb.Append(msg);
|
||||
_infoBuff.Append(msg);
|
||||
|
||||
|
||||
return sb.ToString();
|
||||
return _infoBuff.ToString();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1fef034d48ac449fb99531b40139954e
|
||||
timeCreated: 1718844748
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8c0dd2d4b63445c2828e05c10274d672
|
||||
timeCreated: 1718845536
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
|
||||
|
||||
#if UNITY_ANDROID
|
||||
|
||||
namespace Guru.Notification
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEditor.Android;
|
||||
using System.Xml;
|
||||
|
||||
public class PostCustomActivity: IPostGenerateGradleAndroidProject
|
||||
{
|
||||
private const int POST_ORDER = 10;
|
||||
private const string K_PREMISSION_POST_NOTIFICATIONS = "android.permission.POST_NOTIFICATIONS";
|
||||
private const string K_CUSTOM_NOTIFICATION_ACTIVITY = "custom_notification_android_activity";
|
||||
private const string V_DEFAULT_GURU_ACTIVITY = "com.google.firebase.messaging.MessageForwardingService";
|
||||
const string K_ANDROID_NAMESPACE_URI = "http://schemas.android.com/apk/res/android";
|
||||
|
||||
public int callbackOrder => POST_ORDER;
|
||||
|
||||
public void OnPostGenerateGradleAndroidProject(string path)
|
||||
{
|
||||
SetupAndroidManifest(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置 Android Manifest
|
||||
/// </summary>
|
||||
/// <param name="projectPath"></param>
|
||||
/// <exception cref="FileNotFoundException"></exception>
|
||||
private void SetupAndroidManifest(string projectPath)
|
||||
{
|
||||
var manifestPath = $"{projectPath}/src/main/AndroidManifest.xml";
|
||||
if (!File.Exists(manifestPath))
|
||||
throw new FileNotFoundException($"'{manifestPath}' doesn't exist.");
|
||||
|
||||
XmlDocument manifestDoc = new XmlDocument();
|
||||
manifestDoc.Load(manifestPath);
|
||||
|
||||
InjectAndroidManifest(manifestPath, manifestDoc);
|
||||
|
||||
manifestDoc.Save(manifestPath);
|
||||
}
|
||||
|
||||
internal static void InjectAndroidManifest(string manifestPath, XmlDocument manifestDoc)
|
||||
{
|
||||
string mainActivity = GetLauncherActivity(manifestDoc);
|
||||
|
||||
AppendAndroidMetadataField(manifestPath, manifestDoc, K_CUSTOM_NOTIFICATION_ACTIVITY, mainActivity);
|
||||
AppendAndroidPermissionField(manifestPath, manifestDoc, K_PREMISSION_POST_NOTIFICATIONS);
|
||||
|
||||
UnityEngine.Debug.Log($"<color=#88ff00>Add custom notification activity: {mainActivity} success!!</color>");
|
||||
}
|
||||
|
||||
internal static void AppendAndroidPermissionField(string manifestPath, XmlDocument xmlDoc, string name, string maxSdk = null)
|
||||
{
|
||||
AppendAndroidPermissionField(manifestPath, xmlDoc, "uses-permission", name, maxSdk);
|
||||
}
|
||||
|
||||
internal static void AppendAndroidPermissionField(string manifestPath, XmlDocument xmlDoc, string tagName, string name, string maxSdk)
|
||||
{
|
||||
var manifestNode = xmlDoc.SelectSingleNode("manifest");
|
||||
if (manifestNode == null)
|
||||
throw new ArgumentException(string.Format("Missing 'manifest' node in '{0}'.", manifestPath));
|
||||
|
||||
XmlElement metaDataNode = null;
|
||||
foreach (XmlNode node in manifestNode.ChildNodes)
|
||||
{
|
||||
if (!(node is XmlElement) || node.Name != tagName)
|
||||
continue;
|
||||
|
||||
var element = (XmlElement)node;
|
||||
var elementName = element.GetAttribute("name", K_ANDROID_NAMESPACE_URI);
|
||||
if (elementName == name)
|
||||
{
|
||||
if (maxSdk == null)
|
||||
return;
|
||||
var maxSdkAttr = element.GetAttribute("maxSdkVersion", K_ANDROID_NAMESPACE_URI);
|
||||
if (!string.IsNullOrEmpty(maxSdkAttr))
|
||||
return;
|
||||
metaDataNode = element;
|
||||
}
|
||||
}
|
||||
|
||||
if (metaDataNode == null)
|
||||
{
|
||||
metaDataNode = xmlDoc.CreateElement(tagName);
|
||||
metaDataNode.SetAttribute("name", K_ANDROID_NAMESPACE_URI, name);
|
||||
}
|
||||
if (maxSdk != null)
|
||||
metaDataNode.SetAttribute("maxSdkVersion", K_ANDROID_NAMESPACE_URI, maxSdk);
|
||||
|
||||
manifestNode.AppendChild(metaDataNode);
|
||||
}
|
||||
|
||||
internal static void AppendAndroidMetadataField(string manifestPath, XmlDocument xmlDoc, string name, string value)
|
||||
{
|
||||
var applicationNode = xmlDoc.SelectSingleNode("manifest/application");
|
||||
if (applicationNode == null)
|
||||
throw new ArgumentException(string.Format("Missing 'application' node in '{0}'.", manifestPath));
|
||||
|
||||
var nodes = xmlDoc.SelectNodes("manifest/application/meta-data");
|
||||
if (nodes != null)
|
||||
{
|
||||
// Check if there is a 'meta-data' with the same name.
|
||||
foreach (XmlNode node in nodes)
|
||||
{
|
||||
var element = node as XmlElement;
|
||||
if (element == null)
|
||||
continue;
|
||||
|
||||
var elementName = element.GetAttribute("name", K_ANDROID_NAMESPACE_URI);
|
||||
if (elementName == name)
|
||||
{
|
||||
element.SetAttribute("value", K_ANDROID_NAMESPACE_URI, value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
XmlElement metaDataNode = xmlDoc.CreateElement("meta-data");
|
||||
metaDataNode.SetAttribute("name", K_ANDROID_NAMESPACE_URI, name);
|
||||
metaDataNode.SetAttribute("value", K_ANDROID_NAMESPACE_URI, value);
|
||||
|
||||
applicationNode.AppendChild(metaDataNode);
|
||||
}
|
||||
|
||||
internal static string GetLauncherActivity(XmlDocument xmlDoc)
|
||||
{
|
||||
var applicationNode = xmlDoc.SelectSingleNode("manifest/application");
|
||||
if (applicationNode == null)
|
||||
throw new ArgumentException($"Missing 'application' node in doc.");
|
||||
|
||||
var nodes = xmlDoc.SelectNodes("manifest/application/activity");
|
||||
if (nodes != null)
|
||||
{
|
||||
foreach (XmlNode node in nodes)
|
||||
{
|
||||
var activityNode = node as XmlElement;
|
||||
if (activityNode == null)
|
||||
continue;
|
||||
|
||||
if (activityNode.HasChildNodes)
|
||||
{
|
||||
var intentFilterNode = activityNode.SelectSingleNode("intent-filter");
|
||||
|
||||
if(intentFilterNode == null || !intentFilterNode.HasChildNodes)
|
||||
continue;
|
||||
|
||||
foreach (XmlElement childNode in intentFilterNode)
|
||||
{
|
||||
if(childNode == null) continue;
|
||||
|
||||
// 判断 action/category 二者取其一
|
||||
if (childNode.Name == "action" && childNode.InnerXml.Contains("android.intent.action.MAIN"))
|
||||
{
|
||||
var activityName = activityNode.GetAttribute("name", K_ANDROID_NAMESPACE_URI);
|
||||
return activityName;
|
||||
}
|
||||
|
||||
if (childNode.Name == "category" && childNode.InnerXml.Contains("android.intent.category.LAUNCHER"))
|
||||
{
|
||||
var activityName = activityNode.GetAttribute("name", K_ANDROID_NAMESPACE_URI);
|
||||
return activityName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return V_DEFAULT_GURU_ACTIVITY;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2e6de94961cc47abbabbc7e872085a98
|
||||
timeCreated: 1718934648
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "GuruNotification.Editor",
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e75025463d0c436482bf4c3dab674315
|
||||
timeCreated: 1718845553
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d6dd827f370441718ca2e49f3f603e4e
|
||||
timeCreated: 1718845525
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
namespace Guru.Notification
|
||||
{
|
||||
using System;
|
||||
public interface INotificationAgent
|
||||
{
|
||||
void Init();
|
||||
|
||||
string GetStatus();
|
||||
|
||||
bool IsAllowed();
|
||||
|
||||
void RequestPermission(Action<string> callback = null);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 43e051a65e9b469b8b9e8a9f6b4be944
|
||||
timeCreated: 1718844775
|
||||
|
|
@ -0,0 +1,252 @@
|
|||
namespace Guru.Notification
|
||||
{
|
||||
using System;
|
||||
using UnityEngine;
|
||||
#if UNITY_ANDROID
|
||||
using UnityEngine.Android;
|
||||
using Unity.Notifications.Android;
|
||||
#endif
|
||||
|
||||
public class NotificationAgentAndroid : INotificationAgent
|
||||
{
|
||||
public const string FCM_DEFAULT_CHANNEL_ID = "fcm_default_channel";
|
||||
private const string STATUS_GRANTED = "granted";
|
||||
private const string STATUS_DENIDED = "denied";
|
||||
// private const string STATUS_NOT_DETERMINED = "not_determined";
|
||||
private const int REQUEST_PERMISSION_SDK_VERSION = 33;
|
||||
private const string PERMISSION_POST_NOTIFICATION = "android.permission.POST_NOTIFICATIONS";
|
||||
|
||||
private bool _initOnce = false;
|
||||
private string _notiStatus;
|
||||
|
||||
private string SavedNotiPermStatus
|
||||
{
|
||||
get => PlayerPrefs.GetString(nameof(SavedNotiPermStatus), "");
|
||||
set => PlayerPrefs.SetString(nameof(SavedNotiPermStatus), value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
public void Init()
|
||||
{
|
||||
if (!_initOnce) return;
|
||||
_initOnce = true;
|
||||
|
||||
_notiStatus = STATUS_DENIDED;
|
||||
if (!string.IsNullOrEmpty(SavedNotiPermStatus))
|
||||
{
|
||||
_notiStatus = SavedNotiPermStatus;
|
||||
}
|
||||
|
||||
#if UNITY_ANDROID
|
||||
InitPlugins();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取状态
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string GetStatus()
|
||||
{
|
||||
if (!_initOnce) Init();
|
||||
#if UNITY_ANDROID
|
||||
UpdateNotiStatus();
|
||||
#endif
|
||||
return _notiStatus;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置授权状态
|
||||
/// </summary>
|
||||
/// <param name="status">授权状态</param>
|
||||
private void SetGrantStatus(string status)
|
||||
{
|
||||
_notiStatus = status;
|
||||
SavedNotiPermStatus = status;
|
||||
}
|
||||
|
||||
|
||||
public bool IsAllowed()
|
||||
{
|
||||
return _notiStatus == STATUS_GRANTED;
|
||||
}
|
||||
|
||||
public void RequestPermission(Action<string> callback = null)
|
||||
{
|
||||
#if UNITY_ANDROID
|
||||
RequestAndroidPermission(callback);
|
||||
#endif
|
||||
}
|
||||
|
||||
// -------------------- Android 获取状态逻辑 --------------------
|
||||
|
||||
#if UNITY_ANDROID
|
||||
|
||||
private PermissionStatus _permissionStatus;
|
||||
|
||||
private void TryExecute(Action handler)
|
||||
{
|
||||
try
|
||||
{
|
||||
handler?.Invoke();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 初始化插件
|
||||
/// </summary>
|
||||
private void InitPlugins()
|
||||
{
|
||||
AndroidNotificationCenter.Initialize();
|
||||
Debug.Log($"[Noti][AND] --- Notification Service InitPlugins");
|
||||
|
||||
UpdateNotiStatus();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新 Notification 状态码
|
||||
/// </summary>
|
||||
private void UpdateNotiStatus()
|
||||
{
|
||||
|
||||
TryExecute(() =>
|
||||
{
|
||||
_permissionStatus = AndroidNotificationCenter.UserPermissionToPost;
|
||||
var status = "";
|
||||
switch (_permissionStatus)
|
||||
{
|
||||
// case PermissionStatus.NotRequested:
|
||||
// _notiStatus = STATUS_NOT_DETERMINED;
|
||||
// break;
|
||||
case PermissionStatus.Allowed:
|
||||
status = STATUS_GRANTED;;
|
||||
break;
|
||||
default:
|
||||
status = STATUS_DENIDED;
|
||||
break;
|
||||
}
|
||||
|
||||
SetGrantStatus(status);
|
||||
Debug.LogWarning($"[SDK][AND] --- UpdateNotiStatus:{_notiStatus} | UserPermissionToPost:{_permissionStatus}");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private Action<string> _onPermissionCallback;
|
||||
private PermissionCallbacks _permissionCallbacks;
|
||||
private void RequestAndroidPermission(Action<string> callback = null)
|
||||
{
|
||||
UpdateNotiStatus();
|
||||
|
||||
if (_notiStatus == STATUS_GRANTED)
|
||||
{
|
||||
callback?.Invoke(_notiStatus);
|
||||
return;
|
||||
}
|
||||
|
||||
_onPermissionCallback = callback;
|
||||
TryExecute(() =>
|
||||
{
|
||||
var sdkInt = GetAndroidSDKVersion();
|
||||
if (sdkInt < REQUEST_PERMISSION_SDK_VERSION)
|
||||
{
|
||||
// 低版本处理方式
|
||||
Debug.Log($"[SDK][Noti] --- #2 SDK {sdkInt} not requested -> open channel");
|
||||
AndroidNotificationCenter.RegisterNotificationChannel(new AndroidNotificationChannel(FCM_DEFAULT_CHANNEL_ID,
|
||||
FCM_DEFAULT_CHANNEL_ID, "", Importance.Default)); // 打开ChannelID
|
||||
SetGrantStatus(STATUS_GRANTED);
|
||||
}
|
||||
else
|
||||
{
|
||||
// SDK 33 以上,请求弹窗
|
||||
bool hasPermission = Permission.HasUserAuthorizedPermission(PERMISSION_POST_NOTIFICATION);
|
||||
if (hasPermission)
|
||||
{
|
||||
SetGrantStatus(STATUS_GRANTED);
|
||||
callback?.Invoke(STATUS_GRANTED);
|
||||
return;
|
||||
}
|
||||
Debug.Log($"[SDK][Noti] --- #3 SDK {sdkInt} :: Ask Post Permission");
|
||||
Permission.RequestUserPermission(PERMISSION_POST_NOTIFICATION, SetupPermissionCallbacks());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private PermissionCallbacks SetupPermissionCallbacks()
|
||||
{
|
||||
if(_permissionCallbacks != null) DisposePermissionCallbacks();
|
||||
_permissionCallbacks = new PermissionCallbacks();
|
||||
_permissionCallbacks.PermissionGranted += OnPermissionGranted;
|
||||
_permissionCallbacks.PermissionDenied += OnPermissionDenied;
|
||||
_permissionCallbacks.PermissionDeniedAndDontAskAgain += OnPermissionDenied;
|
||||
return _permissionCallbacks;
|
||||
}
|
||||
|
||||
private void DisposePermissionCallbacks()
|
||||
{
|
||||
if (_permissionCallbacks != null)
|
||||
{
|
||||
_permissionCallbacks.PermissionGranted -= OnPermissionGranted;
|
||||
_permissionCallbacks.PermissionDenied -= OnPermissionDenied;
|
||||
_permissionCallbacks.PermissionDeniedAndDontAskAgain -= OnPermissionDenied;
|
||||
_permissionCallbacks = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 请求通过
|
||||
/// </summary>
|
||||
/// <param name="permissionName"></param>
|
||||
private void OnPermissionGranted(string permissionName)
|
||||
{
|
||||
if (permissionName == PERMISSION_POST_NOTIFICATION)
|
||||
{
|
||||
_notiStatus = STATUS_GRANTED;
|
||||
_onPermissionCallback?.Invoke(_notiStatus);
|
||||
}
|
||||
SetGrantStatus(STATUS_GRANTED);
|
||||
DisposePermissionCallbacks();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 请求拒绝
|
||||
/// </summary>
|
||||
/// <param name="permissionName"></param>
|
||||
private void OnPermissionDenied(string permissionName)
|
||||
{
|
||||
if (permissionName == PERMISSION_POST_NOTIFICATION)
|
||||
{
|
||||
_notiStatus = STATUS_DENIDED;
|
||||
_onPermissionCallback?.Invoke(_notiStatus);
|
||||
}
|
||||
SetGrantStatus(STATUS_DENIDED);
|
||||
DisposePermissionCallbacks();
|
||||
}
|
||||
|
||||
private int GetAndroidSDKVersion()
|
||||
{
|
||||
int sdkInt = 999;
|
||||
TryExecute(() =>
|
||||
{
|
||||
using (AndroidJavaClass jc = new AndroidJavaClass("android.os.Build$VERSION"))
|
||||
{
|
||||
sdkInt = jc.GetStatic<int>("SDK_INT");
|
||||
Debug.LogWarning($"[SDK] --- Android SDK Version:{sdkInt}");
|
||||
}
|
||||
});
|
||||
return sdkInt;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 554fcea56ce74a80a2424e5c037ce6c0
|
||||
timeCreated: 1718844764
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
|
||||
|
||||
namespace Guru.Notification
|
||||
{
|
||||
using System;
|
||||
using UnityEngine;
|
||||
#if UNITY_IOS
|
||||
using System.Threading.Tasks;
|
||||
using Unity.Notifications.iOS;
|
||||
#endif
|
||||
|
||||
public class NotificationAgentIOS : INotificationAgent
|
||||
{
|
||||
|
||||
private const string STATUS_GRANTED = "granted";
|
||||
private const string STATUS_DENIDED = "denied";
|
||||
private const string STATUS_PROVISIONAL = "provisional";
|
||||
private const string STATUS_NOT_DETERMINED = "not_determined";
|
||||
|
||||
private static bool _initOnce;
|
||||
private static int _waitSeconds = 30;
|
||||
private string SavedNotiPermStatus
|
||||
{
|
||||
get => PlayerPrefs.GetString(nameof(SavedNotiPermStatus), "");
|
||||
set => PlayerPrefs.SetString(nameof(SavedNotiPermStatus), value);
|
||||
}
|
||||
|
||||
|
||||
private string _notiStatus;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
if (_initOnce) return;
|
||||
_initOnce = true;
|
||||
|
||||
_notiStatus = SavedNotiPermStatus;
|
||||
if (string.IsNullOrEmpty(_notiStatus))
|
||||
_notiStatus = STATUS_NOT_DETERMINED;
|
||||
|
||||
#if UNITY_IOS
|
||||
InitPlugins();
|
||||
#endif
|
||||
}
|
||||
|
||||
public string GetStatus()
|
||||
{
|
||||
if (!_initOnce) Init();
|
||||
#if UNITY_IOS
|
||||
UpdateStatus();
|
||||
#endif
|
||||
return _notiStatus;
|
||||
}
|
||||
|
||||
public bool IsAllowed()
|
||||
{
|
||||
return _notiStatus == STATUS_GRANTED;
|
||||
}
|
||||
|
||||
public void RequestPermission(Action<string> callback = null)
|
||||
{
|
||||
if (!_initOnce) Init();
|
||||
|
||||
if (_notiStatus == STATUS_GRANTED || _notiStatus == STATUS_DENIDED)
|
||||
{
|
||||
Debug.Log($"[SDK][Noti][iOS] --- Already has Status: {_notiStatus}");
|
||||
callback?.Invoke(_notiStatus); // 已获得授权, 直接返回结果
|
||||
return;
|
||||
}
|
||||
|
||||
#if UNITY_IOS
|
||||
RequestIOSPermission(callback);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if UNITY_IOS
|
||||
|
||||
private void InitPlugins()
|
||||
{
|
||||
UpdateStatus();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新状态
|
||||
/// </summary>
|
||||
private void UpdateStatus()
|
||||
{
|
||||
string status = STATUS_NOT_DETERMINED;
|
||||
var authorizationStatus = iOSNotificationCenter.GetNotificationSettings().AuthorizationStatus;
|
||||
switch (authorizationStatus)
|
||||
{
|
||||
case AuthorizationStatus.Authorized:
|
||||
status = STATUS_GRANTED;
|
||||
break;
|
||||
case AuthorizationStatus.Denied:
|
||||
status = STATUS_DENIDED;
|
||||
break;
|
||||
case AuthorizationStatus.NotDetermined:
|
||||
status = STATUS_NOT_DETERMINED;
|
||||
break;
|
||||
case AuthorizationStatus.Provisional:
|
||||
status = STATUS_PROVISIONAL;
|
||||
break;
|
||||
default:
|
||||
Debug.Log($"[SDK][Noti][iOS] --- Unmarked AuthorizationStatus: {status}");
|
||||
break;
|
||||
}
|
||||
|
||||
SetGrantStatus(status);
|
||||
}
|
||||
|
||||
private void SetGrantStatus(string status)
|
||||
{
|
||||
_notiStatus = status;
|
||||
SavedNotiPermStatus = status;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 请求 IOS 的推送
|
||||
/// </summary>
|
||||
/// <param name="callback"></param>
|
||||
private async void RequestIOSPermission(Action<string> callback = null)
|
||||
{
|
||||
Debug.Log($"[SDK][Noti][iOS] --- RequestIOSPermission start");
|
||||
int timePassed = 0;
|
||||
using (var req = new AuthorizationRequest(AuthorizationOption.Alert | AuthorizationOption.Badge, true))
|
||||
{
|
||||
while (!req.IsFinished && timePassed < _waitSeconds)
|
||||
{
|
||||
timePassed++;
|
||||
await Task.Delay(1000);
|
||||
};
|
||||
|
||||
if (timePassed >= _waitSeconds)
|
||||
{
|
||||
Debug.LogWarning($"[SDK][Noti][iOS] --- RequestIOSPermission timeout");
|
||||
}
|
||||
|
||||
UpdateStatus();
|
||||
callback?.Invoke(_notiStatus);
|
||||
Debug.Log($"[SDK][Noti][iOS] --- User Selected: {_notiStatus}");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5068c6e238e74251955320761c96182b
|
||||
timeCreated: 1718871877
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
|
||||
|
||||
namespace Guru.Notification
|
||||
{
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// For Editor to use Notifications
|
||||
/// </summary>
|
||||
public class NotificationAgentStub: INotificationAgent
|
||||
{
|
||||
private const string STATUS_GRANTED = "granted";
|
||||
private const string STATUS_DENIDED = "denied";
|
||||
private const string STATUS_NOT_DETERMINED = "not_determined";
|
||||
|
||||
private Action<string> _onPermissionCallback;
|
||||
private float _delaySeconds = 1.0f;
|
||||
|
||||
private string EditorGrantedStatus
|
||||
{
|
||||
get => PlayerPrefs.GetString(nameof(EditorGrantedStatus), STATUS_NOT_DETERMINED);
|
||||
set => PlayerPrefs.SetString(nameof(EditorGrantedStatus), value);
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
Debug.Log($"[SDK][Noti][EDT] --- NotificationAgentStub Init: {EditorGrantedStatus}");
|
||||
}
|
||||
|
||||
public string GetStatus() => EditorGrantedStatus;
|
||||
|
||||
public bool IsAllowed()
|
||||
{
|
||||
return EditorGrantedStatus == STATUS_GRANTED;
|
||||
}
|
||||
|
||||
public void RequestPermission(Action<string> callback = null)
|
||||
{
|
||||
Debug.Log($"[SDK][Noti][EDT] --- RequestPermission ---");
|
||||
_onPermissionCallback = callback;
|
||||
DelayCallPermissionHandle();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 延迟模拟回调
|
||||
/// </summary>
|
||||
private async void DelayCallPermissionHandle()
|
||||
{
|
||||
await Task.Delay((int)(1000 * _delaySeconds));
|
||||
EditorGrantedStatus = STATUS_GRANTED;
|
||||
_onPermissionCallback?.Invoke(EditorGrantedStatus);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6550e85d25634c46b7a57c490dcea173
|
||||
timeCreated: 1718850440
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
namespace Guru.Notification
|
||||
{
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 消息管理器
|
||||
/// </summary>
|
||||
public class NotificationService
|
||||
{
|
||||
// 服务版本号
|
||||
public const string Version = "0.0.1";
|
||||
|
||||
|
||||
// 初始化标志位
|
||||
private static bool _initOnce;
|
||||
|
||||
private static string DefaultPermissionStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
#if UNITY_IOS
|
||||
return "not_determined";
|
||||
#endif
|
||||
return "denied";
|
||||
}
|
||||
}
|
||||
|
||||
#region 初始化
|
||||
|
||||
private static INotificationAgent _agent;
|
||||
|
||||
internal static INotificationAgent Agent
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_agent == null)
|
||||
{
|
||||
_agent = GetAgent();
|
||||
}
|
||||
return _agent;
|
||||
}
|
||||
}
|
||||
|
||||
private static INotificationAgent GetAgent()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
return new NotificationAgentStub();
|
||||
#elif UNITY_ANDROID
|
||||
return new NotificationAgentAndroid();
|
||||
#elif UNITY_IOS
|
||||
return new NotificationAgentIOS();
|
||||
#endif
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
public static void Initialize()
|
||||
{
|
||||
if (_initOnce) return;
|
||||
_initOnce = true;
|
||||
Agent?.Init(); // 初始化代理
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region 接口
|
||||
|
||||
/// <summary>
|
||||
/// 拉起 Noti 请求
|
||||
/// </summary>
|
||||
/// <param name="callback"></param>
|
||||
public static void RequestPermission(Action<string> callback = null)
|
||||
{
|
||||
if (Agent != null)
|
||||
{
|
||||
Agent.RequestPermission(callback);
|
||||
return;
|
||||
}
|
||||
Debug.LogError($"[SDK][Noti] --- Agent is missing, return default status: {DefaultPermissionStatus}");
|
||||
callback?.Invoke(DefaultPermissionStatus);
|
||||
}
|
||||
|
||||
public static bool IsPermissionGranted()
|
||||
{
|
||||
return Agent?.IsAllowed() ?? false;
|
||||
}
|
||||
|
||||
public static string GetStatus()
|
||||
{
|
||||
if(!_initOnce) Initialize();
|
||||
|
||||
if(Agent != null) return Agent.GetStatus();
|
||||
|
||||
Debug.LogError($"[SDK][Noti] --- Agent is missing, return default status: {DefaultPermissionStatus}");
|
||||
return DefaultPermissionStatus;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e0f31750e8bc48a6a5e4f56ee2d5e1cc
|
||||
timeCreated: 1718846076
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
namespace Guru
|
||||
{
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
public abstract class RemoteConfigBase<T>: IRemoteConfig<T> where T : IRemoteConfig<T>
|
||||
{
|
||||
|
|
@ -8,6 +10,8 @@ namespace Guru
|
|||
/// 配置是否可用
|
||||
/// </summary>
|
||||
public bool enable { get; set; } = true;
|
||||
|
||||
[JsonIgnore]
|
||||
public Action<T> OnValueChanged { get; set; }
|
||||
/// <summary>
|
||||
/// 转为Json
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
"dependencies": {
|
||||
"com.unity.ads.ios-support": "1.2.0",
|
||||
"com.unity.editorcoroutines": "1.0.0",
|
||||
"com.unity.mobile.notifications": "2.2.2",
|
||||
"com.unity.mobile.android-logcat": "1.3.2",
|
||||
"com.unity.purchasing": "4.10.0"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue