diff --git a/package/Runtime/Plugins/Android/UnityHelper.java b/package/Runtime/Plugins/Android/UnityHelper.java index 70df8a95..2de5208c 100644 --- a/package/Runtime/Plugins/Android/UnityHelper.java +++ b/package/Runtime/Plugins/Android/UnityHelper.java @@ -30,10 +30,6 @@ public final class UnityHelper { static final NimbusAdManager manager = new NimbusAdManager(); - static { - BlockingAdRenderer.setStaticDismissTimeout(10000); - } - public static void makeBannerRequest(Object obj, String position, float bannerFloor, float videoFloor, Object listener) { if (obj instanceof Activity) { final Activity activity = (Activity) obj; @@ -70,7 +66,7 @@ public static void makeRewardedVideoRequest(Object obj, String position, float b public static void render(Object obj, String type, String auctionId, String markup, String network, String placementId, int width, int height, byte isInterstitial, byte isMraid, String position, String[] impressionTrackers, String[] clickTrackers, - int duration, int companionWidth, int companionHeight, Object listener) { + int duration, int companionWidth, int companionHeight, int closeButtonDelaySeconds, Object listener) { if (obj instanceof Activity) { final Activity activity = (Activity) obj; final HashMap trackers = new HashMap<>(); @@ -87,7 +83,7 @@ public static void render(Object obj, String type, String auctionId, String mark public static void renderBlocking(Object obj, String type, String auctionId, String markup, String network, String placementId, int width, int height, byte isInterstitial, byte isMraid, String position, String[] impressionTrackers, String[] clickTrackers, - int duration, int companionWidth, int companionHeight, Object listener) { + int duration, int companionWidth, int companionHeight, int closeButtonDelaySeconds, Object listener) { if (obj instanceof Activity) { final Activity activity = (Activity) obj; final HashMap trackers = new HashMap<>(); @@ -97,15 +93,22 @@ public static void renderBlocking(Object obj, String type, String auctionId, Str isInterstitial, markup, network, placementId, isMraid, position, trackers, duration)); if (companionWidth != 0 && companionHeight != 0) { response.companionAds = new CompanionAd[]{ CompanionAd.end(companionWidth, companionHeight) }; + } else { + response.companionAds = new CompanionAd[]{activity.getResources().getConfiguration().orientation == + Configuration.ORIENTATION_LANDSCAPE ? CompanionAd.end(480, 320) : CompanionAd.end(320, 480)}; } - final AdController controller = Renderer.loadBlockingAd(response, activity); - final NimbusAdManager.Listener callback = (NimbusAdManager.Listener) listener; - if (controller != null) { - controller.start(); - callback.onAdRendered(controller); - } else { - callback.onError(new NimbusError(NimbusError.ErrorType.RENDERER_ERROR, "Error rendering blocking ad", null)); - } + activity.runOnUiThread(() -> { + BlockingAdRenderer.setsCloseButtonDelayRender(closeButtonDelaySeconds * 1000); + final AdController controller = Renderer.loadBlockingAd(response, activity); + final NimbusAdManager.Listener callback = (NimbusAdManager.Listener) listener; + if (controller != null) { + callback.onAdRendered(controller); + controller.start(); + } else { + callback.onError(new NimbusError(NimbusError.ErrorType.RENDERER_ERROR, "Error rendering blocking ad", null)); + } + }); + } } diff --git a/package/Runtime/Scripts/Internal/AdEvents.cs b/package/Runtime/Scripts/Internal/AdEvents.cs index 7c02c3b8..9db341fe 100644 --- a/package/Runtime/Scripts/Internal/AdEvents.cs +++ b/package/Runtime/Scripts/Internal/AdEvents.cs @@ -3,6 +3,7 @@ // ReSharper disable CheckNamespace namespace Nimbus.Runtime.Scripts.Internal { public class AdEvents { + public event Action OnAdLoaded; public event Action OnAdRendered; public event Action OnAdError; public event Action OnAdImpression; @@ -15,7 +16,11 @@ public class AdEvents { internal void EmitOnAdError(NimbusAdUnit obj) { OnAdError?.Invoke(obj); } - + + internal void EmitOnAdLoaded(NimbusAdUnit obj) { + OnAdLoaded?.Invoke(obj); + } + internal void EmitOnAdRendered(NimbusAdUnit obj) { OnAdRendered?.Invoke(obj); } @@ -43,6 +48,8 @@ internal void EmitOnOnVideoAdResume(NimbusAdUnit obj) { internal void EmitOnOnAdCompleted(NimbusAdUnit obj, bool skipped) { OnAdCompleted?.Invoke(obj, skipped); } + + } @@ -50,7 +57,7 @@ internal void EmitOnOnAdCompleted(NimbusAdUnit obj, bool skipped) { public enum AdEventTypes { NOT_LOADED, - // LOADED, + LOADED, IMPRESSION, CLICKED, PAUSED, diff --git a/package/Runtime/Scripts/Internal/Android.cs b/package/Runtime/Scripts/Internal/Android.cs index 2a5baa63..d996c092 100644 --- a/package/Runtime/Scripts/Internal/Android.cs +++ b/package/Runtime/Scripts/Internal/Android.cs @@ -96,7 +96,8 @@ internal override NimbusAdUnit ShowAd(ILogger logger, ref NimbusAdUnit nimbusAdU _helper.CallStatic(functionCall, _currentActivity, response.Type, response.AuctionID, response.Markup, response.Network, response.PlacementID, response.Width, response.Height, response.IsInterstitial, response.IsMraid, response.Position, response.ImpressionTrackers, response.ClickTrackers, - response.Duration, 0, 0, listener); + response.Duration, /* CompanionAd Width */ 0, /* CompanionAd Height */ 0, + nimbusAdUnit.CloseButtonDelayInSeconds, listener); return nimbusAdUnit; } diff --git a/package/Runtime/Scripts/Internal/IAdEvents.cs b/package/Runtime/Scripts/Internal/IAdEvents.cs index dc875ae2..71d2ef04 100644 --- a/package/Runtime/Scripts/Internal/IAdEvents.cs +++ b/package/Runtime/Scripts/Internal/IAdEvents.cs @@ -2,6 +2,7 @@ namespace Nimbus.Runtime.Scripts.Internal { public interface IAdEvents { + void OnAdLoaded(NimbusAdUnit nimbusAdUnit); void OnAdWasRendered(NimbusAdUnit nimbusAdUnit); void OnAdClicked(NimbusAdUnit nimbusAdUnit); void OnAdCompleted(NimbusAdUnit nimbusAdUnit, bool skipped); diff --git a/package/Runtime/Scripts/Internal/NimbusAdUnit.cs b/package/Runtime/Scripts/Internal/NimbusAdUnit.cs index d6b0604e..6612caf9 100644 --- a/package/Runtime/Scripts/Internal/NimbusAdUnit.cs +++ b/package/Runtime/Scripts/Internal/NimbusAdUnit.cs @@ -28,7 +28,7 @@ public NimbusAdUnit(AdUnityType adType, string position, float bannerFloor, floa AdType = adType; BidFloors = new BidFloors(bannerFloor, videoFloor); CurrentAdState = AdEventTypes.NOT_LOADED; - CloseButtonDelayInSeconds = (int) TimeSpan.FromMinutes(60).TotalSeconds; + CloseButtonDelayInSeconds = adType == AdUnityType.Rewarded ? (int) TimeSpan.FromMinutes(60).TotalSeconds : 5; InstanceID = GetHashCode(); Position = position; @@ -93,6 +93,9 @@ internal void EmitOnAdError(NimbusAdUnit obj) { internal void EmitOnAdEvent(AdEventTypes e) { switch (e) { + case AdEventTypes.LOADED: + _adEvents.EmitOnAdLoaded(this); + break; case AdEventTypes.IMPRESSION: _adEvents.EmitOnOnAdImpression(this); break; @@ -229,7 +232,7 @@ internal MetaData(in AndroidJavaObject response) { Type = bid.Get("type"); AuctionID = bid.Get("auction_id"); ADomain = bid.Get("adomain"); - BidRaw = bid.Get("bid_raw"); + BidRaw = bid.Get("bid_raw"); BidInCents = bid.Get("bid_in_cents"); ContentType = bid.Get("content_type"); Crid = bid.Get("crid"); diff --git a/package/Runtime/Scripts/Internal/NimbusAndroidAdManager.cs b/package/Runtime/Scripts/Internal/NimbusAndroidAdManager.cs index 2d7c3a32..6e100a7a 100644 --- a/package/Runtime/Scripts/Internal/NimbusAndroidAdManager.cs +++ b/package/Runtime/Scripts/Internal/NimbusAndroidAdManager.cs @@ -18,6 +18,8 @@ internal AdManagerListener(ILogger logger, in AndroidJavaClass helper, ref Nimbu private void onAdResponse(AndroidJavaObject response) { _adUnit.ResponseMetaData = new MetaData(response); + _adUnit.CurrentAdState = AdEventTypes.LOADED; + _adUnit.EmitOnAdEvent(AdEventTypes.LOADED); } private void onAdRendered(AndroidJavaObject controller) { @@ -34,6 +36,8 @@ private void onError(AndroidJavaObject adError) { _logger.Log($"Listener Ad error: {errMessage}"); _adUnit.AdListenerError = new AdError(errMessage); _adUnit.EmitOnAdError(_adUnit); + // This is where a no bid would occur and is probably checked with the following + // adError.Get("errorType").Call("name") == "NO_BID" } } @@ -51,7 +55,7 @@ private void onAdEvent(AndroidJavaObject adEvent) { _logger.Log("Ad event " + adEvent.Call("name")); var eventState = adEvent.Call("name"); - if (!Enum.TryParse(eventState, out AdEventTypes state)) return; + if (!Enum.TryParse(eventState, out AdEventTypes state) || state == AdEventTypes.LOADED) return; _adUnit.CurrentAdState = state; _adUnit.EmitOnAdEvent(state); } diff --git a/package/Runtime/Scripts/NimbusManager.cs b/package/Runtime/Scripts/NimbusManager.cs index d61c3789..44880711 100644 --- a/package/Runtime/Scripts/NimbusManager.cs +++ b/package/Runtime/Scripts/NimbusManager.cs @@ -74,6 +74,7 @@ private void OnDestroy() { private static void AutoSubscribe() { var iAdEvents = FindObjectsOfType().OfType(); foreach (var iAdEvent in iAdEvents) { + Instance.NimbusEvents.OnAdLoaded += iAdEvent.OnAdLoaded; Instance.NimbusEvents.OnAdRendered += iAdEvent.OnAdWasRendered; Instance.NimbusEvents.OnAdError += iAdEvent.OnAdError; Instance.NimbusEvents.OnAdClicked += iAdEvent.OnAdClicked; @@ -96,6 +97,7 @@ private static void AutoSubscribe() { private static void AutoUnsubscribe() { var iAdEvents = FindObjectsOfType().OfType(); foreach (var iAdEvent in iAdEvents) { + Instance.NimbusEvents.OnAdLoaded -= iAdEvent.OnAdLoaded; Instance.NimbusEvents.OnAdRendered -= iAdEvent.OnAdWasRendered; Instance.NimbusEvents.OnAdError -= iAdEvent.OnAdError; Instance.NimbusEvents.OnAdClicked -= iAdEvent.OnAdClicked; @@ -230,5 +232,78 @@ public void StopRefreshBannerAd(IEnumerator refreshBannerCoroutine) { public void SetGDPRConsentString(string consent) { _nimbusPlatformAPI.SetGDPRConsentString(consent); } + + + /// + /// Calls to Nimbus for a 320x50 banner ad from the server. It does not the ad. To load the ad you must pass + /// reference of the returned add to the ShowLoadedAd method + /// + /// + /// This is a Nimbus specific field, it must not be empty and it represents a generic placement name + /// can be used by the Nimbus dashboard to breakout data + /// + /// + /// Represents your asking price for banner ads during the auction + /// + public NimbusAdUnit LoadBannerAd(string position, float bannerBidFloor) { + var adUnit = new NimbusAdUnit(AdUnityType.Banner, position, bannerBidFloor, 0f, in NimbusEvents); + return _nimbusPlatformAPI.RequestAd(Debug.unityLogger, ref adUnit); + } + + + /// + /// Calls to Nimbus for a full screen interstitial ad, this can either be a static or video ad depending on which wins + /// the auction. It does not the ad. To load the ad you must pass reference of the returned add to the ShowLoadedAd method + /// + /// + /// This is a Nimbus specific field, it must not be empty and it represents a generic placement name + /// can be used by the Nimbus dashboard to breakout data + /// + /// + /// Represents your asking price for banner ads during the auction + /// + /// + /// Represents your asking price for video ads during the auction + /// + public NimbusAdUnit LoadFullScreenAd(string position, float bannerBidFloor, float videoBidFloor) { + var adUnit = new NimbusAdUnit(AdUnityType.Interstitial, position, bannerBidFloor, videoBidFloor, + in NimbusEvents); + return _nimbusPlatformAPI.RequestAd(Debug.unityLogger, ref adUnit); + } + + /// + /// Calls to Nimbus for a video only ad and loads the ad if an ad is returned from the auction. + /// It does not the ad. To load the ad you must pass reference of the returned add to the ShowLoadedAd method + /// + /// + /// This is a Nimbus specific field, it must not be empty and it represents a generic placement name + /// can be used by the Nimbus dashboard to breakout data + /// + /// + /// Represents your asking price for video ads during the auction + /// + public NimbusAdUnit LoadRewardedVideoAd(string position, float videoBidFloor) { + var adUnit = new NimbusAdUnit(AdUnityType.Rewarded, position, 0, videoBidFloor, in NimbusEvents); + return _nimbusPlatformAPI.RequestAd(Debug.unityLogger, ref adUnit); + } + + /// + /// Takes the returned ad unit from LoadBannerAd, LoadFullScreenAd, or LoadRewardedVideoAd and Displayed the won ad + /// + /// + /// A references to a ad unit returned from LoadBannerAd, LoadFullScreenAd, or LoadRewardedVideoAd + /// + public NimbusAdUnit ShowLoadedAd(ref NimbusAdUnit adUnit) { + if (adUnit == null || adUnit.DidHaveAnError()) { + Debug.unityLogger.LogError(adUnit?.InstanceID.ToString(),"there was no ad to render, either there was no fill or there was an error retrieving and ad"); + return adUnit; + } + + if (adUnit.WasAdRendered()) { + Debug.unityLogger.LogError(adUnit.InstanceID.ToString(),"the was already rendered, you cannot render the same ad twice"); + return adUnit; + } + return _nimbusPlatformAPI.ShowAd(Debug.unityLogger, ref adUnit); + } } } \ No newline at end of file diff --git a/sample-app/Assets/Example/Scenes/NimbusDemoScene.unity b/sample-app/Assets/Example/Scenes/NimbusDemoScene.unity index ab1a92e6..5fe931eb 100644 --- a/sample-app/Assets/Example/Scenes/NimbusDemoScene.unity +++ b/sample-app/Assets/Example/Scenes/NimbusDemoScene.unity @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5735c653c2ee828dbb584074407c4173091f7e9f03821f88cd1f357804d8bd7a -size 61896 +oid sha256:925b33f7b813ec0213c81c6d8e574a55a9a7c653bfc68485fc3c010c49d1242d +size 83395 diff --git a/sample-app/Assets/Example/Scripts/GUIButtonTest.cs b/sample-app/Assets/Example/Scripts/GUIButtonTest.cs index 6b50d657..1b103d74 100644 --- a/sample-app/Assets/Example/Scripts/GUIButtonTest.cs +++ b/sample-app/Assets/Example/Scripts/GUIButtonTest.cs @@ -1,3 +1,5 @@ +using System; +using System.Collections.Generic; using Nimbus.Runtime.Scripts; using Nimbus.Runtime.Scripts.Internal; using TMPro; @@ -8,25 +10,19 @@ namespace Example.Scripts { /// This demonstrates how to call for various different ad types within the demo app context /// public class GUIButtonTest : MonoBehaviour, IAdEventsExtended { - public TextMeshProUGUI bannerButtonText; - private NimbusAdUnit _bannerAdUnit; + [SerializeField] private TextMeshProUGUI _loadedBannerButtonText; + [SerializeField] private List _interactableButtons; - private string _currentBannerButtonText; + private NimbusAdUnit _loadAndShowBannerAdUnit; private bool _shouldDestroyBanner; - + private void Awake() { Screen.orientation = ScreenOrientation.Portrait; } - - private void Start() { - if (bannerButtonText == null) - foreach (var txt in FindObjectsOfType()) { - if (txt.name != "BannerTextTMP") continue; - bannerButtonText = txt; - break; - } - - _currentBannerButtonText = bannerButtonText.text; + + public void OnAdLoaded(NimbusAdUnit nimbusAdUnit) { + Debug.unityLogger.Log( + $"Ad unit of {nimbusAdUnit.InstanceID} type {nimbusAdUnit.AdType} for auction id {nimbusAdUnit.ResponseMetaData.AuctionID} was loaded"); } public void OnAdWasRendered(NimbusAdUnit nimbusAdUnit) { @@ -59,26 +55,86 @@ public void OnAdError(NimbusAdUnit nimbusAdUnit) { $"Ad unit of {nimbusAdUnit.InstanceID} type {nimbusAdUnit.AdType} could not be rendered {nimbusAdUnit.ErrorMessage()}"); } - public void LoadBanner() { + public void LoadAndShowBanner() { if (!_shouldDestroyBanner) { _shouldDestroyBanner = true; - bannerButtonText.text = "Destroy Banner"; - _bannerAdUnit = NimbusManager.Instance.LoadAndShowBannerAd("unity_demo_banner_position", 0.0f); + _loadedBannerButtonText.text = "Destroy Banner"; + _loadAndShowBannerAdUnit = NimbusManager.Instance.LoadAndShowBannerAd("unity_demo_banner_position", 0.0f); return; } - _bannerAdUnit?.Destroy(); - _bannerAdUnit = null; + _loadAndShowBannerAdUnit?.Destroy(); + _loadAndShowBannerAdUnit = null; _shouldDestroyBanner = false; - bannerButtonText.text = _currentBannerButtonText; + _loadedBannerButtonText.text = "Load And Show Banner"; } - public void LoadInterstitial() { + public void LoadAndShowInterstitial() { _ = NimbusManager.Instance.LoadAndShowFullScreenAd("unity_demo_interstitial_position", 0.0f, 0.0f); } - public void LoadRewardedVideoAd() { + public void LoadAndShowRewardedVideoAd() { _ = NimbusManager.Instance.LoadAndShowRewardedVideoAd("unity_demo_video_position", 0.0f); } + + public void LoadAdController(int index) { + // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault + switch (_interactableButtons[index].state) { + case AdState.NotLoaded: + LoadAd(index); + break; + case AdState.Loaded: + _interactableButtons[index].CurrentAd = NimbusManager.Instance.ShowLoadedAd(ref _interactableButtons[index].CurrentAd); + break; + case AdState.Displayed: + _interactableButtons[index].DestroyAd(); + break; + } + _interactableButtons[index].NextState(); + } + + private void LoadAd(int index) { + var adType = _interactableButtons[index].adUnityType; + _interactableButtons[index].CurrentAd = adType switch { + AdUnityType.Banner => NimbusManager.Instance.LoadBannerAd("unity_demo_banner_position", 0.0f), + AdUnityType.Interstitial => NimbusManager.Instance.LoadFullScreenAd( + "unity_demo_interstitial_position", 0.0f, 0.0f), + AdUnityType.Rewarded => NimbusManager.Instance.LoadRewardedVideoAd("unity_demo_video_position", + 0.0f), + _ => _interactableButtons[index].CurrentAd + }; + } + } + + + [Serializable] + public class AdController { + [HideInInspector] public AdState state; + public TextMeshProUGUI button; + public AdUnityType adUnityType; + public NimbusAdUnit CurrentAd; + + public void NextState() { + state = (AdState) (((int) state + 1) % 3); + button.text = state switch { + AdState.NotLoaded => $"Load {adUnityType}", + AdState.Loaded => $"{adUnityType} Loaded, Display?", + AdState.Displayed => $"{adUnityType} Displayed, Destroy?", + _ => button.text + }; + } + + public void DestroyAd() { + if (state != AdState.Displayed) return; + CurrentAd?.Destroy(); + CurrentAd = null; + } + } + + [Serializable] + public enum AdState: uint { + NotLoaded, + Loaded, + Displayed, } } \ No newline at end of file diff --git a/sample-app/Assets/Example/Scripts/RewardedVideoExample.cs b/sample-app/Assets/Example/Scripts/RewardedVideoExample.cs index 258f4035..c33bdf5e 100644 --- a/sample-app/Assets/Example/Scripts/RewardedVideoExample.cs +++ b/sample-app/Assets/Example/Scripts/RewardedVideoExample.cs @@ -29,6 +29,17 @@ private void OnTriggerEnter2D(Collider2D other) { _ad = NimbusManager.Instance.LoadAndShowRewardedVideoAd("unity_demo_rewarded_video_position", 0.0f); _alreadyTriggered = true; } + + public void OnAdLoaded(NimbusAdUnit nimbusAdUnit) { + if (_ad?.InstanceID != nimbusAdUnit.InstanceID) return; + Debug.unityLogger.Log( + $"RewardedVideoExample Ad was returned and loaded into memory ad instance {nimbusAdUnit.InstanceID}, " + + $"bid value: {nimbusAdUnit.ResponseMetaData.BidRaw}, " + + $"bid value in cents: {nimbusAdUnit.ResponseMetaData.BidInCents}, " + + $"network: {nimbusAdUnit.ResponseMetaData.Network}, " + + $"placement_id: {nimbusAdUnit.ResponseMetaData.PlacementID}, " + + $"auction_id: {nimbusAdUnit.ResponseMetaData.AuctionID}"); + } public void OnAdWasRendered(NimbusAdUnit nimbusAdUnit) { if (_ad?.InstanceID != nimbusAdUnit.InstanceID) return; diff --git a/sample-app/ProjectSettings/EditorBuildSettings.asset b/sample-app/ProjectSettings/EditorBuildSettings.asset index 1fe9484d..f40ae6d8 100644 --- a/sample-app/ProjectSettings/EditorBuildSettings.asset +++ b/sample-app/ProjectSettings/EditorBuildSettings.asset @@ -5,10 +5,10 @@ EditorBuildSettings: m_ObjectHideFlags: 0 serializedVersion: 2 m_Scenes: - - enabled: 0 + - enabled: 1 path: Assets/Example/Scenes/NimbusDemoScene.unity guid: 2cda990e2423bbf4892e6590ba056729 - - enabled: 1 + - enabled: 0 path: Assets/Example/Scenes/NimbusAdShowCase.unity guid: d100633d77aad4c98bf54382c0cb6493 m_configObjects: {}