From f8160853fe2902a3f570b1995449b4cdffc242df Mon Sep 17 00:00:00 2001 From: Ahmed TARAFI Date: Thu, 23 Feb 2023 17:34:38 +0100 Subject: [PATCH] release:v3.0.0 --- Assets/CrossDK/CrossDKConverter.cs | 19 ++ Assets/CrossDK/CrossDKSingleton.cs | 23 +++ Assets/Plugins/Android/CrossDKBridge.kt | 226 +++++++++++++++--------- README.md | 43 +++-- 4 files changed, 215 insertions(+), 96 deletions(-) diff --git a/Assets/CrossDK/CrossDKConverter.cs b/Assets/CrossDK/CrossDKConverter.cs index 398fb06..870c08d 100644 --- a/Assets/CrossDK/CrossDKConverter.cs +++ b/Assets/CrossDK/CrossDKConverter.cs @@ -24,6 +24,7 @@ public class CrossDKConverter private static AndroidJavaObject crossDKWrapper; private const string CONFIG = "config"; private const string DISMISS = "dismissOverlay"; + private const string LOAD = "loadOverlay"; private const string DISPLAY = "displayOverlay"; #endif @@ -73,6 +74,24 @@ public static void DismissOverlay() #endif } + public static void LoadOverlayWithFormat(OverlayFormat format, OverlayPosition position, bool withCloseButton, bool isRewarded) + { +#if UNITY_EDITOR + Debug.Log("DisplayOverlayWithFormat called in editor"); +#elif UNITY_IOS + displayOverlayWithFormat((int)format, (int)position, withCloseButton, isRewarded); +#endif +#if UNITY_ANDROID + object[] parameters = new object[4]; + parameters[0] = (int)format; + parameters[1] = (int)position; + parameters[2] = withCloseButton; + parameters[3] = isRewarded; + + crossDKWrapper.Call(LOAD, parameters); +#endif + } + public static void DisplayOverlayWithFormat(OverlayFormat format, OverlayPosition position, bool withCloseButton, bool isRewarded) { #if UNITY_EDITOR diff --git a/Assets/CrossDK/CrossDKSingleton.cs b/Assets/CrossDK/CrossDKSingleton.cs index 474401c..a223916 100644 --- a/Assets/CrossDK/CrossDKSingleton.cs +++ b/Assets/CrossDK/CrossDKSingleton.cs @@ -9,6 +9,9 @@ public class CrossDKSingleton : MonoBehaviour private static CrossDKSingleton _instance; public delegate void CrossDKDelegate(string message); + public static CrossDKDelegate overlayWillStartPreloadDelegate; + public static CrossDKDelegate overlayDidFinishPreloadDelegate; + public static CrossDKDelegate overlayPreloadExpiredDelegate; public static CrossDKDelegate overlayWillStartPresentationDelegate; public static CrossDKDelegate overlayDidFinishPresentationDelegate; public static CrossDKDelegate overlayWillStartDismissalDelegate; @@ -60,6 +63,11 @@ public static void DismissOverlay() CrossDKConverter.DismissOverlay(); } + public static void LoadOverlay(OverlayFormat format = OverlayFormat.Interstitial, OverlayPosition position = OverlayPosition.Bottom, bool withCloseButton = true, bool isRewarded = true) + { + CrossDKConverter.LoadOverlayWithFormat(format, position, withCloseButton, isRewarded); + } + public static void DisplayOverlay(OverlayFormat format = OverlayFormat.Interstitial, OverlayPosition position = OverlayPosition.Bottom, bool withCloseButton = true, bool isRewarded = true) { CrossDKConverter.DisplayOverlayWithFormat(format, position, withCloseButton, isRewarded); @@ -74,6 +82,21 @@ public static void DisplayOverlay(OverlayFormat format = OverlayFormat.Interstit // Logger.Log(message); //} + internal void OverlayWillStartPreload(string message) + { + overlayWillStartPreloadDelegate?.Invoke(message); + } + + internal void OverlayDidFinishPreload(string message) + { + overlayDidFinishPreloadDelegate?.Invoke(message); + } + + internal void OverlayPreloadExpired(string message) + { + overlayPreloadExpiredDelegate?.Invoke(message); + } + internal void OverlayWillStartPresentation(string message) { overlayWillStartPresentationDelegate?.Invoke(message); diff --git a/Assets/Plugins/Android/CrossDKBridge.kt b/Assets/Plugins/Android/CrossDKBridge.kt index dc0339c..3d8ddef 100644 --- a/Assets/Plugins/Android/CrossDKBridge.kt +++ b/Assets/Plugins/Android/CrossDKBridge.kt @@ -24,15 +24,15 @@ import com.unity3d.player.UnityPlayer public class CrossDKBridge { private var mUnityPlayerActivity: Activity = UnityPlayer.currentActivity private var mOpenedOverlayFormat: OverlayFormat = OverlayFormat.NONE - private lateinit var mCrossDKView: CrossDKView - private lateinit var mCrossDKMidSizeView: CrossDKMidSizeView - private lateinit var mCrossDKInterstitialView: CrossDKInterstitialView + private var mCrossDKView: CrossDKView? = null + private var mCrossDKMidSizeView: CrossDKMidSizeView? = null + private var mCrossDKInterstitialView: CrossDKInterstitialView? = null /////////////////////////////////////////////////////////////////////////// // CONFIG /////////////////////////////////////////////////////////////////////////// - public fun config(appId: String, apiKey: String, userId: String?, deviceId:String?) { + public fun config(appId: String, apiKey: String, userId: String?, deviceId: String?) { CrossDKConfig.Builder() .apiKey(apiKey) .appId(appId) @@ -50,83 +50,52 @@ public class CrossDKBridge { // DISPLAY /////////////////////////////////////////////////////////////////////////// - public fun displayOverlay( + public fun loadOverlay( format: Int, position: Int, isCloseButtonVisible: Boolean, isRewarded: Boolean ) { - val crossDKContentCallback = object : CrossDKContentCallback { - override fun onConfigurationError() { - unitySendOverlayError("Overlay error: configuration error") - } - - override fun onNoRecommendation() { - unitySendOverlayError("Overlay error: unavailable recommendation") - } - - override fun onShowContentError() { - unitySendOverlayError("Overlay error: show content error") - } - - override fun onRecommendationDisplayed() { - overlayDidFinishPresentation() - } - - override fun onRecommendationClicked() { - overlayShowsRecommendedAppInAppStore() - } - - override fun onRecommendationClosed() { - overlayDidFinishDismissal() - destroyViews() - } - - override fun onUnsupportedApiVersion() { - unitySendOverlayError("Overlay error: unsupported Api version") + mUnityPlayerActivity.runOnUiThread { + overlayWillStartPreload() + mOpenedOverlayFormat = OverlayFormat.fromInt(format) + createFormat(position, isCloseButtonVisible, isRewarded) + when (mOpenedOverlayFormat) { + OverlayFormat.BANNER -> { + mCrossDKView?.load(getCrossDKLoadCallback()) + } + OverlayFormat.MID_SIZE -> { + mCrossDKMidSizeView?.load(getCrossDKLoadCallback()) + } + OverlayFormat.INTERSTITIAL -> { + mCrossDKInterstitialView?.load(getCrossDKLoadCallback()) + } + else -> { + unitySendOverlayError("Overlay error: unsupported format requested") + } } } + } + + public fun displayOverlay( + format: Int, + position: Int, + isCloseButtonVisible: Boolean, + isRewarded: Boolean + ) { + overlayWillStartPresentation() mUnityPlayerActivity.runOnUiThread { - if (mOpenedOverlayFormat != OverlayFormat.NONE) dismissOverlay() - val overlayFormat = OverlayFormat.fromInt(format) - mOpenedOverlayFormat = overlayFormat - overlayWillStartPresentation() - when (overlayFormat) { + mOpenedOverlayFormat = OverlayFormat.fromInt(format) + createFormat(position, isCloseButtonVisible, isRewarded) + when (mOpenedOverlayFormat) { OverlayFormat.BANNER -> { - mCrossDKView = CrossDKView(mUnityPlayerActivity) - mCrossDKView.setCrossDKContentCallback(crossDKContentCallback) - mCrossDKView.setCloseButtonVisibility(if (isCloseButtonVisible) View.VISIBLE else View.INVISIBLE) - mUnityPlayerActivity.addContentView(mCrossDKView, getLayoutParams()) - mCrossDKView.setPosition(if (position == 0) CrossDKPosition.BOTTOM else CrossDKPosition.BOTTOM_RAISED) + mCrossDKView?.show() } OverlayFormat.MID_SIZE -> { - mCrossDKMidSizeView = CrossDKMidSizeView(mUnityPlayerActivity) - mCrossDKMidSizeView.setCrossDKContentCallback(crossDKContentCallback) - mCrossDKMidSizeView.setCloseButtonVisibility(if (isCloseButtonVisible) View.VISIBLE else View.INVISIBLE) - mUnityPlayerActivity.addContentView(mCrossDKMidSizeView, getLayoutParams()) - mCrossDKMidSizeView.setPosition(if (position == 0) CrossDKPosition.BOTTOM else CrossDKPosition.BOTTOM_RAISED) + mCrossDKMidSizeView?.show() } OverlayFormat.INTERSTITIAL -> { - mCrossDKInterstitialView = CrossDKInterstitialView(mUnityPlayerActivity) - mCrossDKInterstitialView.setCrossDKContentCallback(crossDKContentCallback) - mCrossDKInterstitialView.setCloseButtonVisibility(if (isCloseButtonVisible) View.VISIBLE else View.INVISIBLE) - mCrossDKInterstitialView.setRewarded(isRewarded, - object : CrossDKRewardedCallback { - override fun onUserRewarded() { - overlayDidFinishPlayingVideo() - overlayDidRewardUserWithReward() - } - }) - mUnityPlayerActivity.addContentView(mCrossDKInterstitialView, getLayoutParams()) - mCrossDKInterstitialView.load(object : CrossDKLoadCallback { - override fun onRecommendationLoaded() { - mCrossDKInterstitialView.show() - } - - override fun onRecommendationLoadFailure() { - overlayDidFailToLoadWithError(java.lang.Exception("Recommendation load failure")) - } - }) + mCrossDKInterstitialView?.show() } else -> { unitySendOverlayError("Overlay error: unsupported format requested") @@ -140,18 +109,18 @@ public class CrossDKBridge { mUnityPlayerActivity.runOnUiThread { when (mOpenedOverlayFormat) { OverlayFormat.BANNER -> { - mCrossDKView.dismissView(true) + mCrossDKView?.dismissView(true) destroyViews() } OverlayFormat.MID_SIZE -> { - mCrossDKMidSizeView.dismissView(true) + mCrossDKMidSizeView?.dismissView(true) destroyViews() } OverlayFormat.INTERSTITIAL -> { - mCrossDKInterstitialView.dismissView(true) + mCrossDKInterstitialView?.dismissView(true) destroyViews() } - else -> {} + OverlayFormat.NONE -> overlayDidFinishDismissal() } } } @@ -160,27 +129,30 @@ public class CrossDKBridge { mUnityPlayerActivity.runOnUiThread { when (mOpenedOverlayFormat) { OverlayFormat.BANNER -> { - mCrossDKView.destroy() - val parentView = mCrossDKView.parent + mCrossDKView?.destroy() + val parentView = mCrossDKView?.parent if (parentView is ViewGroup) { parentView.removeView(mCrossDKView) } + mCrossDKView = null } OverlayFormat.MID_SIZE -> { - mCrossDKMidSizeView.destroy() - val parentView = mCrossDKMidSizeView.parent + mCrossDKMidSizeView?.destroy() + val parentView = mCrossDKMidSizeView?.parent if (parentView is ViewGroup) { parentView.removeView(mCrossDKMidSizeView) } + mCrossDKMidSizeView = null } OverlayFormat.INTERSTITIAL -> { - mCrossDKInterstitialView.destroy() - val parentView = mCrossDKInterstitialView.parent + mCrossDKInterstitialView?.destroy() + val parentView = mCrossDKInterstitialView?.parent if (parentView is ViewGroup) { parentView.removeView(mCrossDKInterstitialView) } + mCrossDKInterstitialView = null } - else -> {} + else -> mOpenedOverlayFormat = OverlayFormat.NONE } mOpenedOverlayFormat = OverlayFormat.NONE } @@ -190,6 +162,18 @@ public class CrossDKBridge { // LISTENERS /////////////////////////////////////////////////////////////////////////// + private fun overlayWillStartPreload() { + unitySendMessage("OverlayWillStartPreload", "Overlay will start preload"); + } + + private fun overlayDidFinishedPreload() { + unitySendMessage("OverlayDidFinishPreload", "Overlay did finish preload"); + } + + private fun overlayPreloadExpired() { + unitySendMessage("OverlayPreloadExpired", "Overlay preload expired"); + } + private fun overlayWillStartPresentation() { unitySendMessage("OverlayWillStartPresentation", "Overlay will start presentation"); } @@ -240,6 +224,42 @@ public class CrossDKBridge { // HELPERS /////////////////////////////////////////////////////////////////////////// + private fun createFormat( + position: Int, + isCloseButtonVisible: Boolean, + isRewarded: Boolean + ) { + when { + mOpenedOverlayFormat == OverlayFormat.BANNER && mCrossDKView == null -> { + mCrossDKView = CrossDKView(mUnityPlayerActivity) + mCrossDKView?.setCrossDKContentCallback(getCrossDKContentCallback()) + mCrossDKView?.setCloseButtonVisibility(if (isCloseButtonVisible) View.VISIBLE else View.INVISIBLE) + mUnityPlayerActivity.addContentView(mCrossDKView, getLayoutParams()) + mCrossDKView?.setPosition(if (position == 0) CrossDKPosition.BOTTOM else CrossDKPosition.BOTTOM_RAISED) + } + mOpenedOverlayFormat == OverlayFormat.MID_SIZE && mCrossDKMidSizeView == null -> { + mCrossDKMidSizeView = CrossDKMidSizeView(mUnityPlayerActivity) + mCrossDKMidSizeView?.setCrossDKContentCallback(getCrossDKContentCallback()) + mCrossDKMidSizeView?.setCloseButtonVisibility(if (isCloseButtonVisible) View.VISIBLE else View.INVISIBLE) + mUnityPlayerActivity.addContentView(mCrossDKMidSizeView, getLayoutParams()) + mCrossDKMidSizeView?.setPosition(if (position == 0) CrossDKPosition.BOTTOM else CrossDKPosition.BOTTOM_RAISED) + } + mOpenedOverlayFormat == OverlayFormat.INTERSTITIAL && mCrossDKInterstitialView == null -> { + mCrossDKInterstitialView = CrossDKInterstitialView(mUnityPlayerActivity) + mCrossDKInterstitialView?.setCrossDKContentCallback(getCrossDKContentCallback()) + mCrossDKInterstitialView?.setCloseButtonVisibility(if (isCloseButtonVisible) View.VISIBLE else View.INVISIBLE) + mCrossDKInterstitialView?.setRewarded(isRewarded, + object : CrossDKRewardedCallback { + override fun onUserRewarded() { + overlayDidFinishPlayingVideo() + overlayDidRewardUserWithReward() + } + }) + mUnityPlayerActivity.addContentView(mCrossDKInterstitialView, getLayoutParams()) + } + } + } + private fun getLayoutParams(): FrameLayout.LayoutParams { val adParams = FrameLayout.LayoutParams( FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT @@ -266,4 +286,52 @@ public class CrossDKBridge { fun fromInt(value: Int) = values().first { it.value == value } } } + + private fun getCrossDKLoadCallback() = object : CrossDKLoadCallback { + override fun onRecommendationLoaded() { + overlayDidFinishedPreload() + } + + override fun onRecommendationLoadFailure() { + overlayDidFailToLoadWithError(java.lang.Exception("Recommendation load failure")) + destroyViews() + } + + override fun onRecommendationExpired() { + overlayPreloadExpired() + destroyViews() + } + } + + private fun getCrossDKContentCallback() = object : CrossDKContentCallback { + override fun onConfigurationError() { + unitySendOverlayError("Overlay error: configuration error") + } + + override fun onNoRecommendation() { + unitySendOverlayError("Overlay error: unavailable recommendation") + destroyViews() + } + + override fun onShowContentError() { + unitySendOverlayError("Overlay error: show content error") + } + + override fun onRecommendationDisplayed() { + overlayDidFinishPresentation() + } + + override fun onRecommendationClicked() { + overlayShowsRecommendedAppInAppStore() + } + + override fun onRecommendationClosed() { + overlayDidFinishDismissal() + destroyViews() + } + + override fun onUnsupportedApiVersion() { + unitySendOverlayError("Overlay error: unsupported Api version") + } + } } \ No newline at end of file diff --git a/README.md b/README.md index 2415978..95c4cc9 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ The Android version is using **Kotlin 1.6.20**, **Gradle 6.1.1** and **Gradle pl ## Installation -### iOS using CocoaPods +### iOS using CocoaPods To install CocoaPods on MacOS, add the following entry: @@ -28,9 +28,9 @@ To install CocoaPods on MacOS, add the following entry: $ sudo gem install cocoapods ``` -The CrossDK pod is automatically installed in the Xcode project when building with Unity, thanks to [External Dependency Manager for Unity](https://github.com/googlesamples/unity-jar-resolver). +The CrossDK pod is automatically installed in the Xcode project when building with Unity, thanks to [External Dependency Manager for Unity](https://github.com/googlesamples/unity-jar-resolver). -If you already use CocoaPods in your Unity project, you should consider adding your pods with [EDM4U](https://github.com/googlesamples/unity-jar-resolver) as well. +If you already use CocoaPods in your Unity project, you should consider adding your pods with [EDM4U](https://github.com/googlesamples/unity-jar-resolver) as well. ### Android using Github Packages @@ -41,7 +41,7 @@ If you already use CocoaPods in your Unity project, you should consider adding y ## Configuration -To use CrossDK in your Unity project, you must download the `CrossDK.unitypackage` on the [releases page](https://github.com/Adikteev/crossdk-unity/releases), then import it into your project. Once it's finished, you can drag the **CrossDK prefab** (located in `Assets\CrossDK\CrossDK`) into your scene or setup the config in script. +To use CrossDK in your Unity project, you must download the `CrossDK.unitypackage` on the [releases page](https://github.com/Adikteev/crossdk-unity-ios/releases), then import it into your project. Once it's finished, drag the **CrossDK prefab** (located in `Assets\CrossDK\CrossDK`) into your scene. ### Android specific configuration @@ -81,7 +81,7 @@ Note: The CrossDK prefab is not destroyed during scenes changes, so you only nee ## Usage -Here are the configurations for each overlay format : +Here are the configurations for each overlay format : - `OverlayFormat.Banner`: settle its position between `OverlayPosition.Bottom` or `OverlayPosition.BottomRaised`, with or without a close button (the close button is Android only). - `OverlayFormat.MidSize`: settle its position between `OverlayPosition.Bottom` or `OverlayPosition.BottomRaised`, with or without a close button. - `OverlayFormat.Interstitial`: settle it with or without a close button, with or without a rewarded. @@ -107,10 +107,10 @@ public class CrossDKSample : MonoBehaviour } ``` -For IOS only a `SetDeviceId()` method is available in order to use custom device id. You can see the recommendations using another device id than yours. Set it before `DisplayOverlayWithFormat()` function call: +A `DismissOverlay()` method is available in order to prevent screen changes: ```csharp -CrossDKSingleton.SetDeviceId(string deviceId) +CrossDKSingleton.DismissOverlay() ``` ```csharp @@ -119,20 +119,16 @@ using CrossDK; public class CrossDKSample : MonoBehaviour { - public void DisplayMidSizeOverlayExample() + public void DismissExample() { - CrossDKSingleton.SetDeviceId("My custom device ID"); - CrossDKSingleton.DisplayOverlay(...); + CrossDKSingleton.DismissOverlay(); } } ``` -For Android you can directly config the sdk with the desired device ID. -A `DismissOverlay()` method is available in order to prevent screen changes: +## Preload -```csharp -CrossDKSingleton.DismissOverlay() -``` +You can preload overlays before displaying it on screen, this feature is particularly useful on mid size and interstitial format which contain video asset that may take time to load: ```csharp using UnityEngine; @@ -140,12 +136,25 @@ using CrossDK; public class CrossDKSample : MonoBehaviour { - public void DismissExample() + public void DisplayMidSizeOverlayExample() { - CrossDKSingleton.DismissOverlay(); + CrossDKSingleton.LoadOverlay( + OverlayFormat.MidSize, + OverlayPosition.Bottom, + true, + false); } } ``` +Make sure to leave enough time for the assets to load fully +A preloaded overlay becomes expired after '30 minutes' of its load, when this delay expires you can no longer show it on screen. +To monitor the preload action you can use these delagates: + +- `CrossDKSingleton.overlayWillStartPreloadDelegate`: called when the preload starts +- `CrossDKSingleton.overlayDidFinishPreloadDelegate`: called when the preload finishes +- `CrossDKSingleton.overlayPreloadExpiredDelegate`: called when the preload expires + +Please not that when displaying a not fully loaded mid size or interstitial you will only see a banner because of the video not being fully prepared. ## Overlay Delegate