diff --git a/AnyThinkPlugin/AnyThinkAds/Api/ATSDKAPI.cs b/AnyThinkPlugin/AnyThinkAds/Api/ATSDKAPI.cs index 93f60ef..5770dac 100644 --- a/AnyThinkPlugin/AnyThinkAds/Api/ATSDKAPI.cs +++ b/AnyThinkPlugin/AnyThinkAds/Api/ATSDKAPI.cs @@ -229,6 +229,20 @@ namespace AnyThinkAds.Api client.setLocation(longitude, latitude); } + public static void start() + { + client.start(); + } + + public static bool isCnSDK() + { + return client.isCnSDK(); + } + + public static void setLocalStrategyAssetPath(string assetPath) + { + client.setLocalStrategyAssetPath(assetPath); + } + } } - diff --git a/AnyThinkPlugin/AnyThinkAds/Common/IATSDKAPIClient.cs b/AnyThinkPlugin/AnyThinkAds/Common/IATSDKAPIClient.cs index 5783db0..a8d7cba 100644 --- a/AnyThinkPlugin/AnyThinkAds/Common/IATSDKAPIClient.cs +++ b/AnyThinkPlugin/AnyThinkAds/Common/IATSDKAPIClient.cs @@ -31,5 +31,8 @@ namespace AnyThinkAds.Common void getArea(ATGetAreaListener listener); void setWXStatus(bool install); void setLocation(double longitude, double latitude); + void start(); + bool isCnSDK(); + void setLocalStrategyAssetPath(string assetPath); } } diff --git a/AnyThinkPlugin/AnyThinkAds/Platform/ATAdsClientFactory.cs b/AnyThinkPlugin/AnyThinkAds/Platform/ATAdsClientFactory.cs index dfd4504..cf6be1e 100644 --- a/AnyThinkPlugin/AnyThinkAds/Platform/ATAdsClientFactory.cs +++ b/AnyThinkPlugin/AnyThinkAds/Platform/ATAdsClientFactory.cs @@ -162,6 +162,9 @@ namespace AnyThinkAds public void getArea(ATGetAreaListener listener) { } public void setWXStatus(bool install) { } public void setLocation(double longitude, double latitude) { } + public void start() { } + public bool isCnSDK() { return false; } + public void setLocalStrategyAssetPath(string assetPath) { } public void showDebuggerUI() {} public void showDebuggerUI(string debugKey) {} } @@ -490,4 +493,4 @@ namespace AnyThinkAds public void entryScenarioWithPlacementID(string placementId, string scenarioID) {} } -} \ No newline at end of file +} diff --git a/AnyThinkPlugin/AnyThinkAds/Platform/Android/ATSDKAPIClient.cs b/AnyThinkPlugin/AnyThinkAds/Platform/Android/ATSDKAPIClient.cs index 9be644b..702cef1 100644 --- a/AnyThinkPlugin/AnyThinkAds/Platform/Android/ATSDKAPIClient.cs +++ b/AnyThinkPlugin/AnyThinkAds/Platform/Android/ATSDKAPIClient.cs @@ -9,11 +9,13 @@ namespace AnyThinkAds.Android public class ATSDKAPIClient : AndroidJavaProxy, IATSDKAPIClient { private AndroidJavaObject sdkInitHelper; + private AndroidJavaClass sdkClass; private ATSDKInitListener sdkInitListener; public ATSDKAPIClient () : base("com.anythink.unitybridge.sdkinit.SDKInitListener") { this.sdkInitHelper = new AndroidJavaObject( "com.anythink.unitybridge.sdkinit.SDKInitHelper", this); + this.sdkClass = new AndroidJavaClass("com.anythink.core.api.ATSDK"); } public void initSDK(string appId, string appKey) @@ -390,5 +392,59 @@ namespace AnyThinkAds.Android Debug.Log("ATSDKAPIClient : error." + e.Message); } } + + public void start() + { + try + { + if (this.sdkClass != null) + { + this.sdkClass.CallStatic("start"); + } + } + catch (System.Exception e) + { + System.Console.WriteLine("Exception caught: {0}", e); + Debug.Log("ATSDKAPIClient : start error." + e.Message); + } + } + + public bool isCnSDK() + { + try + { + if (this.sdkClass != null) + { + return this.sdkClass.CallStatic("isCnSDK"); + } + } + catch (System.Exception e) + { + System.Console.WriteLine("Exception caught: {0}", e); + Debug.Log("ATSDKAPIClient : isCnSDK error." + e.Message); + } + + return false; + } + + public void setLocalStrategyAssetPath(string assetPath) + { + try + { + if (this.sdkClass != null) + { + using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) + using (AndroidJavaObject currentActivity = unityPlayer.GetStatic("currentActivity")) + { + this.sdkClass.CallStatic("setLocalStrategyAssetPath", currentActivity, assetPath); + } + } + } + catch (System.Exception e) + { + System.Console.WriteLine("Exception caught: {0}", e); + Debug.Log("ATSDKAPIClient : setLocalStrategyAssetPath error." + e.Message); + } + } } } diff --git a/AnyThinkPlugin/AnyThinkAds/Platform/iOS/ATSDKAPIClient.cs b/AnyThinkPlugin/AnyThinkAds/Platform/iOS/ATSDKAPIClient.cs index 3209269..2308e8f 100644 --- a/AnyThinkPlugin/AnyThinkAds/Platform/iOS/ATSDKAPIClient.cs +++ b/AnyThinkPlugin/AnyThinkAds/Platform/iOS/ATSDKAPIClient.cs @@ -193,6 +193,21 @@ namespace AnyThinkAds.iOS { Debug.Log("Unity:ATSDKAPIClient::setLocation()"); ATManager.setLocation(longitude, latitude); } + + public void start() + { + Debug.Log("Unity:ATSDKAPIClient::start() noop on iOS"); + } + + public bool isCnSDK() + { + return false; + } + + public void setLocalStrategyAssetPath(string assetPath) + { + Debug.Log("Unity:ATSDKAPIClient::setLocalStrategyAssetPath() noop on iOS"); + } //iOS显示Debugger UI public void showDebuggerUI() diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1ebe48a --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,14 @@ +# [1.4.7] + +### 新增 + +* 支持中国区 `ATSDK.start()`、本地策略资源路径和自动加载相关配置。 +* 新增激励/插屏广告场景显式上报能力,播放链不再自动补记场景到达。 +* 增加 Android IMGUI 调试样例场景、日志导出和官方 DebugUI 手动入口。 + +### 修复 + +* 激励和插屏切换为更稳的自动加载/预热策略,补强首次播放容错。 +* 修复插屏关闭、展示失败和播放失败回调链不完整的问题。 +* 调整 debug 行为:`topon.debug=true` 仅开启日志,不再自动弹出官方 DebugUI。 + diff --git a/Plugins/Android/anythink_adx_sdk_kuying_6.5.56_necessary.aar b/Plugins/Android/anythink_adx_sdk_kuying_6.5.56_necessary.aar deleted file mode 100644 index 125cfb3..0000000 Binary files a/Plugins/Android/anythink_adx_sdk_kuying_6.5.56_necessary.aar and /dev/null differ diff --git a/Plugins/Android/anythink_adx_sdk_kuying_6.5.56_necessary.aar.meta b/Plugins/Android/anythink_adx_sdk_kuying_6.5.56_necessary.aar.meta deleted file mode 100644 index 90eaf3c..0000000 --- a/Plugins/Android/anythink_adx_sdk_kuying_6.5.56_necessary.aar.meta +++ /dev/null @@ -1,32 +0,0 @@ -fileFormatVersion: 2 -guid: 89da22c2144f47ea823de749873367b4 -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: diff --git a/Plugins/Android/anythink_network_adx_kuying_sdk_necessary_6.5.56.1.1.aar b/Plugins/Android/anythink_network_adx_kuying_sdk_necessary_6.5.56.1.1.aar deleted file mode 100644 index a6bc1e6..0000000 Binary files a/Plugins/Android/anythink_network_adx_kuying_sdk_necessary_6.5.56.1.1.aar and /dev/null differ diff --git a/Plugins/Android/anythink_network_adx_kuying_sdk_necessary_6.5.56.1.1.aar.meta b/Plugins/Android/anythink_network_adx_kuying_sdk_necessary_6.5.56.1.1.aar.meta deleted file mode 100644 index d5834ff..0000000 --- a/Plugins/Android/anythink_network_adx_kuying_sdk_necessary_6.5.56.1.1.aar.meta +++ /dev/null @@ -1,32 +0,0 @@ -fileFormatVersion: 2 -guid: 417c2272592f46b0b62da859413203f3 -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: diff --git a/Samples.meta b/Samples.meta new file mode 100644 index 0000000..e90fab9 --- /dev/null +++ b/Samples.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 744cdc59a095c7f43a810b4344dfbdf4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples/IAAAdDebugSample.meta b/Samples/IAAAdDebugSample.meta new file mode 100644 index 0000000..45c812e --- /dev/null +++ b/Samples/IAAAdDebugSample.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: eeb4602ce24931f4b85a300e6c6826ed +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples/IAAAdDebugSample/Configs.meta b/Samples/IAAAdDebugSample/Configs.meta new file mode 100644 index 0000000..7f4ec72 --- /dev/null +++ b/Samples/IAAAdDebugSample/Configs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1385bb12efc24cb43a93bb40ace7ea14 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples/IAAAdDebugSample/Configs/IAAAdDebugSampleConfig.asset b/Samples/IAAAdDebugSample/Configs/IAAAdDebugSampleConfig.asset new file mode 100644 index 0000000..2005c79 --- /dev/null +++ b/Samples/IAAAdDebugSample/Configs/IAAAdDebugSampleConfig.asset @@ -0,0 +1,48 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 98b31b835f394db4a51957c3f0f3ac32, type: 3} + m_Name: IAAAdDebugSampleConfig + m_EditorClassIdentifier: + ConfigName: IAA Ad Debug Sample + Id: your_taku_app_id + Key: your_taku_app_key + Key2: + BaseAwardAdKeyValue: + key: rewarded + value: your_rewarded_placement_id + BaseInteractionAdKeyValue: + key: interstitial + value: your_interstitial_placement_id + BaseSplashAdKeyValue: + key: splash + value: optional_splash_placement + CommonKeyValues: + - key: topon.debug + value: true + - key: topon.auto_open_debugger_ui + value: false + - key: topon.rewarded_auto_load + value: true + - key: topon.rewarded_prewarm_on_init + value: true + - key: topon.rewarded_max_load_attempts + value: 3 + - key: topon.rewarded_load_retry_delay_ms + value: 1000 + - key: topon.interstitial_auto_load + value: true + - key: topon.interstitial_prewarm_on_init + value: true + - key: topon.interstitial_max_load_attempts + value: 2 + - key: topon.interstitial_load_retry_delay_ms + value: 500 diff --git a/Samples/IAAAdDebugSample/Configs/IAAAdDebugSampleConfig.asset.meta b/Samples/IAAAdDebugSample/Configs/IAAAdDebugSampleConfig.asset.meta new file mode 100644 index 0000000..48b64fc --- /dev/null +++ b/Samples/IAAAdDebugSample/Configs/IAAAdDebugSampleConfig.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5acf750d60576674597ee314d6a39cf8 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples/IAAAdDebugSample/README.md b/Samples/IAAAdDebugSample/README.md new file mode 100644 index 0000000..98207b8 --- /dev/null +++ b/Samples/IAAAdDebugSample/README.md @@ -0,0 +1,112 @@ +# IAA Ad Debug Sample + +## 文件位置 + +- 场景: + - `Assets/Samples/IAAAdDebugSample/Scenes/IAAAdDebugSample.unity` +- 示例配置: + - `Assets/Samples/IAAAdDebugSample/Configs/IAAAdDebugSampleConfig.asset` + +## 使用步骤 + +1. 打开示例配置资产 `IAAAdDebugSampleConfig.asset` +2. 填入以下字段: + - `Id` = Taku AppId + - `Key` = Taku AppKey + - `BaseAwardAdKeyValue.value` = 激励视频广告位ID + - `BaseInteractionAdKeyValue.value` = 插屏广告位ID +3. 打开示例场景 `IAAAdDebugSample.unity` +4. 运行到 Android 手机测试 + +## 当前默认策略 + +- SDK Debug + - `topon.debug=true` + - `topon.auto_open_debugger_ui=false` +- 激励视频 + - 自动加载:开启 + - 初始化预热:开启 + - 最大加载尝试次数:3 + - 首轮失败补偿:1000ms + +- 插屏广告 + - 自动加载:开启 + - 初始化预热:开启 + - 最大加载尝试次数:2 + - 首轮失败补偿:500ms + +## 场景里的 IMGUI 面板支持 + +- 初始化 ADManager +- 打开官方 DebugUI +- 手动 Enter Rewarded / Interstitial Scene +- 手动 Load 激励 / 插屏 +- AsyncPlay 激励 / 插屏 +- 查看当前屏幕分辨率和 SafeArea +- 查看当前策略状态 +- 手动刷新状态快照,避免高频状态查询刷满日志 +- 查看 Rewarded / Interstitial 的状态 JSON +- 一键复制全部日志 +- 自动写入本地 session log 文件 +- 一键导出当前面板可见日志到单独文件 +- 一键复制日志文件路径 +- 查看诊断日志 + +## 关于 Debug 模式 + +- `topon.debug=true` 只用于打开 SDK 调试日志 +- 不会再因为 debug 模式自动弹出官方 DebugUI +- 官方 DebugUI 改为手动点击 `Open DebugUI` + +## 场景到达测试方式 + +推荐验证两条链路: + +1. 先点击 `Enter Rewarded Scene`,再点击 `Async Play Rewarded` +2. 不点击 `Enter Rewarded Scene`,直接点击 `Async Play Rewarded` + +当前实现逻辑: + +- 如果提前调用了 `EnterAdScenario` + - 播放时不会重复上报场景 +- 如果没有提前调用 + - 播放时不再自动补“场景到达”统计 + +## 建议测试流程 + +1. 打开场景并运行到真机 +2. 点击 `Initialize ADManager` +3. 点击 `Open DebugUI`,确认 SDK Debug 面板可打开 +4. 点击 `Enter Rewarded Scene` +5. 点击 `Async Play Rewarded` +6. 点击 `Enter Interstitial Scene` +7. 点击 `Async Play Interstitial` +8. 点击 `Copy All Logs` +9. 将日志直接粘贴给 Codex 分析 + +如果日志太长,不适合走剪贴板: + +1. 点击 `Export Logs To File` +2. 点击 `Copy Log File Path` +3. 到真机的 `Application.persistentDataPath/IAAAdDebugLogs` 下取出日志文件 +4. 将文件内容发给 Codex 分析 + +## 关于分辨率 + +当前 IMGUI 布局按 `1080 x 2340` 纵向手机做参考设计,并根据 `SafeArea` 自动缩放,适合作为真机调试面板。 + +## 关于签名 + +当前示例只负责在 Unity 内和 Android 调试时验证广告链路。 + +- 不需要官方提供签名 +- 只有在你要正式导出 APK / AAB 时,才需要你自己的包名和签名配置 + +## 关于日志文件位置 + +- 运行时会自动创建一份 session log +- 目录:`Application.persistentDataPath/IAAAdDebugLogs` +- Android 上通常类似: + - `/storage/emulated/0/Android/data/<包名>/files/IAAAdDebugLogs` +- 当前测试包名是: + - `com.foldcc.starveg` diff --git a/Samples/IAAAdDebugSample/README.md.meta b/Samples/IAAAdDebugSample/README.md.meta new file mode 100644 index 0000000..62aef6f --- /dev/null +++ b/Samples/IAAAdDebugSample/README.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: a116cafc1b2af4441a5b9b3213aa0b1a +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples/IAAAdDebugSample/Scenes.meta b/Samples/IAAAdDebugSample/Scenes.meta new file mode 100644 index 0000000..2a7aab1 --- /dev/null +++ b/Samples/IAAAdDebugSample/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 79b714e45b20df34490a0062dcbafdd6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Samples/IAAAdDebugSample/Scenes/IAAAdDebugSample.unity b/Samples/IAAAdDebugSample/Scenes/IAAAdDebugSample.unity new file mode 100644 index 0000000..3a3abde --- /dev/null +++ b/Samples/IAAAdDebugSample/Scenes/IAAAdDebugSample.unity @@ -0,0 +1,274 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 3 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 0 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 3 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + buildHeightMesh: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &320557560 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 320557563} + - component: {fileID: 320557562} + - component: {fileID: 320557561} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &320557561 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 320557560} + m_Enabled: 1 +--- !u!20 &320557562 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 320557560} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_Iso: 200 + m_ShutterSpeed: 0.005 + m_Aperture: 16 + m_FocusDistance: 10 + m_FocalLength: 50 + m_BladeCount: 5 + m_Curvature: {x: 2, y: 11} + m_BarrelClipping: 0.25 + m_Anamorphism: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 1 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &320557563 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 320557560} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &924544598 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 924544600} + - component: {fileID: 924544599} + m_Layer: 0 + m_Name: IAA Ad Debug Sample + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &924544599 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 924544598} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 53b297b9bf0d4cfca2b77af0fd74c95c, type: 3} + m_Name: + m_EditorClassIdentifier: + adConfig: {fileID: 11400000, guid: 5acf750d60576674597ee314d6a39cf8, type: 2} + userId: debug_user_001 + rewardedScenario: reward_debug + interstitialScenario: interstitial_debug + autoInitialize: 1 + forcePortrait: 1 + captureUnityLogs: 1 + showVerboseState: 1 +--- !u!4 &924544600 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 924544598} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1660057539 &9223372036854775807 +SceneRoots: + m_ObjectHideFlags: 0 + m_Roots: + - {fileID: 320557563} + - {fileID: 924544600} diff --git a/Samples/IAAAdDebugSample/Scenes/IAAAdDebugSample.unity.meta b/Samples/IAAAdDebugSample/Scenes/IAAAdDebugSample.unity.meta new file mode 100644 index 0000000..4ab58c7 --- /dev/null +++ b/Samples/IAAAdDebugSample/Scenes/IAAAdDebugSample.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f2af01c304b689445904b3b93d20ff83 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Topon_Adapter/Editor/IAAAdAndroidBuild.cs b/Topon_Adapter/Editor/IAAAdAndroidBuild.cs new file mode 100644 index 0000000..d537cc3 --- /dev/null +++ b/Topon_Adapter/Editor/IAAAdAndroidBuild.cs @@ -0,0 +1,166 @@ +#if UNITY_EDITOR +using System; +using System.IO; +using System.Reflection; +using UnityEditor; +using UnityEditor.Build.Reporting; +using UnityEngine; + +public static class IAAAdAndroidBuild +{ + private const string OutputDirectory = "Build/Android"; + private const string OutputApkPath = OutputDirectory + "/IAAAdDebugSample-starveg.apk"; + private const string KeystorePath = "Build/Android/keys/starveg-test.keystore"; + private const string KeystorePassword = "Foldcc123!"; + private const string KeyAliasName = "starvegtest"; + private const string PackageName = "com.foldcc.starveg"; + + [MenuItem("Topon/Build IAA Android Test APK")] + public static void BuildAndroidTestApk() + { + EnsureBuildFolders(); + ConfigurePlayerSettings(); + var resolverState = CaptureResolverState(); + + try + { + ApplyBatchFriendlyResolverSettings(); + + var scenes = new[] { "Assets/Samples/IAAAdDebugSample/Scenes/IAAAdDebugSample.unity" }; + var options = new BuildPlayerOptions + { + scenes = scenes, + target = BuildTarget.Android, + locationPathName = OutputApkPath, + options = BuildOptions.None + }; + + var report = BuildPipeline.BuildPlayer(options); + if (report.summary.result != BuildResult.Succeeded) + { + throw new Exception( + $"Android APK build failed: {report.summary.result} ({report.summary.totalErrors} errors)"); + } + + Debug.Log($"[IAA Build] APK generated: {Path.GetFullPath(OutputApkPath)}"); + } + finally + { + RestoreResolverState(resolverState); + } + } + + public static void BuildAndroidTestApkInBatchMode() + { + BuildAndroidTestApk(); + EditorApplication.Exit(0); + } + + private static void ConfigurePlayerSettings() + { + PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Android, PackageName); + PlayerSettings.defaultInterfaceOrientation = UIOrientation.Portrait; + PlayerSettings.Android.targetArchitectures = AndroidArchitecture.ARMv7 | AndroidArchitecture.ARM64; + PlayerSettings.Android.useCustomKeystore = true; + PlayerSettings.Android.keystoreName = Path.GetFullPath(KeystorePath); + PlayerSettings.Android.keystorePass = KeystorePassword; + PlayerSettings.Android.keyaliasName = KeyAliasName; + PlayerSettings.Android.keyaliasPass = KeystorePassword; + PlayerSettings.bundleVersion = string.IsNullOrWhiteSpace(PlayerSettings.bundleVersion) + ? "1.0.0" + : PlayerSettings.bundleVersion; + PlayerSettings.Android.bundleVersionCode = Mathf.Max(1, PlayerSettings.Android.bundleVersionCode); + AssetDatabase.SaveAssets(); + } + + private static void EnsureBuildFolders() + { + Directory.CreateDirectory(Path.GetFullPath(OutputDirectory)); + Directory.CreateDirectory(Path.GetFullPath(Path.GetDirectoryName(KeystorePath) ?? OutputDirectory)); + } + + private static ResolverState CaptureResolverState() + { + var settingsType = GetResolverSettingsType(); + return new ResolverState + { + EnableAutoResolution = GetResolverBool(settingsType, "EnableAutoResolution"), + AutoResolveOnBuild = GetResolverBool(settingsType, "AutoResolveOnBuild"), + PromptBeforeAutoResolution = GetResolverBool(settingsType, "PromptBeforeAutoResolution") + }; + } + + private static void ApplyBatchFriendlyResolverSettings() + { + var settingsType = GetResolverSettingsType(); + SetResolverBool(settingsType, "EnableAutoResolution", false); + SetResolverBool(settingsType, "AutoResolveOnBuild", false); + SetResolverBool(settingsType, "PromptBeforeAutoResolution", false); + } + + private static void RestoreResolverState(ResolverState state) + { + var settingsType = GetResolverSettingsType(); + SetResolverBool(settingsType, "EnableAutoResolution", state.EnableAutoResolution); + SetResolverBool(settingsType, "AutoResolveOnBuild", state.AutoResolveOnBuild); + SetResolverBool(settingsType, "PromptBeforeAutoResolution", state.PromptBeforeAutoResolution); + } + + private static Type GetResolverSettingsType() + { + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + var type = assembly.GetType("GooglePlayServices.SettingsDialog"); + if (type != null) + { + return type; + } + } + + return null; + } + + private static bool GetResolverBool(Type settingsType, string propertyName) + { + if (settingsType == null) + { + return false; + } + + var property = settingsType.GetProperty( + propertyName, + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); + if (property == null) + { + return false; + } + + return (bool)property.GetValue(null, null); + } + + private static void SetResolverBool(Type settingsType, string propertyName, bool value) + { + if (settingsType == null) + { + return; + } + + var property = settingsType.GetProperty( + propertyName, + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); + if (property == null) + { + return; + } + + property.SetValue(null, value, null); + } + + private struct ResolverState + { + public bool EnableAutoResolution; + public bool AutoResolveOnBuild; + public bool PromptBeforeAutoResolution; + } +} +#endif diff --git a/Topon_Adapter/Editor/IAAAdAndroidBuild.cs.meta b/Topon_Adapter/Editor/IAAAdAndroidBuild.cs.meta new file mode 100644 index 0000000..c47d859 --- /dev/null +++ b/Topon_Adapter/Editor/IAAAdAndroidBuild.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 97e92d5544ec4fc9adf7571f9f4ea241 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Topon_Adapter/Editor/IAAAdDebugSampleCreator.cs b/Topon_Adapter/Editor/IAAAdDebugSampleCreator.cs new file mode 100644 index 0000000..92b6792 --- /dev/null +++ b/Topon_Adapter/Editor/IAAAdDebugSampleCreator.cs @@ -0,0 +1,159 @@ +#if UNITY_EDITOR +using System.Collections.Generic; +using System.IO; +using Runtime.ADAggregator; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; + +public static class IAAAdDebugSampleCreator +{ + private const string SampleRoot = "Assets/Samples/IAAAdDebugSample"; + private const string SampleSceneDirectory = SampleRoot + "/Scenes"; + private const string SampleConfigDirectory = SampleRoot + "/Configs"; + private const string SampleScenePath = SampleSceneDirectory + "/IAAAdDebugSample.unity"; + private const string SampleConfigPath = SampleConfigDirectory + "/IAAAdDebugSampleConfig.asset"; + + [MenuItem("Topon/Create IAA IMGUI Debug Sample")] + public static void CreateOrUpdateSample() + { + EnsureDirectory(SampleRoot); + EnsureDirectory(SampleSceneDirectory); + EnsureDirectory(SampleConfigDirectory); + + var config = LoadOrCreateConfig(); + CreateOrUpdateScene(config); + AppendSceneToBuildSettings(SampleScenePath); + + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + EditorUtility.DisplayDialog( + "IAA Sample Created", + $"Scene: {SampleScenePath}\nConfig: {SampleConfigPath}", + "OK"); + } + + public static void CreateOrUpdateSampleInBatchMode() + { + CreateOrUpdateSample(); + EditorApplication.Exit(0); + } + + private static ADConfig LoadOrCreateConfig() + { + var config = AssetDatabase.LoadAssetAtPath(SampleConfigPath); + if (config == null) + { + config = ScriptableObject.CreateInstance(); + AssetDatabase.CreateAsset(config, SampleConfigPath); + } + + config.ConfigName = "IAA Ad Debug Sample"; + config.Id = string.IsNullOrWhiteSpace(config.Id) ? "fill_taku_app_id" : config.Id; + config.Key = string.IsNullOrWhiteSpace(config.Key) ? "fill_taku_app_key" : config.Key; + config.Key2 = string.IsNullOrWhiteSpace(config.Key2) ? string.Empty : config.Key2; + config.BaseAwardAdKeyValue = config.BaseAwardAdKeyValue ?? new AdKeyValue(); + config.BaseAwardAdKeyValue.key = "rewarded"; + config.BaseAwardAdKeyValue.value = string.IsNullOrWhiteSpace(config.BaseAwardAdKeyValue.value) + ? "fill_rewarded_placement" + : config.BaseAwardAdKeyValue.value; + + config.BaseInteractionAdKeyValue = config.BaseInteractionAdKeyValue ?? new AdKeyValue(); + config.BaseInteractionAdKeyValue.key = "interstitial"; + config.BaseInteractionAdKeyValue.value = string.IsNullOrWhiteSpace(config.BaseInteractionAdKeyValue.value) + ? "fill_interstitial_placement" + : config.BaseInteractionAdKeyValue.value; + + config.BaseSplashAdKeyValue = config.BaseSplashAdKeyValue ?? new AdKeyValue(); + config.BaseSplashAdKeyValue.key = "splash"; + config.BaseSplashAdKeyValue.value = string.IsNullOrWhiteSpace(config.BaseSplashAdKeyValue.value) + ? "optional_splash_placement" + : config.BaseSplashAdKeyValue.value; + + config.CommonKeyValues ??= new List(); + SetOrAddCommonKey(config, "topon.rewarded_auto_load", "true"); + SetOrAddCommonKey(config, "topon.rewarded_prewarm_on_init", "true"); + SetOrAddCommonKey(config, "topon.rewarded_max_load_attempts", "3"); + SetOrAddCommonKey(config, "topon.rewarded_load_retry_delay_ms", "1000"); + SetOrAddCommonKey(config, "topon.interstitial_auto_load", "true"); + SetOrAddCommonKey(config, "topon.interstitial_prewarm_on_init", "true"); + SetOrAddCommonKey(config, "topon.interstitial_max_load_attempts", "2"); + SetOrAddCommonKey(config, "topon.interstitial_load_retry_delay_ms", "500"); + + EditorUtility.SetDirty(config); + return config; + } + + private static void CreateOrUpdateScene(ADConfig config) + { + var scene = EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects, NewSceneMode.Single); + scene.name = "IAAAdDebugSample"; + + var root = new GameObject("IAA Ad Debug Sample"); + var gui = root.AddComponent(); + gui.adConfig = config; + gui.userId = "debug_user_001"; + gui.rewardedScenario = "reward_debug"; + gui.interstitialScenario = "interstitial_debug"; + gui.autoInitialize = true; + gui.forcePortrait = true; + gui.captureUnityLogs = true; + gui.showVerboseState = true; + + EditorSceneManager.SaveScene(scene, SampleScenePath); + } + + private static void AppendSceneToBuildSettings(string scenePath) + { + var scenes = new List(EditorBuildSettings.scenes); + var exists = false; + for (var i = 0; i < scenes.Count; i++) + { + if (scenes[i].path != scenePath) + { + continue; + } + + scenes[i] = new EditorBuildSettingsScene(scenePath, true); + exists = true; + break; + } + + if (!exists) + { + scenes.Add(new EditorBuildSettingsScene(scenePath, true)); + } + + EditorBuildSettings.scenes = scenes.ToArray(); + } + + private static void SetOrAddCommonKey(ADConfig config, string key, string value) + { + foreach (var item in config.CommonKeyValues) + { + if (item != null && item.key == key) + { + item.value = value; + return; + } + } + + config.CommonKeyValues.Add(new AdKeyValue + { + key = key, + value = value + }); + } + + private static void EnsureDirectory(string assetPath) + { + if (AssetDatabase.IsValidFolder(assetPath)) + { + return; + } + + Directory.CreateDirectory(Path.GetFullPath(assetPath)); + AssetDatabase.Refresh(); + } +} +#endif diff --git a/Topon_Adapter/Editor/IAAAdDebugSampleCreator.cs.meta b/Topon_Adapter/Editor/IAAAdDebugSampleCreator.cs.meta new file mode 100644 index 0000000..4e8992c --- /dev/null +++ b/Topon_Adapter/Editor/IAAAdDebugSampleCreator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c488fb4879e14f5cb366ce084ff2b748 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Topon_Adapter/Editor/IAABatchBuildBootstrap.cs b/Topon_Adapter/Editor/IAABatchBuildBootstrap.cs new file mode 100644 index 0000000..bf50c26 --- /dev/null +++ b/Topon_Adapter/Editor/IAABatchBuildBootstrap.cs @@ -0,0 +1,78 @@ +#if UNITY_EDITOR +using System; +using System.IO; +using UnityEditor; +using UnityEngine; + +[InitializeOnLoad] +public static class IAABatchBuildBootstrap +{ + private const string TaskFilePath = "Temp/iaa-batch-task.txt"; + private static bool _subscribed; + private static bool _taskRunning; + + static IAABatchBuildBootstrap() + { + if (!Application.isBatchMode) + { + return; + } + + if (_subscribed) + { + return; + } + + _subscribed = true; + EditorApplication.update += ExecutePendingTaskWhenReady; + } + + private static void ExecutePendingTaskWhenReady() + { + if (!Application.isBatchMode || _taskRunning) + { + return; + } + + var fullPath = Path.GetFullPath(TaskFilePath); + if (!File.Exists(fullPath)) + { + return; + } + + if (EditorApplication.isCompiling || EditorApplication.isUpdating) + { + return; + } + + _taskRunning = true; + EditorApplication.update -= ExecutePendingTaskWhenReady; + + var task = File.ReadAllText(fullPath).Trim(); + File.Delete(fullPath); + + try + { + switch (task) + { + case "create-sample": + IAAAdDebugSampleCreator.CreateOrUpdateSample(); + break; + case "build-apk": + IAAAdAndroidBuild.BuildAndroidTestApk(); + break; + default: + Debug.LogWarning($"[IAA Batch] Unknown task: {task}"); + break; + } + + EditorApplication.Exit(0); + } + catch (Exception exception) + { + Debug.LogException(exception); + EditorApplication.Exit(1); + } + } +} +#endif diff --git a/Topon_Adapter/Editor/IAABatchBuildBootstrap.cs.meta b/Topon_Adapter/Editor/IAABatchBuildBootstrap.cs.meta new file mode 100644 index 0000000..d0b92b9 --- /dev/null +++ b/Topon_Adapter/Editor/IAABatchBuildBootstrap.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 33e31dbdc20d47a78340d49f58d81ef9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Topon_Adapter/Editor/Topon_Adapter.Editor.asmdef b/Topon_Adapter/Editor/Topon_Adapter.Editor.asmdef index 667b14f..397491e 100644 --- a/Topon_Adapter/Editor/Topon_Adapter.Editor.asmdef +++ b/Topon_Adapter/Editor/Topon_Adapter.Editor.asmdef @@ -3,6 +3,7 @@ "rootNamespace": "", "references": [ "GUID:8a3d1447e0a3bdf4fa07035516da8b62", + "GUID:3198a86b02613024e960e3d04a9638cd", "GUID:483a01338fa974b4498cd71261d6e8b9", "GUID:87bccae0237fd4a41ac446d6636f95e0" ], @@ -17,4 +18,4 @@ "defineConstraints": [], "versionDefines": [], "noEngineReferences": false -} \ No newline at end of file +} diff --git a/Topon_Adapter/Runtime/Samples.meta b/Topon_Adapter/Runtime/Samples.meta new file mode 100644 index 0000000..59bd31f --- /dev/null +++ b/Topon_Adapter/Runtime/Samples.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b14a1e102081b4246a56a26fba537991 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Topon_Adapter/Runtime/Samples/IAAAdDebugSampleGui.cs b/Topon_Adapter/Runtime/Samples/IAAAdDebugSampleGui.cs new file mode 100644 index 0000000..b9b181f --- /dev/null +++ b/Topon_Adapter/Runtime/Samples/IAAAdDebugSampleGui.cs @@ -0,0 +1,615 @@ +using System; +using System.Collections.Generic; +using System.IO; +using AnyThinkAds.Api; +using Runtime.ADAggregator; +using UnityEngine; +using UnityEngine.EventSystems; + +public sealed class IAAAdDebugSampleGui : MonoBehaviour +{ + private const float ReferenceWidth = 1080f; + private const float ReferenceHeight = 2340f; + private const int MaxLogEntries = 120; + private const float StatusRefreshInterval = 1f; + + public ADConfig adConfig; + public string userId = "debug_user_001"; + public string rewardedScenario = "reward_debug"; + public string interstitialScenario = "interstitial_debug"; + public bool autoInitialize = true; + public bool forcePortrait = true; + public bool captureUnityLogs = true; + public bool showVerboseState = true; + public bool openDebugUiOnButton = true; + public bool autoWriteLogsToFile = true; + + private readonly List _logs = new List(MaxLogEntries); + private Vector2 _pageScroll; + private Vector2 _logScroll; + private Vector2 _rewardedStatusScroll; + private Vector2 _interstitialStatusScroll; + private bool _initInvoked; + private bool _initCallbackReceived; + private bool _stylesInitialized; + private GUIStyle _titleStyle; + private GUIStyle _sectionStyle; + private GUIStyle _textStyle; + private GUIStyle _buttonStyle; + private GUIStyle _textFieldStyle; + private GUIStyle _logStyle; + private bool _rewardedReadyCache; + private bool _interstitialReadyCache; + private string _rewardedStatusCache = "Not refreshed yet."; + private string _interstitialStatusCache = "Not refreshed yet."; + private float _nextStatusRefreshTime; + private string _sessionLogFilePath; + private string _sessionLogDirectory; + + private void Awake() + { + Application.targetFrameRate = 60; + Screen.sleepTimeout = SleepTimeout.NeverSleep; + if (forcePortrait) + { + Screen.orientation = ScreenOrientation.Portrait; + } + + if (captureUnityLogs) + { + Application.logMessageReceived += OnLogMessageReceived; + } + + ADManager.Instance.GLOBAL_ShowAwardVideoBefore += OnRewardedBefore; + ADManager.Instance.GLOBAL_ShowAwardVideoComplete += OnRewardedComplete; + InitializeLogFile(); + AppendLog("IAA Ad Debug Sample is ready."); + } + + private void Start() + { + if (autoInitialize) + { + TryInitialize(); + } + + RefreshStatusCaches(true); + } + + private void OnDestroy() + { + if (captureUnityLogs) + { + Application.logMessageReceived -= OnLogMessageReceived; + } + + ADManager.Instance.GLOBAL_ShowAwardVideoBefore -= OnRewardedBefore; + ADManager.Instance.GLOBAL_ShowAwardVideoComplete -= OnRewardedComplete; + } + + private void OnGUI() + { + EnsureStyles(); + + var safeArea = Screen.safeArea; + var scaleX = safeArea.width / ReferenceWidth; + var scaleY = safeArea.height / ReferenceHeight; + var previousMatrix = GUI.matrix; + GUI.matrix = Matrix4x4.TRS( + new Vector3(safeArea.x, safeArea.y, 0f), + Quaternion.identity, + new Vector3(scaleX, scaleY, 1f)); + + GUILayout.BeginArea(new Rect(24f, 24f, ReferenceWidth - 48f, ReferenceHeight - 48f), GUI.skin.box); + _pageScroll = GUILayout.BeginScrollView(_pageScroll, false, true); + DrawHeader(); + DrawSetupSection(); + DrawStatusSection(); + DrawControlSection(); + DrawLogSection(); + GUILayout.EndScrollView(); + GUILayout.EndArea(); + + GUI.matrix = previousMatrix; + } + + private void DrawHeader() + { + GUILayout.Label("IAA Ad Debug Sample", _titleStyle); + GUILayout.Label( + $"Screen: {Screen.width} x {Screen.height} | SafeArea: {Screen.safeArea.width:0} x {Screen.safeArea.height:0}", + _textStyle); + GUILayout.Label( + $"Reference Layout: {ReferenceWidth:0} x {ReferenceHeight:0} | Orientation: {Screen.orientation}", + _textStyle); + GUILayout.Space(10f); + } + + private void DrawSetupSection() + { + GUILayout.Label("Setup", _sectionStyle); + GUILayout.Label( + $"ADConfig Asset: {(adConfig != null ? adConfig.name : "Missing")} | Init Invoked: {_initInvoked} | Init Callback: {_initCallbackReceived}", + _textStyle); + + userId = DrawLabeledTextField("User ID", userId); + rewardedScenario = DrawLabeledTextField("Rewarded Scenario", rewardedScenario); + interstitialScenario = DrawLabeledTextField("Interstitial Scenario", interstitialScenario); + + var rewardedPlacement = adConfig?.BaseAwardAdKeyValue?.value ?? string.Empty; + var interstitialPlacement = adConfig?.BaseInteractionAdKeyValue?.value ?? string.Empty; + GUILayout.Label($"Rewarded Placement: {DisplayValue(rewardedPlacement)}", _textStyle); + GUILayout.Label($"Interstitial Placement: {DisplayValue(interstitialPlacement)}", _textStyle); + GUILayout.Space(6f); + + GUILayout.BeginHorizontal(); + if (GUILayout.Button("Initialize ADManager", _buttonStyle, GUILayout.Height(78f))) + { + TryInitialize(); + } + if (GUILayout.Button("Open DebugUI", _buttonStyle, GUILayout.Height(78f))) + { + if (EnsureInitialized("Open DebugUI")) + { + OpenDebugUi(); + } + } + GUILayout.EndHorizontal(); + + GUILayout.Space(10f); + } + + private void DrawStatusSection() + { + GUILayout.Label("Runtime Status", _sectionStyle); + + if (_initInvoked) + { + RefreshStatusCaches(); + GUILayout.Label($"Rewarded Ready: {_rewardedReadyCache}", _textStyle); + GUILayout.Label($"Interstitial Ready: {_interstitialReadyCache}", _textStyle); + } + else + { + GUILayout.Label("Rewarded Ready: not initialized", _textStyle); + GUILayout.Label("Interstitial Ready: not initialized", _textStyle); + } + + var options = ToponAdController.CurrentOptions; + if (options != null) + { + GUILayout.Label( + $"Rewarded Auto/Prewarm/Retry: {options.RewardedAutoLoad}/{options.RewardedPrewarmOnInit}/{options.RewardedMaxLoadAttempts}@{options.RewardedLoadRetryDelayMs}ms", + _textStyle); + GUILayout.Label( + $"Interstitial Auto/Prewarm/Retry: {options.InterstitialAutoLoad}/{options.InterstitialPrewarmOnInit}/{options.InterstitialMaxLoadAttempts}@{options.InterstitialLoadRetryDelayMs}ms", + _textStyle); + GUILayout.Label( + $"Local Strategy Asset: {DisplayValue(options.LocalStrategyAssetPath)}", + _textStyle); + } + + if (showVerboseState && _initInvoked) + { + RefreshStatusCaches(); + GUILayout.Space(6f); + if (GUILayout.Button("Refresh Status Snapshot", _buttonStyle, GUILayout.Height(64f))) + { + RefreshStatusCaches(true); + AppendLog("Manual status snapshot refreshed."); + } + + GUILayout.Label("Rewarded Status", _sectionStyle); + _rewardedStatusScroll = GUILayout.BeginScrollView(_rewardedStatusScroll, GUILayout.Height(240f)); + GUILayout.Label(_rewardedStatusCache, _logStyle); + GUILayout.EndScrollView(); + + GUILayout.Label("Interstitial Status", _sectionStyle); + _interstitialStatusScroll = GUILayout.BeginScrollView(_interstitialStatusScroll, GUILayout.Height(220f)); + GUILayout.Label(_interstitialStatusCache, _logStyle); + GUILayout.EndScrollView(); + } + + GUILayout.Space(10f); + } + + private void DrawControlSection() + { + GUILayout.Label("Ad Actions", _sectionStyle); + + GUILayout.BeginHorizontal(); + if (GUILayout.Button("Enter Rewarded Scene", _buttonStyle, GUILayout.Height(84f))) + { + if (EnsureInitialized("Enter Rewarded Scene")) + { + ADManager.Instance.EnterAdScenario(AD_Type.AwardVideo, rewardedScenario); + AppendLog($"Enter rewarded scene requested. scenario={rewardedScenario}"); + } + } + + if (GUILayout.Button("Enter Interstitial Scene", _buttonStyle, GUILayout.Height(84f))) + { + if (EnsureInitialized("Enter Interstitial Scene")) + { + ADManager.Instance.EnterAdScenario(AD_Type.Interaction, interstitialScenario); + AppendLog($"Enter interstitial scene requested. scenario={interstitialScenario}"); + } + } + GUILayout.EndHorizontal(); + + GUILayout.BeginHorizontal(); + if (GUILayout.Button("Load Rewarded", _buttonStyle, GUILayout.Height(84f))) + { + if (EnsureInitialized("Load Rewarded")) + { + ADManager.Instance.LoadAD(AD_Type.AwardVideo); + AppendLog("Manual rewarded load requested."); + } + } + + if (GUILayout.Button("Async Play Rewarded", _buttonStyle, GUILayout.Height(84f))) + { + if (EnsureInitialized("Play Rewarded")) + { + ADManager.Instance.AsyncPlayAD(AD_Type.AwardVideo, rewardedScenario, result => + { + AppendLog($"Rewarded callback: {result}"); + }); + AppendLog($"AsyncPlayAD rewarded requested. scenario={rewardedScenario}"); + } + } + GUILayout.EndHorizontal(); + + GUILayout.BeginHorizontal(); + if (GUILayout.Button("Load Interstitial", _buttonStyle, GUILayout.Height(84f))) + { + if (EnsureInitialized("Load Interstitial")) + { + ADManager.Instance.LoadAD(AD_Type.Interaction); + AppendLog("Manual interstitial load requested."); + } + } + + if (GUILayout.Button("Async Play Interstitial", _buttonStyle, GUILayout.Height(84f))) + { + if (EnsureInitialized("Play Interstitial")) + { + ADManager.Instance.AsyncPlayAD(AD_Type.Interaction, interstitialScenario, result => + { + AppendLog($"Interstitial callback: {result}"); + }); + AppendLog($"AsyncPlayAD interstitial requested. scenario={interstitialScenario}"); + } + } + GUILayout.EndHorizontal(); + + GUILayout.Space(10f); + } + + private void DrawLogSection() + { + GUILayout.Label("Diagnostics", _sectionStyle); + GUILayout.BeginHorizontal(); + if (GUILayout.Button("Clear Logs", _buttonStyle, GUILayout.Height(70f))) + { + _logs.Clear(); + AppendLog("Logs cleared."); + } + + if (GUILayout.Button("Copy All Logs", _buttonStyle, GUILayout.Height(70f))) + { + GUIUtility.systemCopyBuffer = string.Join("\n", _logs); + AppendLog($"Copied {_logs.Count} log lines to clipboard."); + } + + if (GUILayout.Button("Log Current Strategy", _buttonStyle, GUILayout.Height(70f))) + { + var options = ToponAdController.CurrentOptions; + if (options == null) + { + AppendLog("Current strategy unavailable: controller not initialized."); + } + else + { + AppendLog( + $"Strategy => RewardedAuto={options.RewardedAutoLoad}, RewardedPrewarm={options.RewardedPrewarmOnInit}, InterAuto={options.InterstitialAutoLoad}, InterPrewarm={options.InterstitialPrewarmOnInit}"); + } + } + GUILayout.EndHorizontal(); + + GUILayout.BeginHorizontal(); + if (GUILayout.Button("Export Logs To File", _buttonStyle, GUILayout.Height(70f))) + { + ExportVisibleLogsToFile(); + } + + if (GUILayout.Button("Copy Log File Path", _buttonStyle, GUILayout.Height(70f))) + { + if (string.IsNullOrWhiteSpace(_sessionLogFilePath)) + { + AppendLog("Log file path unavailable."); + } + else + { + GUIUtility.systemCopyBuffer = _sessionLogFilePath; + AppendLog($"Copied log file path: {_sessionLogFilePath}"); + } + } + GUILayout.EndHorizontal(); + + GUILayout.Label($"Log File: {DisplayValue(_sessionLogFilePath)}", _textStyle); + + _logScroll = GUILayout.BeginScrollView(_logScroll, GUILayout.Height(520f)); + foreach (var line in _logs) + { + GUILayout.Label(line, _logStyle); + } + GUILayout.EndScrollView(); + } + + private string DrawLabeledTextField(string label, string value) + { + GUILayout.BeginHorizontal(); + GUILayout.Label(label, _textStyle, GUILayout.Width(260f)); + value = GUILayout.TextField(value ?? string.Empty, _textFieldStyle, GUILayout.Height(64f)); + GUILayout.EndHorizontal(); + return value; + } + + private bool TryInitialize() + { + if (_initInvoked) + { + AppendLog("Initialize skipped: ADManager already initialized by sample."); + return true; + } + + if (adConfig == null) + { + AppendLog("Initialize failed: sample ADConfig asset is missing."); + return false; + } + + if (string.IsNullOrWhiteSpace(adConfig.Id) || string.IsNullOrWhiteSpace(adConfig.Key)) + { + AppendLog("Initialize warning: Taku AppId/AppKey is empty. Please fill the sample ADConfig asset."); + } + + _initInvoked = true; + var controller = new ToponAdController(); + ADManager.Instance.Init(() => + { + _initCallbackReceived = true; + AppendLog("ADManager.Init callback received."); + }, userId, adConfig, controller); + AppendLog($"ADManager.Init invoked with userId={userId}"); + AppendLog($"Rewarded placement={adConfig.BaseAwardAdKeyValue?.value}, interstitial placement={adConfig.BaseInteractionAdKeyValue?.value}"); + return true; + } + + private bool EnsureInitialized(string actionName) + { + if (TryInitialize()) + { + return true; + } + + AppendLog($"{actionName} cancelled: initialization failed."); + return false; + } + + private void RefreshStatusCaches(bool force = false) + { + if (!force && Time.unscaledTime < _nextStatusRefreshTime) + { + return; + } + + _nextStatusRefreshTime = Time.unscaledTime + StatusRefreshInterval; + _rewardedReadyCache = _initInvoked && ADManager.Instance.IsRealy(AD_Type.AwardVideo); + _interstitialReadyCache = _initInvoked && ADManager.Instance.IsRealy(AD_Type.Interaction); + _rewardedStatusCache = GetRewardedStatusText(); + _interstitialStatusCache = GetInterstitialStatusText(); + } + + private string GetRewardedStatusText() + { + var placement = adConfig?.BaseAwardAdKeyValue?.value; + if (string.IsNullOrWhiteSpace(placement)) + { + return "No rewarded placement configured."; + } + + var options = ToponAdController.CurrentOptions; + if (options?.RewardedAutoLoad ?? true) + { + return ATRewardedAutoVideo.Instance.checkAutoAdStatus(placement); + } + + return ATRewardedVideo.Instance.checkAdStatus(placement); + } + + private string GetInterstitialStatusText() + { + var placement = adConfig?.BaseInteractionAdKeyValue?.value; + if (string.IsNullOrWhiteSpace(placement)) + { + return "No interstitial placement configured."; + } + + var options = ToponAdController.CurrentOptions; + if (options?.InterstitialAutoLoad ?? true) + { + return ATInterstitialAutoAd.Instance.checkAutoAdStatus(placement); + } + + return ATInterstitialAd.Instance.checkAdStatus(placement); + } + + private void OnRewardedBefore(string placementId, string scenario) + { + AppendLog($"Rewarded before show => placement={placementId}, scenario={scenario}"); + } + + private void OnRewardedComplete(bool completed) + { + AppendLog($"Rewarded global complete => reward={completed}"); + } + + private void OnLogMessageReceived(string condition, string stackTrace, LogType type) + { + if (type != LogType.Error && type != LogType.Exception && type != LogType.Warning) + { + if (!condition.Contains("[Topon]") && + !condition.Contains("ATSDK") && + !condition.Contains("Rewarded") && + !condition.Contains("Interstitial") && + !condition.Contains("Taku")) + { + return; + } + } + + AppendLog($"[{type}] {condition}"); + } + + private void OpenDebugUi() + { + var options = ToponAdController.CurrentOptions; + if (!openDebugUiOnButton) + { + AppendLog("DebugUI launch disabled by sample toggle."); + return; + } + + if (options != null && !string.IsNullOrWhiteSpace(options.DebuggerKey)) + { + ATSDKAPI.showDebuggerUI(options.DebuggerKey); + AppendLog($"Open DebugUI with debugger key={options.DebuggerKey}"); + return; + } + + ATSDKAPI.showDebuggerUI(); + AppendLog("Open DebugUI with current SDK configuration."); + } + + private void AppendLog(string message) + { + var line = $"{DateTime.Now:HH:mm:ss} {message}"; + if (_logs.Count >= MaxLogEntries) + { + _logs.RemoveAt(0); + } + + _logs.Add(line); + TryAppendLineToFile(line); + _logScroll.y = float.MaxValue; + } + + private void InitializeLogFile() + { + try + { + _sessionLogDirectory = Path.Combine(Application.persistentDataPath, "IAAAdDebugLogs"); + Directory.CreateDirectory(_sessionLogDirectory); + var fileName = $"iaa-ad-debug-{DateTime.Now:yyyyMMdd-HHmmss}.log"; + _sessionLogFilePath = Path.Combine(_sessionLogDirectory, fileName); + File.WriteAllText(_sessionLogFilePath, + $"IAA Ad Debug Sample Log{Environment.NewLine}" + + $"Created: {DateTime.Now:yyyy-MM-dd HH:mm:ss}{Environment.NewLine}" + + $"persistentDataPath: {Application.persistentDataPath}{Environment.NewLine}" + + $"----------------------------------------{Environment.NewLine}"); + } + catch (Exception exception) + { + _sessionLogFilePath = null; + Debug.LogWarning($"[IAA Sample] Failed to initialize log file: {exception.Message}"); + } + } + + private void TryAppendLineToFile(string line) + { + if (!autoWriteLogsToFile || string.IsNullOrWhiteSpace(_sessionLogFilePath)) + { + return; + } + + try + { + File.AppendAllText(_sessionLogFilePath, line + Environment.NewLine); + } + catch (Exception exception) + { + Debug.LogWarning($"[IAA Sample] Failed to append log file: {exception.Message}"); + } + } + + private void ExportVisibleLogsToFile() + { + try + { + if (string.IsNullOrWhiteSpace(_sessionLogDirectory)) + { + InitializeLogFile(); + } + + var exportPath = Path.Combine( + _sessionLogDirectory ?? Application.persistentDataPath, + $"iaa-ad-debug-export-{DateTime.Now:yyyyMMdd-HHmmss}.log"); + File.WriteAllLines(exportPath, _logs); + AppendLog($"Exported {_logs.Count} visible log lines to file: {exportPath}"); + } + catch (Exception exception) + { + AppendLog($"Export log file failed: {exception.Message}"); + } + } + + private static string DisplayValue(string value) + { + return string.IsNullOrWhiteSpace(value) ? "" : value; + } + + private void EnsureStyles() + { + if (_stylesInitialized) + { + return; + } + + _stylesInitialized = true; + _titleStyle = new GUIStyle(GUI.skin.label) + { + fontSize = 54, + fontStyle = FontStyle.Bold, + alignment = TextAnchor.MiddleCenter, + wordWrap = true + }; + _sectionStyle = new GUIStyle(GUI.skin.label) + { + fontSize = 34, + fontStyle = FontStyle.Bold, + wordWrap = true + }; + _textStyle = new GUIStyle(GUI.skin.label) + { + fontSize = 28, + wordWrap = true + }; + _buttonStyle = new GUIStyle(GUI.skin.button) + { + fontSize = 28, + wordWrap = true + }; + _textFieldStyle = new GUIStyle(GUI.skin.textField) + { + fontSize = 28 + }; + _logStyle = new GUIStyle(GUI.skin.label) + { + fontSize = 24, + wordWrap = true, + richText = false + }; + } +} diff --git a/Topon_Adapter/Runtime/Samples/IAAAdDebugSampleGui.cs.meta b/Topon_Adapter/Runtime/Samples/IAAAdDebugSampleGui.cs.meta new file mode 100644 index 0000000..9a8a894 --- /dev/null +++ b/Topon_Adapter/Runtime/Samples/IAAAdDebugSampleGui.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 53b297b9bf0d4cfca2b77af0fd74c95c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Topon_Adapter/Runtime/Scripts/AwardVideoPlayer.cs b/Topon_Adapter/Runtime/Scripts/AwardVideoPlayer.cs index 2eb40b5..a439f3d 100644 --- a/Topon_Adapter/Runtime/Scripts/AwardVideoPlayer.cs +++ b/Topon_Adapter/Runtime/Scripts/AwardVideoPlayer.cs @@ -7,12 +7,15 @@ using UnityEngine; public class AwardVideoPlayer : ADPlayer , ATRewardedVideoListener { private ATRewardedVideo _atRewardedVideo; + private ATRewardedAutoVideo _atRewardedAutoVideo; private Action _onVideoComplete; private ADListenerAggregator _aggregator; + private bool _autoLoadRegistered; public override void OnInit() { this._atRewardedVideo = ATRewardedVideo.Instance; + this._atRewardedAutoVideo = ATRewardedAutoVideo.Instance; // this._atRewardedVideo.client.setListener(this); //由于新版本广告sdk弃用该方式,通过以下方式重新桥接监听事件 this._aggregator = new ADListenerAggregator(); this._aggregator.BindAwardVideoListener(this._atRewardedVideo.client , this); @@ -30,7 +33,15 @@ public class AwardVideoPlayer : ADPlayer , ATRewardedVideoListener this.adListener.onClose = onClose; this.adListener.onVideoComplete = this.OnVideoComplete; var json = new Dictionary { { AnyThinkAds.Api.ATConst.SCENARIO, this.AdScene } }; - this._atRewardedVideo.showAd(this.Key , json); + if (UseAutoLoad()) + { + EnsureAutoLoadRegistered(); + this._atRewardedAutoVideo.showAutoAd(this.Key, json); + } + else + { + this._atRewardedVideo.showAd(this.Key , json); + } } } @@ -42,18 +53,54 @@ public class AwardVideoPlayer : ADPlayer , ATRewardedVideoListener public override bool IsReadly() { - return this.curState == 2; + if (UseAutoLoad()) + { + if (this._atRewardedAutoVideo != null && + this._atRewardedAutoVideo.autoLoadRewardedVideoReadyForPlacementID(this.Key)) + { + this.curState = 2; + return true; + } + + return this.curState == 2; + } + + if (this.curState == 2) + { + return true; + } + + if (this._atRewardedVideo != null && this._atRewardedVideo.hasAdReady(this.Key)) + { + this.curState = 2; + return true; + } + + return false; } public override void LoadAD() { if (curState == 0) { + if (UseAutoLoad()) + { + EnsureAutoLoadRegistered(); + this._atRewardedAutoVideo.setAutoLocalExtra(this.Key, BuildRewardedExtra()); + this.curState = this._atRewardedAutoVideo.autoLoadRewardedVideoReadyForPlacementID(this.Key) ? 2 : 1; + return; + } + + if (this._atRewardedVideo != null && this._atRewardedVideo.hasAdReady(this.Key)) + { + this.curState = 2; + return; + } + { Dictionary jsonmap = new Dictionary(); //ATConst.USERID_KEY必传,用于标识每个用户;ATConst.USER_EXTRA_DATA为可选参数,传入后将透传到开发者的服务器 - jsonmap.Add(ATConst.USERID_KEY, ADManager.Instance.UserId); - jsonmap.Add(ATConst.USER_EXTRA_DATA, "user_extra_data"); + jsonmap = BuildRewardedExtra(); curState = 1; this._atRewardedVideo.loadVideoAd(this.Key, jsonmap); } @@ -130,5 +177,63 @@ public class AwardVideoPlayer : ADPlayer , ATRewardedVideoListener } #endregion + + public override void OnPlayRequestStarted() + { + if (UseAutoLoad()) + { + EnsureAutoLoadRegistered(); + this._atRewardedAutoVideo?.setAutoLocalExtra(this.Key, BuildRewardedExtra()); + } + } + + public override int MaxLoadAttempts => ToponAdController.CurrentOptions?.RewardedMaxLoadAttempts ?? base.MaxLoadAttempts; + + public override float LoadRetryDelaySeconds => + Math.Max(0f, (ToponAdController.CurrentOptions?.RewardedLoadRetryDelayMs ?? 750) / 1000f); + + public override bool AutoPreloadOnInit => ToponAdController.CurrentOptions?.RewardedPrewarmOnInit ?? true; + + private bool UseAutoLoad() + { + return ToponAdController.CurrentOptions?.RewardedAutoLoad ?? true; + } + + private void EnsureAutoLoadRegistered() + { + if (_autoLoadRegistered) + { + return; + } + + this._atRewardedAutoVideo?.addAutoLoadAdPlacementID(new[] { this.Key }); + _autoLoadRegistered = true; + } + + private Dictionary BuildRewardedExtra() + { + return new Dictionary + { + { ATConst.USERID_KEY, ADManager.Instance.UserId }, + { ATConst.USER_EXTRA_DATA, "user_extra_data" } + }; + } + + public override void EnterAdScenario(string scenario) + { + if (string.IsNullOrWhiteSpace(scenario) || string.Equals(scenario, "__default__", StringComparison.Ordinal)) + { + return; + } + + if (UseAutoLoad()) + { + EnsureAutoLoadRegistered(); + this._atRewardedAutoVideo?.entryAutoAdScenarioWithPlacementID(this.Key, scenario); + return; + } + + this._atRewardedVideo?.entryScenarioWithPlacementID(this.Key, scenario); + } -} \ No newline at end of file +} diff --git a/Topon_Adapter/Runtime/Scripts/InteractionPlayer.cs b/Topon_Adapter/Runtime/Scripts/InteractionPlayer.cs index 06d8567..dfd3787 100644 --- a/Topon_Adapter/Runtime/Scripts/InteractionPlayer.cs +++ b/Topon_Adapter/Runtime/Scripts/InteractionPlayer.cs @@ -7,28 +7,83 @@ using UnityEngine; public class InteractionPlayer : ADPlayer , ATInterstitialAdListener { private ATInterstitialAd _atInterstitialAd; + private ATInterstitialAutoAd _atInterstitialAutoAd; private ADListenerAggregator _aggregator; + private bool _autoLoadRegistered; public override void OnInit() { this._atInterstitialAd = ATInterstitialAd.Instance; + this._atInterstitialAutoAd = ATInterstitialAutoAd.Instance; this._aggregator = new ADListenerAggregator(); this._aggregator.BindInterstitialAdListener(this._atInterstitialAd.client,this); } public override void ShowAD(Action onClose, Action onVideoComplete) { - if (curState == 2) + if (this.IsReadly()) { - ATInterstitialAd.Instance.showInterstitialAd(this.Key); + this.adListener.onClose = onClose; + this.adListener.onVideoComplete = onVideoComplete; + if (UseAutoLoad()) + { + EnsureAutoLoadRegistered(); + this._atInterstitialAutoAd.showAutoAd(this.Key, BuildInterstitialExtra()); + } + else + { + ATInterstitialAd.Instance.showInterstitialAd(this.Key); + } curState = 0; } } + + public override bool IsReadly() + { + if (this.curState == 2) + { + return true; + } + + if (UseAutoLoad()) + { + if (this._atInterstitialAutoAd != null && + this._atInterstitialAutoAd.autoLoadInterstitialAdReadyForPlacementID(this.Key)) + { + this.curState = 2; + return true; + } + + return false; + } + + if (this._atInterstitialAd != null && this._atInterstitialAd.hasInterstitialAdReady(this.Key)) + { + this.curState = 2; + return true; + } + + return false; + } public override void LoadAD() { if (curState == 0) { + if (UseAutoLoad()) + { + EnsureAutoLoadRegistered(); + this._atInterstitialAutoAd.setAutoLocalExtra(this.Key, BuildInterstitialExtra()); + this.curState = this._atInterstitialAutoAd.autoLoadInterstitialAdReadyForPlacementID(this.Key) ? 2 : 1; + return; + } + + if (this._atInterstitialAd != null && this._atInterstitialAd.hasInterstitialAdReady(this.Key)) + { + this.curState = 2; + return; + } + { //加载广告 Dictionary jsonmap = new Dictionary(); @@ -61,6 +116,8 @@ public class InteractionPlayer : ADPlayer , ATInterstitialAdListener public void onInterstitialAdFailedToShow(string placementId) { + this.curState = 0; + this.adListener.OnShowError(); } public void onInterstitialAdClose(string placementId, ATCallbackInfo callbackInfo) @@ -83,6 +140,8 @@ public class InteractionPlayer : ADPlayer , ATInterstitialAdListener public void onInterstitialAdFailedToPlayVideo(string placementId, string code, string message) { + this.curState = 0; + this.adListener.OnShowError(); } public void startLoadingADSource(string placementId, ATCallbackInfo callbackInfo) @@ -112,4 +171,61 @@ public class InteractionPlayer : ADPlayer , ATInterstitialAdListener } #endregion -} \ No newline at end of file + + public override void OnPlayRequestStarted() + { + if (UseAutoLoad()) + { + EnsureAutoLoadRegistered(); + this._atInterstitialAutoAd?.setAutoLocalExtra(this.Key, BuildInterstitialExtra()); + } + } + + public override int MaxLoadAttempts => ToponAdController.CurrentOptions?.InterstitialMaxLoadAttempts ?? base.MaxLoadAttempts; + + public override float LoadRetryDelaySeconds => + Math.Max(0f, (ToponAdController.CurrentOptions?.InterstitialLoadRetryDelayMs ?? 500) / 1000f); + + public override bool AutoPreloadOnInit => ToponAdController.CurrentOptions?.InterstitialPrewarmOnInit ?? true; + + private bool UseAutoLoad() + { + return ToponAdController.CurrentOptions?.InterstitialAutoLoad ?? true; + } + + private void EnsureAutoLoadRegistered() + { + if (_autoLoadRegistered) + { + return; + } + + this._atInterstitialAutoAd?.addAutoLoadAdPlacementID(new[] { this.Key }); + _autoLoadRegistered = true; + } + + private Dictionary BuildInterstitialExtra() + { + return new Dictionary + { + { ATConst.SCENARIO, this.AdScene ?? string.Empty } + }; + } + + public override void EnterAdScenario(string scenario) + { + if (string.IsNullOrWhiteSpace(scenario) || string.Equals(scenario, "__default__", StringComparison.Ordinal)) + { + return; + } + + if (UseAutoLoad()) + { + EnsureAutoLoadRegistered(); + this._atInterstitialAutoAd?.entryAutoAdScenarioWithPlacementID(this.Key, scenario); + return; + } + + this._atInterstitialAd?.entryScenarioWithPlacementID(this.Key, scenario); + } +} diff --git a/Topon_Adapter/Runtime/Scripts/ToponAdController.cs b/Topon_Adapter/Runtime/Scripts/ToponAdController.cs index 449dd4d..782bdad 100644 --- a/Topon_Adapter/Runtime/Scripts/ToponAdController.cs +++ b/Topon_Adapter/Runtime/Scripts/ToponAdController.cs @@ -7,6 +7,7 @@ public class ToponAdController : IAdController { public static string LastDetectedArea { get; private set; } public static string LastAreaError { get; private set; } + public static ToponControllerOptions CurrentOptions { get; private set; } private Action _maskAction; private Action _logEventAction; @@ -20,14 +21,16 @@ public class ToponAdController : IAdController _adConfig = adConfig; _controllerOptions = ToponControllerOptions.Resolve(adConfig, args); + CurrentOptions = _controllerOptions; ApplyPreInitOptions(_controllerOptions); var isDebug = _controllerOptions.Debug ?? false; ATSDKAPI.setLogDebug(isDebug); ATSDKAPI.initSDK(adConfig.Id , adConfig.Key); + StartChinaMainlandSdkIfNeeded(); ApplyPostInitOptions(_controllerOptions); - if (isDebug) + if (_controllerOptions.AutoOpenDebuggerUI) { ShowAndroidTest (); } @@ -103,6 +106,11 @@ public class ToponAdController : IAdController return; } + if (!string.IsNullOrWhiteSpace(options.LocalStrategyAssetPath)) + { + ATSDKAPI.setLocalStrategyAssetPath(options.LocalStrategyAssetPath); + } + if (options.InitCustomMap != null && options.InitCustomMap.Count > 0) { ATSDKAPI.initCustomMap(options.InitCustomMap); @@ -163,6 +171,26 @@ public class ToponAdController : IAdController } } + private static void StartChinaMainlandSdkIfNeeded() + { +#if UNITY_ANDROID && !UNITY_EDITOR + try + { + if (!ATSDKAPI.isCnSDK()) + { + return; + } + + ATSDKAPI.start(); + Debug.Log("[Topon] Called ATSDK.start() for China mainland SDK."); + } + catch (Exception exception) + { + Debug.LogWarning($"[Topon] Failed to call ATSDK.start(): {exception.Message}"); + } +#endif + } + private sealed class ToponAreaListener : ATGetAreaListener { private readonly ToponAdController _controller; diff --git a/Topon_Adapter/Runtime/Scripts/ToponControllerOptions.cs b/Topon_Adapter/Runtime/Scripts/ToponControllerOptions.cs index 9ac57b1..90dd5ab 100644 --- a/Topon_Adapter/Runtime/Scripts/ToponControllerOptions.cs +++ b/Topon_Adapter/Runtime/Scripts/ToponControllerOptions.cs @@ -11,6 +11,7 @@ public sealed class ToponControllerOptions public const string SubChannelKey = "topon.sub_channel"; public const string DebugKey = "topon.debug"; public const string DebuggerKeyKey = "topon.debugger_key"; + public const string AutoOpenDebuggerUiKey = "topon.auto_open_debugger_ui"; public const string SDKAreaKey = "topon.sdk_area"; public const string LongitudeKey = "topon.longitude"; public const string LatitudeKey = "topon.latitude"; @@ -19,11 +20,21 @@ public sealed class ToponControllerOptions public const string QueryAreaOnInitKey = "topon.query_area_on_init"; public const string InitCustomMapKey = "topon.custom_map"; public const string RewardedCustomDataKey = "topon.rewarded_custom_data"; + public const string RewardedAutoLoadKey = "topon.rewarded_auto_load"; + public const string RewardedPrewarmOnInitKey = "topon.rewarded_prewarm_on_init"; + public const string RewardedMaxLoadAttemptsKey = "topon.rewarded_max_load_attempts"; + public const string RewardedLoadRetryDelayMsKey = "topon.rewarded_load_retry_delay_ms"; + public const string InterstitialAutoLoadKey = "topon.interstitial_auto_load"; + public const string InterstitialPrewarmOnInitKey = "topon.interstitial_prewarm_on_init"; + public const string InterstitialMaxLoadAttemptsKey = "topon.interstitial_max_load_attempts"; + public const string InterstitialLoadRetryDelayMsKey = "topon.interstitial_load_retry_delay_ms"; + public const string LocalStrategyAssetPathKey = "topon.local_strategy_asset_path"; public string Channel { get; set; } public string SubChannel { get; set; } public bool? Debug { get; set; } public string DebuggerKey { get; set; } + public bool AutoOpenDebuggerUI { get; set; } public int? SDKArea { get; set; } public double? Longitude { get; set; } public double? Latitude { get; set; } @@ -34,6 +45,15 @@ public sealed class ToponControllerOptions public Dictionary RewardedCustomData { get; set; } public Action OnAreaReceived { get; set; } public Action OnAreaError { get; set; } + public bool RewardedAutoLoad { get; set; } = true; + public bool RewardedPrewarmOnInit { get; set; } = true; + public int RewardedMaxLoadAttempts { get; set; } = 3; + public int RewardedLoadRetryDelayMs { get; set; } = 1000; + public bool InterstitialAutoLoad { get; set; } = true; + public bool InterstitialPrewarmOnInit { get; set; } = true; + public int InterstitialMaxLoadAttempts { get; set; } = 2; + public int InterstitialLoadRetryDelayMs { get; set; } = 500; + public string LocalStrategyAssetPath { get; set; } public static ToponControllerOptions Resolve(ADConfig adConfig, object[] args) { @@ -85,6 +105,7 @@ public sealed class ToponControllerOptions SubChannel = GetString(map, SubChannelKey, "sub_channel") ?? SubChannel; Debug = GetBool(map, DebugKey, "debug") ?? Debug; DebuggerKey = GetString(map, DebuggerKeyKey) ?? DebuggerKey; + AutoOpenDebuggerUI = GetBool(map, AutoOpenDebuggerUiKey) ?? AutoOpenDebuggerUI; SDKArea = GetInt(map, SDKAreaKey) ?? SDKArea; Longitude = GetDouble(map, LongitudeKey) ?? Longitude; Latitude = GetDouble(map, LatitudeKey) ?? Latitude; @@ -93,6 +114,15 @@ public sealed class ToponControllerOptions QueryAreaOnInit = GetBool(map, QueryAreaOnInitKey) ?? QueryAreaOnInit; InitCustomMap = MergeMaps(InitCustomMap, GetPrefixedMap(map, InitCustomMapKey + ".")); RewardedCustomData = MergeMaps(RewardedCustomData, GetPrefixedMap(map, RewardedCustomDataKey + ".")); + RewardedAutoLoad = GetBool(map, RewardedAutoLoadKey) ?? RewardedAutoLoad; + RewardedPrewarmOnInit = GetBool(map, RewardedPrewarmOnInitKey) ?? RewardedPrewarmOnInit; + RewardedMaxLoadAttempts = GetInt(map, RewardedMaxLoadAttemptsKey) ?? RewardedMaxLoadAttempts; + RewardedLoadRetryDelayMs = GetInt(map, RewardedLoadRetryDelayMsKey) ?? RewardedLoadRetryDelayMs; + InterstitialAutoLoad = GetBool(map, InterstitialAutoLoadKey) ?? InterstitialAutoLoad; + InterstitialPrewarmOnInit = GetBool(map, InterstitialPrewarmOnInitKey) ?? InterstitialPrewarmOnInit; + InterstitialMaxLoadAttempts = GetInt(map, InterstitialMaxLoadAttemptsKey) ?? InterstitialMaxLoadAttempts; + InterstitialLoadRetryDelayMs = GetInt(map, InterstitialLoadRetryDelayMsKey) ?? InterstitialLoadRetryDelayMs; + LocalStrategyAssetPath = GetString(map, LocalStrategyAssetPathKey) ?? LocalStrategyAssetPath; } private void ApplyLegacyArgs(object[] args) @@ -124,6 +154,7 @@ public sealed class ToponControllerOptions SubChannel = explicitOptions.SubChannel ?? SubChannel; Debug = explicitOptions.Debug ?? Debug; DebuggerKey = explicitOptions.DebuggerKey ?? DebuggerKey; + AutoOpenDebuggerUI = explicitOptions.AutoOpenDebuggerUI || AutoOpenDebuggerUI; SDKArea = explicitOptions.SDKArea ?? SDKArea; Longitude = explicitOptions.Longitude ?? Longitude; Latitude = explicitOptions.Latitude ?? Latitude; @@ -174,6 +205,7 @@ public sealed class ToponControllerOptions SubChannel = GetString(map, SubChannelKey, "sub_channel") ?? SubChannel; Debug = GetBool(map, DebugKey, "debug") ?? Debug; DebuggerKey = GetString(map, DebuggerKeyKey) ?? DebuggerKey; + AutoOpenDebuggerUI = GetBool(map, AutoOpenDebuggerUiKey) ?? AutoOpenDebuggerUI; SDKArea = GetInt(map, SDKAreaKey) ?? SDKArea; Longitude = GetDouble(map, LongitudeKey) ?? Longitude; Latitude = GetDouble(map, LatitudeKey) ?? Latitude; @@ -182,6 +214,15 @@ public sealed class ToponControllerOptions QueryAreaOnInit = GetBool(map, QueryAreaOnInitKey) ?? QueryAreaOnInit; InitCustomMap = MergeMaps(InitCustomMap, GetPrefixedMap(map, InitCustomMapKey + ".")); RewardedCustomData = MergeMaps(RewardedCustomData, GetPrefixedMap(map, RewardedCustomDataKey + ".")); + RewardedAutoLoad = GetBool(map, RewardedAutoLoadKey) ?? RewardedAutoLoad; + RewardedPrewarmOnInit = GetBool(map, RewardedPrewarmOnInitKey) ?? RewardedPrewarmOnInit; + RewardedMaxLoadAttempts = GetInt(map, RewardedMaxLoadAttemptsKey) ?? RewardedMaxLoadAttempts; + RewardedLoadRetryDelayMs = GetInt(map, RewardedLoadRetryDelayMsKey) ?? RewardedLoadRetryDelayMs; + InterstitialAutoLoad = GetBool(map, InterstitialAutoLoadKey) ?? InterstitialAutoLoad; + InterstitialPrewarmOnInit = GetBool(map, InterstitialPrewarmOnInitKey) ?? InterstitialPrewarmOnInit; + InterstitialMaxLoadAttempts = GetInt(map, InterstitialMaxLoadAttemptsKey) ?? InterstitialMaxLoadAttempts; + InterstitialLoadRetryDelayMs = GetInt(map, InterstitialLoadRetryDelayMsKey) ?? InterstitialLoadRetryDelayMs; + LocalStrategyAssetPath = GetString(map, LocalStrategyAssetPathKey) ?? LocalStrategyAssetPath; } private static string GetString(IDictionary map, params string[] keys) diff --git a/package.json b/package.json index 6969eac..d9db7be 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "com.commercialization.topon", "displayName": "Commercialization.topon", "description": "基于topon的广告sdk封装,依赖基础商业化模块", - "version": "1.4.6", + "version": "1.4.7", "unity": "2021.1", "license": "MIT", "repository": { @@ -16,7 +16,7 @@ }, "dependencies": { - "com.foldcc.cc-framework.commercialization" : "http://private.lightyears.ltd:18640/foldcc/CC-Framework.Commercialization.git#1.0.5" + "com.foldcc.cc-framework.commercialization" : "http://private.lightyears.ltd:18640/foldcc/CC-Framework.Commercialization.git#1.0.12" }, "keywords": [ "Framework"