You've already forked Commercialization.tapadn
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9c99482b2d | |||
| 5810501618 | |||
| 894178a4c7 | |||
| f615580729 | |||
| ef5e5073a4 |
@@ -1,3 +1,42 @@
|
|||||||
|
# [1.0.5]
|
||||||
|
|
||||||
|
### 调整
|
||||||
|
|
||||||
|
* Android native 聚合 SDK AAR 升级到 `4.2.7.3`。
|
||||||
|
* Android Pangle 依赖升级到 `7.6.1.2`,GDT 依赖升级到 `4.690.1560`,改由 Gradle 后处理注入 Maven 依赖。
|
||||||
|
* 移除旧本地 Pangle/GDT AAR,避免与 Maven 依赖重复类。
|
||||||
|
|
||||||
|
### 文档
|
||||||
|
|
||||||
|
* 新增 `SDK_MAINTENANCE.md`,记录官方源码改动清单、封装层职责、SDK 升级步骤和发布流程。
|
||||||
|
|
||||||
|
# [1.0.4]
|
||||||
|
|
||||||
|
### 新增
|
||||||
|
|
||||||
|
* 激励视频支持按游戏场景映射不同 TapADN SpaceId,并在未配置或配置非法时回退默认激励广告位。
|
||||||
|
* 手动加载模式下激励视频缓存按 SpaceId 隔离,避免不同场景广告位串用 ready 缓存。
|
||||||
|
* 激励视频缓存默认 10 分钟未消费自动销毁,可通过 `tapadn.rewarded_cache_max_age_seconds` 覆盖。
|
||||||
|
|
||||||
|
### 调整
|
||||||
|
|
||||||
|
* 智能预加载归因缓存增加 SpaceId 维度,避免多广告位场景下 ready/归因状态混用。
|
||||||
|
|
||||||
|
# [1.0.3]
|
||||||
|
|
||||||
|
### 修复
|
||||||
|
|
||||||
|
* iOS 导出后自动修复 `UnityFramework` target 的广告 SDK `FRAMEWORK_SEARCH_PATHS` / `HEADER_SEARCH_PATHS`,覆盖 Dirichlet、CSJ、GDT、DRA、GDTMobSDK、Tquic、BUAdSDK 的真实 xcframework slice。
|
||||||
|
* iOS 导出后清理 CocoaPods `Pods-UnityFramework*.xcconfig` 中的 `XCFrameworkIntermediates` 搜索路径,避免 Xcode 真机 Play 时出现 framework not found 或 search path not found。
|
||||||
|
* 保持签名配置由业务工程负责,广告模块不写入 Team、provisioning profile 或 code signing 设置。
|
||||||
|
|
||||||
|
# [1.0.1]
|
||||||
|
|
||||||
|
### 调整
|
||||||
|
|
||||||
|
* 包声明升级到 Unity `2022.3`,项目版本固定为 `2022.3.62f2c1`。
|
||||||
|
* 依赖的 `CC-Framework.Commercialization` 升级到 `1.0.15`,对齐源头商业化框架发布包。
|
||||||
|
|
||||||
# [1.0.0]
|
# [1.0.0]
|
||||||
|
|
||||||
### 新增
|
### 新增
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "Dirichlet.Mediation.Editor",
|
||||||
|
"references": [],
|
||||||
|
"includePlatforms": [
|
||||||
|
"Editor"
|
||||||
|
],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ffd2503194414b5e8b8e687a6a70a19f
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -23,6 +23,8 @@ namespace Dirichlet.Mediation.Editor
|
|||||||
private const string DIRICHLET_DEPS_END = "// Dirichlet Mediation Dependencies End";
|
private const string DIRICHLET_DEPS_END = "// Dirichlet Mediation Dependencies End";
|
||||||
private const string DIRICHLET_REPOS_START = "// Dirichlet Mediation Repositories Start";
|
private const string DIRICHLET_REPOS_START = "// Dirichlet Mediation Repositories Start";
|
||||||
private const string DIRICHLET_REPOS_END = "// Dirichlet Mediation Repositories End";
|
private const string DIRICHLET_REPOS_END = "// Dirichlet Mediation Repositories End";
|
||||||
|
private const string PangleSdkVersion = "7.6.1.2";
|
||||||
|
private const string GdtSdkVersion = "4.690.1560";
|
||||||
|
|
||||||
public int callbackOrder => 100; // Run after EDM4U (which uses lower values)
|
public int callbackOrder => 100; // Run after EDM4U (which uses lower values)
|
||||||
|
|
||||||
@@ -149,32 +151,24 @@ namespace Dirichlet.Mediation.Editor
|
|||||||
var depsBlock = new StringBuilder();
|
var depsBlock = new StringBuilder();
|
||||||
depsBlock.AppendLine(DIRICHLET_DEPS_START);
|
depsBlock.AppendLine(DIRICHLET_DEPS_START);
|
||||||
|
|
||||||
// Core Mediation AAR
|
// Local AAR files are imported by Unity's PluginImporter from Assets/Plugins/Android.
|
||||||
depsBlock.AppendLine(" implementation(name: 'DirichletAD_Mediation_4.2.5.0', ext: 'aar')");
|
// Do not declare them here again, otherwise exported Gradle projects can get duplicate classes.
|
||||||
|
|
||||||
// CSJ (穿山甲) Adapter and SDK
|
|
||||||
if (enableCsj)
|
|
||||||
{
|
|
||||||
depsBlock.AppendLine(" implementation(name: 'DirichletAD_CSJ_Adapter_4.2.5.0', ext: 'aar')");
|
|
||||||
depsBlock.AppendLine(" implementation(name: 'open_ad_sdk_7.4.2.2', ext: 'aar')");
|
|
||||||
}
|
|
||||||
|
|
||||||
// GDT (广点通) Adapter and SDK
|
|
||||||
if (enableGdt)
|
|
||||||
{
|
|
||||||
depsBlock.AppendLine(" implementation(name: 'DirichletAD_GDT_Adapter_4.2.5.0', ext: 'aar')");
|
|
||||||
depsBlock.AppendLine(" implementation(name: 'GDTSDK.unionNormal.4.671.1541', ext: 'aar')");
|
|
||||||
}
|
|
||||||
|
|
||||||
// IQY (爱奇艺) Adapter and SDK
|
|
||||||
if (enableIqy)
|
if (enableIqy)
|
||||||
{
|
{
|
||||||
depsBlock.AppendLine(" implementation(name: 'DirichletAD_IQY_Adapter_4.2.5.0', ext: 'aar')");
|
|
||||||
depsBlock.AppendLine(" implementation(name: 'iadsdk-release-2.3.102.110', ext: 'aar')");
|
|
||||||
depsBlock.AppendLine(" implementation 'com.android.support.constraint:constraint-layout:1.1.3'");
|
depsBlock.AppendLine(" implementation 'com.android.support.constraint:constraint-layout:1.1.3'");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maven dependencies (required for SDK functionality)
|
// Maven dependencies (required for SDK functionality)
|
||||||
|
if (enableCsj)
|
||||||
|
{
|
||||||
|
depsBlock.AppendLine($" implementation('com.pangle.cn:ads-sdk-pro:{PangleSdkVersion}') {{");
|
||||||
|
depsBlock.AppendLine(" exclude group: 'com.android.support'");
|
||||||
|
depsBlock.AppendLine(" }");
|
||||||
|
}
|
||||||
|
if (enableGdt)
|
||||||
|
{
|
||||||
|
depsBlock.AppendLine($" implementation 'com.qq.e.union:union:{GdtSdkVersion}'");
|
||||||
|
}
|
||||||
depsBlock.AppendLine(" implementation 'com.android.support:recyclerview-v7:28.0.0'");
|
depsBlock.AppendLine(" implementation 'com.android.support:recyclerview-v7:28.0.0'");
|
||||||
depsBlock.AppendLine(" implementation 'com.github.bumptech.glide:glide:4.9.0'");
|
depsBlock.AppendLine(" implementation 'com.github.bumptech.glide:glide:4.9.0'");
|
||||||
depsBlock.AppendLine(" implementation 'com.android.support:support-v4:28.0.0'");
|
depsBlock.AppendLine(" implementation 'com.android.support:support-v4:28.0.0'");
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ namespace Dirichlet.Mediation.Editor
|
|||||||
private const string ENV_ATT_DESCRIPTION = "DIRICHLET_IOS_ATT_DESCRIPTION";
|
private const string ENV_ATT_DESCRIPTION = "DIRICHLET_IOS_ATT_DESCRIPTION";
|
||||||
private const string PrefKeyIOSSDKVersion = "Dirichlet.iOS.SDKVersion";
|
private const string PrefKeyIOSSDKVersion = "Dirichlet.iOS.SDKVersion";
|
||||||
private const string PrefKeyATTDescription = "Dirichlet.iOS.TrackingUsageDescription";
|
private const string PrefKeyATTDescription = "Dirichlet.iOS.TrackingUsageDescription";
|
||||||
|
private const string IOSDeviceSlice = "ios-arm64";
|
||||||
|
private const string TquicIOSDeviceSlice = "ios-arm64_armv7";
|
||||||
|
private static readonly string[] GDTDynamicFrameworkNames = { "GDTMobSDK.framework", "Tquic.framework" };
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 解析出的 target 信息
|
/// 解析出的 target 信息
|
||||||
@@ -78,8 +81,8 @@ namespace Dirichlet.Mediation.Editor
|
|||||||
// 4. Run pod install
|
// 4. Run pod install
|
||||||
RunPodInstall(pathToBuiltProject);
|
RunPodInstall(pathToBuiltProject);
|
||||||
|
|
||||||
// 5. Embed GDT dynamic frameworks into app target (must run after pod install)
|
// 5. Fix ad SDK framework/header search paths and embed GDT dynamic frameworks (must run after pod install)
|
||||||
EmbedGDTDynamicFrameworks(pathToBuiltProject, targetInfo);
|
FixAdSDKIOSFrameworkConfiguration(pathToBuiltProject, targetInfo);
|
||||||
|
|
||||||
Debug.Log("[DirichletMediation] iOS post-process completed successfully.");
|
Debug.Log("[DirichletMediation] iOS post-process completed successfully.");
|
||||||
}
|
}
|
||||||
@@ -517,20 +520,16 @@ namespace Dirichlet.Mediation.Editor
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// GDTMobSDK 提供的是预编译动态库(GDTMobSDK.framework, Tquic.framework),
|
/// UnityFramework 链接阶段需要显式搜索广告 SDK xcframework 内部的真机 slice,
|
||||||
/// 必须 embed 到 app bundle 中才能在运行时加载。
|
/// 否则 Xcode 可能报 ld: framework 'GDTMobSDK' not found 或 XCFrameworkIntermediates 搜索路径缺失。
|
||||||
/// 由于所有 pods 都在 Framework target 上,CocoaPods 不会自动 embed 到 app target,
|
/// 同时 GDTMobSDK 提供的是预编译动态库(GDTMobSDK.framework, Tquic.framework),
|
||||||
/// 需要在 pod install 之后手动处理。
|
/// 必须 embed 到 app bundle 中才能在运行时加载。由于所有 pods 都在 Framework target 上,
|
||||||
|
/// CocoaPods 不会自动 embed 到 app target,需要在 pod install 之后手动处理。
|
||||||
/// 依赖 UnityEditor.iOS.Xcode.Extensions 中的 AddFileToEmbedFrameworks 扩展方法。
|
/// 依赖 UnityEditor.iOS.Xcode.Extensions 中的 AddFileToEmbedFrameworks 扩展方法。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static void EmbedGDTDynamicFrameworks(string projectPath, TargetInfo targetInfo)
|
private static void FixAdSDKIOSFrameworkConfiguration(string projectPath, TargetInfo targetInfo)
|
||||||
{
|
{
|
||||||
var enableGdt = EditorPrefs.GetBool("Dirichlet.iOS.EnableGDT", true);
|
var enableGdt = EditorPrefs.GetBool("Dirichlet.iOS.EnableGDT", true);
|
||||||
if (!enableGdt)
|
|
||||||
{
|
|
||||||
Debug.Log("[DirichletMediation] GDT adapter disabled, skipping dynamic framework embedding");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var xcodeProjectName = DetectXcodeProjectName(projectPath);
|
var xcodeProjectName = DetectXcodeProjectName(projectPath);
|
||||||
var projectFilePath = Path.Combine(projectPath, $"{xcodeProjectName}.xcodeproj/project.pbxproj");
|
var projectFilePath = Path.Combine(projectPath, $"{xcodeProjectName}.xcodeproj/project.pbxproj");
|
||||||
@@ -543,35 +542,281 @@ namespace Dirichlet.Mediation.Editor
|
|||||||
mainTargetGuid = pbxProject.TargetGuidByName(targetInfo.AppTargetName);
|
mainTargetGuid = pbxProject.TargetGuidByName(targetInfo.AppTargetName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// GDTMobSDK 的动态框架列表
|
var targetGuid = ResolveUnityFrameworkTargetGuid(pbxProject, targetInfo);
|
||||||
var dynamicFrameworkNames = new[] { "GDTMobSDK.framework", "Tquic.framework" };
|
var frameworkSearchPaths = GetAdSDKFrameworkSearchPaths();
|
||||||
|
var headerSearchPaths = GetAdSDKHeaderSearchPaths(frameworkSearchPaths);
|
||||||
|
|
||||||
|
var searchPathsAdded = AddAdSDKSearchPaths(pbxProject, targetGuid, frameworkSearchPaths, headerSearchPaths);
|
||||||
|
PatchPodsXCConfigSearchPaths(projectPath, targetInfo, frameworkSearchPaths, headerSearchPaths);
|
||||||
|
|
||||||
var podsDir = Path.Combine(projectPath, "Pods");
|
var podsDir = Path.Combine(projectPath, "Pods");
|
||||||
var embedded = 0;
|
var embedded = 0;
|
||||||
|
|
||||||
foreach (var frameworkName in dynamicFrameworkNames)
|
if (enableGdt)
|
||||||
{
|
{
|
||||||
var frameworkPath = FindDynamicFramework(podsDir, frameworkName);
|
foreach (var frameworkName in GDTDynamicFrameworkNames)
|
||||||
if (string.IsNullOrEmpty(frameworkPath))
|
{
|
||||||
|
var frameworkPath = FindDynamicFramework(podsDir, frameworkName);
|
||||||
|
if (string.IsNullOrEmpty(frameworkPath))
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[DirichletMediation] Dynamic framework not found: {frameworkName}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var relativePath = frameworkPath.StartsWith(projectPath)
|
||||||
|
? frameworkPath.Substring(projectPath.Length + 1)
|
||||||
|
: frameworkPath;
|
||||||
|
|
||||||
|
var fileGuid = pbxProject.AddFile(relativePath, "Frameworks/" + frameworkName);
|
||||||
|
PBXProjectExtensions.AddFileToEmbedFrameworks(pbxProject, mainTargetGuid, fileGuid);
|
||||||
|
embedded++;
|
||||||
|
Debug.Log($"[DirichletMediation] Embedded dynamic framework: {frameworkName}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.Log("[DirichletMediation] GDT adapter disabled, skipping dynamic framework embedding");
|
||||||
|
}
|
||||||
|
|
||||||
|
pbxProject.WriteToFile(projectFilePath);
|
||||||
|
if (searchPathsAdded)
|
||||||
|
{
|
||||||
|
Debug.Log($"[DirichletMediation] Added ad SDK framework/header search paths to {targetInfo.FrameworkTargetName}");
|
||||||
|
}
|
||||||
|
Debug.Log($"[DirichletMediation] Embedded {embedded} GDT dynamic frameworks into {targetInfo.AppTargetName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ResolveUnityFrameworkTargetGuid(PBXProject pbxProject, TargetInfo targetInfo)
|
||||||
|
{
|
||||||
|
#if UNITY_2019_3_OR_NEWER
|
||||||
|
var targetGuid = pbxProject.GetUnityFrameworkTargetGuid();
|
||||||
|
#else
|
||||||
|
var targetGuid = targetInfo.FrameworkTargetGuid;
|
||||||
|
#endif
|
||||||
|
if (string.IsNullOrEmpty(targetGuid))
|
||||||
|
{
|
||||||
|
targetGuid = targetInfo.FrameworkTargetGuid;
|
||||||
|
}
|
||||||
|
if (string.IsNullOrEmpty(targetGuid))
|
||||||
|
{
|
||||||
|
targetGuid = pbxProject.TargetGuidByName(targetInfo.FrameworkTargetName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return targetGuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool AddAdSDKSearchPaths(PBXProject pbxProject, string targetGuid, string[] frameworkSearchPaths, string[] headerSearchPaths)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(targetGuid))
|
||||||
|
{
|
||||||
|
Debug.LogWarning("[DirichletMediation] UnityFramework target GUID is empty, skipping ad SDK search paths");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pbxProject.AddBuildProperty(targetGuid, "FRAMEWORK_SEARCH_PATHS", "$(inherited)");
|
||||||
|
foreach (var searchPath in frameworkSearchPaths)
|
||||||
|
{
|
||||||
|
pbxProject.AddBuildProperty(targetGuid, "FRAMEWORK_SEARCH_PATHS", searchPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
pbxProject.AddBuildProperty(targetGuid, "HEADER_SEARCH_PATHS", "$(inherited)");
|
||||||
|
foreach (var searchPath in headerSearchPaths)
|
||||||
|
{
|
||||||
|
pbxProject.AddBuildProperty(targetGuid, "HEADER_SEARCH_PATHS", searchPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PatchPodsXCConfigSearchPaths(
|
||||||
|
string projectPath,
|
||||||
|
TargetInfo targetInfo,
|
||||||
|
string[] frameworkSearchPaths,
|
||||||
|
string[] headerSearchPaths)
|
||||||
|
{
|
||||||
|
var targetSupportRoot = Path.Combine(projectPath, "Pods", "Target Support Files");
|
||||||
|
if (!Directory.Exists(targetSupportRoot))
|
||||||
|
{
|
||||||
|
Debug.LogWarning("[DirichletMediation] CocoaPods target support files not found, skipping xcconfig search path patch");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetPrefix = "Pods-" + targetInfo.FrameworkTargetName;
|
||||||
|
var xcconfigFiles = Directory.GetFiles(targetSupportRoot, "*.xcconfig", SearchOption.AllDirectories)
|
||||||
|
.Where(path => Path.GetFileNameWithoutExtension(path).StartsWith(targetPrefix, System.StringComparison.OrdinalIgnoreCase))
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
if (xcconfigFiles.Length == 0)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[DirichletMediation] No CocoaPods xcconfig found for {targetInfo.FrameworkTargetName}, skipping search path patch");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var patched = 0;
|
||||||
|
foreach (var xcconfigPath in xcconfigFiles)
|
||||||
|
{
|
||||||
|
var lines = File.ReadAllLines(xcconfigPath).ToList();
|
||||||
|
var changed = UpsertXCConfigBuildSetting(lines, "FRAMEWORK_SEARCH_PATHS", frameworkSearchPaths);
|
||||||
|
changed |= UpsertXCConfigBuildSetting(lines, "HEADER_SEARCH_PATHS", headerSearchPaths);
|
||||||
|
|
||||||
|
if (!changed)
|
||||||
{
|
{
|
||||||
Debug.LogWarning($"[DirichletMediation] Dynamic framework not found: {frameworkName}");
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var relativePath = frameworkPath.StartsWith(projectPath)
|
File.WriteAllLines(xcconfigPath, lines);
|
||||||
? frameworkPath.Substring(projectPath.Length + 1)
|
patched++;
|
||||||
: frameworkPath;
|
|
||||||
|
|
||||||
var fileGuid = pbxProject.AddFile(relativePath, "Frameworks/" + frameworkName);
|
|
||||||
PBXProjectExtensions.AddFileToEmbedFrameworks(pbxProject, mainTargetGuid, fileGuid);
|
|
||||||
embedded++;
|
|
||||||
Debug.Log($"[DirichletMediation] Embedded dynamic framework: {frameworkName}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (embedded > 0)
|
Debug.Log($"[DirichletMediation] Patched {patched} CocoaPods xcconfig search path file(s) for {targetInfo.FrameworkTargetName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool UpsertXCConfigBuildSetting(System.Collections.Generic.List<string> lines, string propertyName, string[] searchPaths)
|
||||||
|
{
|
||||||
|
var insertIndex = -1;
|
||||||
|
var originalLines = new System.Collections.Generic.List<string>();
|
||||||
|
var tokens = new System.Collections.Generic.List<string>();
|
||||||
|
|
||||||
|
for (var i = lines.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
pbxProject.WriteToFile(projectFilePath);
|
if (!IsXCConfigBuildSettingLine(lines[i], propertyName))
|
||||||
Debug.Log($"[DirichletMediation] Embedded {embedded} GDT dynamic frameworks into {targetInfo.AppTargetName}");
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
insertIndex = i;
|
||||||
|
originalLines.Add(lines[i]);
|
||||||
|
CollectXCConfigBuildSettingTokens(lines[i], tokens);
|
||||||
|
lines.RemoveAt(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ContainsXCConfigToken(tokens, "$(inherited)"))
|
||||||
|
{
|
||||||
|
tokens.Insert(0, "$(inherited)");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var searchPath in searchPaths)
|
||||||
|
{
|
||||||
|
AddXCConfigTokenIfMissing(tokens, searchPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
var newLine = $"{propertyName} = {string.Join(" ", tokens)}";
|
||||||
|
|
||||||
|
if (insertIndex < 0)
|
||||||
|
{
|
||||||
|
lines.Add(newLine);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.Insert(insertIndex, newLine);
|
||||||
|
return originalLines.Count != 1 || originalLines[0] != newLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CollectXCConfigBuildSettingTokens(string line, System.Collections.Generic.List<string> tokens)
|
||||||
|
{
|
||||||
|
var separatorIndex = line.IndexOf('=');
|
||||||
|
if (separatorIndex < 0 || separatorIndex >= line.Length - 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var value = line.Substring(separatorIndex + 1).Trim();
|
||||||
|
var lineTokens = value.Split(new[] { ' ', '\t' }, System.StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
foreach (var token in lineTokens)
|
||||||
|
{
|
||||||
|
if (token.IndexOf("XCFrameworkIntermediates", System.StringComparison.OrdinalIgnoreCase) >= 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
AddXCConfigTokenIfMissing(tokens, token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ContainsXCConfigToken(System.Collections.Generic.List<string> tokens, string value)
|
||||||
|
{
|
||||||
|
var normalizedValue = NormalizeXCConfigToken(value);
|
||||||
|
return tokens.Any(token => NormalizeXCConfigToken(token) == normalizedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddXCConfigTokenIfMissing(System.Collections.Generic.List<string> tokens, string value)
|
||||||
|
{
|
||||||
|
if (!ContainsXCConfigToken(tokens, value))
|
||||||
|
{
|
||||||
|
tokens.Add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeXCConfigToken(string token)
|
||||||
|
{
|
||||||
|
return (token ?? string.Empty).Trim().Trim('"');
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsXCConfigBuildSettingLine(string line, string propertyName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(line))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var trimmed = line.TrimStart();
|
||||||
|
return trimmed.StartsWith(propertyName + " =", System.StringComparison.Ordinal)
|
||||||
|
|| trimmed.StartsWith(propertyName + "=", System.StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string[] GetAdSDKFrameworkSearchPaths()
|
||||||
|
{
|
||||||
|
var sdkVersion = ResolveIOSSDKVersion();
|
||||||
|
var enableCsj = EditorPrefs.GetBool("Dirichlet.iOS.EnableCSJ", true);
|
||||||
|
var enableGdt = EditorPrefs.GetBool("Dirichlet.iOS.EnableGDT", true);
|
||||||
|
var paths = new System.Collections.Generic.List<string>
|
||||||
|
{
|
||||||
|
VersionedDirichletFrameworkSearchPath("DirichletAdSDK", sdkVersion),
|
||||||
|
VersionedDirichletFrameworkSearchPath("DirichletCoreSDK", sdkVersion),
|
||||||
|
VersionedDirichletFrameworkSearchPath("DirichletMediationSDK", sdkVersion),
|
||||||
|
VersionedDirichletFrameworkSearchPath("DirichletMediationAdapterDRA", sdkVersion)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (enableCsj)
|
||||||
|
{
|
||||||
|
paths.Add(VersionedDirichletFrameworkSearchPath("DirichletMediationAdapterCSJ", sdkVersion));
|
||||||
|
paths.Add("$(PODS_ROOT)/Ads-CN/SDK/BUAdSDK.xcframework/" + IOSDeviceSlice);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enableGdt)
|
||||||
|
{
|
||||||
|
paths.Add(VersionedDirichletFrameworkSearchPath("DirichletMediationAdapterGDT", sdkVersion));
|
||||||
|
paths.Add("$(PODS_ROOT)/GDTMobSDK/GDTFramework/GDTMobSDK.xcframework/" + IOSDeviceSlice);
|
||||||
|
paths.Add("$(PODS_ROOT)/GDTMobSDK/GDTFramework/Tquic.xcframework/" + TquicIOSDeviceSlice);
|
||||||
|
}
|
||||||
|
|
||||||
|
return paths.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string[] GetAdSDKHeaderSearchPaths(string[] frameworkSearchPaths)
|
||||||
|
{
|
||||||
|
return frameworkSearchPaths
|
||||||
|
.Select(ToFrameworkHeaderSearchPath)
|
||||||
|
.Where(path => !string.IsNullOrEmpty(path))
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string VersionedDirichletFrameworkSearchPath(string frameworkName, string sdkVersion)
|
||||||
|
{
|
||||||
|
return $"$(PODS_ROOT)/{frameworkName}/{frameworkName}-{sdkVersion}/{frameworkName}.xcframework/{IOSDeviceSlice}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ToFrameworkHeaderSearchPath(string frameworkSearchPath)
|
||||||
|
{
|
||||||
|
var parts = frameworkSearchPath.Split(new[] { '/' }, System.StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
var xcframeworkPart = parts.LastOrDefault(part => part.EndsWith(".xcframework", System.StringComparison.OrdinalIgnoreCase));
|
||||||
|
if (string.IsNullOrEmpty(xcframeworkPart))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var frameworkName = xcframeworkPart.Substring(0, xcframeworkPart.Length - ".xcframework".Length);
|
||||||
|
return $"{frameworkSearchPath}/{frameworkName}.framework/Headers";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,33 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 90b3b17e0788494398662e729c344ee2
|
|
||||||
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:
|
|
||||||
|
|
||||||
Binary file not shown.
@@ -1,33 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 930136242e574a2b89110e6a25e49065
|
|
||||||
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:
|
|
||||||
|
|
||||||
115
Assets/SDK_MAINTENANCE.md
Normal file
115
Assets/SDK_MAINTENANCE.md
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
# TapADN SDK 封装维护说明
|
||||||
|
|
||||||
|
本文记录 `Commercialization.tapadn` 对官方 Dirichlet/TapADN 聚合 SDK 的封装、改动和升级注意事项。维护或升级 SDK 时先读本文,再改文件。
|
||||||
|
|
||||||
|
## 当前 SDK 版本
|
||||||
|
|
||||||
|
官方链接:
|
||||||
|
|
||||||
|
* 资源下载页:https://ssp.dirichlet.cn/docs/resource-download/
|
||||||
|
* 聚合 Unity 接入文档:https://ssp.dirichlet.cn/docs/dirichlet-mediation-sdk/dirichlet-mediation-sdk-guide-unity/
|
||||||
|
* 聚合 Android 接入文档:https://ssp.dirichlet.cn/docs/dirichlet-mediation-sdk/dirichlet-mediation-sdk-guide-android/
|
||||||
|
|
||||||
|
| 范围 | 当前版本 | 来源 | 说明 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Unity 聚合 SDK | 4.2.5.0 | 官方 `dirichlet_mediation_unity_4.2.5.0.unitypackage` | 官方资源页当前仍只提供 Unity 4.2.5.0。 |
|
||||||
|
| Android 聚合 native AAR | 4.2.7.3 | 官方 `dirichlet_ad_mediation_4.2.7.3.zip` | 已单独升级 Android native 侧。 |
|
||||||
|
| Android Pangle | 7.6.1.2 | 官方 Android 聚合接入文档 | 通过 Gradle 后处理注入 Maven 依赖。 |
|
||||||
|
| Android GDT | 4.690.1560 | 官方 Android 聚合接入文档 | 通过 Gradle 后处理注入 Maven 依赖。 |
|
||||||
|
| iOS 聚合 SDK Pod | 4.2.0.1 | 官方 iOS 聚合版本 | 默认值在 `DirichletMediationIOSPostProcessor`,可用环境变量覆盖。 |
|
||||||
|
|
||||||
|
## 模块结构
|
||||||
|
|
||||||
|
| 路径 | 归属 | 维护说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `Assets/DirichletMediation` | 官方 Unity SDK 基础层 | 不再视为纯官方源码,部分文件已有本地改动。 |
|
||||||
|
| `Assets/Plugins/Android/DirichletMediation` | 官方 Android Unity bridge + native AAR | Java bridge 当前与官方 Unity 包一致;AAR 已升级到 Android native 4.2.7.3。 |
|
||||||
|
| `Assets/Plugins/Android/libs` | Android 本地依赖 | 保留 `iadsdk-release-2.3.102.110.aar`;Pangle/GDT 不再放本地 AAR,改由 Maven 注入。 |
|
||||||
|
| `Assets/Tapadn_Adapter` | 本模块封装层 | 对接 `CC-Framework.Commercialization`,不要让业务层直接调用官方 SDK。 |
|
||||||
|
| `Assets/Samples~` | 可选调试样例 | 不是官方 sample,是本模块验证 `ADManager` 流程的样例。 |
|
||||||
|
|
||||||
|
## 官方源码改动清单
|
||||||
|
|
||||||
|
以下文件来自官方 Unity SDK,但本仓库已经修改过。升级官方 Unity SDK 时不能直接覆盖,必须三方合并:
|
||||||
|
|
||||||
|
| 文件 | 本地改动目的 |
|
||||||
|
| --- | --- |
|
||||||
|
| `Assets/DirichletMediation/Runtime/DirichletAdTypes.cs` | 补齐 Unity 侧 ad handle、auto-ad fallback、事件会话保活、`IsValid` / `Destroy` 包装等桥接行为。 |
|
||||||
|
| `Assets/DirichletMediation/Runtime/DirichletMediationSdk.cs` | 增强 Android/iOS/noop bridge、Unity 线程派发、iOS load/init callback receiver 和平台兼容逻辑。 |
|
||||||
|
| `Assets/DirichletMediation/Editor/DirichletGradlePostProcessor.cs` | 改为适配 Unity 导出的 Gradle 工程,注入仓库、support/glide/okhttp、Pangle/GDT Maven 依赖,避免本地 AAR 重复类。 |
|
||||||
|
| `Assets/DirichletMediation/Editor/DirichletMediationIOSPostProcessor.cs` | 自动生成 Podfile,处理 UnityFramework target、framework/header search paths、SKAdNetwork、ATT、GDT 动态 framework 嵌入和 CocoaPods 异常兜底。 |
|
||||||
|
|
||||||
|
以下官方文件当前与官方 Unity 4.2.5.0 包一致,升级时可优先直接替换后验证:
|
||||||
|
|
||||||
|
| 文件 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `Assets/DirichletMediation/Editor/DirichletMediationDependencies.xml` | EDM4U 依赖声明。 |
|
||||||
|
| `Assets/Plugins/Android/DirichletMediation/src/main/java/com/dirichlet/unity/DirichletUnityBridge.java` | Android Java bridge。 |
|
||||||
|
| `Assets/Plugins/Android/AndroidManifest.xml` | 官方基础 Manifest。 |
|
||||||
|
| `Assets/Plugins/Android/proguard-user.txt` | 当前为空文件。 |
|
||||||
|
|
||||||
|
## 封装层做了什么
|
||||||
|
|
||||||
|
`Tapadn_Adapter` 的目标是让业务项目只面对 `ADManager` / `ADConfig` / `AD_Type`:
|
||||||
|
|
||||||
|
1. `TapadnAdController` 从 `ADConfig` 和 `CommonKeyValues` 解析媒体 ID、MediaKey、广告位、渠道、debug、权限、预加载和展示超时配置。
|
||||||
|
2. `TapadnCommercialization` 提供创建 controller、初始化 `ADManager`、创建默认 `ADConfig`、批量写入激励场景广告位映射的便捷入口。
|
||||||
|
3. `TapadnAdRequestFactory` 统一构建 `DirichletAdRequest`,保证 SpaceId 是正整数,并注入 UserId、奖励名、奖励数量、展示尺寸。
|
||||||
|
4. `TapadnAwardVideoPlayer` 对接激励视频。手动 load/show 模式下,按 SpaceId 隔离缓存;展示关闭、展示失败、`IsValid == false`、或默认 600 秒未消费都会销毁缓存。
|
||||||
|
5. `TapadnInteractionPlayer` 对接插屏;`TapadnSplashPlayer` 对接开屏。两者保持单默认广告位语义。
|
||||||
|
6. `TapadnSmartLoadOrchestrator` 维护场景学习数据和智能预加载归因。当前是保守策略:默认不开启;开启后先观察用户点击广告的概率,达到阈值才自动预加载。
|
||||||
|
|
||||||
|
## 场景广告位规则
|
||||||
|
|
||||||
|
激励视频支持游戏场景映射不同 SpaceId:
|
||||||
|
|
||||||
|
| 配置 key | 示例 |
|
||||||
|
| --- | --- |
|
||||||
|
| `tapadn.rewarded_scene_slot.<scene_id>` | `tapadn.rewarded_scene_slot.level_clear = 200101` |
|
||||||
|
| `tapadn.rewarded_scene_slots` | `level_clear=200101,daily_bonus=200102` |
|
||||||
|
| `tapadn.rewarded_scene_slots_json` | `{ "Mappings": [ { "Scene": "level_clear", "SlotId": "200101" } ] }` |
|
||||||
|
|
||||||
|
回退规则:
|
||||||
|
|
||||||
|
1. 未传场景、场景为空、场景未配置、配置 SpaceId 非法时,使用 `BaseAwardAdKeyValue.value`。
|
||||||
|
2. 如果两个场景配置到同一个 SpaceId,它们共享同一个广告位缓存。
|
||||||
|
3. 如果 A 场景 SpaceId 已加载但没展示,用户去 B 场景展示 B SpaceId,A 缓存不会被 B 消费;A 会等回到 A 时复用,或超时/失效后销毁。
|
||||||
|
|
||||||
|
## Android 升级步骤
|
||||||
|
|
||||||
|
1. 查官方资源页和聚合 Android 发布记录,确认最新 Android 聚合版本、Pangle 版本、GDT 版本。
|
||||||
|
2. 下载 `dirichlet_ad_mediation_<version>.zip`,校验官方 MD5。
|
||||||
|
3. 替换 `Assets/Plugins/Android/DirichletMediation/libs/DirichletAD_*_<version>.aar`。
|
||||||
|
4. 检查官方 Android 接入文档中的 Maven 依赖,更新 `DirichletGradlePostProcessor` 里的 `PangleSdkVersion`、`GdtSdkVersion`。
|
||||||
|
5. 不要同时保留 Pangle/GDT 本地 AAR 和 Maven 依赖,否则容易出现 duplicate classes。当前策略是 Pangle/GDT 走 Maven。
|
||||||
|
6. 搜索旧版本号,更新 `GLOBAL_DESIGN.md`、`README.md`、`CHANGELOG.md` 和 `Assets/package.json`。
|
||||||
|
7. 至少执行 `dotnet build Commercialization.tapadn.sln --no-restore` 和 `git diff --check`。
|
||||||
|
8. Android 真机构建仍需在消费项目里跑项目自带构建流程验证 Manifest merge、Gradle dependency resolution 和广告展示回调。
|
||||||
|
|
||||||
|
## Unity SDK 升级步骤
|
||||||
|
|
||||||
|
1. 下载官方新的 `dirichlet_mediation_unity_<version>.unitypackage` 并校验 MD5。
|
||||||
|
2. 解包到临时目录,对比 `Assets/DirichletMediation` 和 `Assets/Plugins/Android`。
|
||||||
|
3. 对“官方源码改动清单”里的 4 个文件做三方合并,不允许直接覆盖。
|
||||||
|
4. 官方 sample 不进入默认包;如需要保留调试能力,继续放到 `Samples~`。
|
||||||
|
5. 检查 Android Java bridge API 是否和当前 C# bridge 匹配,重点是 load/show/destroy/isValid/auto-ad callback。
|
||||||
|
6. 检查 iOS Pod 名称和版本是否变化,同步 `DirichletMediationIOSPostProcessor` 的默认版本和 search path 规则。
|
||||||
|
7. 重新验证 Android/iOS editor compile;真机展示验证至少覆盖初始化、激励 load/show/close/reward、插屏、开屏、无填充失败。
|
||||||
|
|
||||||
|
## 发布步骤
|
||||||
|
|
||||||
|
包根是 `Assets/package.json`,消费者应使用:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"com.commercialization.tapadn": "http://private.lightyears.ltd:18650/foldcc/Commercialization.tapadn.git?path=/Assets#<version>"
|
||||||
|
```
|
||||||
|
|
||||||
|
发布命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git subtree split --prefix=Assets --branch upm
|
||||||
|
git tag <version>
|
||||||
|
git push origin master upm <version>
|
||||||
|
```
|
||||||
|
|
||||||
|
注意:当前版本 tag 打在完整仓库 `master` 上,因此消费者必须带 `?path=/Assets`。
|
||||||
7
Assets/SDK_MAINTENANCE.md.meta
Normal file
7
Assets/SDK_MAINTENANCE.md.meta
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: dcf571baef764df0aa6789990d47c36a
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,11 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<androidPackages>
|
<!-- WeChat OpenSDK is shipped as a local AAR under Assets/Plugins/Android. -->
|
||||||
<androidPackage spec="com.tencent.mm.opensdk:wechat-sdk-android:6.8.34">
|
|
||||||
<repositories>
|
|
||||||
<repository>https://maven.aliyun.com/repository/public</repository>
|
|
||||||
<repository>https://repo.maven.apache.org/maven2</repository>
|
|
||||||
</repositories>
|
|
||||||
</androidPackage>
|
|
||||||
</androidPackages>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Dirichlet.Mediation;
|
using Dirichlet.Mediation;
|
||||||
using Runtime.ADAggregator;
|
using Runtime.ADAggregator;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@@ -7,8 +8,11 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
|
|||||||
{
|
{
|
||||||
private const float RewardCloseSettleDelaySeconds = 0.25f;
|
private const float RewardCloseSettleDelaySeconds = 0.25f;
|
||||||
|
|
||||||
|
private readonly Dictionary<string, RewardedSlotCache> _slotCaches = new Dictionary<string, RewardedSlotCache>(StringComparer.Ordinal);
|
||||||
|
|
||||||
private DirichletAdNative _adNative;
|
private DirichletAdNative _adNative;
|
||||||
private DirichletRewardVideoAd _loadedAd;
|
private string _defaultSlotId;
|
||||||
|
private string _activeSlotId;
|
||||||
private bool _rewardVerified;
|
private bool _rewardVerified;
|
||||||
private bool _rewardVerifyReceived;
|
private bool _rewardVerifyReceived;
|
||||||
private bool _closePendingRewardVerify;
|
private bool _closePendingRewardVerify;
|
||||||
@@ -22,30 +26,46 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
|
|||||||
|
|
||||||
public override void OnInit()
|
public override void OnInit()
|
||||||
{
|
{
|
||||||
|
_defaultSlotId = Key;
|
||||||
_adNative = DirichletAdManager.CreateAdNative();
|
_adNative = DirichletAdManager.CreateAdNative();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsReadly()
|
public override bool IsReadly()
|
||||||
{
|
{
|
||||||
|
var slotId = ResolveCurrentSlotId();
|
||||||
if (UseAutoLoad())
|
if (UseAutoLoad())
|
||||||
{
|
{
|
||||||
return TapadnAdRequestFactory.TryParseSlotId(Key, out _);
|
return TapadnAdRequestFactory.TryParseSlotId(slotId, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_loadedAd != null && _loadedAd.IsLoaded && _loadedAd.IsValid)
|
var cache = GetCache(slotId);
|
||||||
|
if (IsCacheReady(cache))
|
||||||
{
|
{
|
||||||
curState = 2;
|
curState = 2;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cache != null && cache.Loading)
|
||||||
|
{
|
||||||
|
curState = 1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cache != null)
|
||||||
|
{
|
||||||
|
RemoveCache(slotId);
|
||||||
|
}
|
||||||
|
|
||||||
|
curState = 0;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void LoadAD()
|
public override void LoadAD()
|
||||||
{
|
{
|
||||||
if (!TapadnAdRequestFactory.TryParseSlotId(Key, out _))
|
var slotId = ResolveCurrentSlotId();
|
||||||
|
if (!TapadnAdRequestFactory.TryParseSlotId(slotId, out _))
|
||||||
{
|
{
|
||||||
Debug.LogError($"[TapADN] Invalid rewarded slot id: {Key}");
|
Debug.LogError($"[TapADN] Invalid rewarded slot id: {slotId}");
|
||||||
curState = 0;
|
curState = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -55,7 +75,7 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
|
|||||||
curState = 2;
|
curState = 2;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_adNative.PreLoad(TapadnAdRequestFactory.BuildRewarded(Key, TapadnAdController.CurrentOptions), 3);
|
_adNative.PreLoad(TapadnAdRequestFactory.BuildRewarded(slotId, TapadnAdController.CurrentOptions), 3);
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
@@ -64,33 +84,37 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (curState == 1 || IsReadly())
|
var cache = GetCache(slotId);
|
||||||
|
if (cache != null && (cache.Loading || IsCacheReady(cache)))
|
||||||
{
|
{
|
||||||
|
curState = cache.Loading ? 1 : 2;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cache = new RewardedSlotCache { Loading = true };
|
||||||
|
_slotCaches[slotId] = cache;
|
||||||
curState = 1;
|
curState = 1;
|
||||||
TapadnSmartLoadOrchestrator.OnLoadStarted(AD_Type.AwardVideo, AdScene);
|
TapadnSmartLoadOrchestrator.OnLoadStarted(AD_Type.AwardVideo, AdScene, slotId);
|
||||||
_adNative.LoadRewardVideoAd(
|
_adNative.LoadRewardVideoAd(
|
||||||
TapadnAdRequestFactory.BuildRewarded(Key, TapadnAdController.CurrentOptions),
|
TapadnAdRequestFactory.BuildRewarded(slotId, TapadnAdController.CurrentOptions),
|
||||||
ad =>
|
ad =>
|
||||||
{
|
{
|
||||||
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.AwardVideo, AdScene, true);
|
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.AwardVideo, AdScene, true, slotId);
|
||||||
_loadedAd?.Destroy();
|
DestroyCachedAd(cache);
|
||||||
_loadedAd = ad;
|
cache.Ad = ad;
|
||||||
_loadedAd.Shown += OnManualShown;
|
cache.Loading = false;
|
||||||
_loadedAd.Clicked += OnManualClicked;
|
cache.LoadedUnix = GetNowUnixSeconds();
|
||||||
_loadedAd.ShowFailed += OnManualShowFailed;
|
RegisterManualEvents(slotId, cache.Ad);
|
||||||
_loadedAd.RewardVerified += OnManualRewardVerify;
|
ScheduleCacheExpiration(slotId, cache);
|
||||||
_loadedAd.Closed += OnManualClosed;
|
curState = string.Equals(slotId, ResolveCurrentSlotId(), StringComparison.Ordinal) ? 2 : curState;
|
||||||
curState = 2;
|
Debug.Log($"[TapADN] Rewarded loaded. scene={NormalizeScenario(AdScene)}, slot={slotId}");
|
||||||
Debug.Log($"[TapADN] Rewarded loaded. slot={Key}");
|
|
||||||
},
|
},
|
||||||
error =>
|
error =>
|
||||||
{
|
{
|
||||||
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.AwardVideo, AdScene, false);
|
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.AwardVideo, AdScene, false, slotId);
|
||||||
curState = 0;
|
RemoveCache(slotId);
|
||||||
Debug.LogError($"[TapADN] Rewarded load failed. code={error.Code}, message={error.Message}");
|
curState = string.Equals(slotId, ResolveCurrentSlotId(), StringComparison.Ordinal) ? 0 : curState;
|
||||||
|
Debug.LogError($"[TapADN] Rewarded load failed. slot={slotId}, code={error.Code}, message={error.Message}");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,16 +128,20 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
|
|||||||
_showSettled = false;
|
_showSettled = false;
|
||||||
_rewardCloseSettleHandler?.Kill();
|
_rewardCloseSettleHandler?.Kill();
|
||||||
_rewardCloseSettleHandler = null;
|
_rewardCloseSettleHandler = null;
|
||||||
|
_activeSlotId = ResolveCurrentSlotId();
|
||||||
|
Key = _activeSlotId;
|
||||||
curState = 0;
|
curState = 0;
|
||||||
|
|
||||||
if (UseAutoLoad())
|
if (UseAutoLoad())
|
||||||
{
|
{
|
||||||
_adNative.ShowRewardVideoAutoAd(TapadnAdRequestFactory.BuildRewarded(Key, TapadnAdController.CurrentOptions), this);
|
_adNative.ShowRewardVideoAutoAd(TapadnAdRequestFactory.BuildRewarded(_activeSlotId, TapadnAdController.CurrentOptions), this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_loadedAd == null || !_loadedAd.Show())
|
var cache = GetCache(_activeSlotId);
|
||||||
|
if (!IsCacheReady(cache) || cache.Ad == null || !cache.Ad.Show())
|
||||||
{
|
{
|
||||||
|
RemoveCache(_activeSlotId);
|
||||||
OnError(new DirichletError("show_failed", "ShowRewardVideoAd returned false"));
|
OnError(new DirichletError("show_failed", "ShowRewardVideoAd returned false"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -130,14 +158,14 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
|
|||||||
_rewardCloseSettleHandler?.Kill();
|
_rewardCloseSettleHandler?.Kill();
|
||||||
_rewardCloseSettleHandler = null;
|
_rewardCloseSettleHandler = null;
|
||||||
curState = 0;
|
curState = 0;
|
||||||
TapadnSmartLoadOrchestrator.OnShowError(AD_Type.AwardVideo, AdScene);
|
TapadnSmartLoadOrchestrator.OnShowError(AD_Type.AwardVideo, AdScene, _activeSlotId);
|
||||||
Debug.LogError($"[TapADN] Rewarded show failed. code={error?.Code}, message={error?.Message}");
|
Debug.LogError($"[TapADN] Rewarded show failed. slot={_activeSlotId}, code={error?.Code}, message={error?.Message}");
|
||||||
adListener.OnShowError();
|
adListener.OnShowError();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnAdShow()
|
public void OnAdShow()
|
||||||
{
|
{
|
||||||
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.AwardVideo, AdScene);
|
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.AwardVideo, AdScene, _activeSlotId);
|
||||||
NotifyShowStarted();
|
NotifyShowStarted();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,12 +203,23 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
|
|||||||
|
|
||||||
public override void OnPlayRequestStarted()
|
public override void OnPlayRequestStarted()
|
||||||
{
|
{
|
||||||
TapadnSmartLoadOrchestrator.OnPlayRequestStarted(AD_Type.AwardVideo, AdScene, !UseAutoLoad() && IsReadly());
|
var slotId = ResolveCurrentSlotId();
|
||||||
|
Key = slotId;
|
||||||
|
TapadnSmartLoadOrchestrator.OnPlayRequestStarted(AD_Type.AwardVideo, AdScene, !UseAutoLoad() && IsReadly(), slotId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void EnterAdScenario(string scenario)
|
public override void EnterAdScenario(string scenario)
|
||||||
{
|
{
|
||||||
TapadnSmartLoadOrchestrator.OnEnterAdScenario(AD_Type.AwardVideo, scenario, Key);
|
AdScene = NormalizeScenario(scenario);
|
||||||
|
var slotId = ResolveCurrentSlotId();
|
||||||
|
Key = slotId;
|
||||||
|
TapadnSmartLoadOrchestrator.OnEnterAdScenario(AD_Type.AwardVideo, AdScene, slotId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ResolveCurrentSlotId()
|
||||||
|
{
|
||||||
|
var slotId = TapadnAdController.CurrentOptions?.ResolveRewardedSlotId(_defaultSlotId, AdScene) ?? _defaultSlotId;
|
||||||
|
return string.IsNullOrWhiteSpace(slotId) ? _defaultSlotId : slotId.Trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool UseAutoLoad()
|
private bool UseAutoLoad()
|
||||||
@@ -188,8 +227,66 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
|
|||||||
return TapadnAdController.CurrentOptions?.RewardedAutoLoad ?? true;
|
return TapadnAdController.CurrentOptions?.RewardedAutoLoad ?? true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnManualShown()
|
private RewardedSlotCache GetCache(string slotId)
|
||||||
{
|
{
|
||||||
|
return !string.IsNullOrWhiteSpace(slotId) && _slotCaches.TryGetValue(slotId, out var cache) ? cache : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsCacheReady(RewardedSlotCache cache)
|
||||||
|
{
|
||||||
|
if (cache == null || cache.Loading || cache.Ad == null || !cache.Ad.IsLoaded || !cache.Ad.IsValid)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var maxAgeSeconds = TapadnAdController.CurrentOptions?.RewardedCacheMaxAgeSeconds ?? 0;
|
||||||
|
return maxAgeSeconds <= 0 || GetNowUnixSeconds() - cache.LoadedUnix <= maxAgeSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterManualEvents(string slotId, DirichletRewardVideoAd ad)
|
||||||
|
{
|
||||||
|
if (ad == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ad.Shown += () => OnManualShown(slotId);
|
||||||
|
ad.Clicked += OnManualClicked;
|
||||||
|
ad.ShowFailed += error => OnManualShowFailed(slotId, error);
|
||||||
|
ad.RewardVerified += OnManualRewardVerify;
|
||||||
|
ad.Closed += () => OnManualClosed(slotId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ScheduleCacheExpiration(string slotId, RewardedSlotCache cache)
|
||||||
|
{
|
||||||
|
cache.ExpireHandler?.Kill();
|
||||||
|
cache.ExpireHandler = null;
|
||||||
|
|
||||||
|
var maxAgeSeconds = TapadnAdController.CurrentOptions?.RewardedCacheMaxAgeSeconds ?? 600;
|
||||||
|
if (maxAgeSeconds <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.ExpireHandler = ADManager.Instance.CreateTimer(maxAgeSeconds, () =>
|
||||||
|
{
|
||||||
|
if (!ReferenceEquals(GetCache(slotId), cache))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Log($"[TapADN] Rewarded cache expired. slot={slotId}, maxAgeSeconds={maxAgeSeconds}");
|
||||||
|
RemoveCache(slotId);
|
||||||
|
if (string.Equals(slotId, ResolveCurrentSlotId(), StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
curState = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnManualShown(string slotId)
|
||||||
|
{
|
||||||
|
_activeSlotId = slotId;
|
||||||
OnAdShow();
|
OnAdShow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,8 +294,9 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnManualShowFailed(DirichletError error)
|
private void OnManualShowFailed(string slotId, DirichletError error)
|
||||||
{
|
{
|
||||||
|
RemoveCache(slotId);
|
||||||
OnError(error);
|
OnError(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,13 +305,49 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
|
|||||||
OnRewardVerify(args);
|
OnRewardVerify(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnManualClosed()
|
private void OnManualClosed(string slotId)
|
||||||
{
|
{
|
||||||
_loadedAd?.Destroy();
|
RemoveCache(slotId);
|
||||||
_loadedAd = null;
|
|
||||||
OnAdClose();
|
OnAdClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RemoveCache(string slotId)
|
||||||
|
{
|
||||||
|
var cache = GetCache(slotId);
|
||||||
|
if (cache == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DestroyCachedAd(cache);
|
||||||
|
_slotCaches.Remove(slotId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DestroyCachedAd(RewardedSlotCache cache)
|
||||||
|
{
|
||||||
|
if (cache?.Ad == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
cache.ExpireHandler?.Kill();
|
||||||
|
cache.ExpireHandler = null;
|
||||||
|
cache.Ad.Destroy();
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[TapADN] Rewarded Destroy failed: {exception.Message}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
cache.Ad = null;
|
||||||
|
cache.Loading = false;
|
||||||
|
cache.LoadedUnix = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void CompleteRewardedClose()
|
private void CompleteRewardedClose()
|
||||||
{
|
{
|
||||||
if (_showSettled)
|
if (_showSettled)
|
||||||
@@ -228,4 +362,22 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
|
|||||||
adListener.OnRewardVerify(_rewardVerified, TapadnAdController.CurrentOptions?.RewardAmount ?? 1, TapadnAdController.CurrentOptions?.RewardName ?? string.Empty);
|
adListener.OnRewardVerify(_rewardVerified, TapadnAdController.CurrentOptions?.RewardAmount ?? 1, TapadnAdController.CurrentOptions?.RewardName ?? string.Empty);
|
||||||
adListener.OnAdClose();
|
adListener.OnAdClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string NormalizeScenario(string scenario)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(scenario) ? "__default__" : scenario.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long GetNowUnixSeconds()
|
||||||
|
{
|
||||||
|
return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class RewardedSlotCache
|
||||||
|
{
|
||||||
|
public DirichletRewardVideoAd Ad;
|
||||||
|
public bool Loading;
|
||||||
|
public long LoadedUnix;
|
||||||
|
public AdTimeHandler ExpireHandler;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Runtime.ADAggregator;
|
using Runtime.ADAggregator;
|
||||||
|
|
||||||
public static class TapadnCommercialization
|
public static class TapadnCommercialization
|
||||||
@@ -31,4 +32,31 @@ public static class TapadnCommercialization
|
|||||||
config.BaseSplashAdKeyValue = new AdKeyValue { key = "splash", value = splashSlotId };
|
config.BaseSplashAdKeyValue = new AdKeyValue { key = "splash", value = splashSlotId };
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void SetRewardedSceneSlots(ADConfig config, IDictionary<string, string> sceneSlotIds)
|
||||||
|
{
|
||||||
|
if (config == null || sceneSlotIds == null || sceneSlotIds.Count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.CommonKeyValues == null)
|
||||||
|
{
|
||||||
|
config.CommonKeyValues = new List<AdKeyValue>();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var entry in sceneSlotIds)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(entry.Key) || string.IsNullOrWhiteSpace(entry.Value))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
config.CommonKeyValues.Add(new AdKeyValue
|
||||||
|
{
|
||||||
|
key = TapadnControllerOptions.RewardedSceneSlotPrefix + entry.Key.Trim(),
|
||||||
|
value = entry.Value.Trim()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ public sealed class TapadnControllerOptions
|
|||||||
public const string RewardedMaxLoadAttemptsKey = "tapadn.rewarded_max_load_attempts";
|
public const string RewardedMaxLoadAttemptsKey = "tapadn.rewarded_max_load_attempts";
|
||||||
public const string RewardedLoadRetryDelayMsKey = "tapadn.rewarded_load_retry_delay_ms";
|
public const string RewardedLoadRetryDelayMsKey = "tapadn.rewarded_load_retry_delay_ms";
|
||||||
public const string RewardedShowTimeoutMsKey = "tapadn.rewarded_show_timeout_ms";
|
public const string RewardedShowTimeoutMsKey = "tapadn.rewarded_show_timeout_ms";
|
||||||
|
public const string RewardedSceneSlotPrefix = "tapadn.rewarded_scene_slot.";
|
||||||
|
public const string RewardedSceneSlotsKey = "tapadn.rewarded_scene_slots";
|
||||||
|
public const string RewardedSceneSlotsJsonKey = "tapadn.rewarded_scene_slots_json";
|
||||||
|
public const string RewardedCacheMaxAgeSecondsKey = "tapadn.rewarded_cache_max_age_seconds";
|
||||||
public const string RewardNameKey = "tapadn.reward_name";
|
public const string RewardNameKey = "tapadn.reward_name";
|
||||||
public const string RewardAmountKey = "tapadn.reward_amount";
|
public const string RewardAmountKey = "tapadn.reward_amount";
|
||||||
public const string InterstitialAutoLoadKey = "tapadn.interstitial_auto_load";
|
public const string InterstitialAutoLoadKey = "tapadn.interstitial_auto_load";
|
||||||
@@ -64,6 +68,8 @@ public sealed class TapadnControllerOptions
|
|||||||
public int RewardedMaxLoadAttempts { get; set; } = 1;
|
public int RewardedMaxLoadAttempts { get; set; } = 1;
|
||||||
public int RewardedLoadRetryDelayMs { get; set; } = 500;
|
public int RewardedLoadRetryDelayMs { get; set; } = 500;
|
||||||
public int RewardedShowTimeoutMs { get; set; } = 20000;
|
public int RewardedShowTimeoutMs { get; set; } = 20000;
|
||||||
|
public int RewardedCacheMaxAgeSeconds { get; set; } = 600;
|
||||||
|
public Dictionary<string, string> RewardedSceneSlotIds { get; private set; } = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||||
public string RewardName { get; set; } = "reward";
|
public string RewardName { get; set; } = "reward";
|
||||||
public int RewardAmount { get; set; } = 1;
|
public int RewardAmount { get; set; } = 1;
|
||||||
public bool InterstitialAutoLoad { get; set; } = false;
|
public bool InterstitialAutoLoad { get; set; } = false;
|
||||||
@@ -205,6 +211,11 @@ public sealed class TapadnControllerOptions
|
|||||||
RewardedMaxLoadAttempts = incoming.RewardedMaxLoadAttempts;
|
RewardedMaxLoadAttempts = incoming.RewardedMaxLoadAttempts;
|
||||||
RewardedLoadRetryDelayMs = incoming.RewardedLoadRetryDelayMs;
|
RewardedLoadRetryDelayMs = incoming.RewardedLoadRetryDelayMs;
|
||||||
RewardedShowTimeoutMs = incoming.RewardedShowTimeoutMs;
|
RewardedShowTimeoutMs = incoming.RewardedShowTimeoutMs;
|
||||||
|
RewardedCacheMaxAgeSeconds = incoming.RewardedCacheMaxAgeSeconds;
|
||||||
|
if (incoming.RewardedSceneSlotIds != null && incoming.RewardedSceneSlotIds.Count > 0)
|
||||||
|
{
|
||||||
|
RewardedSceneSlotIds = CloneSceneSlotMap(incoming.RewardedSceneSlotIds);
|
||||||
|
}
|
||||||
RewardName = incoming.RewardName ?? RewardName;
|
RewardName = incoming.RewardName ?? RewardName;
|
||||||
RewardAmount = incoming.RewardAmount;
|
RewardAmount = incoming.RewardAmount;
|
||||||
InterstitialAutoLoad = incoming.InterstitialAutoLoad;
|
InterstitialAutoLoad = incoming.InterstitialAutoLoad;
|
||||||
@@ -266,6 +277,8 @@ public sealed class TapadnControllerOptions
|
|||||||
RewardedMaxLoadAttempts = GetInt(map, RewardedMaxLoadAttemptsKey) ?? RewardedMaxLoadAttempts;
|
RewardedMaxLoadAttempts = GetInt(map, RewardedMaxLoadAttemptsKey) ?? RewardedMaxLoadAttempts;
|
||||||
RewardedLoadRetryDelayMs = GetInt(map, RewardedLoadRetryDelayMsKey) ?? RewardedLoadRetryDelayMs;
|
RewardedLoadRetryDelayMs = GetInt(map, RewardedLoadRetryDelayMsKey) ?? RewardedLoadRetryDelayMs;
|
||||||
RewardedShowTimeoutMs = GetInt(map, RewardedShowTimeoutMsKey) ?? RewardedShowTimeoutMs;
|
RewardedShowTimeoutMs = GetInt(map, RewardedShowTimeoutMsKey) ?? RewardedShowTimeoutMs;
|
||||||
|
RewardedCacheMaxAgeSeconds = Math.Max(0, GetInt(map, RewardedCacheMaxAgeSecondsKey) ?? RewardedCacheMaxAgeSeconds);
|
||||||
|
ApplyRewardedSceneSlots(map);
|
||||||
RewardName = GetString(map, RewardNameKey) ?? RewardName;
|
RewardName = GetString(map, RewardNameKey) ?? RewardName;
|
||||||
RewardAmount = GetInt(map, RewardAmountKey) ?? RewardAmount;
|
RewardAmount = GetInt(map, RewardAmountKey) ?? RewardAmount;
|
||||||
InterstitialAutoLoad = GetBool(map, InterstitialAutoLoadKey) ?? InterstitialAutoLoad;
|
InterstitialAutoLoad = GetBool(map, InterstitialAutoLoadKey) ?? InterstitialAutoLoad;
|
||||||
@@ -286,6 +299,116 @@ public sealed class TapadnControllerOptions
|
|||||||
ExpressHeight = GetInt(map, ExpressHeightKey) ?? ExpressHeight;
|
ExpressHeight = GetInt(map, ExpressHeightKey) ?? ExpressHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string ResolveRewardedSlotId(string defaultSlotId, string scenario)
|
||||||
|
{
|
||||||
|
var normalizedScenario = NormalizeScenario(scenario);
|
||||||
|
if (RewardedSceneSlotIds != null &&
|
||||||
|
RewardedSceneSlotIds.TryGetValue(normalizedScenario, out var mappedSlotId) &&
|
||||||
|
TapadnAdRequestFactory.TryParseSlotId(mappedSlotId, out _))
|
||||||
|
{
|
||||||
|
return mappedSlotId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultSlotId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyRewardedSceneSlots(IDictionary<string, string> map)
|
||||||
|
{
|
||||||
|
foreach (var entry in map)
|
||||||
|
{
|
||||||
|
if (entry.Key == null || !entry.Key.StartsWith(RewardedSceneSlotPrefix, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var scene = entry.Key.Substring(RewardedSceneSlotPrefix.Length);
|
||||||
|
AddRewardedSceneSlot(scene, entry.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseSceneSlotPairs(GetString(map, RewardedSceneSlotsKey), AddRewardedSceneSlot);
|
||||||
|
ParseSceneSlotJson(GetString(map, RewardedSceneSlotsJsonKey), AddRewardedSceneSlot);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddRewardedSceneSlot(string scene, string slotId)
|
||||||
|
{
|
||||||
|
scene = NormalizeScenario(scene);
|
||||||
|
if (string.IsNullOrWhiteSpace(slotId) || !TapadnAdRequestFactory.TryParseSlotId(slotId.Trim(), out _))
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.LogWarning($"[TapADN] Ignore invalid rewarded scene slot. scene={scene}, slot={slotId}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RewardedSceneSlotIds[scene] = slotId.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ParseSceneSlotPairs(string value, Action<string, string> add)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(value) || add == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pairs = value.Split(new[] { ',', ';', '\n', '\r', '|' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
foreach (var pair in pairs)
|
||||||
|
{
|
||||||
|
var separatorIndex = pair.IndexOf('=');
|
||||||
|
if (separatorIndex < 0)
|
||||||
|
{
|
||||||
|
separatorIndex = pair.IndexOf(':');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (separatorIndex <= 0 || separatorIndex >= pair.Length - 1)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
add(pair.Substring(0, separatorIndex), pair.Substring(separatorIndex + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ParseSceneSlotJson(string json, Action<string, string> add)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(json) || add == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var parsed = JsonUtility.FromJson<TapadnSceneSlotMapConfig>(json);
|
||||||
|
if (parsed?.Mappings == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var mapping in parsed.Mappings)
|
||||||
|
{
|
||||||
|
if (mapping == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
add(mapping.Scene, mapping.SlotId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.LogWarning($"[TapADN] Parse rewarded scene slot JSON failed: {exception.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Dictionary<string, string> CloneSceneSlotMap(Dictionary<string, string> source)
|
||||||
|
{
|
||||||
|
return source == null
|
||||||
|
? new Dictionary<string, string>(StringComparer.Ordinal)
|
||||||
|
: new Dictionary<string, string>(source, StringComparer.Ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeScenario(string scenario)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(scenario) ? "__default__" : scenario.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
private static string GetString(IDictionary<string, string> map, params string[] keys)
|
private static string GetString(IDictionary<string, string> map, params string[] keys)
|
||||||
{
|
{
|
||||||
foreach (var key in keys)
|
foreach (var key in keys)
|
||||||
@@ -383,4 +506,17 @@ public sealed class TapadnControllerOptions
|
|||||||
|
|
||||||
return value.ToString();
|
return value.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
private sealed class TapadnSceneSlotMapConfig
|
||||||
|
{
|
||||||
|
public List<TapadnSceneSlotMapping> Mappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
private sealed class TapadnSceneSlotMapping
|
||||||
|
{
|
||||||
|
public string Scene;
|
||||||
|
public string SlotId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,12 +56,12 @@ public sealed class TapadnInteractionPlayer : ADPlayer, IDirichletInterstitialAu
|
|||||||
}
|
}
|
||||||
|
|
||||||
curState = 1;
|
curState = 1;
|
||||||
TapadnSmartLoadOrchestrator.OnLoadStarted(AD_Type.Interaction, AdScene);
|
TapadnSmartLoadOrchestrator.OnLoadStarted(AD_Type.Interaction, AdScene, Key);
|
||||||
_adNative.LoadInterstitialAd(
|
_adNative.LoadInterstitialAd(
|
||||||
TapadnAdRequestFactory.BuildInterstitial(Key, TapadnAdController.CurrentOptions),
|
TapadnAdRequestFactory.BuildInterstitial(Key, TapadnAdController.CurrentOptions),
|
||||||
ad =>
|
ad =>
|
||||||
{
|
{
|
||||||
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Interaction, AdScene, true);
|
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Interaction, AdScene, true, Key);
|
||||||
_loadedAd?.Destroy();
|
_loadedAd?.Destroy();
|
||||||
_loadedAd = ad;
|
_loadedAd = ad;
|
||||||
_loadedAd.Shown += OnManualShown;
|
_loadedAd.Shown += OnManualShown;
|
||||||
@@ -73,7 +73,7 @@ public sealed class TapadnInteractionPlayer : ADPlayer, IDirichletInterstitialAu
|
|||||||
},
|
},
|
||||||
error =>
|
error =>
|
||||||
{
|
{
|
||||||
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Interaction, AdScene, false);
|
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Interaction, AdScene, false, Key);
|
||||||
curState = 0;
|
curState = 0;
|
||||||
Debug.LogError($"[TapADN] Interstitial load failed. code={error.Code}, message={error.Message}");
|
Debug.LogError($"[TapADN] Interstitial load failed. code={error.Code}, message={error.Message}");
|
||||||
});
|
});
|
||||||
@@ -106,7 +106,7 @@ public sealed class TapadnInteractionPlayer : ADPlayer, IDirichletInterstitialAu
|
|||||||
}
|
}
|
||||||
|
|
||||||
_showSettled = true;
|
_showSettled = true;
|
||||||
TapadnSmartLoadOrchestrator.OnShowError(AD_Type.Interaction, AdScene);
|
TapadnSmartLoadOrchestrator.OnShowError(AD_Type.Interaction, AdScene, Key);
|
||||||
curState = 0;
|
curState = 0;
|
||||||
Debug.LogError($"[TapADN] Interstitial show failed. code={error?.Code}, message={error?.Message}");
|
Debug.LogError($"[TapADN] Interstitial show failed. code={error?.Code}, message={error?.Message}");
|
||||||
adListener.OnShowError();
|
adListener.OnShowError();
|
||||||
@@ -114,7 +114,7 @@ public sealed class TapadnInteractionPlayer : ADPlayer, IDirichletInterstitialAu
|
|||||||
|
|
||||||
public void OnAdShow()
|
public void OnAdShow()
|
||||||
{
|
{
|
||||||
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Interaction, AdScene);
|
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Interaction, AdScene, Key);
|
||||||
NotifyShowStarted();
|
NotifyShowStarted();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,12 +136,13 @@ public sealed class TapadnInteractionPlayer : ADPlayer, IDirichletInterstitialAu
|
|||||||
|
|
||||||
public override void OnPlayRequestStarted()
|
public override void OnPlayRequestStarted()
|
||||||
{
|
{
|
||||||
TapadnSmartLoadOrchestrator.OnPlayRequestStarted(AD_Type.Interaction, AdScene, !UseAutoLoad() && IsReadly());
|
TapadnSmartLoadOrchestrator.OnPlayRequestStarted(AD_Type.Interaction, AdScene, !UseAutoLoad() && IsReadly(), Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void EnterAdScenario(string scenario)
|
public override void EnterAdScenario(string scenario)
|
||||||
{
|
{
|
||||||
TapadnSmartLoadOrchestrator.OnEnterAdScenario(AD_Type.Interaction, scenario, Key);
|
AdScene = string.IsNullOrWhiteSpace(scenario) ? "__default__" : scenario.Trim();
|
||||||
|
TapadnSmartLoadOrchestrator.OnEnterAdScenario(AD_Type.Interaction, AdScene, Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool UseAutoLoad()
|
private bool UseAutoLoad()
|
||||||
@@ -151,7 +152,7 @@ public sealed class TapadnInteractionPlayer : ADPlayer, IDirichletInterstitialAu
|
|||||||
|
|
||||||
private void OnManualShown()
|
private void OnManualShown()
|
||||||
{
|
{
|
||||||
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Interaction, AdScene);
|
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Interaction, AdScene, Key);
|
||||||
NotifyShowStarted();
|
NotifyShowStarted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public static class TapadnSmartLoadOrchestrator
|
|||||||
private static TapadnSmartLoadConfig _runtimeConfig;
|
private static TapadnSmartLoadConfig _runtimeConfig;
|
||||||
private static Dictionary<string, TapadnSmartLoadSceneState> _states = new Dictionary<string, TapadnSmartLoadSceneState>(StringComparer.Ordinal);
|
private static Dictionary<string, TapadnSmartLoadSceneState> _states = new Dictionary<string, TapadnSmartLoadSceneState>(StringComparer.Ordinal);
|
||||||
private static Dictionary<string, TapadnSmartLoadPolicyItem> _policies = new Dictionary<string, TapadnSmartLoadPolicyItem>(StringComparer.Ordinal);
|
private static Dictionary<string, TapadnSmartLoadPolicyItem> _policies = new Dictionary<string, TapadnSmartLoadPolicyItem>(StringComparer.Ordinal);
|
||||||
private static Dictionary<int, TapadnSmartLoadCacheState> _cacheStates = new Dictionary<int, TapadnSmartLoadCacheState>();
|
private static Dictionary<string, TapadnSmartLoadCacheState> _cacheStates = new Dictionary<string, TapadnSmartLoadCacheState>(StringComparer.Ordinal);
|
||||||
private static bool _initialized;
|
private static bool _initialized;
|
||||||
private static bool _enabled;
|
private static bool _enabled;
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ public static class TapadnSmartLoadOrchestrator
|
|||||||
_enabled = options?.SmartPreloadEnabled ?? false;
|
_enabled = options?.SmartPreloadEnabled ?? false;
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
_states = new Dictionary<string, TapadnSmartLoadSceneState>(StringComparer.Ordinal);
|
_states = new Dictionary<string, TapadnSmartLoadSceneState>(StringComparer.Ordinal);
|
||||||
_cacheStates = new Dictionary<int, TapadnSmartLoadCacheState>();
|
_cacheStates = new Dictionary<string, TapadnSmartLoadCacheState>(StringComparer.Ordinal);
|
||||||
LoadStates();
|
LoadStates();
|
||||||
EnsureDefaultPolicies(_runtimeConfig);
|
EnsureDefaultPolicies(_runtimeConfig);
|
||||||
}
|
}
|
||||||
@@ -135,10 +135,10 @@ public static class TapadnSmartLoadOrchestrator
|
|||||||
record.ShowRequestCount = Math.Max(0, record.ShowRequestCount);
|
record.ShowRequestCount = Math.Max(0, record.ShowRequestCount);
|
||||||
SaveStates();
|
SaveStates();
|
||||||
|
|
||||||
TryPreload(adType, normalizedScenario);
|
TryPreload(adType, normalizedScenario, _slotId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnPlayRequestStarted(AD_Type adType, string scenario, bool cacheReadyAtRequest)
|
public static void OnPlayRequestStarted(AD_Type adType, string scenario, bool cacheReadyAtRequest, string slotId = null)
|
||||||
{
|
{
|
||||||
if (!_initialized || !_enabled)
|
if (!_initialized || !_enabled)
|
||||||
{
|
{
|
||||||
@@ -152,49 +152,49 @@ public static class TapadnSmartLoadOrchestrator
|
|||||||
record.LastUpdatedUnix = GetNowUnixSeconds();
|
record.LastUpdatedUnix = GetNowUnixSeconds();
|
||||||
if (cacheReadyAtRequest)
|
if (cacheReadyAtRequest)
|
||||||
{
|
{
|
||||||
MarkImmediateHit(adType, normalizedScenario);
|
MarkImmediateHit(adType, normalizedScenario, slotId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MarkCacheExpiredIfStale(adType);
|
MarkCacheExpiredIfStale(adType, slotId);
|
||||||
}
|
}
|
||||||
|
|
||||||
SaveStates();
|
SaveStates();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnLoadRequested(AD_Type adType, string scenario)
|
public static void OnLoadRequested(AD_Type adType, string scenario, string slotId = null)
|
||||||
{
|
{
|
||||||
if (!_initialized || !_enabled)
|
if (!_initialized || !_enabled)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PrepareSmartLoadRequest(adType, scenario);
|
PrepareSmartLoadRequest(adType, scenario, slotId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnLoadStarted(AD_Type adType, string scenario)
|
public static void OnLoadStarted(AD_Type adType, string scenario, string slotId = null)
|
||||||
{
|
{
|
||||||
if (!_initialized || !_enabled)
|
if (!_initialized || !_enabled)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BeginLoadRequest(adType, scenario);
|
BeginLoadRequest(adType, scenario, slotId);
|
||||||
SaveStates();
|
SaveStates();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnLoadResult(AD_Type adType, string scenario, bool success)
|
public static void OnLoadResult(AD_Type adType, string scenario, bool success, string slotId = null)
|
||||||
{
|
{
|
||||||
if (!_initialized || !_enabled)
|
if (!_initialized || !_enabled)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CompleteLoadRequest(adType, scenario, success);
|
CompleteLoadRequest(adType, scenario, success, slotId);
|
||||||
SaveStates();
|
SaveStates();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnShowStart(AD_Type adType, string scenario)
|
public static void OnShowStart(AD_Type adType, string scenario, string slotId = null)
|
||||||
{
|
{
|
||||||
if (!_initialized || !_enabled)
|
if (!_initialized || !_enabled)
|
||||||
{
|
{
|
||||||
@@ -204,11 +204,11 @@ public static class TapadnSmartLoadOrchestrator
|
|||||||
var record = GetOrCreateState(adType, NormalizeScenario(scenario));
|
var record = GetOrCreateState(adType, NormalizeScenario(scenario));
|
||||||
record.ShowStartCount = Math.Max(0, record.ShowStartCount) + 1;
|
record.ShowStartCount = Math.Max(0, record.ShowStartCount) + 1;
|
||||||
record.LastUpdatedUnix = GetNowUnixSeconds();
|
record.LastUpdatedUnix = GetNowUnixSeconds();
|
||||||
MarkCacheConsumed(adType, scenario);
|
MarkCacheConsumed(adType, scenario, slotId);
|
||||||
SaveStates();
|
SaveStates();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnShowError(AD_Type adType, string scenario)
|
public static void OnShowError(AD_Type adType, string scenario, string slotId = null)
|
||||||
{
|
{
|
||||||
if (!_initialized || !_enabled)
|
if (!_initialized || !_enabled)
|
||||||
{
|
{
|
||||||
@@ -218,11 +218,11 @@ public static class TapadnSmartLoadOrchestrator
|
|||||||
var record = GetOrCreateState(adType, NormalizeScenario(scenario));
|
var record = GetOrCreateState(adType, NormalizeScenario(scenario));
|
||||||
record.ShowFailureCount = Math.Max(0, record.ShowFailureCount) + 1;
|
record.ShowFailureCount = Math.Max(0, record.ShowFailureCount) + 1;
|
||||||
record.LastUpdatedUnix = GetNowUnixSeconds();
|
record.LastUpdatedUnix = GetNowUnixSeconds();
|
||||||
MarkCacheShowFailed(adType, scenario);
|
MarkCacheShowFailed(adType, scenario, slotId);
|
||||||
SaveStates();
|
SaveStates();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void TryPreload(AD_Type adType, string scenario)
|
private static void TryPreload(AD_Type adType, string scenario, string slotId)
|
||||||
{
|
{
|
||||||
if (!_initialized || !_enabled)
|
if (!_initialized || !_enabled)
|
||||||
{
|
{
|
||||||
@@ -257,11 +257,11 @@ public static class TapadnSmartLoadOrchestrator
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MarkCacheExpiredIfStale(adType);
|
MarkCacheExpiredIfStale(adType, slotId);
|
||||||
OnLoadRequested(adType, scenario);
|
OnLoadRequested(adType, scenario, slotId);
|
||||||
ADManager.Instance.LoadAD(adType);
|
ADManager.Instance.LoadAD(adType);
|
||||||
var smartLoadStarted = HasPendingSmartLoadForScene(adType, scenario);
|
var smartLoadStarted = HasPendingSmartLoadForScene(adType, scenario, slotId);
|
||||||
ClearPreparedLoadIfNotStarted(adType);
|
ClearPreparedLoadIfNotStarted(adType, slotId);
|
||||||
if (!smartLoadStarted)
|
if (!smartLoadStarted)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -302,9 +302,9 @@ public static class TapadnSmartLoadOrchestrator
|
|||||||
return Mathf.Lerp(policy.BaseProbability, observedRate, trust);
|
return Mathf.Lerp(policy.BaseProbability, observedRate, trust);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void PrepareSmartLoadRequest(AD_Type adType, string scenario)
|
private static void PrepareSmartLoadRequest(AD_Type adType, string scenario, string slotId)
|
||||||
{
|
{
|
||||||
var state = GetOrCreateCacheState(adType);
|
var state = GetOrCreateCacheState(adType, slotId);
|
||||||
if (state.PendingSmartLoad)
|
if (state.PendingSmartLoad)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -316,9 +316,9 @@ public static class TapadnSmartLoadOrchestrator
|
|||||||
state.PreparedRequestUnix = GetNowUnixSeconds();
|
state.PreparedRequestUnix = GetNowUnixSeconds();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void BeginLoadRequest(AD_Type adType, string scenario)
|
private static void BeginLoadRequest(AD_Type adType, string scenario, string slotId)
|
||||||
{
|
{
|
||||||
var state = GetOrCreateCacheState(adType);
|
var state = GetOrCreateCacheState(adType, slotId);
|
||||||
var now = GetNowUnixSeconds();
|
var now = GetNowUnixSeconds();
|
||||||
if (state.PreparedSmartLoad)
|
if (state.PreparedSmartLoad)
|
||||||
{
|
{
|
||||||
@@ -346,9 +346,9 @@ public static class TapadnSmartLoadOrchestrator
|
|||||||
state.PreparedRequestUnix = 0;
|
state.PreparedRequestUnix = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ClearPreparedLoadIfNotStarted(AD_Type adType)
|
private static void ClearPreparedLoadIfNotStarted(AD_Type adType, string slotId)
|
||||||
{
|
{
|
||||||
var state = GetOrCreateCacheState(adType);
|
var state = GetOrCreateCacheState(adType, slotId);
|
||||||
if (state.PendingSmartLoad)
|
if (state.PendingSmartLoad)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -360,16 +360,16 @@ public static class TapadnSmartLoadOrchestrator
|
|||||||
state.PreparedRequestUnix = 0;
|
state.PreparedRequestUnix = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool HasPendingSmartLoadForScene(AD_Type adType, string scenario)
|
private static bool HasPendingSmartLoadForScene(AD_Type adType, string scenario, string slotId)
|
||||||
{
|
{
|
||||||
var state = GetOrCreateCacheState(adType);
|
var state = GetOrCreateCacheState(adType, slotId);
|
||||||
return state.PendingSmartLoad &&
|
return state.PendingSmartLoad &&
|
||||||
string.Equals(NormalizeScenario(state.PendingOriginScene), NormalizeScenario(scenario), StringComparison.Ordinal);
|
string.Equals(NormalizeScenario(state.PendingOriginScene), NormalizeScenario(scenario), StringComparison.Ordinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CompleteLoadRequest(AD_Type adType, string scenario, bool success)
|
private static void CompleteLoadRequest(AD_Type adType, string scenario, bool success, string slotId)
|
||||||
{
|
{
|
||||||
var cacheState = GetOrCreateCacheState(adType);
|
var cacheState = GetOrCreateCacheState(adType, slotId);
|
||||||
var normalizedScenario = NormalizeScenario(scenario);
|
var normalizedScenario = NormalizeScenario(scenario);
|
||||||
var originScene = cacheState.PendingSmartLoad ? NormalizeScenario(cacheState.PendingOriginScene) : normalizedScenario;
|
var originScene = cacheState.PendingSmartLoad ? NormalizeScenario(cacheState.PendingOriginScene) : normalizedScenario;
|
||||||
var originRecord = GetOrCreateState(adType, originScene);
|
var originRecord = GetOrCreateState(adType, originScene);
|
||||||
@@ -408,23 +408,23 @@ public static class TapadnSmartLoadOrchestrator
|
|||||||
cacheState.PendingRequestUnix = 0;
|
cacheState.PendingRequestUnix = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void MarkImmediateHit(AD_Type adType, string scenario)
|
private static void MarkImmediateHit(AD_Type adType, string scenario, string slotId)
|
||||||
{
|
{
|
||||||
var consumedScene = NormalizeScenario(scenario);
|
var consumedScene = NormalizeScenario(scenario);
|
||||||
var currentSceneRecord = GetOrCreateState(adType, consumedScene);
|
var currentSceneRecord = GetOrCreateState(adType, consumedScene);
|
||||||
currentSceneRecord.ImmediateHitCount = Math.Max(0, currentSceneRecord.ImmediateHitCount) + 1;
|
currentSceneRecord.ImmediateHitCount = Math.Max(0, currentSceneRecord.ImmediateHitCount) + 1;
|
||||||
currentSceneRecord.LastUpdatedUnix = GetNowUnixSeconds();
|
currentSceneRecord.LastUpdatedUnix = GetNowUnixSeconds();
|
||||||
|
|
||||||
var cacheState = GetOrCreateCacheState(adType);
|
var cacheState = GetOrCreateCacheState(adType, slotId);
|
||||||
if (!cacheState.HasReadyCache)
|
if (!cacheState.HasReadyCache)
|
||||||
{
|
{
|
||||||
currentSceneRecord.UnattributedCacheHitCount = Math.Max(0, currentSceneRecord.UnattributedCacheHitCount) + 1;
|
currentSceneRecord.UnattributedCacheHitCount = Math.Max(0, currentSceneRecord.UnattributedCacheHitCount) + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void MarkCacheConsumed(AD_Type adType, string scenario)
|
private static void MarkCacheConsumed(AD_Type adType, string scenario, string slotId)
|
||||||
{
|
{
|
||||||
var cacheState = GetOrCreateCacheState(adType);
|
var cacheState = GetOrCreateCacheState(adType, slotId);
|
||||||
if (!cacheState.HasReadyCache || cacheState.Consumed)
|
if (!cacheState.HasReadyCache || cacheState.Consumed)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -458,9 +458,9 @@ public static class TapadnSmartLoadOrchestrator
|
|||||||
ClearReadyCache(cacheState);
|
ClearReadyCache(cacheState);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void MarkCacheShowFailed(AD_Type adType, string scenario)
|
private static void MarkCacheShowFailed(AD_Type adType, string scenario, string slotId)
|
||||||
{
|
{
|
||||||
var cacheState = GetOrCreateCacheState(adType);
|
var cacheState = GetOrCreateCacheState(adType, slotId);
|
||||||
if (!cacheState.HasReadyCache)
|
if (!cacheState.HasReadyCache)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -479,9 +479,9 @@ public static class TapadnSmartLoadOrchestrator
|
|||||||
ClearReadyCache(cacheState);
|
ClearReadyCache(cacheState);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void MarkCacheExpiredIfStale(AD_Type adType)
|
private static void MarkCacheExpiredIfStale(AD_Type adType, string slotId)
|
||||||
{
|
{
|
||||||
var cacheState = GetOrCreateCacheState(adType);
|
var cacheState = GetOrCreateCacheState(adType, slotId);
|
||||||
if (!cacheState.HasReadyCache)
|
if (!cacheState.HasReadyCache)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -502,9 +502,9 @@ public static class TapadnSmartLoadOrchestrator
|
|||||||
ClearReadyCache(cacheState);
|
ClearReadyCache(cacheState);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TapadnSmartLoadCacheState GetOrCreateCacheState(AD_Type adType)
|
private static TapadnSmartLoadCacheState GetOrCreateCacheState(AD_Type adType, string slotId)
|
||||||
{
|
{
|
||||||
var key = (int)adType;
|
var key = ComposeCacheKey(adType, slotId);
|
||||||
if (_cacheStates.TryGetValue(key, out var state) && state != null)
|
if (_cacheStates.TryGetValue(key, out var state) && state != null)
|
||||||
{
|
{
|
||||||
return state;
|
return state;
|
||||||
@@ -879,6 +879,16 @@ public static class TapadnSmartLoadOrchestrator
|
|||||||
return ((int)adType) + "|" + NormalizeScenario(scenario);
|
return ((int)adType) + "|" + NormalizeScenario(scenario);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string ComposeCacheKey(AD_Type adType, string slotId)
|
||||||
|
{
|
||||||
|
return ((int)adType) + "|" + NormalizeSlotId(slotId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeSlotId(string slotId)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(slotId) ? "__default_slot__" : slotId.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
private static string NormalizeScenario(string scenario)
|
private static string NormalizeScenario(string scenario)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(scenario))
|
if (string.IsNullOrWhiteSpace(scenario))
|
||||||
|
|||||||
@@ -56,12 +56,12 @@ public sealed class TapadnSplashPlayer : ADPlayer, IDirichletSplashAutoAdListene
|
|||||||
}
|
}
|
||||||
|
|
||||||
curState = 1;
|
curState = 1;
|
||||||
TapadnSmartLoadOrchestrator.OnLoadStarted(AD_Type.Splash, AdScene);
|
TapadnSmartLoadOrchestrator.OnLoadStarted(AD_Type.Splash, AdScene, Key);
|
||||||
_adNative.LoadSplashAd(
|
_adNative.LoadSplashAd(
|
||||||
TapadnAdRequestFactory.BuildSplash(Key, TapadnAdController.CurrentOptions),
|
TapadnAdRequestFactory.BuildSplash(Key, TapadnAdController.CurrentOptions),
|
||||||
ad =>
|
ad =>
|
||||||
{
|
{
|
||||||
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Splash, AdScene, true);
|
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Splash, AdScene, true, Key);
|
||||||
_loadedAd?.Destroy();
|
_loadedAd?.Destroy();
|
||||||
_loadedAd = ad;
|
_loadedAd = ad;
|
||||||
_loadedAd.Shown += OnManualShown;
|
_loadedAd.Shown += OnManualShown;
|
||||||
@@ -73,7 +73,7 @@ public sealed class TapadnSplashPlayer : ADPlayer, IDirichletSplashAutoAdListene
|
|||||||
},
|
},
|
||||||
error =>
|
error =>
|
||||||
{
|
{
|
||||||
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Splash, AdScene, false);
|
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Splash, AdScene, false, Key);
|
||||||
curState = 0;
|
curState = 0;
|
||||||
Debug.LogError($"[TapADN] Splash load failed. code={error.Code}, message={error.Message}");
|
Debug.LogError($"[TapADN] Splash load failed. code={error.Code}, message={error.Message}");
|
||||||
});
|
});
|
||||||
@@ -106,7 +106,7 @@ public sealed class TapadnSplashPlayer : ADPlayer, IDirichletSplashAutoAdListene
|
|||||||
}
|
}
|
||||||
|
|
||||||
_showSettled = true;
|
_showSettled = true;
|
||||||
TapadnSmartLoadOrchestrator.OnShowError(AD_Type.Splash, AdScene);
|
TapadnSmartLoadOrchestrator.OnShowError(AD_Type.Splash, AdScene, Key);
|
||||||
curState = 0;
|
curState = 0;
|
||||||
Debug.LogError($"[TapADN] Splash show failed. code={error?.Code}, message={error?.Message}");
|
Debug.LogError($"[TapADN] Splash show failed. code={error?.Code}, message={error?.Message}");
|
||||||
adListener.OnShowError();
|
adListener.OnShowError();
|
||||||
@@ -114,7 +114,7 @@ public sealed class TapadnSplashPlayer : ADPlayer, IDirichletSplashAutoAdListene
|
|||||||
|
|
||||||
public void OnAdShow()
|
public void OnAdShow()
|
||||||
{
|
{
|
||||||
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Splash, AdScene);
|
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Splash, AdScene, Key);
|
||||||
NotifyShowStarted();
|
NotifyShowStarted();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,12 +136,13 @@ public sealed class TapadnSplashPlayer : ADPlayer, IDirichletSplashAutoAdListene
|
|||||||
|
|
||||||
public override void OnPlayRequestStarted()
|
public override void OnPlayRequestStarted()
|
||||||
{
|
{
|
||||||
TapadnSmartLoadOrchestrator.OnPlayRequestStarted(AD_Type.Splash, AdScene, !UseAutoLoad() && IsReadly());
|
TapadnSmartLoadOrchestrator.OnPlayRequestStarted(AD_Type.Splash, AdScene, !UseAutoLoad() && IsReadly(), Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void EnterAdScenario(string scenario)
|
public override void EnterAdScenario(string scenario)
|
||||||
{
|
{
|
||||||
TapadnSmartLoadOrchestrator.OnEnterAdScenario(AD_Type.Splash, scenario, Key);
|
AdScene = string.IsNullOrWhiteSpace(scenario) ? "__default__" : scenario.Trim();
|
||||||
|
TapadnSmartLoadOrchestrator.OnEnterAdScenario(AD_Type.Splash, AdScene, Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool UseAutoLoad()
|
private bool UseAutoLoad()
|
||||||
@@ -151,7 +152,7 @@ public sealed class TapadnSplashPlayer : ADPlayer, IDirichletSplashAutoAdListene
|
|||||||
|
|
||||||
private void OnManualShown()
|
private void OnManualShown()
|
||||||
{
|
{
|
||||||
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Splash, AdScene);
|
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Splash, AdScene, Key);
|
||||||
NotifyShowStarted();
|
NotifyShowStarted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
"name": "com.commercialization.tapadn",
|
"name": "com.commercialization.tapadn",
|
||||||
"displayName": "Commercialization.tapadn",
|
"displayName": "Commercialization.tapadn",
|
||||||
"description": "TapADN / Dirichlet mediation implementation for CC-Framework.Commercialization.",
|
"description": "TapADN / Dirichlet mediation implementation for CC-Framework.Commercialization.",
|
||||||
"version": "1.0.0",
|
"version": "1.0.5",
|
||||||
"unity": "2021.1",
|
"unity": "2022.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "http://private.lightyears.ltd:18650/foldcc/Commercialization.tapadn"
|
"url": "http://private.lightyears.ltd:18650/foldcc/Commercialization.tapadn.git"
|
||||||
},
|
},
|
||||||
"author": {
|
"author": {
|
||||||
"name": "foldcc",
|
"name": "foldcc",
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
"url": "https://gitee.com/foldcc"
|
"url": "https://gitee.com/foldcc"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"com.foldcc.cc-framework.commercialization": "http://private.lightyears.ltd:18650/foldcc/CC-Framework.Commercialization.git#1.0.14"
|
"com.foldcc.cc-framework.commercialization": "http://private.lightyears.ltd:18650/foldcc/CC-Framework.Commercialization.git#1.0.15"
|
||||||
},
|
},
|
||||||
"samples": [
|
"samples": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
## 目录
|
## 目录
|
||||||
|
|
||||||
* `Assets/DirichletMediation`: 官方聚合 Unity SDK `4.2.5.0`,已删除官方 Sample。
|
* `Assets/DirichletMediation`: 官方聚合 Unity SDK 基础层 `4.2.5.0`,已删除官方 Sample;Android native AAR 单独升级到 `4.2.7.3`。
|
||||||
* `Assets/Plugins/Android`: 官方 Android AAR、Manifest、ProGuard、本地微信 OpenSDK AAR。
|
* `Assets/Plugins/Android`: 官方 Android AAR、Manifest、ProGuard、本地微信 OpenSDK AAR。
|
||||||
* `Assets/Plugins/iOS`: iOS Objective-C++ bridge。
|
* `Assets/Plugins/iOS`: iOS Objective-C++ bridge。
|
||||||
* `Assets/Tapadn_Adapter/Runtime/Scripts`: 商业化抽象层适配。
|
* `Assets/Tapadn_Adapter/Runtime/Scripts`: 商业化抽象层适配。
|
||||||
@@ -110,7 +110,7 @@ iOS runtime 桥接负责:
|
|||||||
发布包入口是 `Assets/package.json`,其中 `com.foldcc.cc-framework.commercialization` 依赖保持为远程 Git URL:
|
发布包入口是 `Assets/package.json`,其中 `com.foldcc.cc-framework.commercialization` 依赖保持为远程 Git URL:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"com.foldcc.cc-framework.commercialization": "http://private.lightyears.ltd:18650/foldcc/CC-Framework.Commercialization.git#1.0.14"
|
"com.foldcc.cc-framework.commercialization": "http://private.lightyears.ltd:18650/foldcc/CC-Framework.Commercialization.git#1.0.15"
|
||||||
```
|
```
|
||||||
|
|
||||||
当前仓库自身作为 Unity 验证工程时,可以在 `Packages/manifest.json` 使用本地 `file:` 引用:
|
当前仓库自身作为 Unity 验证工程时,可以在 `Packages/manifest.json` 使用本地 `file:` 引用:
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
m_EditorVersion: 2022.3.62f3c1
|
m_EditorVersion: 2022.3.62f2c1
|
||||||
m_EditorVersionWithRevision: 2022.3.62f3c1 (1623fc0bbb97)
|
m_EditorVersionWithRevision: 2022.3.62f2c1 (92e6e6be66dc)
|
||||||
|
|||||||
57
README.md
57
README.md
@@ -8,8 +8,8 @@
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"com.foldcc.cc-framework.commercialization": "http://private.lightyears.ltd:18650/foldcc/CC-Framework.Commercialization.git#1.0.14",
|
"com.foldcc.cc-framework.commercialization": "http://private.lightyears.ltd:18650/foldcc/CC-Framework.Commercialization.git#1.0.15",
|
||||||
"com.commercialization.tapadn": "http://private.lightyears.ltd:18650/foldcc/Commercialization.tapadn.git#1.0.0"
|
"com.commercialization.tapadn": "http://private.lightyears.ltd:18650/foldcc/Commercialization.tapadn.git?path=/Assets#1.0.5"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -73,6 +73,10 @@ ADManager.Instance.Init(callback, userId, adConfig, new TapadnAdController());
|
|||||||
* `tapadn.rewarded_max_load_attempts`
|
* `tapadn.rewarded_max_load_attempts`
|
||||||
* `tapadn.rewarded_load_retry_delay_ms`
|
* `tapadn.rewarded_load_retry_delay_ms`
|
||||||
* `tapadn.rewarded_show_timeout_ms`
|
* `tapadn.rewarded_show_timeout_ms`
|
||||||
|
* `tapadn.rewarded_scene_slot.<scene_id>`(激励视频场景广告位映射,值为对应 SpaceId)
|
||||||
|
* `tapadn.rewarded_scene_slots`(激励视频场景广告位批量映射,格式 `scene_a=10001,scene_b=10002`)
|
||||||
|
* `tapadn.rewarded_scene_slots_json`(激励视频场景广告位 JSON,格式见下方)
|
||||||
|
* `tapadn.rewarded_cache_max_age_seconds`(可选本地缓存年龄上限;默认 `600` 秒,设为 `0` 表示只使用 SDK `IsValid` 判断)
|
||||||
* `tapadn.interstitial_auto_load`
|
* `tapadn.interstitial_auto_load`
|
||||||
* `tapadn.interstitial_prewarm_on_init`
|
* `tapadn.interstitial_prewarm_on_init`
|
||||||
* `tapadn.interstitial_max_load_attempts`
|
* `tapadn.interstitial_max_load_attempts`
|
||||||
@@ -92,6 +96,52 @@ ADManager.Instance.Init(callback, userId, adConfig, new TapadnAdController());
|
|||||||
|
|
||||||
默认激励、插屏、开屏都使用手动 load/show;如无特殊策略验证需求,不建议开启 auto-ad。若要做 auto-ad AB 测试,再将对应 `*_auto_load` 设为 `true`。
|
默认激励、插屏、开屏都使用手动 load/show;如无特殊策略验证需求,不建议开启 auto-ad。若要做 auto-ad AB 测试,再将对应 `*_auto_load` 设为 `true`。
|
||||||
|
|
||||||
|
### 激励视频场景广告位
|
||||||
|
|
||||||
|
激励视频支持按游戏场景路由不同 TapADN SpaceId。项目层继续调用 `ADManager.EnterAdScenario(AD_Type.AwardVideo, sceneId)` 和 `ADManager.AsyncPlayAD(AD_Type.AwardVideo, sceneId, callback)`;TapADN adapter 会按 `sceneId` 查表:
|
||||||
|
|
||||||
|
* 命中 `tapadn.rewarded_scene_slot.<scene_id>` 或批量配置时,使用该场景 SpaceId。
|
||||||
|
* 未传场景、场景为空、场景未配置、配置的 SpaceId 非法时,回退到 `BaseAwardAdKeyValue.value` 默认激励视频广告位。
|
||||||
|
* 手动 load/show 模式下,缓存按 SpaceId 隔离;A 场景加载的激励视频不会被 B 场景误认为 ready。
|
||||||
|
|
||||||
|
单项配置示例:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
adConfig.CommonKeyValues.Add(new AdKeyValue
|
||||||
|
{
|
||||||
|
key = "tapadn.rewarded_scene_slot.level_clear",
|
||||||
|
value = "200101"
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
批量字符串配置示例:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
adConfig.CommonKeyValues.Add(new AdKeyValue
|
||||||
|
{
|
||||||
|
key = "tapadn.rewarded_scene_slots",
|
||||||
|
value = "level_clear=200101,daily_bonus=200102"
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
JSON 配置示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Mappings": [
|
||||||
|
{ "Scene": "level_clear", "SlotId": "200101" },
|
||||||
|
{ "Scene": "daily_bonus", "SlotId": "200102" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
生命周期策略按官方文档保持保守:
|
||||||
|
|
||||||
|
* `DirichletAdNative` 仍由每个 TapADN player 统一持有,不在每个场景重复创建。
|
||||||
|
* 手动加载返回的 `DirichletRewardVideoAd` 按 SpaceId 缓存;展示关闭、展示失败、SDK `IsValid == false` 或超过 `tapadn.rewarded_cache_max_age_seconds` 后销毁。
|
||||||
|
* 官方文档没有给出固定过期秒数;本模块默认 10 分钟未消费主动销毁,同时每次 ready/show 前仍调用 SDK `IsValid`。
|
||||||
|
* Android auto-ad 仍交给官方 `ShowRewardVideoAutoAd` / `PreLoad` 的 native 缓存管理;iOS/Editor fallback 仍是 load 成功后立即 show,不承诺 native 缓存语义。
|
||||||
|
|
||||||
### 智能预加载(实验)
|
### 智能预加载(实验)
|
||||||
|
|
||||||
默认会按“场景进入次数 + 场景播放请求次数”维护一个小样本统计:
|
默认会按“场景进入次数 + 场景播放请求次数”维护一个小样本统计:
|
||||||
@@ -138,11 +188,14 @@ ADManager.Instance.Init(callback, userId, adConfig, new TapadnAdController());
|
|||||||
|
|
||||||
包内包含官方 `DirichletMediation` SDK、Android AAR、iOS bridge、EDM4U 依赖声明和构建后处理。
|
包内包含官方 `DirichletMediation` SDK、Android AAR、iOS bridge、EDM4U 依赖声明和构建后处理。
|
||||||
|
|
||||||
|
维护和升级说明见 `SDK_MAINTENANCE.md`,其中记录了官方源码改动清单、Android/iOS/Unity SDK 升级步骤和发布流程。
|
||||||
|
|
||||||
构建后处理会自动补齐:
|
构建后处理会自动补齐:
|
||||||
|
|
||||||
* TapADN 所需权限。
|
* TapADN 所需权限。
|
||||||
* TapADN `TapADFileProvider` 与 `tapad_ad_file_path.xml`。
|
* TapADN `TapADFileProvider` 与 `tapad_ad_file_path.xml`。
|
||||||
* 微信 OpenSDK `WXEntryActivity`、`queries`、本地 `wechat-sdk-android-6.8.34.aar`。
|
* 微信 OpenSDK `WXEntryActivity`、`queries`、本地 `wechat-sdk-android-6.8.34.aar`。
|
||||||
|
* Pangle `com.pangle.cn:ads-sdk-pro:7.6.1.2` 和 GDT `com.qq.e.union:union:4.690.1560` Maven 依赖。
|
||||||
* `android.useAndroidX=true` 与 `android.enableJetifier=true`。
|
* `android.useAndroidX=true` 与 `android.enableJetifier=true`。
|
||||||
|
|
||||||
包内不默认暴露可视化编辑面板;调试样例通过 `Samples~` 作为可选导入内容。
|
包内不默认暴露可视化编辑面板;调试样例通过 `Samples~` 作为可选导入内容。
|
||||||
|
|||||||
Reference in New Issue
Block a user