# Improve Digital InApp > Mobile Monetization Made Easy This file contains all documentation content in a single document following the llmstxt.org standard. ## Documentation Welcome to the **Improve Digital InApp** developer documentation. The BlueStack SDK lets you monetize your mobile app with industry-standard ad formats — banner, interstitial, rewarded, native, and app-open — with built-in GDPR/TCF compliance. ## Native app integrations - [Android](/android) — native Android SDK (Kotlin & Java) - [iOS](/ios) — native iOS SDK (Swift & Objective-C) - [Location SDK](/location) — privacy-first location signals ## Framework app integrations - [Unity](/unity) - [React Native](/react-native) - [Flutter](/flutter) - [MAUI](/maui) ## Platform - [Reporting](/reporting) — revenue, fill rate, and performance reporting Need an account? Head to the [Console](https://console.bluestack.app). --- ## App Open Ads ## Overview App open ads are full-screen ads designed to appear during app launch moments. They are similar to interstitial ads but are specifically tailored for two key scenarios: **Cold start** — When the user opens the app fresh (not previously in memory). See [Handling Cold Starts with Loading Screens](#handling-cold-starts-with-loading-screens) for implementation details. **Soft launch** — When the user returns to the app from the background. The app is still in memory but was suspended. In both cases, the ad is displayed before the user reaches the main content. Users can dismiss the ad at any time. For a working implementation of this ad format, see the [azerion-inapp-demo-android](https://github.com/azerion/azerion-inapp-demo-android) demo app. ## Implementation Steps At a high level, integrating app open ads involves the following: 1. Build a manager class that preloads an ad so it's ready when needed. 2. Display the ad when the app comes to the foreground. 3. React to ad lifecycle and presentation callbacks. ## Create an App Open Ad ### Implement a Manager Class App open ads should appear immediately when the user opens or returns to your app, so it's important to have the ad loaded and ready before it's needed. The best approach is to create a manager class that takes care of loading ads ahead of time, checking whether a loaded ad is still valid, and displaying it at the right moment. Create a class called `AppOpenAdManager`: ```java showLineNumbers import android.app.Activity; import com.azerion.bluestack.appopen.AppOpenAd; import com.azerion.bluestack.appopen.AppOpenAdListener; public class AppOpenAdManager implements AppOpenAdListener { private static final String TAG = "AppOpenAdManager"; private ActivityContextProvider activityContextProvider; private AppOpenAd appOpenAd; private OnShowAdCompleteListener onShowAdCompleteListener; public boolean isShowingAd = false; public AppOpenAdManager(ActivityContextProvider activityContextProvider) { this.activityContextProvider = activityContextProvider; } public interface OnShowAdCompleteListener { void onShowAdComplete(); } public interface ActivityContextProvider { Activity getActivity(); } private void initializeAppOpenAd() { if (appOpenAd == null) { appOpenAd = new AppOpenAd("APP_OPEN_PLACEMENT_ID"); appOpenAd.setAppOpenAdListener(this); } } } ``` ```kotlin showLineNumbers import android.app.Activity import com.azerion.bluestack.appopen.AppOpenAd import com.azerion.bluestack.appopen.AppOpenAdListener class AppOpenAdManager(private val activityContextProvider: ActivityContextProvider) : AppOpenAdListener { companion object { private const val TAG = "AppOpenAdManager" } private var appOpenAd: AppOpenAd? = null private var onShowAdCompleteListener: OnShowAdCompleteListener? = null var isShowingAd = false interface OnShowAdCompleteListener { fun onShowAdComplete() } interface ActivityContextProvider { fun getActivity(): Activity? } private fun initializeAppOpenAd() { if (appOpenAd == null) { appOpenAd = AppOpenAd("APP_OPEN_PLACEMENT_ID") appOpenAd?.setAppOpenAdListener(this) } } } ``` ### Standalone Creation To create an app open ad directly, instantiate an `AppOpenAd` with a placement ID. ```java showLineNumbers import com.azerion.bluestack.appopen.AppOpenAd; public class MainActivity extends AppCompatActivity { private AppOpenAd appOpenAd; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); appOpenAd = new AppOpenAd("APP_OPEN_PLACEMENT_ID"); } } ``` ```kotlin showLineNumbers import com.azerion.bluestack.appopen.AppOpenAd class MainActivity : AppCompatActivity() { private lateinit var appOpenAd: AppOpenAd override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) appOpenAd = AppOpenAd("APP_OPEN_PLACEMENT_ID") } } ``` ## Load an App Open Ad ### With AppOpenAdManager The recommended way to load an app open ad is through the `AppOpenAdManager` class. Add the `loadAd()` method to the manager class: ```java showLineNumbers public void loadAd(Activity activity) { initializeAppOpenAd(); appOpenAd.load(activity); } ``` ```kotlin showLineNumbers fun loadAd(activity: Activity) { initializeAppOpenAd() appOpenAd?.load(activity) } ``` ### Standalone Loading App open ads can be loaded by calling the `load(activity)` method: ```java showLineNumbers appOpenAd.load(activity); ``` ```kotlin showLineNumbers appOpenAd.load(activity) ``` :::info Ad load call will preload an ad before you show it so that ads can be shown with zero latency when needed. This preloaded ad will expire after a certain period. If you try to show an expired ad, you will get `AdError.AD_EXPIRED` error in the `onAdFailedToDisplay` callback. Once the ad expires, you can call load again using the existing instance to preload a new ad. ::: ## Show an Ad ### With AppOpenAdManager Before showing the ad, the manager checks whether an ad is already on screen. Add these methods to the `AppOpenAdManager` class: ```java showLineNumbers public void showAdIfAvailable(Activity activity) { showAdIfAvailable(activity, new OnShowAdCompleteListener() { @Override public void onShowAdComplete() { // Empty because the user will go back to the activity that shows the ad. } }); } public void showAdIfAvailable(Activity activity, OnShowAdCompleteListener onShowAdCompleteListener) { initializeAppOpenAd(); if (isShowingAd) { Log.d(TAG, "The app open ad is already showing."); return; } if (appOpenAd == null || !appOpenAd.isReady()) { onShowAdCompleteListener.onShowAdComplete(); loadAd(activity); return; } this.onShowAdCompleteListener = onShowAdCompleteListener; isShowingAd = true; appOpenAd.show(activity); } ``` ```kotlin showLineNumbers fun showAdIfAvailable(activity: Activity) { showAdIfAvailable(activity, object : OnShowAdCompleteListener { override fun onShowAdComplete() { // Empty because the user will go back to the activity that shows the ad. } }) } fun showAdIfAvailable(activity: Activity, onShowAdCompleteListener: OnShowAdCompleteListener) { initializeAppOpenAd() if (isShowingAd) { Log.d(TAG, "The app open ad is already showing.") return } if (appOpenAd?.isReady() == false) { onShowAdCompleteListener.onShowAdComplete() loadAd(activity) return } this.onShowAdCompleteListener = onShowAdCompleteListener isShowingAd = true appOpenAd?.show(activity) } ``` ### Standalone Showing To show an app open ad directly, check `isReady()` on the `AppOpenAd` instance and call `show(activity)`. ```java showLineNumbers if (appOpenAd != null && appOpenAd.isReady()) { appOpenAd.show(activity); } ``` ```kotlin showLineNumbers if (appOpenAd?.isReady() == true) { appOpenAd?.show(activity) } ``` ## Show the Ad During App Foregrounding To display the ad whenever the user returns to your app, implement a lifecycle observer in your Application class. Create a custom Application class that integrates with Android's lifecycle callbacks: ```java showLineNumbers import android.app.Activity; import android.app.Application; import android.os.Bundle; import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.ProcessLifecycleOwner; import com.azerion.bluestack.MobileAds; public class MyApplication extends Application implements Application.ActivityLifecycleCallbacks, DefaultLifecycleObserver, AppOpenAdManager.ActivityContextProvider { private AppOpenAdManager appOpenAdManager; private Activity currentActivity; @Override public void onCreate() { super.onCreate(); appOpenAdManager = new AppOpenAdManager(this); registerActivityLifecycleCallbacks(this); ProcessLifecycleOwner.get().getLifecycle().addObserver(this); } @Override public void onActivityStarted(Activity activity) { if (!appOpenAdManager.isShowingAd) { currentActivity = activity; } } @Override public void onStart(LifecycleOwner owner) { if (MobileAds.isInitialized() && currentActivity != null) { // Don't show app open ad on launcher/splash activities // as they manage their own app open ad flow boolean shouldShowAd = !(currentActivity instanceof CustomLauncherActivity) && !(currentActivity instanceof SplashScreenActivity); if (shouldShowAd) { appOpenAdManager.showAdIfAvailable(currentActivity); } } } public void loadAd(Activity activity) { appOpenAdManager.loadAd(activity); } public void showAdIfAvailable(Activity activity, AppOpenAdManager.OnShowAdCompleteListener listener) { appOpenAdManager.showAdIfAvailable(activity, listener); } @Override public Activity getActivity() { return currentActivity; } // Other ActivityLifecycleCallbacks methods... @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {} @Override public void onActivityResumed(Activity activity) {} @Override public void onActivityPaused(Activity activity) {} @Override public void onActivityStopped(Activity activity) {} @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {} @Override public void onActivityDestroyed(Activity activity) {} } ``` ```kotlin showLineNumbers import android.app.Activity import android.app.Application import android.os.Bundle import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ProcessLifecycleOwner import com.azerion.bluestack.MobileAds class MyApplication : Application(), Application.ActivityLifecycleCallbacks, DefaultLifecycleObserver, AppOpenAdManager.ActivityContextProvider { private val appOpenAdManager = AppOpenAdManager(this) private var currentActivity: Activity? = null override fun onCreate() { super.onCreate() registerActivityLifecycleCallbacks(this) ProcessLifecycleOwner.get().lifecycle.addObserver(this) } override fun onActivityStarted(activity: Activity) { if (!appOpenAdManager.isShowingAd) { currentActivity = activity } } override fun onStart(owner: LifecycleOwner) { super.onStart(owner) if (MobileAds.isInitialized()) { currentActivity?.let { activity -> // Don't show app open ad on launcher/splash activities // as they manage their own app open ad flow val shouldShowAd = activity !is CustomLauncherActivity && activity !is SplashScreenActivity if (shouldShowAd) { appOpenAdManager.showAdIfAvailable(activity) } } } } fun loadAd(activity: Activity) { appOpenAdManager.loadAd(activity) } fun showAdIfAvailable( activity: Activity, onShowAdCompleteListener: AppOpenAdManager.OnShowAdCompleteListener ) { appOpenAdManager.showAdIfAvailable(activity, onShowAdCompleteListener) } override fun getActivity() = currentActivity // Other ActivityLifecycleCallbacks methods... override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {} override fun onActivityResumed(activity: Activity) {} override fun onActivityPaused(activity: Activity) {} override fun onActivityStopped(activity: Activity) {} override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {} override fun onActivityDestroyed(activity: Activity) {} } ``` Don't forget to register your custom Application class in the `AndroidManifest.xml`: ```xml showLineNumbers ... ``` ## Destroying an App Open Ad When you have finished displaying an app open ad, call `destroy()` to free resources. ```java showLineNumbers @Override protected void onDestroy() { if (appOpenAd != null) { appOpenAd.destroy(); appOpenAd = null; } super.onDestroy(); } ``` ```kotlin showLineNumbers override fun onDestroy() { appOpenAd?.destroy() appOpenAd = null super.onDestroy() } ``` ## Handling Cold Starts with Loading Screens The examples above focus on showing app open ads when users return to an app that is already in memory (soft launch). Cold starts — when the app is launched fresh and was not previously in memory — require additional consideration. During a cold start, there is no previously loaded ad ready to show immediately. The delay between requesting an ad and receiving one can create a situation where the user briefly sees app content before an ad unexpectedly appears. This is a poor user experience and should be avoided. The recommended approach is to use a loading or splash screen during the app's startup sequence and only show the app open ad while that screen is still visible. Here's an example implementation: ```java showLineNumbers import android.content.Intent; import android.os.Bundle; import android.os.CountDownTimer; import androidx.appcompat.app.AppCompatActivity; import com.azerion.bluestack.MobileAds; import com.azerion.bluestack.initialization.InitializationListener; import com.azerion.bluestack.initialization.SDKInitializationStatus; import java.util.concurrent.TimeUnit; public class CustomLauncherActivity extends AppCompatActivity { private static final String TAG = "CustomLauncherActivity"; // Simulate app loading time (5 seconds) private static final long COUNTER_TIME_MILLISECONDS = 5000L; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_splash); initializeCMP(); } private void initializeCMP() { // Initialize your Consent Management Platform here // Once consent is obtained, initialize BlueStack SDK initializeBlueStackSDK(); } private void initializeBlueStackSDK() { MobileAds.setDebugModeEnabled(true); MobileAds.initialize(this, "YOUR_APP_ID", new InitializationListener() { @Override public void onInitialized(SDKInitializationStatus status) { // Load the app open ad after initialization ((MyApplication) getApplication()).loadAd(CustomLauncherActivity.this); } }); createTimer(); } private void navigateToMainActivity() { Intent intent = new Intent(this, MainActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); startActivity(intent); finish(); } private void createTimer() { new CountDownTimer(COUNTER_TIME_MILLISECONDS, 1000) { @Override public void onTick(long millisUntilFinished) { Log.d(TAG, "App is done loading in: " + (TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished) + 1)); } @Override public void onFinish() { // Show the app open ad when the splash screen is ready to dismiss ((MyApplication) getApplication()).showAdIfAvailable( CustomLauncherActivity.this, new AppOpenAdManager.OnShowAdCompleteListener() { @Override public void onShowAdComplete() { navigateToMainActivity(); } } ); } }.start(); } } ``` ```kotlin showLineNumbers import android.content.Intent import android.os.Bundle import android.os.CountDownTimer import android.util.Log import androidx.appcompat.app.AppCompatActivity import com.azerion.bluestack.MobileAds import com.azerion.bluestack.initialization.InitializationListener import com.azerion.bluestack.initialization.SDKInitializationStatus import java.util.concurrent.TimeUnit class CustomLauncherActivity : AppCompatActivity() { private val TAG = "CustomLauncherActivity" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_splash) initializeCMP() } private fun initializeCMP() { // Initialize your Consent Management Platform here // Once consent is obtained, initialize BlueStack SDK initializeBlueStackSDK() } private fun initializeBlueStackSDK() { MobileAds.setDebugModeEnabled(true) MobileAds.initialize(this, "YOUR_APP_ID", object : InitializationListener { override fun onInitialized(status: SDKInitializationStatus) { // Load the app open ad after initialization (application as MyApplication).loadAd(this@CustomLauncherActivity) } }) createTimer() } private fun navigateToMainActivity() { val intent = Intent(this, MainActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK } startActivity(intent) finish() } private fun createTimer() { object : CountDownTimer(COUNTER_TIME_MILLISECONDS, 1000) { override fun onTick(millisUntilFinished: Long) { Log.d(TAG, "App is done loading in: ${ TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished) + 1 }") } override fun onFinish() { // Show the app open ad when the splash screen is ready to dismiss (application as MyApplication).showAdIfAvailable( this@CustomLauncherActivity, object : AppOpenAdManager.OnShowAdCompleteListener { override fun onShowAdComplete() { navigateToMainActivity() } } ) } }.start() } companion object { // Simulate app loading time (5 seconds) private const val COUNTER_TIME_MILLISECONDS = 5000L } } ``` Follow these guidelines: - **Allocate sufficient time for ad loading.** Set your timer duration to give the SDK enough time to load the ad. In the example above, 5 seconds is typically sufficient. Adjust based on your app's needs and expected network conditions. - **Show the ad from the loading screen only.** If your app finishes loading and has already moved the user to the main content, do not show the ad — the moment has passed. - **Dismiss the loading screen in `onShowAdComplete`.** Wait for the callback before transitioning to app content. This ensures a smooth flow from splash screen → ad → app content with no flicker or content flash in between. ## Ad events ### Register for app open events `AppOpenAd` delivers lifecycle and presentation events through the `AppOpenAdListener` interface, which includes both load-related and display-related callbacks. Use the `setAppOpenAdListener()` method to register your listener: ```java showLineNumbers appOpenAd.setAppOpenAdListener(this); ``` ```kotlin showLineNumbers appOpenAd.setAppOpenAdListener(this) ``` ### App Open ad lifecycle events `AppOpenAdListener` notifies you when the ad has finished loading or when a load attempt fails. On failure, reset state and attempt to reload when appropriate. ```java showLineNumbers public class AppOpenAdManager implements AppOpenAdListener { // ... @Override public void onAdLoaded() { Log.i(TAG, "App open ad loaded"); } @Override public void onAdFailedToLoad(Exception exception) { Log.e(TAG, "App open ad failed to load", exception); } } ``` ```kotlin showLineNumbers class AppOpenAdManager(private val activityContextProvider: ActivityContextProvider) : AppOpenAdListener { // ... override fun onAdLoaded() { Log.i(TAG, "App open ad loaded") } override fun onAdFailedToLoad(exception: Exception) { Log.e(TAG, "App open ad failed to load", exception) } } ``` ### App Open ad full-screen events `AppOpenAdListener` also reports when the ad is displayed, clicked, dismissed, or fails to display. After the ad is dismissed or fails to display, reset state, notify the listener, and immediately start loading a new ad so one is ready for the next app foregrounding. ```java showLineNumbers public class AppOpenAdManager implements AppOpenAdListener { // ... @Override public void onAdDisplayed() { Log.i(TAG, "App open ad displayed"); } @Override public void onAdFailedToDisplay(Exception exception) { Log.e(TAG, "App open ad failed to display", exception); isShowingAd = false; if (onShowAdCompleteListener != null) { onShowAdCompleteListener.onShowAdComplete(); } Activity activity = activityContextProvider.getActivity(); if (activity != null) { loadAd(activity); } } @Override public void onAdClicked() { Log.i(TAG, "App open ad clicked"); } @Override public void onAdDismissed() { Log.i(TAG, "App open ad dismissed"); isShowingAd = false; if (onShowAdCompleteListener != null) { onShowAdCompleteListener.onShowAdComplete(); } Activity activity = activityContextProvider.getActivity(); if (activity != null) { loadAd(activity); } } } ``` ```kotlin showLineNumbers class AppOpenAdManager(private val activityContextProvider: ActivityContextProvider) : AppOpenAdListener { // ... override fun onAdDisplayed() { Log.i(TAG, "App open ad displayed") } override fun onAdFailedToDisplay(exception: Exception) { Log.e(TAG, "App open ad failed to display", exception) isShowingAd = false onShowAdCompleteListener?.onShowAdComplete() activityContextProvider.getActivity()?.let { loadAd(it) } } override fun onAdClicked() { Log.i(TAG, "App open ad clicked") } override fun onAdDismissed() { Log.i(TAG, "App open ad dismissed") isShowingAd = false onShowAdCompleteListener?.onShowAdComplete() activityContextProvider.getActivity()?.let { loadAd(it) } } } ``` ## Best Practices App open ads are a great way to monetize your app's loading screen, but it's important to follow best practices so that your users continue to enjoy using your app: - **Show ads only during natural waiting moments.** App open ads work best when users are already expecting a brief pause, such as during app launch or when returning from the background. Avoid surprising users with ads at unexpected times. - **Always show a splash or loading screen first.** See [Handling Cold Starts with Loading Screens](#handling-cold-starts-with-loading-screens) for details. - **Initialize the SDK before loading ads.** Make sure the BlueStack SDK has fully initialized before you attempt to load an app open ad. Loading ads before initialization may result in failed requests. - **Preload the ad early.** Load the ad as soon as possible so there is no delay when it's time to show it. Avoid loading other ad formats in parallel, as this can strain device resources and reduce fill rates. - **Respect user experience with frequency controls.** See [Control Ad Frequency](#control-ad-frequency) for recommended strategies. - **Be mindful of new users.** Hold off on showing app open ads until users have opened and used your app a few times. This helps build a positive first impression before introducing ads. - **Handle ad expiration.** See [Consider Ad Expiration](#consider-ad-expiration) for details on how to manage preloaded ad validity. - **Coordinate your loading screen with the ad.** If you have a loading screen running behind the app open ad and it finishes before the user dismisses the ad, dismiss the loading screen in the `onAdDismissed` callback to ensure a smooth transition to your app content. ## Consider Ad Expiration A preloaded ad can become stale if too much time passes between loading and displaying. The BlueStack SDK handles ad expiration internally. If you attempt to show an expired ad, you will receive an `AdError.AD_EXPIRED` error in the `onAdFailedToDisplay()` callback. When this occurs, clean up the expired ad reference and call `loadAd()` to preload a fresh ad for the next opportunity. ## Control Ad Frequency To maintain a positive user experience, avoid showing an app open ad on every single foreground event. Consider implementing frequency controls such as: - **Skip opportunities** — Show an ad on every second or third app open instead of every time. - **Minimum background duration** — Only show an ad if the user was away from the app for a certain amount of time (e.g., 30 seconds, 2 minutes, or 15 minutes). - **Cooldown after cold start** — If you showed an ad during a cold start, skip soft launch ads for a set period afterward. - **Frequency caps** — Limit the total number of app open ads shown per session or per day. Where possible, tailor caps based on user cohorts or engagement levels. --- ## Banner Ads ## Overview Before You Start. Make sure that you have correctly integrated the BlueStack SDK into your application. Integration is outlined [here](../index.md). For a working implementation of this ad format, see the [azerion-inapp-demo-android](https://github.com/azerion/azerion-inapp-demo-android) demo app. ## Create a BannerView To create a banner you have to init an object with type BannerView. ```java showLineNumbers // Create a new bannerView. BannerView bannerView = new BannerView(getActivity()); bannerView.setPlacementId("BANNER_PLACEMENT_ID") bannerView.setAdSize(BannerAdSize.BANNER) // Add new bannerView into adLayout. adLayout.removeAllViews(); adLayout.addView(bannerView); ``` ```kotlin showLineNumbers // Create a new bannerView. val bannerView = BannerView(requireActivity()) bannerView.setPlacementId("BANNER_PLACEMENT_ID") bannerView.setAdSize(BannerAdSize.BANNER) // Add new bannerView into adLayout. adLayout.removeAllViews() adLayout.addView(bannerView) ``` ```xml ``` **Note:** if your application support different screen sizes on Tablet and Phone, it is better to use following [AdSize](#adsize): ``` java BannerAdSize adSize = getResources().getBoolean(R.bool.is_tablet) ? BannerAdSize.DYNAMIC_LEADERBOARD : BannerAdSize.DYNAMIC_BANNER; bannerView.setAdSize(adSize) ``` ```kotlin val adSize = if (resources.getBoolean(R.bool.is_tablet)) BannerAdSize.DYNAMIC_LEADERBOARD else BannerAdSize.DYNAMIC_BANNER bannerView.setAdSize(adSize) ``` ## Load a Banner Ad **Note:** Make all calls to the BlueStack SDK on the main thread. ### With RequestOptions To request an Banner ad using [RequestOptions](../30-advanced-topics/03-targetting.md), provide an instance of RequestOptions in the BannerView's load method: ```java showLineNumbers bannerView.load(requestOptions) ``` ```kotlin showLineNumbers bannerView.load(requestOptions) ``` ### Without RequestOptions ```java showLineNumbers bannerView.load() ``` ```kotlin showLineNumbers bannerView.load() ``` ## Ad events ### Register for banner events To receive ad's lifecycle events register a listener in `BannerView`. ```java showLineNumbers public class BannerAdFragment extends Fragment implements BannerViewListener { ... @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... bannerView = new BannerView(requireActivity()); bannerView.setBannerViewListener(this); .. } ... } ``` ```kotlin showLineNumbers class BannerAdFragment : Fragment(), BannerViewListener { ... override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { ... bannerView = BannerView(requireActivity()) bannerView.setBannerViewListener(this) ... } ... } ``` ### Implement banner events The SDK will notify your Listener of all possible events listed below : - onAdLoad(): will be called by the SDK when banner ad finishes loading. ```java showLineNumbers @Override public void onAdLoad(int preferredHeightDP){ Log.d(TAG, "your banner is ready"); ... // it's preferable to adjust the ad container view size to match the returned Ad size for a better display ... } ``` ```kotlin showLineNumbers override fun onAdLoad(preferredHeightDP: Int){ Log.d(TAG, "your banner is ready") ... // it's preferable to adjust the ad container view size to match the returned Ad size for a better display ... } ``` - onAdFailToLoad(): will be called when all ads servers fail. it will return the error of last called ads server. ```java showLineNumbers @Override public void onAdFailToLoad(Exception exception) { Log.e(TAG, "banner did fail :" + exception); } ``` ```kotlin showLineNumbers override fun onAdFailToLoad(exception: Exception) { Log.e(TAG, "banner did fail :$exception") } ``` - onResize() : will be called when the banner has changed size ```java showLineNumbers @Override public void onResize(Size size) { ... // it's preferable to adjust the ad container view size to match the returned Ad size for a better display Log.d(TAG, "Banner did resize w dp " + size.getWidth() + " h dp " + size.getHeight()); ... } ``` ```kotlin showLineNumbers override fun onResize(size: Size) { ... // it's preferable to adjust the ad container view size to match the returned Ad size for a better display Log.d(TAG, "Banner did resize w dp " + size.width + " h dp " + size.height) ... } ``` - onAdRefresh() : will be called when the banner has refreshed ```java showLineNumbers @Override public void onAdRefresh() { Log.d(TAG, "banner refresh succeed") } ``` ```kotlin showLineNumbers override fun onAdRefresh() { Log.d(TAG, "banner refresh succeed") } ``` - onAdFailToRefresh() : will be called when the banner fail to refresh. ```java showLineNumbers @Override public void onAdFailToRefresh(Exception exception) { Log.e(TAG, "banner did fail to refresh :" + exception); } ``` ```kotlin showLineNumbers override fun onAdFailToRefresh(exception: Exception) { Log.e(TAG, "banner did fail to refresh :$exception") } ``` - onAdClicked(): will be called when user click the banner ad. ```java showLineNumbers @Override public void onAdClicked() { Log.d(TAG, "Ad Clicked"); } ``` ```kotlin showLineNumbers override fun onAdClicked() { Log.d(TAG, "Ad Clicked") } ``` ## Destroying Banner Ad When you have finished your ads plant you must free the memory. ```java showLineNumbers @Override protected void onDestroy() { bannerView.destroy(); bannerView = null; super.onDestroy(); } ``` ```kotlin showLineNumbers override fun onDestroy() { bannerView.destroy() bannerView = null super.onDestroy() } ``` ## AdSize BlueStack ads provides variant pre-defined sizes, See table below for details about our supported standard banner sizes: | MNGAdSize | Description |Dimensions in dp (WxH) | --- | --- | --- | | BANNER | Standard Banner | 320 x 50 | | LARGE_BANNER | Large Banner |320 x 100 | | MEDIUM_RECTANGLE | Medium Rectangular Banner |300 x 250 | | DYNAMIC_BANNER |Adjusted Banner| Screen width x 50 | | FULL_BANNER | Full Banner | 468 x 60 | | LEADERBOARD | Standard Banner for tablet | 728 x 90 | | DYNAMIC_LEADERBOARD | Adjusted Banner for tablet | Screen width x 90 | ## Adapt the banner size after loading You can resize the Banner View height to match the creative's width/height ratio, this is often the case when your Banner View needs to deliver view over 50 dp. (This does not happen when setting your view as wrap_content) Here's an example: *XML Code :* ```xml showLineNumbers ``` ```java showLineNumbers @Override public void onResize(Size size) { // convert Dp To Pixel Resources resources = context.getResources(); DisplayMetrics metrics = resources.getDisplayMetrics(); float bannerPreferredHeightPx = size.getHeight() * (metrics.densityDpi / 160f); // adapt the banner size bannerContainer.getLayoutParams().height = bannerPreferredHeightPx; bannerContainer.requestLayout(); } ``` **OR** ```java showLineNumbers @Override public void onAdLoad(int preferredHeightDP) { // convert Dp To Pixel Resources resources = context.getResources(); DisplayMetrics metrics = resources.getDisplayMetrics(); float bannerPreferredHeightPx = preferredHeightDP * (metrics.densityDpi / 160f); // adapt the banner size bannerContainer.getLayoutParams().height = bannerPreferredHeightPx; bannerContainer.requestLayout(); } ``` ```kotlin showLineNumbers override fun onResize(size: Size) { // convert Dp To Pixel val resources = context.resources val metrics: DisplayMetrics = resources.displayMetrics val bannerPreferredHeightPx = size.height * (metrics.densityDpi / 160f) // adapt the banner size bannerContainer.layoutParams.height = bannerPreferredHeightPx bannerContainer.requestLayout() } ``` **OR** ```java showLineNumbers override fun onAdLoad(preferredHeightDP: Int) { // convert Dp To Pixel val resources = context.resources val metrics: DisplayMetrics = resources.displayMetrics val bannerPreferredHeightPx = preferredHeightDP * (metrics.densityDpi / 160f) // adapt the banner size bannerContainer.layoutParams.height = bannerPreferredHeightPx bannerContainer.requestLayout() } ``` # Example Example | Description| ------------- | ------------- | ![banner50-mngads-android-min.png](https://bitbucket.org/repo/GyRXRR/images/288211594-banner50-mngads-android-min.png) Banner - (50dp or 90dp ) | A banner is a small bar ad that appears at the bottom or top of your content. Usually sized 320 x 50. Only include one ad per page or show one ad at a time if scrolling. In all cases, **the banner width is flexible with a minimum of 320px.**. If you are building your app for iPad consider using 90px and 50px for iphone. ![banner250-mngads-android-min.png](https://bitbucket.org/repo/GyRXRR/images/4181983461-banner250-mngads-android-min.png) Square - Medium rectangle (300 x 250) |Square banner also known as a *medium rectangle* (300 x 250). This format can increase earnings when both text and image ads are enabled. Performs well when embedded within text content or at the end of articles. --- ## Interstitial Ads ## Overview Before You Start. Make sure that you have correctly integrated the BlueStack SDK into your application. Integration is outlined [here](../index.md). For a working implementation of this ad format, see the [azerion-inapp-demo-android](https://github.com/azerion/azerion-inapp-demo-android) demo app. ## Create an Interstitial Ad To create an interstitial you must init an object with type InterstitialAd : ```java showLineNumbers InterstitialAd interstitialAd = new InterstitialAd(activity, "INTERSTITIAL_PLACEMENT_ID"); ``` ```kotlin showLineNumbers val interstitialAd = InterstitialAd(activity, "INTERSTITIAL_PLACEMENT_ID") ``` ## Load an Interstitial Ad **Note:** Make all calls to the BlueStack SDK on the main thread. ### With RequestOptions To request an interstitial ad using [RequestOptions](../30-advanced-topics/03-targetting.md), provide an instance of RequestOptions in the InterstitialAd's load method: ```java showLineNumbers interstitialAd.load(requestOptions); ``` ```kotlin showLineNumbers interstitialAd.load(requestOptions) ``` ### Without RequestOptions ```java showLineNumbers interstitialAd.load() ``` ```kotlin showLineNumbers interstitialAd.load() ``` **Note:** - To avoid stacking up two interstitial Ad BlueStack added lock system : - InterstitialAd will ignore any interstitial request while there is a pending request or there is interstitial shown at that moment. ## Ad events ### Register for Interstitial events To receive ad's lifecycle events register a listener in `InterstitialAd`. ```java showLineNumbers // set intertitial listener public class InterstitialFragment extends Fragment implements InterstitialAdListener { ... @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... interstitialAd.setInterstitialAdListener(this); ... } ... } ``` ```kotlin showLineNumbers // set intertitial listener class InterstitialFragment : Fragment(), InterstitialAdListener { ... override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { ... interstitialAd.setInterstitialAdListener(this) ... } ... } ``` ### Implement Interstitial events The SDK will notify your Listener of all possible events listed below : - onAdLoaded(): will be called by the SDK when your Interstitial is ready. ```java showLineNumbers @Override public void onAdLoaded() { Log.d(TAG, "interstitial did load"); } ``` ```kotlin showLineNumbers override fun onAdLoaded() { Log.d(TAG, "interstitial did load") } ``` - onAdFailedToLoad(Exception adsException): will be called when all ads servers fail. it will return the error of last called ads server. ```java showLineNumbers @Override public void onAdFailedToLoad(Exception adsException) { Log.e(TAG, "interstitial did fail :" + adsException.toString()); } ``` ```kotlin showLineNumbers override fun onAdFailedToLoad(adsException: Exception) { Log.e(TAG, "interstitial did fail :" + adsException.toString()) } ``` - onAdDisplayed(): will be called when interstitial was shown. ```java showLineNumbers @Override public void onAdDisplayed() { Log.d(TAG, "interstitial Did Shown"); } ``` ```kotlin showLineNumbers override fun onAdDisplayed() { Log.d(TAG, "interstitial Did Shown") } ``` - onAdFailedToDisplay(@NonNull Exception exception): will be called when interstitial is failed to display. ```java showLineNumbers @Override public void onAdFailedToDisplay(@NonNull Exception exception) { Log.e(TAG, "interstitial did fail :" + adsException.toString()); } ``` ```kotlin showLineNumbers override fun onAdFailedToDisplay(@NonNull Exception exception)p pbgh { Log.e(TAG, "interstitial did fail :" + adsException.toString()); } ``` - onAdClicked(): will be called when user click the Interstitial ad. ```java showLineNumbers @Override public void onAdClicked() { Log.d(TAG, "Ad Clicked"); } ``` ```kotlin showLineNumbers override fun onAdClicked() { Log.d(TAG, "Ad Clicked") } ``` - onAdDismissed(): will be called when interstitial was dismissed. ```java showLineNumbers @Override public void onAdDismissed() { Log.d(TAG, "interstitial disappear"); } ``` ```kotlin showLineNumbers override fun onAdDismissed() { Log.d(TAG, "interstitial disappear") } ``` ## Show an Interstitial Ad To check if the interstitial is ready to be shown, you must call isReady() and show() in order to display the ad (in case of success) : ```java showLineNumbers ... if (interstitialAd.isReady()) { interstitialAd.show(); } else { Log.d(TAG, "Interstitial not ready "); } ... ``` ```kotlin showLineNumbers ... if (interstitialAd.isReady()) { interstitialAd.show() } else { Log.d(TAG, "Interstitial not ready") } ... ``` ## Destroying Interstitial Ad When you have finished your ads plant you must free the memory. ```java showLineNumbers @Override protected void onDestroy() { if (interstitialAd != null) { interstitialAd.destroy(); interstitialAd = null; } super.onDestroy(); } ``` ```kotlin showLineNumbers override fun onDestroy() { interstitialAd?.destroy() interstitialAd = null super.onDestroy() } ``` --- ## Native Ads ## Overview Before You Start. Make sure that you have correctly integrated the BlueStack SDK into your application. Integration is outlined [here](../index.md). For a working implementation of this ad format, see the [azerion-inapp-demo-android](https://github.com/azerion/azerion-inapp-demo-android) demo app. A native ad is a custom designed ad that fits seamlessly with your app. If done well, ads can blend in naturally with your interface. ## Create a Native Ad ### Init AdsFactory To create a native ad you have to init an object with type `AdsFactory`. ```java showLineNumbers AdsFactory nativeAdsFactory = new AdsFactory(getActivity()); ``` ```kotlin showLineNumbers val nativeAdsFactory = AdsFactory(activity) ``` ### Set Placement ID You have also to set placement Id : ```java showLineNumbers nativeAdsFactory.setPlacementId("/YOUR_APP_ID/PLACEMENT_ID"); ``` ```kotlin showLineNumbers nativeAdsFactory.setPlacementId("/YOUR_APP_ID/PLACEMENT_ID") ``` ## Load a Native Ad **Note:** Make all calls to the BlueStack SDK on the main thread. To make a request you have to call 'loadNative()'. This is a void method, result will be returned in the callback. ```java showLineNumbers nativeAdsFactory.loadNative(); ``` ```kotlin showLineNumbers nativeAdsFactory.loadNative() ``` ## Ad events ### Register for Native events To receive ad's lifecycle events register a listener in `AdsFactory`. ```java showLineNumbers nativeAdsFactory.setNativeListener(...); ``` ```kotlin showLineNumbers nativeAdsFactory.setNativeListener(...) ``` ### Implement Native events The SDK will notify your Listener of all possible events listed below : - nativeObjectDidLoad(): will be called by the SDK when your nativeObject is ready. now you can create your own view. ```java showLineNumbers @Override public void nativeObjectDidLoad(NativeObject nativeObject) { Log.d(TAG, "native Object did load "); } ``` ```kotlin showLineNumbers override fun nativeObjectDidLoad(nativeObject: NativeObject) { Log.d(TAG, "native Object did load") } ``` - nativeObjectDidFail(Exception adsException): will be called when all ads servers fail. it will return the error of last called ads server. ```java showLineNumbers @Override public void nativeObjectDidFail(Exception adsException) { Log.e(TAG, "nativeObject Did Fail : " + adsException.toString()); } ``` ```kotlin showLineNumbers override fun nativeObjectDidFail(adsException: Exception) { Log.e(TAG, "nativeObject Did Fail : " + adsException.toString()) } ``` ## Show a Native Ad ### Build Native Ad UI Once a native ad is loaded, you may retrieve its metadata with the following methods: ```java showLineNumbers // Get the app name String title = nativeObject.getTitle(); // Get the app description (tagline) String body = nativeObject.getBody(); // Get the "Ad" badge bitmap. You must show this bitmap on your ad view to denote an ad Bitmap badge = nativeObject.getBadge(); // Get the localized text to print on the call to action button, such as "DOWNLOAD , LEARNE MORE ..." String callToAction = nativeObject.getCallToAction(); ``` ```kotlin showLineNumbers // Get the app name val title: String = nativeObject.getTitle() // Get the app description (tagline) val body: String = nativeObject.getBody() // Get the "Ad" badge bitmap. You must show this bitmap on your ad view to denote an ad val badge: Bitmap = nativeObject.getBadge() // Get the localized text to print on the call to action button, such as "DOWNLOAD , LEARNE MORE ..." val callToAction: String = nativeObject.getCallToAction() ``` ##### **Ad Title** - 50 maximum character length string of ad headline - Provide enough space to display the entire length of the Ad Title - asset name : **nativeObject.getTitle()** ##### **Ad Text** - 150 maximum character length string of ad text - Provide enough space to display the entire length of the Ad Text - asset name : **nativeObject.getBody()** ##### **CTA Text** - Text for a button - 12 characters maximum - asset name : **nativeObject.getCallToAction()** ##### **Sponsored Marker** - Badge view (an icon) - change according ad network - must be inserted on top right - this is automatically added to the TOP-RIGHT corner of your native ad layout. ##### **Distinguishable Ad** - “Ad” (can be localized) - Badge that says “AD” and is at least 15x15px (can be localized) - change according ad network - must be inserted on top left - asset name : **nativeObject.getBadge()** ### Registering views used to render the ad NativeObject have all required metadata to build your customized native UI. Your native ad layout should have MAdvertiseNativeContainer as it's root viewGroup container. ```java showLineNumbers // Register your custom ad view to automatically report impressions and clicks, and to display icon, image cover or the media video // This is mandatory nativeObject.registerViewForInteraction(nativeAdContainerView,mediaViewGroup,iconImageView,nativeAdCallToActionView); ``` ```kotlin showLineNumbers // Register your custom ad view to automatically report impressions and clicks, and to display icon, image cover or the media video // This is mandatory nativeObject.registerViewForInteraction(nativeAdContainerView,mediaViewGroup,iconImageView,nativeAdCallToActionView) ``` The registerViewForInteraction method : - Handles all the user interactions with your custom layout (clicks, impressions ...) - Display icon, image cover or the media video It accepts four arguments: - The first is the MAdvertiseNativeContainer that should be your custom layout's root view. - The second is the media Container, the sdk will handle the rendering process ( displaying) the image cover or the media video inside the view group that depends on the ad network result. - The third is the image View for NativeAd's ad Icon - The fourth is the callToAction View that handles the click event of your ad. **Note :**The MAdvertiseNativeContainer is a custom ViewGroup that extends FrameLayout so you can use it as it is or you can put your layout inside of it which is the method we recommend. ![2587222597-nativeAd-min-min (1).png](https://bitbucket.org/repo/GyRXRR/images/165955247-2587222597-nativeAd-min-min%20%281%29.png) ## Destroying Native Ad When you have finished your ads plant you must free the memory. ```java showLineNumbers @Override protected void onDestroy() { nativeAdsFactory.releaseMemory(); super.onDestroy(); } ``` ```kotlin showLineNumbers override fun onDestroy() { nativeAdsFactory.releaseMemory() super.onDestroy() } ``` ## Troubleshooting ### Hide Icon or Image Cover Put **null** to hide icon (instead of iconImageView) or image cover (instead of mediaViewGroup). ```java showLineNumbers nativeObject.registerViewForInteraction(nativeAdContainerView,null,null,nativeAdCallToActionView); ``` ```kotlin showLineNumbers nativeObject.registerViewForInteraction(nativeAdContainerView,null,null,nativeAdCallToActionView) ``` ### Customize Native Ad Badge Text You can use a custom badge Text for the native ad. ```java showLineNumbers nativeObject.getBadge(getActivity(), "String to be displayed in the badge"); ``` ```kotlin showLineNumbers nativeObject.getBadge(activity, "String to be displayed in the badge") ``` ### Cache Ad metadata that you receive can be cached and re-used for up to 3 hours. If you plan to use the metadata after this time period, make a call to load a new ad. ### isBusy Before making a request if you want to check that factory is not busy (Ads factory is busy means that it has not finished the previous request yet). isBusy will be set to : - **true :** when factory starts handling request. - **false :** when factory finishes handling request. **Example**: ```java showLineNumbers if (!nativeAdsFactory.isBusy()) { Log.d(TAG, "Ads Factory is not busy"); nativeAdsFactory.loadInterstitial(false); } else { Log.d(TAG, "Ads Factory is busy"); } ``` ```kotlin showLineNumbers if (!nativeAdsFactory.isBusy()) { Log.d(TAG, "Ads Factory is not busy") nativeAdsFactory.loadInterstitial(false) } else { Log.d(TAG, "Ads Factory is busy") } ``` ### Customize Native Ad AdChoice The adChoice is automatically added to the top right corner of your native ad layout but you can change that position by using the Preference.setAdChoicePosition(int position) before loading your ad. The position argument can be one of these: ```java showLineNumbers TOP_RIGHT TOP_LEFT BOTTOM_RIGHT BOTTOM_LEFT ``` For example: ```java showLineNumbers preference.setAdChoicePosition(Preference.TOP_LEFT); nativeAdsFactory.loadNative(preference); ``` ```kotlin showLineNumbers preference.setAdChoicePosition(Preference.TOP_LEFT) nativeAdsFactory.loadNative(preference) ``` ### Click - registerViewForInteraction It's **HIGHLY** recommended to only register ONE and ONLY one view for interaction , because some of the AdNetworks only accept one view and if you try to assign more than one then probably none of the views you assign will be responsive. ### Ad click listener You can then implement MNG AdListener callback to detect when an Ad is clicked ```java showLineNumbers // set click listener nativeAdsFactory.setClickListener(this); ... @Override public void onAdClicked() { Log.d(TAG, "Ad Clicked"); } ... ``` ```kotlin showLineNumbers // set click listener nativeAdsFactory.setClickListener(this) ... override fun onAdClicked() { Log.d(TAG, "Ad Clicked") } ... ``` ### Ad refresh listener You can also implement MNG refresh listener callback to detect when an Ad refreshed ```java showLineNumbers // set refresh listener nativeAdsFactory.setRefreshListener(this); ... @Override public void onRefreshSucceed() { Log.d(TAG, "refresh succeed"); } @Override public void onRefreshFailed(Exception e) { Log.d(TAG, "refresh failed"); } ... ``` ```kotlin showLineNumbers // set refresh listener nativeAdsFactory.setRefreshListener(this) ... override fun onRefreshSucceed() { Log.d(TAG, "refresh succeed") } override fun onRefreshFailed(e: Exception) { Log.d(TAG, "refresh failed") } ... ``` ### Preferences Object Preferences object is an optional parameter that allow you select ads by user info. informations that you can set are: - **Age :** age of user - **Location :** geographical position of the user. - **Language :** : language of user (ISO code) - **Gender :** gender of user - **KeyWord :** Use free-form key-values when you want to pass targeting values dynamically into an ad tag based on information you collect from your users. You can also use free-form key-values when there are too many possible values to define in advance. Separator in case of multiple entries is **;**. - **Content URL :** URL for content related to your app (url must be a string which length not exceed 512 caracters). ```java showLineNumbers Location myLocation = new Location("I"); myLocation.setLatitude(35.757866); myLocation.setLongitude(10.810547); preference = new Preference(); preference.setLocation(location,CONSENT_FLAG,context); preference.setAge(28); preference.setGender(Gender.GenderFemale); preference.setKeyword("brand=myBrand;category=sport"); preference.setContentUrl("put your content url here"); nativeAdsFactory.loadNative(preference); ``` ```kotlin showLineNumbers val myLocation = Location("I") myLocation.setLatitude(35.757866) myLocation.setLongitude(10.810547) val preference = Preference() preference.setLocation(location,CONSENT_FLAG,context) preference.setAge(28) preference.setGender(Gender.GenderFemale) preference.setKeyword("brand=myBrand;category=sport") preference.setContentUrl("put your content url here") nativeAdsFactory.loadNative(preference) ``` **Note :** - This [link] can help you to get device location. - Do not serialize Location object (like transforming it into a string using gson library), this may lead to a fatal runtime error when that instance is reused. - The setLocation method takes the following parameters: - the Location instance. - the CONSENT_FLAG value (corresponds to a int : 0,1,2 or 3). - 0 = Not allow to send location. - 1 = When you managed location according to consent value. - 2 and 3 = Allow the SDK to managed location directly in accordance with the consent value use TCF v1 or TCF v2, see with the Azerion team as it depends on your implementation. - the Context instance. content Ad | carousel Ad | carousel Ad ------------- | ------------- | ------------- | ------------- ![nativeAd-1.png](https://bitbucket.org/repo/GyRXRR/images/1430534000-nativeAd-1.png)|![nativeAd-2.png](https://bitbucket.org/repo/GyRXRR/images/2633774569-nativeAd-2.png)|![carousel2-mngads-android-min.png](https://bitbucket.org/repo/GyRXRR/images/771135904-carousel2-mngads-android-min.png) --- ## Rewarded Ads ## Overview **RewardedAd** that will serve to deliver rewarded video ads which are a full screen experience where users opt-in to view a video ad in exchange for something of value, such as virtual currency, in-app items, exclusive content, and more. The ad experience is 15-30 second non-skippable and contains an end card with a call to action. Upon completion of the full video, you will receive a callback to grant the suggested reward to the user. Before You Start. Make sure that you have correctly integrated the BlueStack SDK into your application. Integration is outlined [here](../index.md). For a working implementation of this ad format, see the [azerion-inapp-demo-android](https://github.com/azerion/azerion-inapp-demo-android) demo app. ## Create a Rewarded Ad In order to use the rewarded ad feature you have to instantiate the RewardedAd class. ```java showLineNumbers RewardedAd rewardedAd = new RewardedAd(getActivity(), "REWARDED_PLACEMENT_ID"); ``` ```kotlin showLineNumbers RewardedAd rewardedAd = RewardedAd(requireActivity(), "REWARDED_PLACEMENT_ID") ``` ## Load a Rewarded Ad **Note:** Make all calls to the BlueStack SDK on the main thread. ### With RequestOptions To request a rewarded ad using [RequestOptions](../30-advanced-topics/03-targetting.md), provide an instance of RequestOptions in the RewardedAd's load method: ```java showLineNumbers rewardedAd.load(requestOptions) ``` ```kotlin showLineNumbers rewardedAd.load(requestOptions) ``` ### Without RequestOptions ```java showLineNumbers rewardedAd.load() ``` ```kotlin showLineNumbers rewardedAd.load() ``` ## Ad events ### Register for Rewarded events To receive ad's lifecycle events register a listener in `RewardedAd`. ```java showLineNumbers public class RewardedAdFragment extends Fragment implements RewardedAdListener { ... @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... rewardedAd.setRewardedAdListener(this); ... } ... } ``` ```kotlin showLineNumbers // set a rewarded ad listener class RewardedAdFragment : Fragment(), RewardedAdListener { ... override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { ... rewardedAd.setRewardedAdListener(this) ... } ... } ``` ### Implement Rewarded events The SDK will notify your Listener of all possible events listed below : - onAdLoaded(): will be called by the SDK when your Rewarded ad is ready. ```java showLineNumbers @Override public void onAdLoaded() { Log.d(TAG, "Rewarded did load"); } ``` ```kotlin showLineNumbers override fun onAdLoaded() { Log.d(TAG, "Rewarded did load") } ``` - onAdFailedToLoad(): will be called when all ads servers fail. it will return the error of the last called ad server. ```java showLineNumbers @Override public void onAdFailedToLoad(Exception adsException) { Log.e(TAG, "Rewarded did fail to load :" + adsException.toString()); } ``` ```kotlin showLineNumbers override fun onAdFailedToLoad(adsException: Exception) { Log.e(TAG, "Rewarded did fail to load :" + adsException.toString()) } ``` - onAdDisplayed(): will be called when Rewarded ad was shown. ```java showLineNumbers @Override public void onAdDisplayed() { Log.d(TAG, "Rewarded Did Shown"); } ``` ```kotlin showLineNumbers override fun onAdDisplayed() { Log.d(TAG, "Rewarded Did Shown") } ``` - onAdFailedToDisplay(): will be called when Rewarded ad is failed to display. ```java showLineNumbers @Override public void onAdFailedToDisplay(@NonNull Exception exception) { Log.e(TAG, "Rewarded did fail :" + adsException.toString()); } ``` ```kotlin showLineNumbers override fun onAdFailedToDisplay(@NonNull Exception exception)p pbgh { Log.e(TAG, "Rewarded did fail :" + adsException.toString()); } ``` - onAdClicked(): will be called when user click the Rewarded ad. ```java showLineNumbers @Override public void onAdClicked() { Log.d(TAG, "Ad Clicked"); } ``` ```kotlin showLineNumbers override fun onAdClicked() { Log.d(TAG, "Ad Clicked") } ``` - onAdDismissed(): will be called when Rewarded ad was dismissed. ```java showLineNumbers @Override public void onAdDismissed() { Log.d(TAG, "Rewarded disappear"); } ``` ```kotlin showLineNumbers override fun onAdDismissed() { Log.d(TAG, "Rewarded disappear") } ``` - onEarnedReward(): will be called when user earned a reward. ```java showLineNumbers @Override public void onEarnedReward(@Nullable Reward reward) { if (reward != null) { Log.d("TAG", "onVideoRewarded, type: " + reward.getType() + " , amount: " + reward.getAmount()); } else { Log.d("TAG", "onVideoRewarded with no reward object"); } } ``` ```kotlin showLineNumbers override fun onEarnedReward(reward: Reward?) { if (reward != null) { Log.d( TAG, "onEarnedReward, type: ${reward.type}, amount: ${reward.amount}" ) } else { Log.d(TAG, "onEarnedReward with no reward object") } } ``` ## Show Rewarded Ad To check if the rewarded is ready to be shown, you must call isReady() and show() in order to display the ad (in case of success) : ```java showLineNumbers ... if (rewardedAd.isReady()) { rewardedAd.show(); } else { Log.d(TAG, "RewardedAd not ready "); } ... ``` ```kotlin showLineNumbers ... if (rewardedAd.isReady()) { rewardedAd.show() } else { Log.d(TAG, "RewardedAd not ready") } ... ``` ## Destroying Rewarded Ad When you have finished your ads plant you must free the memory. ```java showLineNumbers @Override protected void onDestroy() { if (rewardedAd != null) { rewardedAd.destroy(); rewardedAd = null; } super.onDestroy(); } ``` ```kotlin showLineNumbers override fun onDestroy() { rewardedAd?.destroy() rewardedAd = null super.onDestroy() } ``` --- ## Supported Networks BlueStack's mediation feature enables you to deliver advertisements to your app from various sources, including BlueStack itself and third-party ad networks. This section provides guidance on integrating mediation partner SDKs through BlueStack's third-party SDK adapters. :::warning ```shell -keep public class com.azerion.bluestack.mediation.** { *; } ``` Please add the above rule to your proguard file when you encounter errors similar to what you see here: ```shell java.lang.NoSuchMethodException: com.azerion.bluestack.mediation.* ``` ```shell Exception com.azerion.bluestack.error.AdapterNotFoundError: com.azerion.bluestack.mediation.* ``` ::: :::info **Recommended:** include all mediation adapters by default. Omit an adapter only if you have a specific reason not to ship that demand source. The [Get Started](../../index.md#add-mediation-partners) page shows the full bundle; the sections below cover per-network details and opt-in extras. ::: ## In-App Bidding ```groovy showLineNumbers title="build.gradle" repositories { maven { url 'https://packagecloud.io/smartadserver/android/maven2' } } ``` **Note:** In-App-Bidding has a default dependency with Smart Display SDK. ## Google Mobile Ads For Google you need to add your Google App ID to your app's AndroidManifest.xml file: ```groovy showLineNumbers title="AndroidManifest.xml" ``` ## Equativ ```groovy showLineNumbers title="build.gradle" repositories { google() mavenCentral() maven { url 'https://packagecloud.io/smartadserver/android/maven2' } } ``` ## Supported Ad Networks | Ad Network | SDK Version | Adapter Version | Supported Ad Formats | |-----------------------|-------------|-----------------|-------------------------------------------------------| | **Google** | | | Banner / MREC, Interstitial, Rewarded Ads, Native Ads | | **Equativ** | | | Banner / MREC, Interstitial | | **BlueStack Bidding** | N/A | | Banner / MREC, Interstitial | ### Notes - Ensure all dependencies are included as outlined in each network’s integration guide. - Ad formats may require additional configurations or testing to confirm functionality. --- ## AppLovin This guide shows you how to integrate our BlueStack mediation adapter of AppLovin MAX SDK with your current Android app and set up additional request parameters. Release notes can be found [here](./02-applovin-changelog.md) ## Supported ad formats - Banner - Interstitial - MREC - Rewarded ## Requirements - Android 5 (API level 21) or higher. - CompileSdkVersion at least 33. ## AppLovin Configuration When in the Applovin MAX dashboard, navigate to **Manage -> Networks**, at the bottom of the page you have the option to **add a Custom Network**. You'll be directe to a new page. Please use the following details when setting up the custom network: ![img.png](applovin_config.png) ```shell Custom Network Name: Azerion Bluestack iOS Class name: BlueStackAppLovinAdapter.BlueStackMediationAdapter Android Class Name: com.azerion.bluestack.adapters.applovin.ApplovinAdapter ``` Next navigate to **Manage -> Ad Units**, and select the Ad Unit you would like to have Bluestack added. On the ad unit configuration page, scroll down to **Custom Networks** and click on Azerion Bluestack to show the configuration options. The Bluestack Application ID and the Placement ID's can be configured here. All ID's and CPM configuration will be provided by our publishing team. ![img.png](applovin_placements.png) ## Integrate MNGAds in your application project In the main build.gradle of your project, you must declare the Bluestack repository: ```groovy allprojects { repositories { google() mavenCentral() maven { url 'https://packagecloud.io/smartadserver/android/maven2' } } } ``` In the build.gradle of to your application module, you can now import the Bluestack Google Adapter SDK by declaring it in the dependencies section: ```groovy dependencies { implementation 'com.applovin:applovin-sdk:12.1.0' implementation 'com.azerion:bluestack-applovin-adapter:5.3.2.0' implementation 'com.azerion:bluestack-sdk-core:5.3.2' } ``` # Ad Formats ## Banner ### Supported MaxAdViewAdapterListener Callback - void onAdViewAdLoaded(View var1, @Nullable Bundle var2) The BlueStacks AppLovin adapter includes the `preferredHeightDP` when passing the banner adview in the banner load callback. ``` override fun bannerDidLoad(adView: View, preferredHeightDP: Int) { val bundle = Bundle() bundle.putInt(BlueStackKeys.BANNER_PREFERRED_HEIGHT, preferredHeightDP) maxAdViewAdapterListener?.onAdViewAdLoaded(adView, bundle) } ``` You can use the `preferredHeightDP` to resize your banner container. - void onAdViewAdLoadFailed(MaxAdapterError var1) - void onAdViewAdClicked() ### Passing Location data to BlueStack ``` adView = MaxAdView("YOUR_AD_UNIT_ID", this) adView.setLocalExtraParameter(BlueStackKeys.LOCATION_LONGITUDE, 1.2281) adView.setLocalExtraParameter(BlueStackKeys.LOCATION_LATITUDE, 0.2819) adView.setLocalExtraParameter(BlueStackKeys.LOCATION_CONSENT_FLAG, 3) ``` ## MREC ### Supported MaxAdViewAdapterListener Callback - void onAdViewAdLoaded(View var1, @Nullable Bundle var2) The BlueStacks AppLovin adapter includes the `preferredHeightDP` when passing the banner adview in the banner load callback. ``` override fun bannerDidLoad(adView: View, preferredHeightDP: Int) { val bundle = Bundle() bundle.putInt(BlueStackKeys.BANNER_PREFERRED_HEIGHT, preferredHeightDP) maxAdViewAdapterListener?.onAdViewAdLoaded(adView, bundle) } ``` You can use the `preferredHeightDP` to resize your banner container. - void onAdViewAdLoadFailed(MaxAdapterError var1) - void onAdViewAdClicked() ### Passing Location data to BlueStack ``` adView = MaxAdView("YOUR_AD_UNIT_ID", MaxAdFormat.MREC, this) adView.setLocalExtraParameter(BlueStackKeys.LOCATION_LONGITUDE, 1.2281) adView.setLocalExtraParameter(BlueStackKeys.LOCATION_LATITUDE, 0.2819) adView.setLocalExtraParameter(BlueStackKeys.LOCATION_CONSENT_FLAG, 3) ``` ## Interstitial ### Supported MaxInterstitialAdapterListener Callback - void onInterstitialAdLoaded() - void onInterstitialAdLoadFailed(MaxAdapterError var1) - void onInterstitialAdDisplayed() - void onInterstitialAdClicked() - void onInterstitialAdHidden() ### Passing Location data to BlueStack ``` interstitialAd = MaxInterstitialAd("YOUR_AD_UNIT_ID", this) interstitialAd.setLocalExtraParameter(BlueStackKeys.LOCATION_LONGITUDE, 1.2281) interstitialAd.setLocalExtraParameter(BlueStackKeys.LOCATION_LATITUDE, 0.2819) interstitialAd.setLocalExtraParameter(BlueStackKeys.LOCATION_CONSENT_FLAG, 3) ``` ## Rewarded ### Supported MaxRewardedAdapterListener Callback - void onRewardedAdLoaded() - void onRewardedAdLoadFailed(MaxAdapterError var1) - void onRewardedAdDisplayed() - void onRewardedAdClicked() - void onRewardedAdHidden() - void onUserRewarded(MaxReward var1) ### Passing Location data to BlueStack ``` rewardedAd = MaxRewardedAd.getInstance("YOUR_AD_UNIT_ID", this) rewardedAd.setLocalExtraParameter(BlueStackKeys.LOCATION_LONGITUDE, 1.2281) rewardedAd.setLocalExtraParameter(BlueStackKeys.LOCATION_LATITUDE, 0.2819) rewardedAd.setLocalExtraParameter(BlueStackKeys.LOCATION_CONSENT_FLAG, 3) ``` ## Meaning of LOCATION_CONSENT_FLAG - 0 = Not allow to send location. - 1 = When you managed location according to consent value. - 2 and 3 = Allow the SDK to managed location directly in accordance with the consent value use TCF v1 or TCF v2, see with the Azerion team as it depends on your implementation. [MNGAds]:index.md --- ## AppLovin - Release Notes ## [5.3.2.0] - Unreleased ### Updated - BlueStack core sdk dependency to 5.3.2 ### Changed - Package renamed to ```com.azerion.bluestack.adapters.applovin.ApplovinAdapter``` ## Version 4.3.0.0 ### Release date: Januari 12th, 2024 **Added** - Initial release based on BluestackSDk/Core 4.3.0 - Banner - MREC - Interstitial - Rewarded ```java showLineNumbers implementation 'com.azerion:bluestack-applovin-adapter:4.3.0.0' ``` --- ## Google Mobile Ads This guide shows you how to integrate our BlueStack mediation adapter of Google Mobile Ads SDK with your current Android app and set up additional request parameters. Release notes can be found [here](./04-gma-changelog.md) ## Supported ad formats - Banners - Interstitials - Native Ads - Rewarded Video ## Requirements - Android SDK 6.0 (API level 23) or later - Google Play services 24.9.0 or later ## Set up Google Ad Manager The following steps are needed to add us as a Demand partner in Ad Manager. These changes need to be set up in [Google Ad Manager](https://admanager.google.com/). ### Add a new Ad Network First you need to add us as an Ad Network in Google Ad Manager 1. Under **Admin**, go to **Companies** 2. Click the _New Company_ button and select **Ad Network** 3. For the name, you can use **BlueStack**, but you are free to enter what you want here 4. For Ad Network, please select **Improve Digital** 5. Don't forget to enable the _Medation_ toggle 6. Other fields can be ignored 7. Press _Save_ ![New ad network](./img/gam/step_1.png) ### Add Yield Groups Next we need to add some yield groups. The basic rule is that for each available format you add one yield group (so one for Banner, one for Interstitial, etc.) If you already have `Yield Groups` set up you can skip this step. 1. Under **Delivery**, go to **Yield Groups** 2. Click the _New Yield Group_ button 3. Insert any name you wish to use 4. Select the correct Ad Format 5. Inventory type should be set to Mobile App 6. For Banner, select at least one size that best fits 7. Please make sure your app's placements are targetted for this Yield Group ![New yield group](./img/gam/step_2.png) ### Add A Yield Partner and Define a custom event Now you have to add us as a `Yield` partner in the `Yield Group` you just created, or on a yield group you already have set before. 1. Open the `Yield Group` you want to add us as a partner 2. Scroll down on the page and click the _Add yield partner_ button 3. As yield partner, choose the company you added in [Add a new Ad Network] 4. Select integration type **Custom Event** 5. Select Platform **Android** 6. Select Status **Active** 7. Default CPM will be provided by your Azerion representative 8. For Label, use: **GADBlueStackMediationAdapter** 9. For Class Name, use: **com.azerion.GADBlueStackMediationAdapter** 10. As parameter, please enter the placement ID provided by your Azerion representative that matches the format you intend to use this yield group for 11. Repeat for each Yield group / format ![Add yield partner](./img/gam/step_3.png) ## Set up BlueStack Mediation adapter in Application ### SDK Integration #### Add BlueStack Core SDK: In the main build.gradle of your project, you must declare the Bluestack repository: ```groovy allprojects { repositories { google() mavenCentral() } } ``` In the build.gradle of to your application module, you can now import the Bluestack Google Adapter SDK by declaring it in the dependencies section: ```groovy showLineNumbers dependencies { implementation 'com.azerion:bluestack-sdk-core:6.0.6' implementation 'com.azerion:bluestack-google-adapter:6.0.6.0' } ``` #### Initialize the BlueStack Core SDK See the [Set Up Sdk Section] #### Bluestack Mediation See the [Mediation Partners] ### Set up Ad Formats #### Banner and Interstitial **No additional code is required for integration.** may now use MNG DFP Adapter to show [Interstitial Ads] and [Banner Ads] the same way it's described in the [DFP Documentation].The adapter code and the setup you did on your Google Ad Manager UI will allow MNG Ads to deliver ads. #### Native Ads 1) Assumes that your ad layout is in a file call **ad_unit_dfp.xml** for exemple in the res/layout folder. 2) The following code demonstrates how to build an AdLoader that can load native ads: ```kotlin showLineNumbers val adLoader = AdLoader.Builder(this, "YOUR PLACEMENT ID") .forNativeAd { nativeAd -> // This method sets the text, images, and the native ad, etc. into the ad view. val unifiedAdBinding = AdUnifiedBinding.inflate(layoutInflater) populateNativeAdView(nativeAd, unifiedAdBinding) } .withAdListener(object : AdListener() { override fun onAdFailedToLoad(errorCode: LoadAdError) { // Handle the failure by logging, altering the UI, etc. } }) .withNativeAdOptions(adOptions) .build() adLoader.loadAd(AdManagerAdRequest.Builder().build()) ``` 3) Once you have loaded an ad, all that remains is to display it to your users. ```kotlin showLineNumbers /** * Populates a [NativeAdView] object with data from a given [NativeAd]. * * @param nativeAd the object containing the ad's assets * @param unifiedAdBinding the binding object of the layout that has NativeAdView as the root view */ private fun populateNativeAdView(nativeAd: NativeAd, unifiedAdBinding: AdUnifiedBinding) { val nativeAdView = unifiedAdBinding.root // Set the media view. nativeAdView.mediaView = unifiedAdBinding.adMedia // Set other ad assets. nativeAdView.headlineView = unifiedAdBinding.adHeadline nativeAdView.bodyView = unifiedAdBinding.adBody nativeAdView.callToActionView = unifiedAdBinding.adCallToAction nativeAdView.iconView = unifiedAdBinding.adAppIcon nativeAdView.priceView = unifiedAdBinding.adPrice nativeAdView.starRatingView = unifiedAdBinding.adStars nativeAdView.storeView = unifiedAdBinding.adStore nativeAdView.advertiserView = unifiedAdBinding.adAdvertiser // The headline and media content are guaranteed to be in every NativeAd. unifiedAdBinding.adHeadline.text = nativeAd.headline nativeAd.mediaContent?.let { unifiedAdBinding.adMedia.setMediaContent(it) } // These assets aren't guaranteed to be in every NativeAd, so it's important to // check before trying to display them. if (nativeAd.body == null) { unifiedAdBinding.adBody.visibility = View.INVISIBLE } else { unifiedAdBinding.adBody.visibility = View.VISIBLE unifiedAdBinding.adBody.text = nativeAd.body } if (nativeAd.callToAction == null) { unifiedAdBinding.adCallToAction.visibility = View.INVISIBLE } else { unifiedAdBinding.adCallToAction.visibility = View.VISIBLE unifiedAdBinding.adCallToAction.text = nativeAd.callToAction } if (nativeAd.icon == null) { unifiedAdBinding.adAppIcon.visibility = View.GONE } else { unifiedAdBinding.adAppIcon.setImageDrawable(nativeAd.icon?.drawable) unifiedAdBinding.adAppIcon.visibility = View.VISIBLE } if (nativeAd.price == null) { unifiedAdBinding.adPrice.visibility = View.INVISIBLE } else { unifiedAdBinding.adPrice.visibility = View.VISIBLE unifiedAdBinding.adPrice.text = nativeAd.price } if (nativeAd.store == null) { unifiedAdBinding.adStore.visibility = View.INVISIBLE } else { unifiedAdBinding.adStore.visibility = View.VISIBLE unifiedAdBinding.adStore.text = nativeAd.store } if (nativeAd.starRating == null) { unifiedAdBinding.adStars.visibility = View.INVISIBLE } else { unifiedAdBinding.adStars.rating = nativeAd.starRating!!.toFloat() unifiedAdBinding.adStars.visibility = View.VISIBLE } if (nativeAd.advertiser == null) { unifiedAdBinding.adAdvertiser.visibility = View.INVISIBLE } else { unifiedAdBinding.adAdvertiser.text = nativeAd.advertiser unifiedAdBinding.adAdvertiser.visibility = View.VISIBLE } // This method tells the Google Mobile Ads SDK that you have finished populating your // native ad view with this native ad. nativeAdView.setNativeAd(nativeAd) } ``` #### Rewarded video Ads 1) The following code demonstrates how to load a rewarded ad: ```java showLineNumbers AdManagerAdRequest.Builder adRequestBuilder = new AdManagerAdRequest.Builder(); adRequestBuilder.addNetworkExtrasBundle( GADBlueStackMediationAdapter.class, getExtrasData() ); RewardedAd.load( context, DFP_REWARDED_AD_UNIT, adRequestBuilder.build(), new RewardedAdLoadCallback() { @Override public void onAdLoaded(@NonNull RewardedAd rewarded) { rewarded.setFullScreenContentCallback(new FullScreenContentCallback() { @Override public void onAdFailedToShowFullScreenContent(@NonNull AdError adError) { // Handle the failure by logging, altering the UI... } }); } @Override public void onAdFailedToLoad(@NonNull LoadAdError error) { // Handle the failure by logging, altering the UI... } } ); ``` 2) Once you have loaded an ad, all that remains is to display it to your users. ```java showLineNumbers private void displayRewardedAd() { mRewardedAd.show(activity, new OnUserEarnedRewardListener() { @Override public void onUserEarnedReward(@NonNull RewardItem rewardItem) { // TODO: Handle the reward (e.g., give the user coins or unlock content) } }); } ``` ### Custom targeting / Keywords If you need to send your custom key-value pairs. You can specify key-value-targeting and keywords information in the ad request as follows: ```java showLineNumbers AdManagerAdRequest request = new AdManagerAdRequest.Builder() .addKeyword("Keyword") .addCustomTargeting("key1", "value1") .addCustomTargeting("key2", "value2") .build(); ``` and you must send your **custom key-value pairs** also as follows: 1- Create a bundle of the extras : ```java showLineNumbers Bundle extras = new Bundle(); extras.putString("customTargeting","key1=value1;key2=value2"); extras.putString("keywords","key1=value1;key2=value2"); ``` 2- Add the extras to the addNetworkExtrasBundle() method as follows: ```java showLineNumbers AdManagerAdRequest adRequest = new AdManagerAdRequest.Builder(); adRequest.addNetworkExtrasBundle(GADBlueStackMediationAdapter.class,extras) .build(); ``` The GADBlueStackMediationAdapter value corresponds to custom event adapter class name. [Banner Ads]:https://developers.google.com/ad-manager/mobile-ads-sdk/android/banner [Interstitial Ads]:https://developers.google.com/ad-manager/mobile-ads-sdk/android/interstitial [Set Up Sdk Section]:/index.md [Mediation Partners]:../1-primairy/supported-networks.md [DFP Documentation]:https://developers.google.com/ad-manager/mobile-ads-sdk/android/quick-start [Google Ad Manager UI]:https://admanager.google.com/ [MNGAds]:/index.md [Step 1]:#1-create-a-yield-groups [Step 2]:#2-add-a-yield-group [Demo]:https://bitbucket.org/mngcorp/mngads-demo-android/src/master/MngAdsDemo/app/src/main/java/com/example/mngadsdemo/fragment/DFPFragment.kt --- ## GMA - Release Notes ## [6.0.6.0] - 2026-07-03 ### Changed - Updated BlueStack core SDK dependency to `6.0.6` - Adopted core SDK API changes ## [5.4.1.0] - 2026-02-10 ### Changed - BlueStack core SDK dependency to 5.4.1 - Migrated Google Mobile Ads SDK from version 23.6.0 to 24.9.0 - Increased minimum supported Android SDK version from 19 to 23 - Updated Kotlin version to 2.1.0 (required by Google Mobile Ads SDK 24.x) - Rewarded ad callback to use new AdMob API `onUserEarnedReward` ## [5.3.5.0] - 2026-01-09 ### Fixed - Impression and viewability tracking issue for NativeAd. ## [5.3.2.0] - 2025-11-07 ### Updated - BlueStack core sdk dependency to 5.3.2 - Refactored NativeAd implementation. ## [5.1.4.0] - 2025-04-24 ### Updated - BlueStack core sdk dependency to 5.1.4 - Semantic version scheme to core-sdk-version.x ## [5.0.2.0] - 2024-12-31 ### Updated - BlueStack core sdk dependency to 5.0.2 ### Changed - Semantic version scheme to core-sdk-version.x ## [2.5.1] - 2023-10-25 **Updated** - BlueStack core dependency to 4.3.0 ## [2.5.0] - 2023-02-23 ### Added - Added the onUserRewardEarned(MAdvertiseVideoReward) callback to **BlueStackRewardedAdRender.class**. ### Removed - Removed the onRewardedVideoCompleted(MAdvertiseVideoReward) callback from **BlueStackRewardedAdRender.class**. ### Changed - Changed the groupId of the BlueStack DFP Adapter mediation to: **`com.azerion:bluestack-gam-adapter`** ```java showLineNumbers implementation 'com.azerion:bluestack-gam-adapter:2.5.0' ``` - Updated the version of BlueStack Mediation SDK to 4.2.0 - Updated the version of Google Ads SDK to 21.1.0 ## [2.4.0] - 2022-02-03 ### New features : - Added implementation of interstitialDidShown callback of MNGInterstitialListener in **MadvertiseCustomEventInterstitial.class**. ### Updated SDKs : - Use new version of BlueStack Mediation SDK (Version : 4.0.3) - Use new version of Google Ads SDK (Version : 20.4.0) ```java showLineNumbers implementation 'com.madvertise:bluestack-gam-adapter:2.4.0' ``` ## [2.3.0] - 2020-06-30 ### New features : - Added support of Google SDK v20.0.0 ### Updated SDKs : - Use new version of BlueStack Mediation SDK (Version : 3.6.2) - Use new version of Google Ads SDK (Version : 20.2.0) ## [2.2.2] - 2020-09-18 **SDK is now delivered through Maven/Bitbucket repository** ```groovy allprojects { repositories { google() jcenter() // For All Bluestack SDKs (Mediation, CMP, Location, Adapter) maven { credentials { username "madvertise-maven" password "GpdGZ9GE9SK7ByWdM987" } url "https://api.bitbucket.org/2.0/repositories/mngcorp/deploy-maven-bluestack/src/master" authentication { basic(BasicAuthentication) } } } } } ``` ## [2.2.0] - 2020-08-12 ### Update Location method Replace the following code from : ```groovy PublisherAdRequest request = new PublisherAdRequest.Builder() .setLocation(location) .build(); ``` With the following code : 1- Create a bundle of the extras : ```java showLineNumbers Bundle extras = new Bundle(); extras.putString("consentFlag","CONSENT_FLAG"); ``` The CONSENT_FLAG value (corresponds to a string : "0","1","2" or "3"). - "0" = Not allow to send location. - "1" = When you managed location according to consent value. - "2" or "3" = Allow the SDK to managed location directly in accordance with the consent value use TCF v1 or TCF v2, see with the Azerion team as it depends on your implementation. 2- Add the extras to the addCustomEventExtrasBundle() method as follows: ```java showLineNumbers PublisherAdRequest adRequest = new PublisherAdRequest.Builder() .addCustomEventExtrasBundle("Madvertise_Custom_Event",extras) .build(); ``` The Madvertise_Custom_Event value corresponds to custom event adapter class name : - **MadvertiseCustomEventBanner.class** for Banner Ads - **MadvertiseCustomEventInterstitial.class** for Interstitial Ads - **MadvertiseCustomEventNativead.class** for Native Ads ### Update SDKs In your app's build.gradle, don't forget to update your dependencies as following: ```java showLineNumbers //MNG Ads SDK implementation(name: 'mngads-sdk-3.2.0', ext: 'aar') //Google Ads SDK implementation 'com.google.android.gms:play-services-ads:19.2.0' ``` --- ## Unity LevelPlay This guide shows you how to integrate our BlueStack mediation adapter of Unity LevelPlay with your current Android app and set up additional request parameters. Release notes can be found [here](./06-unity-levelplay-changelog.md) ## Supported Ad Formats - Banner - Rewarded - Interstitial ## Requirements - Android SDK 4.4 (API level 19) or later - Google Play Services 21.1.0 or later - Iron Source Mediation SDK 8.6.0 or higher ## Unity LevelPlay Configuration When in the Unity LevelPlay dashboard, under your app in the left panel navigate to **Setup -> Networks**, in the right panel at the bottom of the page you have the option to **Add custom network**. ![img.png](levelplay_ad_configuration_step_1.png) You'll be directed to a new page. Please enter **15c080481** in the **Network Key** input field and click **Confirm Key**. After confirmation, it will show the network name **Improve Digital**. Then click **Save**. ![img.png](levelplay_ad_configuration_step_2.png) ```shell Custom Network Key: 15c080481 Custom Network Name: Improve Digital ``` **Congratulations!!!** You have added our network successfully. Next, navigate to **Setup -> Instances** in the left panel, and click **Improve Digital** in the right panel. ![img.png](levelplay_ad_configuration_step_3.png) This will navigate you to a new page where you will add new instances for different types of ads. ![img.png](levelplay_ad_configuration_step_4.png) Click on the button ***+ Add Instance* at the bottom in the right panel, select the type of ad you want to add. ![img.png](levelplay_ad_configuration_step_5.png) Enter **Instance Name**, **placementId**, **Rate**, and select target **Mediation Groups**. Also, add your **appId** at the top in the right panel and click **Save**. **Note:** The **placementId**s will be provided by the **Azerion team**. And **Rate** is used for **IronSource** reporting only; it will not reflect the actual money. Rates can be aligned with the **Azerion team** as well. ![img.png](levelplay_ad_configuration_step_6.png) ## Ad Formats ### Banner #### Supported LevelPlayBannerAdViewListener Callback ```java // Indicates that a banner ad was loaded successfully @Override public void onAdLoaded(LevelPlayAdInfo adInfo) { // Handle banner ad loaded } // The banner ad failed to load. Use LevelPlayAdError ErrorTypes (No Fill / Other) @Override public void onAdLoadFailed(LevelPlayAdError error) { // Handle banner ad load failure } // Indicates an ad was clicked @Override public void onAdClicked(LevelPlayAdInfo adInfo) { // Handle banner ad click } ``` ### Initialization and Load Banner Ad ```java LevelPlayBannerAdView bannerAd = new LevelPlayBannerAdView(this, "YOUR_BANNER_AD_UNIT_ID"); // 1. Recommended - Adaptive ad size that adjusts to the screen width LevelPlayAdSize adSize = LevelPlayAdSize.createAdaptiveAdSize(this); // 2. Adaptive ad size using fixed width ad size // LevelPlayAdSize adSize = LevelPlayAdSize.createAdaptiveAdSize(this, 400); // 3. Specific banner size - BANNER, LARGE, MEDIUM_RECTANGLE // LevelPlayAdSize adSize = LevelPlayAdSize.BANNER; if (adSize != null) { // set the banner listener // bannerAd?.setBannerListener(new YourBannerAdListener(this)) // add LevelPlayBannerAdView to your container bannerAd.setAdSize(adSize); bannerAd.loadAd(); } else { // Handle banner ad creation failure } ``` ### Interstitial #### Supported ISInterstitialAdDelegate Callback ```java // Indicates that the interstitial ad was loaded successfully @Override public void onAdLoaded(LevelPlayAdInfo adInfo) { // Handle interstitial ad loaded } // The interstitial ad failed to load. Use IronSource ErrorTypes (No Fill / Other) @Override public void onAdLoadFailed(LevelPlayAdError error) { // Handle interstitial ad load failure } // Indicates the ad was displayed successfully to the user. This indicates an impression. @Override public void onAdDisplayed(LevelPlayAdInfo adInfo) { // Handle interstitial ad opened } // User closed the interstitial ad @Override public void onAdClosed(LevelPlayAdInfo adInfo) { // Handle interstitial ad closed } // The ad could not be displayed @Override public void onAdDisplayFailed(LevelPlayAdError error, LevelPlayAdInfo adInfo) { // Handle interstitial ad failed to show } // Indicates the ad was clicked @Override public void onAdClicked(LevelPlayAdInfo adInfo) { // Handle interstitial ad click } ``` ### Initialization, Load, and Show Interstitial Ad ```java LevelPlayInterstitialAd interstitialAd = new LevelPlayInterstitialAd("YOUR_INTERSTITIAL_AD_UNIT_ID"); // interstitialAd.setListener(new YourInterstitialAdListener(this)); interstitialAd.loadAd(); if (interstitialAd.isAdReady()) { interstitialAd.showAd(activity); } else { // Handle ad not ready scenario } ``` ### Rewarded #### Supported ISRewardedVideoAdDelegate Callback ```java // Indicates that rewarded video ad was loaded successfully @Override public void onAdLoaded(LevelPlayAdInfo adInfo) { // Handle rewarded video ad loaded } // The rewarded video ad failed to load. Use IronSource ErrorTypes (No Fill / Other) @Override public void onAdLoadFailed(LevelPlayAdError error) { // Handle rewarded video ad load failure } // The rewarded video ad was displayed successfully to the user. This indicates an impression. @Override public void onAdOpened(AdInfo adInfo) { // Handle rewarded video ad opened } // User closed the rewarded video ad @Override public void onAdClosed(AdInfo adInfo) { // Handle rewarded video ad closed } // The ad could not be displayed @Override public void onAdShowFailed(IronSourceError ironSourceError, AdInfo adInfo) { // Handle rewarded video ad failed to show } // User clicked the rewarded video ad @Override public void onAdClicked(Placement placement, AdInfo adInfo) { // Handle rewarded video ad click } // User received a reward after watching the ad @Override public void onAdRewarded(Placement placement, AdInfo adInfo) { // Handle user earned reward } ``` ### Initialization, Load, and Show Rewarded Ad ```java LevelPlayRewardedAd rewardedAd = new LevelPlayRewardedAd("YOUR_REWARDED_VIDEO_AD_UNIT_ID"); // rewardedAd.setListener(new YourRewardedAdListener(this)); rewardedAd.loadAd(); if (rewardedAd.isAdReady()) { rewardedAd.showAd(activity); } ``` ### Set Privacy Settings Change the privacy settings accordingly. By default, both are **false**. Please note that you have to set them before initializing the **IronSource/LevelPlay SDK**. ```java BlueStackPrivacySettings.setIsAgeRestrictedUser(true, context); BlueStackPrivacySettings.setIsUserOptOut(true, context); ``` ### Error Handling When handling ad operations, consider the following: - Check if the ad is ready before showing it. - Implement callbacks to handle errors during ad loading and displaying. ```java if (interstitialAd.isAdReady()) { interstitialAd.showAd(); } else { // Handle ad not ready scenario } ``` --- ## Unity LevelPlay - Release Notes ## [5.3.0.0] - 2025-10-15 ### Updated - BlueStack core SDK to version 5.3.0 - Unity Levelplay to version 9.0.0 ## [5.1.2.0] - 2025-03-04 ### Added - Initial release based on BlueStack sdk core 5.1.2 and IronSource 8.6.0 - Banner - Rewarded - Interstitial ```java showLineNumbers implementation 'com.azerion:bluestack-levelplay-mediation:5.1.2.0' ``` --- ## F.A.Q This document answers the following frequently asked questions: ## If your app uses Proguard, you must edit your Proguard settings to avoid stripping Google Play out of your app If your app uses Proguard, you must edit your Proguard settings to avoid stripping Google Play out of your app. Edit your project’s proguard-project.txt file to add the following: [https://bitbucket.org/mngcorp/mngads-demo-android/src/HEAD/MngAdsDemo/app/proguard-rules.pro?at=master&fileviewer=file-view-default] (https://bitbucket.org/mngcorp/mngads-demo-android/src/HEAD/MngAdsDemo/app/proguard-rules.pro?at=master&fileviewer=file-view-default) ## Interstitial did load callback without display **You must use an instance per activity.** Some AdNetworks show their interstitials on top of an Activity layout and then disappear. Therefore, you must instantiate **once InterstitialAd by activity** (used for display the interstitial). If you want to build an interstitials manager for your app to handle all interstitials requests, you should make sure to instantiate InterstitialAd with the activity that will make the request. ## android:noHistory http://developer.android.com/guide/topics/manifest/activity-element.html#nohist android:noHistory Whether or not the activity should be removed from the activity stack and finished (its finish() method called) when the user navigates away from it and it's no longer visible on screen . Therefore, do not use ```java showLineNumbers android:noHistory = "true" ``` --- ## Error Handling In case where an ad fails to load or display, BlueStack provides the following exceptions in error callback. | Exception | Error Code | Message | Meaning | |-----------|------------|---------|---------| | WrongPlacementIdError | WRONG_PLACEMENT_ERROR = 0 | Wrong placement | Invalid placement ID configured | | InternetError | NO_INTERNET_ERROR = 1 | No Internet | No internet connection available | | SDKUninitializedError | SDK_UNINITIALIZED_ERROR = 2 | BlueStack is not initialized | SDK must be initialized first | | RequestCappedError | CAPPED_REQUEST_ERROR = 3 | Your request has been capped | Request limit reached. Check placement capping settings | | LockedPlacementError | LOCKED_PLACEMENT_ERROR = 4 | This placement is locked by an other factory | Another factory is loading an ad for this placement | | BusyFactoryError | BUSY_FACTORY_ERROR = 5 | Your factory is busy | Factory is processing another request | | NoAdError | NO_AD_ERROR = 7 | No Ad found | No ad available to deliver | | InterstitialCoolDownError | INTERSTITIAL_COOLDOWN_ERROR = 8 | Interstitial cooldown active | Interstitial ad is in cooldown period. Wait before showing another | | AlreadyShownInterstitialError | INTERSTITIAL_ALREADY_SHOWN_ERROR = 9 | Other Interstitial is shown | Only one interstitial can be shown at a time | | TimeOutError | TIME_OUT_ERROR = 10 | no ad to deliver before time out | Ad request timed out before response | | AdapterNotFoundError | ADAPTER_NOT_FOUND_ERROR = 11 | No adapter found | Mediation adapter not found. See [Mediation Partners](../20-mediation/1-primairy/supported-networks.md) | | BlockedByGDPRError | BLOCKED_BY_GDPR = 12 | Request blocked by GDPR | Ad request blocked due to GDPR consent requirements | | AdExpiredError | AD_EXPIRED = 13 | Ad has expired | Ad exceeded display time limit (typically for interstitials) | | NoAdapterFoundForPlacementIdError | NO_ADAPTER_FOUND_FOR_PLACEMENT_ID = 14 | No adapter found for placementId | No mediation adapter configured for the specific placement ID | **Handle Error :** To determine which exception was triggered, cast the exception to AdError in the fail callback and use getErrorCode() to retrieve the error code. You can also get the exception message by calling getMessage(). In the example below, we use onAdFailToLoad, but this logic can be applied to any fail callback, such as onAdFailToRefresh, onAdFailedToDisplay, infeedDidFail and nativeObjectDidFail etc. ```java showLineNumbers @Override public void onAdFailedToLoad(Exception e) { AdError adError = (AdError)e; switch (adError.getErrorCode()) { case AdError.BUSY_FACTORY_ERROR : case AdError.INTERSTITIAL_ALREADY_SHOWN_ERROR : . . . } Log.e(TAG, "Banner did fail : " + adError.getMessage()+" error code "+adError.getErrorCode()); } ``` ```kotlin showLineNumbers override fun onAdFailedToLoad(e: Exception) { val adError = e as AdError when (adError.errorCode){ AdError.BUSY_FACTORY_ERROR -> {...} AdError.INTERSTITIAL_ALREADY_SHOWN_ERROR -> {...} . . . } Log.e(TAG, "Banner did fail : ${adError.message} error code ${adError.errorCode}") } ``` --- ## Targeting Audiences In order to take advantage of our targeting campaign, you must pass an instance of **RequestOptions object** in ad load call. ```java showLineNumbers Location myLocation = new Location("I"); myLocation.setLatitude(35.757866); myLocation.setLongitude(10.810547); RequestOptions requestOptions = new RequestOptions( context = context, age = 25, consentFlag = CONSENT_FLAG, location = location, language = "en", keyword = "brand=myBrand;category=sport", contentUrl = "put your content url here", gender = MNGGender.MNGGenderMale ) bannerView.load(requestOptions); interstitialAd.load(requestOptions); rewardedAd.load(requestOptions); // Native ad still use the old MNGPreference object but in future it will be changed to use RequestOptions mngAdsNativeAdsFactory.loadNative(requestOptions.toMNGPreference()); ``` ```kotlin showLineNumbers val myLocation = Location("I") myLocation.setLatitude(35.757866) myLocation.setLongitude(10.810547) val requestOptions = RequestOptions( context = context, age = 25, consentFlag = CONSENT_FLAG, location = location, language = "en", keyword = "brand=myBrand;category=sport", contentUrl = "put your content url here", gender = MNGGender.MNGGenderMale ) bannerView.load(requestOptions) interstitialAd.load(requestOptions) rewardedAd.load(requestOptions) // Native ad still use the old MNGPreference object but in future it be will changed to use RequestOptions mngAdsNativeAdsFactory.loadNative(requestOptions.toMNGPreference()) ``` ## Location Targeting **Our adserver and certain ad can use your user’s location to send more targeted ads by passing Latitude and Longitude.** ```java showLineNumbers Location myLocation = new Location("I"); myLocation.setLatitude(35.757866); myLocation.setLongitude(10.810547); RequestOptions requestOptions = new RequestOptions( ... consentFlag = CONSENT_FLAG, location = location, ... ) ``` ```kotlin showLineNumbers val myLocation = Location("I") myLocation.setLatitude(35.757866) myLocation.setLongitude(10.810547) val requestOptions = RequestOptions( ... consentFlag = CONSENT_FLAG, location = location, ... ) ``` **Note :** - This [device location] can help you to get device location. - Do not serialize Location object (like transforming it into a string using gson library), this may lead to a fatal runtime error when that instance is reused. - The setLocation method takes the following parameters: - the Location instance. - the CONSENT_FLAG value (corresponds to a int : 0,1,2 or 3). - 0 = Not allow to send location. - 1 = When you managed location according to consent value. - 2 and 3 = Allow the SDK to managed location directly in accordance with the consent value use TCF v1 or TCF v2, see with the Azerion team as it depends on your implementation. ## Keyword Targeting Keywords allow you to target certain ad requests with user data. Keywords are useless for targeting, if you can provide **dynamic values** per users/devices. To add keyword targeting, you will need to pass these keywords up through the application (They should be formatted as key/value pairs) : - Characters per key: 20 - Characters per value: 40 ```java showLineNumbers RequestOptions requestOptions = new RequestOptions( ... keyword = "brand=myBrand;category=sport", ... ) ``` ```kotlin showLineNumbers val requestOptions = RequestOptions( ... keyword = "brand=myBrand;category=sport", ... ) ``` ## User Demographic Targeting When people are signed in on your app, can you please share **Demographic informations** from their settings with following code : ```java showLineNumbers Location myLocation = new Location("I"); myLocation.setLatitude(35.757866); myLocation.setLongitude(10.810547); RequestOptions requestOptions = new RequestOptions( ... age = 25, gender = MNGGender.MNGGenderMale ... ) ``` ```kotlin showLineNumbers val myLocation = Location("I") myLocation.setLatitude(35.757866) myLocation.setLongitude(10.810547) val requestOptions = RequestOptions( ... age = 25, gender = MNGGender.MNGGenderMale ... ) ``` ## BlueStack Audience Targeting Audiences can be defined by date of birth, gender, locations, Apple's Advertising Identifier (IDFA), Android's advertising ID or by a combination of rules used to identify users who took specific actions on your app (device, carrier, ...). ## Content mapping for apps **(Since v2.4)** [https://support.google.com/adxseller/answer/6270563](https://support.google.com/adxseller/answer/6270563) ```java showLineNumbers RequestOptions requestOptions = new RequestOptions( ... contentUrl = "put your content url here", ... ) ``` ```kotlin showLineNumbers val requestOptions = RequestOptions( ... contentUrl = "put your content url here", ... ) ``` [device location]:https://developer.android.com/training/location/retrieve-current.html --- ## Debugging Debugging the BlueStack SDK involves configuring and testing to ensure a smooth development experience. Enabling debug mode will provide detailed logs for the BlueStack SDK and all of it's adapters for easy troubleshooting. For deeper insights, use tools like logcat for real-time debugging. Check network configurations to address connectivity issues and monitor SDK events for performance metrics. For specific issues, [refer to error codes provided in the SDK documentation](./02-error-handling.md). ## Enable Debug Mode To enable debug, you must first register your device as a Test Device in the BlueStack Console. Select _Inventory \> Your App \> Test Devices_ and press the **New +** button to add a device. ![img.png](images/add_test_device.png) Enter any name for the device that your adding, and it's corresponding IDFA (iOS) or GAID (Android). The device is now registered as a test device and debug mode is automatically enabled . ![img.png](images/device_added.png) It might take a few minutes before the changes are fully propagated to the SDK, alternatively you can also enable the debug mode programmatically: ```java showLineNumbers MobileAds.INSTANCE.setDebugModeEnabled(true); ``` ```kotlin showLineNumbers MobileAds.setDebugModeEnabled(true) ``` ## Open Debug Screen Once Debugging has been enabled, you can simply bring up the debug menu by checking your device from left to right. It will present you with a set of buttons, one of which will allow you to clear any local caching from the SDK (config, ads, etc) | Network Infomation | Debug placements | |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | On the Mediation settings page you'll be able to see which adapaters have been properly actived. If they indicated a cross they either have been misconfigured, not installed, or perhaps another issue that might have traces of debug information in the debug logs. | The Debug SDK also allows you to inspect the last 3 ad requests made. You'll be able to see the response status for all the networks that have been activated on the placement. The passed Request Options will also be visible for inspection. | | | | ## Generating APK with Support for Network Proxying The Azerion team may request a debuggable APK that can proxy it's traffic by using Android' s [Network Security Config](https://developer.android.com/privacy-and-security/security-config). In order to generate such an APK you need to do the following: 1. Make sure the following XML file is available: `res/xml/network_security_config.xml` with this content: ```xml ``` 2. Add the following `android` attribute configuration to the `application` element your `AndroidManifest.xml`: ```xml ... ``` 3. Generate an APK with the [debug configuration](https://developer.android.com/studio/run/rundebugconfig) :::warning Please make sure that builds such as the one above never end up in a production environment, **these configurations are for Testing purposes only!** ::: ## Test Placements A quick way to enable testing is to use the predefined test placements below. They will always deliver ads for a specific format and they will not be reported towards your account. First initialize your SDK instance with the test App id `3167505`: ```java showLineNumbers import com.azerion.bluestack.MobileAds; import com.azerion.bluestack.initialization.InitializationListener; import com.azerion.bluestack.initialization.SDKInitializationStatus; class MainActivity extends AppCompatActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... MobileAds.INSTANCE.initialize(this, "3167505", initializationStatus -> { initializationStatus.getMediationAdapterStatusMap().forEach((adNetworkName, adapterStatus) -> Log.d(TAG, "name: " + adapterStatus.getName() + "," + "state: " + adapterStatus.getState() + "," + "description: " + adapterStatus.getDescription())); }); ... } } ``` ```kotlin showLineNumbers import com.azerion.bluestack.MobileAds import com.azerion.bluestack.initialization.InitializationListener import com.azerion.bluestack.initialization.SDKInitializationStatus class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate() ... MobileAds.initialize(this, "3167505", object : InitializationListener { override fun onInitialized(status: SDKInitializationStatus) { status.mediationAdapterStatusMap.forEach {(adNetworkName, adapterStatus) -> Log.d(TAG, "name: ${adapterStatus.name}, state: ${adapterStatus.state}, description: ${adapterStatus.description}") } } }) ... } } ``` | Ad format | Test Placement ID | |----------------|-----------------------| | App Open | /3167505/appopen | | Banner | /3167505/banner | | MREC | /3167505/mrec | | Interstitial | /3167505/interstitial | | Rewarded Video | /3167505/rewarded | | Native | /3167505/native | :::warning Please make sure you replace the **App ID** AND **Placement ID's** above with the ones provided by your Azerion representative before going live ::: --- ## Get Started BlueStack Ads SDK provides functionalities for monetizing your mobile application: from premium sales with rich media, video and innovative formats, it facilitates inserting native mobile ads as well all standard display formats. :::info Looking for a working reference? Our public demo app on GitHub — [azerion/azerion-inapp-demo-android](https://github.com/azerion/azerion-inapp-demo-android) — showcases the BlueStack Android SDK across multiple ad formats. ::: ## Prerequisites Before You Start, BlueStack Ads requires minimum : ## ExoPlayer Compatibility BlueStack SDK uses AndroidX Media3 ExoPlayer for video ad playback. If your app also uses ExoPlayer, ensure compatibility: - BlueStack SDK v6.0.0+ uses `androidx.media3:media3-exoplayer` version - If you use ExoPlayer in your app, use a compatible version to avoid runtime conflicts - For conflicts, align your ExoPlayer version with the SDK's Media3 version or use dependency resolution strategies in Gradle :::tip If you encounter `AbstractMethodError` or similar runtime errors related to ExoPlayer, verify that all ExoPlayer/Media3 dependencies are aligned to compatible versions. ::: ## Configure your app ### Installation using Gradle **1) In the settings.gradle of your project, you must declare there repositories :** ```groovy showLineNumbers dependencyResolutionManagement { ... repositories { ... google() mavenCentral() ... } ... } ``` **2) Add the following dependency to your app's build.gradle, and make sure the latest SDK is used:** **Mandatory :** - Bluestack Mediation SDK ### Update AndroidManifest.xml Add the following permissions to your AndroidManifest.xml file inside the manifest tag but outside the \ tag, if not done already: ```java showLineNumbers ``` ## Add mediation partners Mediation adapters are added as native dependencies in your app-level `build.gradle`. We recommend including all adapters by default so the SDK can serve from every available demand source — omit an adapter only if you have a specific reason not to ship it. :::info **Recommended default:** include every mediation adapter. The snippet below adds the full bundle, pinned to exact versions that are kept current automatically. ::: For Google Mobile Ads, also add your AdMob App ID to `AndroidManifest.xml`: ```groovy showLineNumbers title="AndroidManifest.xml" ``` For per-partner setup, version-specific adapters, ProGuard rules, and the compatibility matrix, see [Supported Networks](./20-mediation/1-primairy/supported-networks.md). ## Initialize the BlueStack Ads SDK Before loading ads, initialize the BlueStack Ads SDK by calling `MobileAds.initialize()`. Once the SDK completes initialization, it will provide an InitializationStatus instance through the initialization callback. This needs to be done only once. ```java showLineNumbers import com.azerion.bluestack.MobileAds; import com.azerion.bluestack.initialization.InitializationListener; import com.azerion.bluestack.initialization.SDKInitializationStatus; class MainActivity extends AppCompatActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... MobileAds.INSTANCE.initialize(this, "YOUR_APP_ID", initializationStatus -> { initializationStatus.getMediationAdapterStatusMap().forEach((adNetworkName, adapterStatus) -> Log.d(TAG, "name: " + adapterStatus.getName() + "," + "state: " + adapterStatus.getState() + "," + "description: " + adapterStatus.getDescription())); }); ... } } ``` ```kotlin showLineNumbers import com.azerion.bluestack.MobileAds import com.azerion.bluestack.initialization.InitializationListener import com.azerion.bluestack.initialization.SDKInitializationStatus class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate() ... MobileAds.initialize(this, "YOUR_APP_ID", object : InitializationListener { override fun onInitialized(status: SDKInitializationStatus) { status.mediationAdapterStatusMap.forEach {(adNetworkName, adapterStatus) -> Log.d(TAG, "name: ${adapterStatus.name}, state: ${adapterStatus.state}, description: ${adapterStatus.description}") } } }) ... } } ``` **Note:** If the BlueStack SDK fails to initialize, it will return an SDKInitializationStatus object containing an empty mediation adapter status map. --- ## Privacy and Compliance The BlueStack SDK is designed to help publishers meet global privacy regulations, including the General Data Protection Regulation (GDPR) and industry standards like the IAB Transparency and Consent Framework (TCF v2). This ensures that your app can operate responsibly in a privacy-conscious environment while optimizing ad delivery and maintaining user trust. --- ## General Data Protection Regulation (GDPR) The GDPR is a comprehensive data protection law enacted in the European Union. It establishes rules for collecting, processing, and storing personal data while prioritizing user rights such as data access, correction, and erasure. Under GDPR, publishers are required to: 1. Obtain informed consent before processing user data. 2. Provide users with clear information about how their data will be used. 3. Offer users the ability to withdraw consent at any time. --- ## Transparency and Consent Framework (TCF v2) TCF v2, developed by the Interactive Advertising Bureau (IAB), is an industry-standard framework that streamlines how consent and data preferences are shared across the digital advertising ecosystem. It ensures a standardized approach to managing consent signals and enables seamless communication between publishers, advertisers, and ad tech partners. ### Key Benefits of TCF v2 - **Standardized Consent Management**: Ensures all parties in the ad chain are informed about user preferences. - **Greater Transparency**: Provides users with detailed information about data processing purposes, vendors, and partners. - **Flexible Compliance**: Supports different legal bases for processing data, including legitimate interest and consent. The BlueStack SDK is fully compatible with TCF v2 allowing publishers to: - Integrate any consent management platform (CMP) that supports TCF v2. - Manage consent signals efficiently across the ad ecosystem. --- ## Prohibition on Collecting Children's Data, Using the Services for Children, or Targeting Apps Exclusively to Children SDK version 5.1.0 introduces a new privacy setting for age-restricted users. You are required to determine whether a user qualifies as a 'child' under applicable laws, such as COPPA, GDPR, and other age-related regulations, as well as the policies of the Apple App Store and Google Play Store. If a user is classified as a 'child,' you must set the age-restricted user flag accordingly before initializing or using the BlueStack SDK. ```java BlueStackPrivacySettings.setIsAgeRestrictedUser(true, context) ``` ```kotlin BlueStackPrivacySettings.setIsAgeRestrictedUser(true, context) ``` If the user does not qualify as a 'child' under applicable laws, ensure the age-restricted user flag is set appropriately before initializing or using the BlueStack SDK. ```java BlueStackPrivacySettings.setIsAgeRestrictedUser(false, context) ``` ```kotlin BlueStackPrivacySettings.setIsAgeRestrictedUser(false, context) ``` --- ## Opt out of displaying user-based advertising You have the option to opt out of displaying ads that are tailored to users’ interests, demographics, or past interactions with advertisers. ```java BlueStackPrivacySettings.setIsUserOptOut(true, context) ``` ```kotlin BlueStackPrivacySettings.setIsUserOptOut(true, context) ``` --- ## Features for Privacy Compliance The BlueStack SDK offers several privacy-centric features to help publishers comply with GDPR, TCF v2, and other regulations: - **Consent Management Integration**: Easily integrate with leading CMPs for seamless consent handling. - **Data Minimization**: Collect only essential data needed for app functionality and ad performance. - **Automated Signals**: Generate and propagate consent signals to ad partners in real time. By implementing these features, the BlueStack SDK empowers publishers to protect user privacy while maintaining effective ad operations. --- ## Best Practices for Publishers To maximize the benefits of the BlueStack SDK and ensure compliance: 1. Use a trusted CMP to manage consent collection and communication. 2. Regularly review your privacy policy to ensure alignment with evolving regulations. 3. Inform users about how their data is used and their rights under GDPR. 4. Test the SDK’s consent mechanisms to verify proper functioning across all scenarios. --- The BlueStack SDK is your trusted solution for navigating the complexities of data privacy while fostering transparency and trust with your users. For more details, refer to our technical documentation or contact our support team. ` --- ## Release Notes ## [6.0.7] - 2026-08-12 ### Changed - Relaxed the Media3 ExoPlayer dependency requirement to support versions `1.9.2–1.11.0`, preventing dependency conflicts when apps use a compatible Media3 version within this range. ## [6.0.6] - 2026-07-02 ### Added - Added debug logs to track ad lifecycle events, making it easier to trace and troubleshoot ad behavior during integration. ### Fixed - Resolved an issue where the Open Measurement `sessionFinish` event was not being sent. - Fixed a video ad crash caused by audio-focus callbacks running off the main thread (notably in React Native apps). ## [6.0.5] - 2026-06-22 ### Added - Passed viewability verification details to the bidding process to support accurate ad viewability tracking. ### Changed - Improved network efficiency by reusing existing connections for ad requests. ## [6.0.4] - 2026-05-13 ### Changed - Updated `androidx.media3:media3-exoplayer` to `1.10.1` ## [6.0.3] - 2026-05-06 ### Fixed - An issue where impression events for HTML banner ads were not being reported. ## [6.0.2] - 2026-04-23 :::warning[Minimum compileSdk Version] `androidx.media3:media3-exoplayer` `1.10.0` requires **compileSdk 36** or higher. ::: ### Changed - Updated `androidx.media3:media3-exoplayer` to `1.10.0` - Improved mute/unmute stability for video ads with stable APIs ## [6.0.1] - 2026-04-15 ### Fixed - Fixed a null-safety issue in interstitial ad loading flow and improved thread-safe initialization. - Fixed a race condition where impression and close events could fire within a very short interval, causing inconsistent state. ## [6.0.0] - 2026-03-13 ### Added - **App Open Ads**: New full-screen ad format for app launch and foreground events - New `AppOpenAd` class for loading and displaying app open ads - See complete guide: [App Open Ads Documentation](10-ad-formats/app-open.md) ### Breaking Changes :::danger[Major Version Update] BlueStack SDK v6.0.0 removes the `BlueStack` and `MNG` prefixes from all public API classes. This is a **major breaking change** that requires code updates. ::: #### Core SDK Classes - **BlueStack** → **MobileAds** — SDK initialization class - **BlueStackPrivacySettings** → **PrivacySettings** — Privacy configuration #### Native Ads Classes - **MNGAdsFactory** → **AdsFactory** — Native ad factory - **MNGNativeObject** → **NativeObject** — Native ad object - **MNGPreference** → **Preference** — Ad preferences - **MNGGender** → **Gender** — Gender enum - `MNGGender.MNGGenderUnknown` → `Gender.GenderUnknown` - `MNGGender.MNGGenderMale` → `Gender.GenderMale` - `MNGGender.MNGGenderFemale` → `Gender.GenderFemale` #### Banner Ad Classes - **AdSize** → **BannerAdSize** — Banner ad size constants - `AdSize.BANNER` → `BannerAdSize.BANNER` - `AdSize.FULL_BANNER` → `BannerAdSize.FULL_BANNER` - `AdSize.LARGE_BANNER` → `BannerAdSize.LARGE_BANNER` - `AdSize.LEADERBOARD` → `BannerAdSize.LEADERBOARD` - `AdSize.MEDIUM_RECTANGLE` → `BannerAdSize.MEDIUM_RECTANGLE` - `AdSize.DYNAMIC_BANNER` → `BannerAdSize.DYNAMIC_BANNER` - `AdSize.DYNAMIC_LEADERBOARD` → `BannerAdSize.DYNAMIC_LEADERBOARD` :::tip[Migration Guide Available] See the [complete migration guide](/blog/bluestack-sdk-6_0_0/migration-guide-android) for detailed code examples and step-by-step instructions. ::: **Note:** Error handling classes and constants remain unchanged from v5.4.1 ## [5.4.4] - 2026-05-13 ### Changed - Updated `androidx.media3:media3-exoplayer` to `1.10.1` ## [5.4.3] - 2026-04-23 :::warning[Minimum compileSdk Version] `androidx.media3:media3-exoplayer` `1.10.0` requires **compileSdk 36** or higher. ::: ### Changed - Updated `androidx.media3:media3-exoplayer` from `1.9.2` to `1.10.0` - Replaced `exoPlayer.setVolume(0/1)` calls with stable `Player.mute()` and `Player.unmute()` APIs in `MNGBlurVideoView` ## [5.4.2] - 2026-03-12 ### Fixed - Resolved a runtime `AbstractMethodError` for `getTargetPreloadStatus` by updating `media3-exoplayer` to v1.9.2 ## [5.4.1] - 2026-02-09 ### Added - Option for client-side capping to reset on day change ### Fixed - Some Interstitial ads had transparency issues - Capping period logic that incorrectly allowed an extra ad when the capping period was set to 1 ## [5.4.0] - 2026-01-08 ### Added - Introduce Validator interface and ValidationError data class - Add necessary classes for adapters ### Updated - Enhance Ad Presenter support for adapters ## [5.3.5] - 2026-01-09 ### Fixed - Impression and viewability tracking issue for NativeAd of cross-platform. ## [5.3.3] - 2025-11-25 ### Fixed - Fixed NullPointerException when fetching network connection type for some devices ## [5.3.2] - 2025-11-07 ### Added - Exposed MediaView as Generic View for Secondary Native Ad mediation ## [5.3.1] - 2025-10-23 ### Removed - Dropped support for TLS v1.1 to meet server specification. ## [5.3.0] - 2025-10-03 ### Added - Global placement timeout settings for RewardedAd and Interstitial ### Updated - SDK initialization reliability and error handling - Debugging logs enhancement for troubleshooting ad issues - Overall SDK stability and performance ## [5.2.3] - 2025-11-25 ### Fixed - Fixed NullPointerException when fetching network connection type for some devices ## [5.2.2] - 2025-11-05 ### Removed - Dropped support for TLS v1.1 to meet server specification. ## [5.2.1] - 2025-08-01 ### Changed - Updated list of available adapters in debug menu ## [5.2.0] - 2025-07-03 ### Added - Support for preloading HTML interstitial ads - Support for preloading VAST interstitial ads - Ability to load multiple interstitial ads independently using individual ad instances - Logic to show only valid (non-expired) interstitial ads, even if some among the loaded ones have expired - Introduced AdExpiredError — [learn more](https://developers.bluestack.app/android/advanced-topics/error-handling) ### Fixed - Removed Play/Replay button overlay after video playback ends ## [5.1.4] - 2025-04-24 ### Updated - AGP to 8.8.2 ### Removed - RenderScript support and video blur background implementation to support 16 KB page sizes. ## [5.1.3] - 2025-03-12 ### Updated - BLSTCK-1727 Updated OM SDK dependency to version 1.5.4 - BLSTCK-1728 Updated OMID JS for Android ## [5.1.2] - 2025-03-03 ### Fixed - If both rewarded and interstitial ads are loaded in the BlueStack SDK, displaying the interstitial ad first causes the SDK to fail when attempting to show the rewarded ad. ## [5.1.1] - 2025-02-24 ### Updated - Renamed `getAdUnitId` method to `getPlacementId` in InterstitialAd and RewardedAd class. ## [5.1.0] - 2025-02-05 ### Added - BlueStackPrivacySettings for Publisher to enable/disable UserOptOut and AgeRestrictedUser privacy setting option. - Sanitization process to remove age, gender, location and AdvertisingId from http request for age restricted user. ### Changed - staging and production url to aws ### Updated - Used system User-Agent instead of BlueStack custom User-Agent for age restricted user. ## [5.0.2] - 2024-12-31 ### Fixed - App crash for dispatcher cache response missing exception. - Initialization flow doesn't fire error callback when both server and cache response are empty or null. ## [5.0.1] - 2024-12-19 ### Fixed - issue where Initialization callback wasn't fired ## [5.0.0] - 2024-12-04 ### Added - Added `BlueStack` class for initializing the SDK. - Introduced new `BannerView` class for showing banner ads. - Implemented `InterstitialAd` class for loading and showing interstitial ads. - Added `RewardedAd` class for displaying rewarded video ads. - New `RequestOptions` class for sending target specific information. --- ## App Open Ads(10-ad-formats) ## Overview App open ads are full-screen ads designed to appear during app launch moments. They are similar to interstitial ads but are specifically tailored for two key scenarios: **Cold start** — When the user opens the app fresh (not previously in memory). See [Handling Cold Starts with Loading Screens](#handling-cold-starts-with-loading-screens) for implementation details. **Soft launch** — When the user returns to the app from the background or unlocks the device while the app is in the foreground. The app is still in memory but was suspended. In both cases, the ad is displayed before the user reaches the main content. Users can dismiss the ad at any time. For a working implementation of this ad format, see the [azerion-inapp-demo-ios](https://github.com/azerion/azerion-inapp-demo-ios) demo app. ## Implementation Steps At a high level, integrating app open ads involves the following: 1. Build a manager class that preloads an ad so it's ready when needed. 2. Display the ad when the app enters the foreground. 3. React to ad lifecycle and presentation callbacks. ## Create an App Open Ad ### Implement a Manager Class App open ads should appear immediately when the user opens or returns to your app, so it's important to have the ad loaded and ready before it's needed. The best approach is to create a manager class that takes care of loading ads ahead of time, checking whether a loaded ad is still valid, and displaying it at the right moment. Create a class called `AppOpenAdManager` with an `AppOpenAdManagerDelegate` protocol to get notified when the ad flow completes: ```objectivec showLineNumbers #import @protocol AppOpenAdManagerDelegate - (void)appOpenAdDidComplete:(AppOpenAdManager *)appOpenAdManager; @end @interface AppOpenAdManager : NSObject @property (class, nonatomic, readonly) AppOpenAdManager *shared; @property (nonatomic, strong, nullable) BLSAppOpenAd *appOpenAd; @property (nonatomic, weak, nullable) id appOpenAdManagerDelegate; @property (nonatomic, assign) BOOL isLoadingAd; @property (nonatomic, assign) BOOL isShowingAd; @property (nonatomic, strong, nullable) NSDate *loadTime; @property (nonatomic, assign) NSTimeInterval timeoutInterval; @end @implementation AppOpenAdManager + (AppOpenAdManager *)shared { static AppOpenAdManager *sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [[AppOpenAdManager alloc] init]; }); return sharedInstance; } - (instancetype)init { self = [super init]; if (self) { _isLoadingAd = NO; _isShowingAd = NO; _timeoutInterval = 4 * 3600; } return self; } @end ``` ```swift showLineNumbers import Foundation import BlueStackSDK protocol AppOpenAdManagerDelegate: AnyObject { func appOpenAdDidComplete(_ appOpenAdManager: AppOpenAdManager) } class AppOpenAdManager: NSObject { static let shared = AppOpenAdManager() var appOpenAd: AppOpenAd? weak var appOpenAdManagerDelegate: AppOpenAdManagerDelegate? /// Indicates whether an ad load request is in progress. var isLoadingAd = false /// Indicates whether an ad is currently being displayed. var isShowingAd = false /// Tracks when the ad was loaded, used for expiration checks. var loadTime: Date? /// Maximum age of a loaded ad before it's considered expired. let timeoutInterval: TimeInterval = 4 * 3_600 private func hasNotExpired(within: TimeInterval) -> Bool { if let loadTime = loadTime { return Date().timeIntervalSince(loadTime) < timeoutInterval } return false } private func isAppOpenAdAvailableToShow() -> Bool { return appOpenAd != nil && hasNotExpired(within: timeoutInterval) } } ``` ### Standalone Creation To create an app open ad, instantiate an instance of `AppOpenAd` with a placement Id. ```objectivec showLineNumbers #import @interface AppDelegate () @property (strong, nonatomic) BLSAppOpenAd *appOpenAd; @end @implementation AppDelegate self.appOpenAd = [[BLSAppOpenAd alloc] initWithPlacementID:@"APP_OPEN_PLACEMENT_ID"]; @end ``` ```swift showLineNumbers import BlueStackSDK class AppDelegate: UIApplicationDelegate { private lazy var appOpenAd = AppOpenAd(placementID: "APP_OPEN_PLACEMENT_ID") } ``` ## Load an App Open Ad ### With AppOpenAdManager The recommended way to load an app open ad is through the `AppOpenAdManager` class. To load an app open ad, call the `loadAppOpenAd()` method. The method guards against duplicate requests — it won't start a new load if one is already in progress or if a valid (non-expired) ad is already available. ```objectivec showLineNumbers - (void)loadAppOpenAd { if (self.isLoadingAd || [self isAppOpenAdAvailableToShow]) { return; } self.isLoadingAd = YES; // Clean up any existing app open ad instance before creating a new one if (self.appOpenAd) { self.appOpenAd = nil; } // Create an AppOpenAd instance with your placement ID // The placement ID identifies the ad unit in the BlueStack dashboard self.appOpenAd = [[BLSAppOpenAd alloc] initWithPlacementID:@"APP_OPEN_PLACEMENT_ID"]; // Set the AppOpenAdDelegate to receive ad lifecycle callbacks // This delegate handles onAdLoaded and onAdFailedToLoad events self.appOpenAd.delegate = self; // Set the FullScreenDelegate to receive display-related callbacks // This delegate handles onAdDisplayed, onAdFailedToDisplay, onAdClicked, and onAdDismissed events self.appOpenAd.fullScreenDelegate = self; /// Creates and returns RequestOptions for ad targeting /// These parameters help deliver more relevant ads to users RequestOptions *requestOptions = [[RequestOptions alloc] initWithAge:@(25) location:[[CLLocation alloc] initWithLatitude:48.87610 longitude:10.453] gender:GenderMale keyword:@"brand=myBrand;category=sport" // Load the app open ad with optional RequestOptions // The load() method initiates an ad request to the ad server // RequestOptions are optional - pass nil if you don't need targeting parameters // The delegate will receive onAdLoaded() or onAdFailedToLoad() callbacks contentUrl:@"https://my_content_url.com/"]; [self.appOpenAd loadWithRequestOptions:requestOptions]; } ``` ```swift showLineNumbers func loadAppOpenAd() { if isLoadingAd || isAppOpenAdAvailableToShow() { return } isLoadingAd = true // Clean up any existing app open ad instance before creating a new one if appOpenAd != nil { appOpenAd = nil } // Create an AppOpenAd instance with your placement ID // The placement ID identifies the ad unit in the BlueStack dashboard appOpenAd = AppOpenAd(placementID: "APP_OPEN_PLACEMENT_ID") // Set the AppOpenAdDelegate to receive ad lifecycle callbacks // This delegate handles onAdLoaded and onAdFailedToLoad events appOpenAd?.delegate = self // Set the FullScreenDelegate to receive display-related callbacks // This delegate handles onAdDisplayed, onAdFailedToDisplay, onAdClicked, and onAdDismissed events appOpenAd?.fullScreenDelegate = self /// Creates and returns RequestOptions for ad targeting /// These parameters help deliver more relevant ads to users let requestOptions = RequestOptions( age: 25, location: CLLocation.init(latitude: 48.87610, longitude: 10.453), gender: .male, keyword: "brand=myBrand;category=sport", contentUrl: "https://my_content_url.com/" ) // Load the app open ad with optional RequestOptions // The load() method initiates an ad request to the ad server // RequestOptions are optional - pass nil if you don't need targeting parameters // The delegate will receive onAdLoaded() or onAdFailedToLoad() callbacks appOpenAd?.load(requestOptions: requestOptions) } ``` ### Standalone Loading App open ad can be loaded by calling `load(requestOptions:)` method with a `RequestOptions` instance for supplying targeting information or simply calling `load()` method. ```objectivec showLineNumbers RequestOptions *requestOptions = [[RequestOptions alloc] initWithAge:@(25) location:[[CLLocation alloc] initWithLatitude:48.87610 longitude:10.453] gender:GenderMale keyword:@"brand=myBrand;category=sport" contentUrl:@"https://my_content_url.com/"]; [self.appOpenAd loadWithRequestOptions:requestOptions]; // Or // [self.appOpenAd load]; ``` ```swift showLineNumbers func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { let requestOptions = RequestOptions( age: 25, location: CLLocation.init(latitude: 48.87610, longitude: 10.453), gender: .male, keyword: "brand=myBrand;category=sport", contentUrl: "https://my_content_url.com/" ) appOpenAd?.load(requestOptions: requestOptions) // Or // appOpenAd?.load() return true } ``` :::info Ad load call will preload ad before you show it so that ads can be shown with zero latency when needed. Note that preloaded ads expire after a certain period — see [Consider Ad Expiration](#consider-ad-expiration) for details. Once an ad expires, you can call `load` again on the existing instance to preload a new ad. ::: ## Show an Ad ### With AppOpenAdManager Before showing the ad, the manager checks whether an ad is already on screen and whether a valid ad is available. If no ad is ready, it notifies the delegate and triggers a new load so an ad will be available next time. ```objectivec showLineNumbers - (void)showAppOpenAdIfAvailable { if (self.isShowingAd) { NSLog(@"App open ad is already showing."); return; } if (![self isAppOpenAdAvailableToShow]) { NSLog(@"App open ad is not ready yet."); [self.appOpenAdManagerDelegate appOpenAdDidComplete:self]; [self loadAppOpenAd]; return; } // Check if the ad is ready and show it // Ensure if the ad is ready to be displayed using isReady() before calling show() // The show() method presents the full-screen app open ad // The FullScreenDelegate callbacks will be triggered during the ad lifecycle dispatch_async(dispatch_get_main_queue(), ^{ if (self.appOpenAd.isReady) { [self.appOpenAd showFromRootViewController:self]; } }); self.isShowingAd = YES; } ``` ```swift showLineNumbers func showAppOpenAdIfAvailable() { if isShowingAd { return print("App open ad is already showing.") } if !isAppOpenAdAvailableToShow() { print("App open ad is not ready yet.") appOpenAdManagerDelegate?.appOpenAdDidComplete(self) loadAppOpenAd() return } // Check if the ad is ready and show it // Ensure if the ad is ready to be displayed using isReady before calling show() // The show() method presents the full-screen app open ad // The FullScreenDelegate callbacks will be triggered during the ad lifecycle DispatchQueue.main.async { if self.appOpenAd?.isReady ?? false { // Pass nil to let the SDK resolve the top-most view controller automatically self.appOpenAd?.show(fromRootViewController: nil) } } isShowingAd = true } ``` ### Standalone Showing To show an app open ad directly, check `isReady` on the `AppOpenAd` instance and call `show(fromRootViewController:)` with an optional root view controller. ```objectivec showLineNumbers if (self.appOpenAd.isReady) { [self.appOpenAd showFromRootViewController:self]; } ``` ```swift showLineNumbers if appOpenAd?.isReady ?? false { appOpenAd?.show(fromRootViewController: self) } ``` ## Show the Ad During App Foregrounding To display the ad whenever the user returns to your app, call `showAppOpenAdIfAvailable()` from your `AppDelegate`'s `applicationDidBecomeActive:` method. This ensures the ad is shown (or a new one is loaded) each time the app comes to the foreground. ```objectivec showLineNumbers - (void)applicationDidBecomeActive:(UIApplication *)application { [AppOpenAdManager.shared showAppOpenAdIfAvailable]; } ``` ```swift showLineNumbers func applicationDidBecomeActive(_ application: UIApplication) { AppOpenAdManager.shared.showAppOpenAdIfAvailable() } ``` :::tip If your app uses `Scenes`, implement `sceneDidBecomeActive:` in your `UISceneDelegate` instead of `applicationDidBecomeActive:`. ::: ## Destroying an App Open Ad When you have finished displaying an app open ad, release it by setting it to `nil` to free resources. ```objectivec showLineNumbers self.appOpenAd = nil; ``` ```swift showLineNumbers appOpenAd = nil ``` ## Handling Cold Starts with Loading Screens The examples above focus on showing app open ads when users return to an app that is already suspended in memory (soft launch). Cold starts — when the app is launched fresh and was not previously in memory — require additional consideration. During a cold start, there is no previously loaded ad ready to show immediately. The delay between requesting an ad and receiving one can create a situation where the user briefly sees app content before an ad unexpectedly appears. This is a poor user experience and should be avoided. The recommended approach is to use a loading or splash screen during the app's startup sequence and only show the app open ad while that screen is still visible. Here's an example implementation: ```objectivec showLineNumbers #import #import #import @interface SplashViewController : UIViewController @property (nonatomic, copy, nullable) void (^onInitializationComplete)(void); @end @implementation SplashViewController { CMPManager *_cmpManager; CMPManagerFactory *_cmpFactory; /// Number of seconds remaining to show the app open ad. /// This simulates the time needed to load the app. NSInteger _secondsRemaining; /// The countdown timer. NSTimer *_countdownTimer; } - (void)viewDidLoad { [super viewDidLoad]; _cmpFactory = [[CMPManagerFactory alloc] init]; _secondsRemaining = 5; [AppOpenAdManager shared].appOpenAdManagerDelegate = self; [self.navigationController setNavigationBarHidden:YES animated:NO]; [self requestAppTracking]; } #pragma mark - Step 1: Request App Tracking Transparency (ATT) Permission /// App Tracking Transparency (ATT) is required by Apple for iOS 14+ to track users across apps and websites. /// This must be requested BEFORE initializing the BlueStack SDK to ensure proper consent handling. - (void)requestAppTracking { if (@available(iOS 14, *)) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) { dispatch_async(dispatch_get_main_queue(), ^{ // Proceed to CMP regardless of ATT status // The SDK will handle the tracking permission internally [self requestCMP]; }); }]; }); } else { [self requestCMP]; } } #pragma mark - Step 2: Request Consent Management Platform (CMP) Consent /// CMP handles GDPR/CCPA compliance by collecting user consent for data processing. /// This step is crucial for European users and privacy-conscious regions. /// Reference: https://developers.bluestack.app/ios/privacy - (void)requestCMP { _cmpManager = [_cmpFactory createCMPManager]; [_cmpManager startWithViewController:self]; if (_cmpManager.hasConsent) { [self startTimer]; [self startInitializingBlueStackSDK]; } if (_secondsRemaining <= 0) { [AppOpenAdManager shared].appOpenAdManagerDelegate = nil; if (self.onInitializationComplete) { self.onInitializationComplete(); } } } #pragma mark - Step 3: Initialize BlueStack SDK /// This method should ONLY be called after: /// 1. ATT permission has been requested (iOS 14+) /// 2. CMP consent has been obtained or CMP has failed - (void)startInitializingBlueStackSDK { [[MobileAds sharedInstance] setDebugModeEnabled:YES]; [[MobileAds sharedInstance] initializeWithAppID:@"YOUR_APP_ID" completionHandler:^(BLSInitializationStatus *status) { for (BLSAdapterStatus *adapterStatus in status.adapterStatuses.allValues) { NSLog(@"adapter name %@ has this state %ld with Description %@", adapterStatus.name, (long)adapterStatus.state, adapterStatus.statusDescription); } dispatch_async(dispatch_get_main_queue(), ^{ [[AppOpenAdManager shared] loadAppOpenAd]; }); }]; } - (void)startTimer { _countdownTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(decrementCounter) userInfo:nil repeats:YES]; } - (void)decrementCounter { _secondsRemaining -= 1; if (_secondsRemaining > 0) { return; } [_countdownTimer invalidate]; [[AppOpenAdManager shared] showAppOpenAdIfAvailable]; } #pragma mark - CMPManagerDelegate - (void)onRequestsToShowConsentTool:(CMPManager *)consentManager { [consentManager showConsentFromViewController:self]; } - (void)onConsentStringDidChange:(CMPManager *)consentManager consentString:(NSString *)consentString { [self startTimer]; [self startInitializingBlueStackSDK]; } - (void)onConsentManagerDidFail:(CMPManager *)consentManager error:(NSError *)error { [self startTimer]; [self startInitializingBlueStackSDK]; } - (void)onConsentManagerRequestsToPresentPrivacyPolicy:(CMPManager *)consentManager url:(NSString *)url { } #pragma mark - AppOpenAdManagerDelegate - (void)appOpenAdDidComplete:(AppOpenAdManager *)appOpenAdManager { NSLog(@"appOpenAdManagerAdDidComplete"); dispatch_async(dispatch_get_main_queue(), ^{ [AppOpenAdManager shared].appOpenAdManagerDelegate = nil; if (self.onInitializationComplete) { self.onInitializationComplete(); } }); } @end ``` ```swift showLineNumbers import Foundation import UIKit import AppTrackingTransparency import BlueStackSDK class SplashViewController: UIViewController { var onInitializationComplete: (() -> Void)? private var cmpManager: CMPManager? private var cmpFactory: CMPManagerFactory = CMPManagerFactory() /// Number of seconds remaining to show the app open ad. /// This simulates the time needed to load the app. var secondsRemaining: Int = 5 /// The countdown timer. var countdownTimer: Timer? override func viewDidLoad() { super.viewDidLoad() AppOpenAdManager.shared.appOpenAdManagerDelegate = self navigationController?.setNavigationBarHidden(true, animated: false) requestAppTracking() } /// Step 1: Request App Tracking Transparency (ATT) Permission /// App Tracking Transparency (ATT) is required by Apple for iOS 14+ to track users across apps and websites. /// This must be requested BEFORE initializing the BlueStack SDK to ensure proper consent handling. private func requestAppTracking() { if #available(iOS 14, *) { DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { ATTrackingManager.requestTrackingAuthorization { _ in DispatchQueue.main.async { // Proceed to CMP regardless of ATT status // The SDK will handle the tracking permission internally self.requestCMP() } } } } else { self.requestCMP() } } /// Step 2: Request Consent Management Platform (CMP) Consent /// CMP handles GDPR/CCPA compliance by collecting user consent for data processing. /// This step is crucial for European users and privacy-conscious regions. /// Reference: https://developers.bluestack.app/ios/privacy private func requestCMP() { self.cmpManager = self.cmpFactory.createCMPManager() self.cmpManager?.start(with: self) if self.cmpManager?.hasConsent == true { self.startTimer() self.startInitializingBlueStackSDK() } if self.secondsRemaining <= 0 { AppOpenAdManager.shared.appOpenAdManagerDelegate = nil self.onInitializationComplete?() } } /// Step 3: Initialize BlueStack SDK /// This method should ONLY be called after: /// 1. ATT permission has been requested (iOS 14+) /// 2. CMP consent has been obtained or CMP has failed private func startInitializingBlueStackSDK() { MobileAds.sharedInstance().setDebugMode(enabled: true) MobileAds.sharedInstance().initialize(appID: "YOUR_APP_ID") { initializationStatus in for ( _ , adapterStatus) in initializationStatus.adapterStatuses { print("adapter name \(adapterStatus.name) has this state \(adapterStatus.state) with Description \(String(describing: adapterStatus.statusDescription))") } DispatchQueue.main.async { AppOpenAdManager.shared.loadAppOpenAd() } } } func startTimer() { countdownTimer = Timer.scheduledTimer( timeInterval: 1.0, target: self, selector: #selector(SplashViewController.decrementCounter), userInfo: nil, repeats: true) } @objc func decrementCounter() { secondsRemaining -= 1 guard secondsRemaining <= 0 else { return } countdownTimer?.invalidate() AppOpenAdManager.shared.showAppOpenAdIfAvailable() } } extension SplashViewController: CMPManagerDelegate { func onRequestsToShowConsentTool(consentManager: CMPManager) { consentManager.showConsent(from: self) } func onConsentStringDidChange(consentManager: CMPManager, consentString: String) { self.startTimer() self.startInitializingBlueStackSDK() } func onConsentManagerDidFail(consentManager: CMPManager, error: Error) { self.startTimer() self.startInitializingBlueStackSDK() } func onConsentManagerRequestsToPresentPrivacyPolicy(consentManager: CMPManager, url: String) {} } extension SplashViewController: AppOpenAdManagerDelegate { func appOpenAdDidComplete(_ appOpenAdManager: AppOpenAdManager) { print("appOpenAdManagerAdDidComplete") DispatchQueue.main.async { AppOpenAdManager.shared.appOpenAdManagerDelegate = nil self.onInitializationComplete?() } } } ``` Follow these guidelines: - **Allocate sufficient time for ad loading.** Set your timer duration to give the SDK enough time to load the ad. In the example above, 5 seconds is typically sufficient. Adjust based on your app's needs and expected network conditions. - **Show the ad from the loading screen only.** If your app finishes loading and has already moved the user to the main content, do not show the ad — the moment has passed. - **Dismiss the loading screen in `appOpenAdDidComplete`.** Wait for the callback before transitioning to app content. This ensures a smooth flow from splash screen → ad → app content with no flicker or content flash in between. ## Ad events ### Register for app open events `AppOpenAd` delivers lifecycle and presentation events through two delegate protocols: `AppOpenAdDelegate` for load-related events and `FullScreenDelegate` for display-related events. ```objectivec showLineNumbers self.appOpenAd.delegate = self; self.appOpenAd.fullScreenDelegate = self; ``` ```swift showLineNumbers appOpenAd?.delegate = self appOpenAd?.fullScreenDelegate = self ``` ### App Open ad lifecycle events `AppOpenAdDelegate` notifies you when the ad has finished loading or when a load attempt fails. On a successful load, record the load time so you can later check for expiration. On failure, clean up the ad reference and reset state. ```objectivec showLineNumbers @interface AppOpenAdManager () ... @end #pragma mark - AppOpenAdDelegate @implementation AppOpenAdManager - (void)didLoadAppOpenAd:(AppOpenAd *)ad { NSLog(@"App open ad loaded"); self.isLoadingAd = NO; self.loadTime = [NSDate date]; } - (void)appOpenAd:(AppOpenAd *)ad didFailedToLoadWithError:(NSError *)error { NSLog(@"Failed to load app open ad with error: %@", error.localizedDescription); self.isLoadingAd = NO; self.appOpenAd = nil; self.loadTime = nil; } @end ``` ```swift showLineNumbers // MARK: - AppOpenAdDelegate extension AppOpenAdManager: AppOpenAdDelegate { func onAdLoaded(_ ad: BlueStackSDK.AppOpenAd) { print("App open ad loaded") isLoadingAd = false loadTime = Date() } func onAdFailedToLoad(_ ad: BlueStackSDK.AppOpenAd, _ error: any Error) { print("Failed to load app open ad with error: \(error.localizedDescription)") isLoadingAd = false appOpenAd = nil loadTime = nil } } ``` ### App Open ad full-screen events `FullScreenDelegate` reports when the ad is displayed, clicked, dismissed, or fails to display. After the ad is dismissed or fails to display, release the current ad, reset state, notify the delegate, and immediately start loading a new ad so one is ready for the next app foregrounding. ```objectivec showLineNumbers @interface AppOpenAdManager () ... @end #pragma mark - FullScreenDelegate @implementation AppOpenAdManager - (void)didDisplayAd:(id)ad { NSLog(@"App open ad displayed"); } - (void)ad:(id)ad didFailedToDisplayWithError:(NSError *)error { NSLog(@"Failed to display app open ad with error: %@", error.localizedDescription); self.appOpenAd = nil; self.isShowingAd = NO; [self.appOpenAdManagerDelegate appOpenAdDidComplete:self]; [self loadAppOpenAd]; } - (void)didClickAd:(id)ad { NSLog(@"App open ad clicked."); } - (void)didDismissAd:(id)ad { NSLog(@"App open ad dismissed"); self.appOpenAd = nil; self.isShowingAd = NO; [self.appOpenAdManagerDelegate appOpenAdDidComplete:self]; [self loadAppOpenAd]; } @end ``` ```swift showLineNumbers // MARK: - FullScreenDelegate extension AppOpenAdManager: FullScreenDelegate { func onAdDisplayed(_ ad: any FullScreenDisplayableAd) { print("App open ad displayed") } func onAdFailedToDisplay(_ ad: any FullScreenDisplayableAd, _ error: any Error) { print("Failed to display app open ad with error: \(error.localizedDescription)") appOpenAd = nil isShowingAd = false appOpenAdManagerDelegate?.appOpenAdDidComplete(self) loadAppOpenAd() } func onAdClicked(_ ad: any FullScreenDisplayableAd) { print("App open ad clicked.") } func onAdDismissed(_ ad: any FullScreenDisplayableAd) { print("App open ad dismissed") appOpenAd = nil isShowingAd = false appOpenAdManagerDelegate?.appOpenAdDidComplete(self) loadAppOpenAd() } } ``` ## Best Practices App open ads are a great way to monetize your app's loading screen, but it's important to follow best practices so that your users continue to enjoy using your app: - **Show ads only during natural waiting moments.** App open ads work best when users are already expecting a brief pause, such as during app launch or when returning from the background. Avoid surprising users with ads at unexpected times. - **Always show a splash or loading screen first.** See [Handling Cold Starts with Loading Screens](#handling-cold-starts-with-loading-screens) for details. - **Initialize the SDK before loading ads.** Make sure the BlueStack SDK has fully initialized before you attempt to load an app open ad. Loading ads before initialization may result in failed requests. - **Preload the ad early.** Load the ad as soon as possible so there is no delay when it's time to show it. Avoid loading other ad formats in parallel, as this can strain device resources and reduce fill rates. - **Respect user experience with frequency controls.** See [Control Ad Frequency](#control-ad-frequency) for recommended strategies. - **Be mindful of new users.** Hold off on showing app open ads until users have opened and used your app a few times. This helps build a positive first impression before introducing ads. - **Handle ad expiration.** See [Consider Ad Expiration](#consider-ad-expiration) for details on how to manage preloaded ad validity. - **Coordinate your loading screen with the ad.** If you have a loading screen running behind the app open ad and it finishes before the user dismisses the ad, dismiss the loading screen in the `onAdDismissed` callback to ensure a smooth transition to your app content. ## Consider Ad Expiration A preloaded ad can become stale if too much time passes between loading and displaying. The BlueStack SDK handles ad expiration internally. If you attempt to show an expired ad, you will receive an `AdErrorAdExpired` error in the `onAdFailedToDisplay(_:_:)` callback. When this occurs, clean up the expired ad reference and call `loadAppOpenAd()` to preload a fresh ad for the next opportunity. ## Control Ad Frequency To maintain a positive user experience, avoid showing an app open ad on every single foreground event. Consider implementing frequency controls such as: - **Skip opportunities** — Show an ad on every second or third app open instead of every time. - **Minimum background duration** — Only show an ad if the user was away from the app for a certain amount of time (e.g., 30 seconds, 2 minutes, or 15 minutes). - **Cooldown after cold start** — If you showed an ad during a cold start, skip soft launch ads for a set period afterward. - **Frequency caps** — Limit the total number of app open ads shown per session or per day. Where possible, tailor caps based on user cohorts or engagement levels. --- ## Banner Ads(10-ad-formats) ## Overview A Banner ad typically appears as a rectangular or square graphic or text on your app's user interface. Banner ads are mostly displayed at the top or bottom of the screen. Inline banner ads are placed inside the scrollable contents. For a working implementation of this ad format, see the [azerion-inapp-demo-ios](https://github.com/azerion/azerion-inapp-demo-ios) demo app. ## Create a BannerView Banner ads are displayed using a `BannerView` instance. You can create a `BannerView` instance either programmatically or from interface builder. ### Programmatically Following code shows, initializing a `BannerView` instance with a [`AdSize`](#banner-ad-sizes) and adding it to the view using autolayout. **Note:** When adding a banner view programmatically, you do not need to specify explicit width or height constraints, as the banner view will automatically get its size based on the creative’s width and height. If you add the leading and trailing constraints, the creative will be stretched horizontally to match the parent view, and its height will be adjusted according to the aspect ratio. ```objectivec showLineNumbers #import @interface ViewController () @property(nonatomic, strong) BLSBannerView *bannerView; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Initialize the BannerView instance self.bannerView = [[BLSBannerView alloc] initWithAdSize:AdSizeBanner]; // Add BannerView to the parent view [self addBannerViewToView:self.bannerView]; } - (void)addBannerViewToView:(UIView *)bannerView { bannerView.translatesAutoresizingMaskIntoConstraints = NO; [self.view addSubview:bannerView]; [self.view addConstraints:@[ [NSLayoutConstraint constraintWithItem:bannerView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view.safeAreaLayoutGuide attribute:NSLayoutAttributeBottom multiplier:1 constant:0], [NSLayoutConstraint constraintWithItem:bannerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0] ]]; } @end ``` ```swift showLineNumbers import BlueStackSDK import UIKit class ViewController: UIViewController { var bannerView: BannerView! override func viewDidLoad() { super.viewDidLoad() // Initialize the BannerView instance bannerView = BannerView(adSize: .banner) // Add BannerView to the parent view addBannerViewToView(bannerView) } func addBannerViewToView(_ bannerView: BannerView) { bannerView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(bannerView) view.addConstraints( [NSLayoutConstraint(item: bannerView, attribute: .bottom, relatedBy: .equal, toItem: view.safeAreaLayoutGuide, attribute: .bottom, multiplier: 1, constant: 0), NSLayoutConstraint(item: bannerView, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1, constant: 0) ]) } } ``` ### Interface Builder In your storyboard or xib add a `UIView` and assign `BannerView` to the class of that view in Identity Inspector. Add required position layout constraints to the view. Add a `IBOutlet` to the `BannerView`. ![BannerView from Interface Builder](../00-images/bannerview_interface_builder.png) **Note:** When adding a banner view using interface builder, you do not need to specify explicit width or height constraints, as the banner view will automatically get its size based on the creative’s width and height. If you add the leading and trailing constraints, the creative will be stretched horizontally to match the parent view, and its height will be adjusted according to the aspect ratio. ![BannerView from Interface Builder Constraints](../00-images/banner-view-constraint-light.png#gh-light-mode-only)![BannerView from Interface Builder Constraints](../00-images/banner-view-constraint-dark.png#gh-dark-mode-only) ## Load a banner ad After initializing and adding `BannerView` to the view hierarchy you need to load the `BannerView` by calling `load(requestOptions:)` method with a `RequestOptions` object or simply calling `load()` method. ### Without RequestOptions ```objectivec showLineNumbers self.bannerView.placementID = @"BANNER_PLACEMENT_ID_HERE"; self.bannerView.viewController = self; [self.bannerView load]; ``` ```swift showLineNumbers bannerView.placementID = "BANNER_PLACEMENT_ID_HERE" bannerView.viewController = self bannerView.load() ``` To load banner ad, it is mandetory to provide placementID to `BannerView` instance. It also takes a optional `viewController` from which it will present full screen content after user interacts with the `BannerView`. ### With RequestOptions Create `RequestOptions` for supplying targeting information while loading ads. ```objectivec showLineNumbers RequestOptions *requestOptions = [[RequestOptions alloc] initWithAge:@(25) location:[[CLLocation alloc] initWithLatitude:48.87610 longitude:10.453] gender:GenderMale keyword:@"brand=myBrand;category=sport" contentUrl:@"https://my_content_url.com/"]; [self.bannerView loadWithRequestOptions:requestOptions]; ``` ```swift showLineNumbers let requestOptions = RequestOptions( age: 25, location: CLLocation.init(latitude: 48.87610, longitude: 10.453), gender: .male, keyword: "brand=myBrand;category=sport", contentUrl: "https://my_content_url.com/" ) bannerView.load(requestOptions: requestOptions) ``` ## Ad events To get `BannerView` lifecycle events, you need to set the `BannerViewDelegate` before loading the banner ad. ### Register for BannerView events ```objectivec showLineNumbers self.bannerView.delegate = self; // typically it is the viewController in which you are adding the BannerView ``` ```swift showLineNumbers bannerView.delegate = self // typically it is the viewController in which you are adding the BannerView ``` If you are using interface builder you can set the delegate from the connections inspector. ![BannerViewDelegate from Interface Builder](../00-images/bannerview_delegate.png) ### Implement banner events BannerView has the following events for notifying it's lifecycle, receiving click and resizes. - `onLoad(bannerView:preferredHeight:)` will be called by the SDK when banner ad finishes loading. ```objectivec showLineNumbers - (void)bannerView:(BLSBannerView * _Nonnull)bannerView didLoadWithPreferredHeight:(CGFloat)preferredHeight { NSLog(@"BannerView successfully loaded"); } ``` ```swift showLineNumbers func onLoad(_ bannerView: BlueStackSDK.BannerView, _ preferredHeight: CGFloat) { print("Banner successfully loaded with preferredHeight: \(preferredHeight)") } ``` - `onFailedToLoad(bannerView:error:)` will be called when all the ad server fails to load the ad. It will return the error of last called ad server. ```objectivec showLineNumbers - (void)bannerView:(BLSBannerView * _Nonnull)bannerView didFailedToLoadWithError:(NSError * _Nonnull)error { NSLog(@"BannerView failed to load ad with error: %@", error.localizedDescription); } ``` ```swift showLineNumbers func onFailedToLoad(_ bannerView: BlueStackSDK.BannerView, _ error: any Error) { print("Banner Ad failed to load with error: \(error.localizedDescription)") } ``` - `onRefresh(bannerView:)` will be called when the banner has refreshed ```objectivec showLineNumbers - (void)didRefreshBannerView:(BLSBannerView * _Nonnull)bannerView { NSLog(@"BannerView refreshed"); } ``` ```swift showLineNumbers func onRefresh(_ bannerView: BlueStackSDK.BannerView) { print("Banner Ad refreshed") } ``` - `onFailedToRefresh(bannerView:error:)` will be called when the banner fail to refresh. ```objectivec showLineNumbers - (void)bannerView:(BLSBannerView * _Nonnull)bannerView didFailedToRefreshWithError:(NSError * _Nonnull)error { NSLog(@"BannerView failed to refresh ad with error: %@", error.localizedDescription); } ``` ```swift showLineNumbers func onFailedToRefresh(_ bannerView: BlueStackSDK.BannerView, _ error: any Error) { print("Banner Ad failed to refresh with error: \(error.localizedDescription)") } ``` - `onClick(bannerView:)` will be called when user click the banner ad. ```objectivec showLineNumbers - (void)didClick:(BLSBannerView *)bannerView { NSLog(@"BannerView received a click"); } ``` ```swift showLineNumbers func onClick(_ bannerView: BlueStackSDK.BannerView) { print("Banner Ad clicked") } ``` - `onResize(bannerView:size:)` will be called when the banner has changed size ```objectivec showLineNumbers - (void)bannerView:(BLSBannerView *)bannerView didResizedToSize:(CGSize)size { NSLog(@"BannerView size has been changed to: %@", NSStringFromCGSize(size)); } ``` ```swift showLineNumbers func onResize(_ bannerView: BlueStackSDK.BannerView, _ size: CGSize) { print("Banner Ad resized to size: \(size)") } ``` ## Destroying Banner Ad You need to keep a strong reference to the `BannerView` instance, if you want to add the banner view to the parent view after it completes loading a banner ad. A `BannerView` instance maintains lifecycle of a single banner ad even after it refresh. You must not call `load(requestOptions:)` method multiple times using single `BannerView` instance. Also when you are done with `BannerView` instance you can release the instance as follows: ```objectivec showLineNumbers [self.bannerView removeFromSuperview]; self.bannerView.delegate = nil; self.bannerView = nil; ``` ```swift showLineNumbers bannerView.removeFromSuperview() bannerView.delegate = nil bannerView = nil ``` ## Banner Ad sizes BannerView provides `AdSize` enum for it's available ad sizes. | AdSize | Description | Dimensions |-----------------------------------|---------------------------|-------------------| | banner | Small Banner | 320 x 50 | | dynamicBanner | Small Banner Screen | Screen width x 50 | | largeBanner | Large Banner | 320 x 100 | | fullBanner | Full Banner ipad | 468 x 60 | | dynamicLeaderboardBanner | Landscape Banner ipad | 728 x 90 | | leaderboard | Landscape Banner ipad | Screen width x 90 | | mediumRectangle | Square Banner | 300 x 250 | ## Get the banner size after loading Although the `BannerView` instance takes care of resizing the creative to make it fit properly in the banner view size, you can also get the height of the banner from the below event. The current creative's height can be found directly from the `preferredHeight` parameter retrieved in the `onLoad(_ bannerView: BannerView, _ preferredHeight: CGFloat)` delegate method. ```objectivec showLineNumbers - (void)bannerView:(BLSBannerView * _Nonnull)bannerView didLoadWithPreferredHeight:(CGFloat)preferredHeight { NSLog(@"Banner Ad loaded. preferredHeight: %f", preferredHeight); } ``` ```swift showLineNumbers func onLoad(_ bannerView: BlueStackSDK.BannerView, _ preferredHeight: CGFloat) { print("Banner Ad loaded. preferredHeight: \(preferredHeight)") } ``` You will found size of the refreshed banner ad from the `size` parameter of `onResize(_ bannerView: BannerView, _ size: CGSize)` delegate method. ```objectivec showLineNumbers - (void)bannerView:(BLSBannerView *)bannerView didResizedToSize:(CGSize)size { NSLog(@"Banner Ad resized to width: %f, height: %f", size.width, size.height); } ``` ```swift showLineNumbers func onResize(_ bannerView: BlueStackSDK.BannerView, _ size: CGSize) { print("Banner Ad resized to width: \(size.width), height: \(size.height)") } ``` --- ## Interstitial Ads(10-ad-formats) ## Overview Interstitial ads are full-screen, short clips that appears at natural transition points in an app. They come in various formats, including static (for image or text) and rich media (for video). For a working implementation of this ad format, see the [azerion-inapp-demo-ios](https://github.com/azerion/azerion-inapp-demo-ios) demo app. ## Create an Interstitial Ad To create an interstitial ad, instantiate an instance of `InterstitialAd` with a placement Id. ```objectivec showLineNumbers #import @interface ViewController () @property (strong, nonatomic) BLSInterstitialAd *interstitialAd; @end @implementation ViewController - (void)viewDidLoad { self.interstitialAd = [[BLSInterstitialAd alloc] initWithPlacementID:@"INTERSTITIAL_PLACEMENT_ID"]; } @end ``` ```swift showLineNumbers import BlueStackSDK class ViewController: UIViewController { var interstitialAd: InterstitialAd? override func viewDidLoad() { super.viewDidLoad() interstitialAd = InterstitialAd(placementID: "INTERSTITIAL_PLACEMENT_ID") } } ``` ## Load an Interstitial Ad Interstitial ad can be loaded by calling `load(requestOptions:)` method with a `RequestOptions` instance for supplying targeting information or simply calling `load()` method. ```objectivec showLineNumbers RequestOptions *requestOptions = [[RequestOptions alloc] initWithAge:@(25) location:[[CLLocation alloc] initWithLatitude:48.87610 longitude:10.453] gender:GenderMale keyword:@"brand=myBrand;category=sport" contentUrl:@"https://my_content_url.com/"]; [self.interstitialAd loadWithRequestOptions:requestOptions]; // Or // [self.interstitialAd load]; ``` ```swift showLineNumbers let requestOptions = RequestOptions( age: 25, location: CLLocation.init(latitude: 48.87610, longitude: 10.453), gender: .male, keyword: "brand=myBrand;category=sport", contentUrl: "https://my_content_url.com/" ) interstitialAd?.load(requestOptions: requestOptions) // Or // interstitialAd?.load() ``` Multiple instances of `InterstitialAd` can be used to load multiple ads using the same placementId. :::info Ad load call will preload ad before you show it so that ads can be shown with zero latency when needed. This preloaded ad will expire after certain period. If you try to show an expired ad, you will get `BlueStackErrorAdExpired` error on `func onAdFailedToDisplay(_ ad: any FullScreenDisplayableAd, _ error: any Error)` callback. Once the ad get expired, you can call load again using existing instance to preload a new ad. ::: ## Ad events ### Register for interstitial events InterstitialAd provides lifecycle events and other full-screen events through `InterstitialAdDelegate` and `FullScreenDelegate`. Use following code to listen to all the events that interstitial ad invokes. ```objectivec showLineNumbers self.interstitialAd.delegate = self; self.interstitialAd.fullScreenDelegate = self; ``` ```swift showLineNumbers interstitialAd?.delegate = self interstitialAd?.fullScreenDelegate = self ``` ### Interstitial ad lifecycle events `InterstitialAdDelegate` has the following methods for listening to interstitial ad's lifecycle events. ```objectivec showLineNumbers - (void)didLoadInterstitialAd:(BLSInterstitialAd *)ad { NSLog(@"Interstitial ad loaded"); } - (void)interstitialAd:(BLSInterstitialAd *)ad didFailedToLoadWithError:(NSError *)error { NSLog(@"Failed to load interstitial ad with error: %@", error.localizedDescription); } ``` ```swift showLineNumbers func onAdLoaded(_ ad: BlueStackSDK.InterstitialAd) { print("Interstitial ad loaded") } func onAdFailedToLoad(_ ad: BlueStackSDK.InterstitialAd, _ error: any Error) { print("Failed to load interstitial ad with error: \(error.localizedDescription)") } ``` ### Interstitial ad full-screen events `FullScreenDelegate` has the following methods for receiving full-screen content events. ```objectivec showLineNumbers - (void)didDisplayAd:(id)ad { NSLog(@"Interstitial ad displayed"); } - (void)ad:(id)ad didFailedToDisplayWithError:(NSError *)error { NSLog(@"Failed to display interstitial ad with error: %@", error.localizedDescription); } - (void)didClickAd:(id)ad { NSLog(@"Interstitial ad clicked."); } - (void)didDismissAd:(id)ad { NSLog(@"Interstitial ad dismissed"); } ``` ```swift showLineNumbers func onAdDisplayed(_ ad: any FullScreenDisplayableAd) { print("Interstitial ad displayed") } func onAdFailedToDisplay(_ ad: any FullScreenDisplayableAd, _ error: any Error) { print("Failed to display interstitial ad with error: \(error.localizedDescription)") } func onAdClicked(_ ad: any FullScreenDisplayableAd) { print("Interstitial ad clicked.") } func onAdDismissed(_ ad: any FullScreenDisplayableAd) { print("Interstitial ad dismissed") } ``` ## Show an Interstitial Ad To show an interstitial ad call `show(fromRootViewController:)` method on `InterstitialAd` instance with an optional root view controller. ```objectivec showLineNumbers if (self.interstitialAd.isReady) { [self.interstitialAd showFromRootViewController:self]; } ``` ```swift showLineNumbers if interstitialAd.isReady { interstitialAd.show() } ``` ## Destroying an Interstitial Ad You need to keep a strong reference to the `InterstitialAd` instance. You can release it when you are done showing the interstitial ad (Interstitial ad has dismissed) simply by setting it to `nil`. ```objectivec showLineNumbers self.interstitialAd = nil; ``` ```swift showLineNumbers interstitialAd = nil ``` :::info We discourage to create and load new interstitial ad instance in load failed callback `onAdFailedToLoad(_ ad: , _ error:)`. ::: --- ## Native Ads(10-ad-formats) ## Overview BlueStack supports native ads, that allow you to retrieve the metadata of ad campaigns and present the ads yourself, within the context of your app, using your own art style. You are fully responsible for rendering the ad views using the information we supply. Native ads however offer methods to help you register impressions and clicks on your custom view. For a working implementation of this ad format, see the [azerion-inapp-demo-ios](https://github.com/azerion/azerion-inapp-demo-ios) demo app. ## Create a Native Ad ### Step 1. Import the SDK ```objectivec showLineNumbers #import ``` ```swift showLineNumbers import BlueStackSDK ``` ### Step 2. Initialize the factory To create a nativeAd you have to init an object with type AdsSDKFactory and set the nativeDelegate. ```objectivec showLineNumbers nativeAdsFactory = [[AdsSDKFactory alloc]init]; nativeAdsFactory.nativeDelegate = self; ``` ```swift showLineNumbers nativeAdFactory = AdsSDKFactory() nativeAdFactory.nativeDelegate = self ``` You also have to set placementId (minimum one time) ```objectivec showLineNumbers nativeAdsFactory.placementId = @"/YOUR_APP_ID/PLACEMENT_ID"; ``` ```swift showLineNumbers nativeAdFactory.placementId = "/YOUR_APP_ID/PLACEMENT_ID" ``` ## Load a Native Ad Using Preference you can set the preferred ad choices position , although you need to keep in mind that in some cases it might not position it where mentioned since some of the adnetworks wont take this parameter into consideration , so preferably set the preferred position here as well in the didLoad once the request succeeds. ### Native Ad AdChoice: Finally to execute the request you have to call `loadNativeWithPreferences:`. By default it will load with Cover Image. ```objectivec showLineNumbers Preference *preferences = [[Preference alloc]init]; [nativeAdsFactory loadNativeWithPreferences:preferences]; ``` ```swift showLineNumbers let preferences = Preference() nativeAdFactory.loadNative(withPreferences: preferences) ``` ### Native Ad AdChoice Without Cover Image if you like to execute the request without cover Image you can set the option **withCover** to NO : ```objectivec showLineNumbers Preference *preferences = [[Preference alloc]init]; [nativeAdsFactory loadNativeWithPreferences:preferences withCover:NO]; ``` ```swift showLineNumbers let preferences = Preference() nativeAdFactory.loadNative(withPreferences: preferences,withCover:false) ``` ## Ad Events ### Register for Native Ad Events To register for Native Ad events, set the `AdsAdapterNativeDelegate` delegate. ```objectivec showLineNumbers nativeAdFactory.nativeDelegate = self; ``` ```swift showLineNumbers nativeAdFactory.nativeDelegate = self ``` ### Implement Native Ad Events `adsAdapter:nativeObjectDidLoad:` will be called by the SDK when your nativeObject is ready. now you can create your own view. ```objectivec showLineNumbers -(void)adsAdapter:(AdsAdapter *)adsAdapter nativeObjectDidLoad:(NativeObject *)nativeObject{ NSLog(@"adsAdapterNativeObjectDidLoad:"); self.titleLabel.text = nativeObject.title; self.contextLabel.text = nativeObject.socialContext; self.bodyLabel.text = nativeObject.body; //possibility to customize the badge title [nativeObject updateBadgeTitle:@"Publicité"]; badgeView = nativeObject.badgeView; [_nativeObject registerViewForInteraction:self.nativeView withMediaView:self.backgroundImage withIconImageView:self.iconImage withViewController:[APP_DELEGATE drawerViewController] withClickableView:self.callToActionButton]; ... } ``` ```swift showLineNumbers func adsAdapter(_ adsAdapter: AdsAdapter!, nativeObjectDidLoad nativeObject: NativeObject!) { nativeView.layer.borderWidth = 1 nativeView.layer.borderColor = UIColor.lightGray.cgColor titleLabel.text = nativeObject.title socialContextLabel.text = nativeObject.socialContext descriptionLabel.text = nativeObject.body if self.badgeView != nil { self.badgeView?.removeFromSuperview() } self.nativeObject = nativeObject badgeView = self.nativeObject?.badgeView if (badgeView != nil) { var frame = badgeView?.frame frame?.origin.y = 3 frame?.origin.x = 3 badgeView?.frame = frame! self.nativeView.addSubview(badgeView!) } if adChoiceBadgeView != nil { adChoiceBadgeView?.removeFromSuperview() } adChoiceBadgeView = self.nativeObject?.adChoiceBadgeView if adChoiceBadgeView != nil { var frame = adChoiceBadgeView?.frame let widthFrame = frame!.size.width - 3 frame?.origin.y = 3 frame?.origin.x = self.nativeView.frame.size.width - widthFrame adChoiceBadgeView?.frame = frame! self.nativeView.addSubview(adChoiceBadgeView!) } // download images self.backgroundImage.image = nil self.iconImage.image = nil self.iconImage.layer.cornerRadius = 16 self.iconImage.clipsToBounds = true self.callToActionButton.setTitle(self.nativeObject?.callToAction, for: UIControl.State()) if self.nativeObject?.displayType == DisplayType.appInstall { self.callToActionButton.setImage(#imageLiteral(resourceName: "download"), for: UIControl.State()) }else if self.nativeObject?.displayType == .content { self.callToActionButton.setImage(#imageLiteral(resourceName: "arrow"), for: UIControl.State()) } self.callToActionButton.titleLabel?.textAlignment = .center self.nativeObject?.registerView(forInteraction: self.nativeView, withMediaView: self.backgroundImage, withIconImageView: self.iconImage, with: self, withClickableView: self.callToActionButton) self.nativeView.isHidden = false } ``` `adsAdapter:nativeObjectDidFail:` will be called when all ads servers fail. it will return the error of last called ads server. ```objectivec showLineNumbers -(void)adsAdapter:(AdsAdapter *)adsAdapter nativeObjectDidFailWithError:(NSError *)error withCover:(BOOL)cover { } ``` ```swift showLineNumbers func adsAdapter(_ adsAdapter: AdsAdapter!, nativeObjectDidFailWithError error: Error!, withCover cover: Bool){ } ``` ## Native Ad Assets Once a native ad is loaded, you may retrieve its metadata with the following methods: ### **Ad Title** - 50 maximum character length string of ad headline - Provide enough space to display the entire length of the Ad Title - asset name : **nativeObject.title** ### **Ad Text** - 150 maximum character length string of ad text - Provide enough space to display the entire length of the Ad Text - asset name : **nativeObject.body** ### **CTA Text** - Text for a button - 12 characters maximum - asset name : **nativeObject.callToAction** ### **Sponsored Marker** - Badge view (an icon) - change according ad network - must be inserted on top right - asset name : **nativeObject.adChoiceBadgeView** ### **Distinguishable Ad** - “Ad” (can be localized) - Badge that says “AD” and is at least 15x15px (can be localized) - change according ad network - must be inserted on top left - asset name : **nativeObject.badgeView** ```objectivec showLineNumbers // Get the app name title=nativeObject.title; // Get the app description (tagline) body=nativeObject.body; // Get the "Ad" badge view. You must show this view on your ad view to denote an ad if(nativeObject.badgeView){ badge=nativeObject.badgeView; ... } // Get the "AdChoice" badge view. You must show this view on your ad view to denote an ad if(nativeObject.adChoiceBadgeView){ adChoiceBadge=nativeObject.adChoiceBadgeView; ... } // Get the localized text to print on the call to action button, such as "DOWNLOAD , LEARN MORE ..." callToAction=nativeObject.callToAction; [_nativeObject registerViewForInteraction:...]; ``` ```swift showLineNumbers // Get the app name title=nativeObject.title; // Get the app description (tagline) body=nativeObject.body; // Get the "Ad" badge view. You must show this view on your ad view to denote an ad if(nativeObject.badgeView){ badge=nativeObject.badgeView; ... } // Get the "AdChoice" badge view. You must show this view on your ad view to denote an ad if(nativeObject.adChoiceBadgeView){ adChoiceBadge=nativeObject.adChoiceBadgeView; ... } // Get the localized text to print on the call to action button, such as "DOWNLOAD , LEARN MORE ..." callToAction=nativeObject.callToAction; self.nativeObject?.registerView(forInteraction: self.nativeView, withMediaView: self.backgroundImage, withIconImageView: self.iconImage, with: self, withClickableView: self.callToActionButton) ``` ## Caching Native Ad Ad metadata that you receive can be cached and re-used for up to 3 hours. If you plan to use the metadata after this time period, make a call to load a new ad. ## Assets download we provide method to download assets. ## registerViewForInteraction parameters * self.nativeView. : containerView of native Ad * withMediaView : coverImageView * withIconImageView : iconImageView * withViewController : parent ViewController of nativeAd * withClickableView : the button of nativeAd ## Native Ad Without Cover Image ```objectivec showLineNumbers [_nativeObject registerViewForInteraction:self.nativeView withMediaView:nil withIconImageView:self.iconImage withViewController:[APP_DELEGATE drawerViewController] withClickableView:self.callToActionButton]; ``` ```swift showLineNumbers self.nativeObject?.registerView(forInteraction: self.nativeView, withMediaView: nil, withIconImageView: self.iconImage, with: self, withClickableView: self.callToActionButton) ``` ## Native Ad With Cover Image ```objectivec showLineNumbers [_nativeObject registerViewForInteraction:self.nativeView withMediaView:self.backgroundImage withIconImageView:self.iconImage withViewController:[APP_DELEGATE drawerViewController] withClickableView:self.callToActionButton]; ``` ```swift showLineNumbers self.nativeObject?.registerView(forInteraction: self.nativeView, withMediaView: self.backgroundImage, withIconImageView: self.iconImage, with: self, withClickableView: self.callToActionButton) ``` ## Customizable Badge Badge in the nativeAd is customizable now using the following method: ```objectivec showLineNumbers [_nativeObject updateBadgeTitle:@"newBadgeTitle"]; ``` ```swift showLineNumbers self.nativeObject?.updateBadgeTitle("newBadgeTitle") ``` >note that the new method returns a BOOL indicating if the update was successful or not. ## Click - registerViewForInteraction It's **HIGHLY** recommended to only register ONE and ONLY one view for interaction , because some of the AdNetworks only accept one view and if you try to assign more than one then probably none of the views you assign will be responsive. --- ## Rewarded Ads(10-ad-formats) ## Overview A rewarded ad is an ad that user can choose to watch in exchange for some rewards. For a working implementation of this ad format, see the [azerion-inapp-demo-ios](https://github.com/azerion/azerion-inapp-demo-ios) demo app. ## Create a Rewarded Ad To create a rewarded ad, instantiate an instance of `RewardedAd` with a placement Id. ```objectivec showLineNumbers #import @interface ViewController () @property (strong, nonatomic) BLSRewardedAd *rewardedAd; @end @implementation ViewController - (void)viewDidLoad { self.rewardedAd = [[BLSRewardedAd alloc] initWithPlacementID:@"REWARDED_PLACEMENT_ID"]; } @end ``` ```swift showLineNumbers import BlueStackSDK class ViewController: UIViewController { var rewardedAd: RewardedAd? override func viewDidLoad() { super.viewDidLoad() rewardedAd = RewardedAd(placementID: "REWARDED_PLACEMENT_ID") } } ``` ## Load a Rewarded Ad Rewarded ad can be loaded by calling `load(requestOptions:)` method with a `RequestOptions` instance for supplying targeting information or simply calling `load()` method. ```objectivec showLineNumbers RequestOptions *requestOptions = [[RequestOptions alloc] initWithAge:@(25) location:[[CLLocation alloc] initWithLatitude:48.87610 longitude:10.453] gender:GenderMale keyword:@"brand=myBrand;category=sport" contentUrl:@"https://my_content_url.com/"]; [self.rewardedAd loadWithRequestOptions:requestOptions]; // Or // [self.rewardedAd load]; ``` ```swift showLineNumbers let requestOptions = RequestOptions( age: 25, location: CLLocation.init(latitude: 48.87610, longitude: 10.453), gender: .male, keyword: "brand=myBrand;category=sport", contentUrl: "https://my_content_url.com/" ) rewardedAd?.load(requestOptions: requestOptions) // Or // rewardedAd?.load() ``` ## Ad Events ### Registering for Rewarded Ad events Rewarded ad provides lifecycle events and other full-screen events through `RewardedAdDelegate` and `FullScreenDelegate`. Use following code to listen to all the events that rewarded ad invokes. ```objectivec showLineNumbers self.rewardedAd.delegate = self; self.rewardedAd.fullScreenDelegate = self; ``` ```swift showLineNumbers rewardedAd?.delegate = self rewardedAd?.fullScreenDelegate = self ``` ### Rewarded ad lifecycle events `RewardedAdDelegate` has the following methods for listening to rewarded ad's lifecycle events. ```objectivec showLineNumbers - (void)didLoadRewardedAd:(BLSRewardedAd *)ad { NSLog(@"Rewarded ad loaded"); } - (void)rewardedAd:(BLSRewardedAd *)ad didFailedToLoadWithError:(NSError *)error { NSLog(@"Failed to load rewarded ad with error: %@", error.localizedDescription); } ``` ```swift showLineNumbers func onAdLoaded(_ ad: BlueStackSDK.RewardedAd) { print("Rewarded ad loaded") } func onAdFailedToLoad(_ ad: BlueStackSDK.RewardedAd, _ error: any Error) { print("Failed to load rewarded ad with error: \(error.localizedDescription)") } ``` You can get the reward information by implementing the `onRewardEarned(_ ad: , _ reward:)` delegate method. ```objectivec showLineNumbers - (void)rewardedAd:(BLSRewardedAd *)ad didEarnedReward:(Reward *)reward { NSLog(@"Reward Earned: %f", reward.amount.floatValue); } ``` ```swift showLineNumbers func onRewardEarned(_ ad: BlueStackSDK.RewardedAd, _ reward: BlueStackSDK.Reward?) { if let amount = reward?.amount { print("Rewarde earned: \(amount.floatValue)") } } ``` ### Rewarded ad full-screen events `FullScreenDelegate` has the following methods for receiving full-screen content events. ```objectivec showLineNumbers - (void)didDisplayAd:(id)ad { NSLog(@"Rewarded ad displayed"); } - (void)ad:(id)ad didFailedToDisplayWithError:(NSError *)error { NSLog(@"Failed to display rewarded ad with error: %@", error.localizedDescription); } - (void)didClickAd:(id)ad { NSLog(@"Rewarded ad clicked."); } - (void)didDismissAd:(id)ad { NSLog(@"Rewarded ad dismissed"); } ``` ```swift showLineNumbers func onAdDisplayed(_ ad: any FullScreenDisplayableAd) { print("Rewarded ad displayed") } func onAdFailedToDisplay(_ ad: any FullScreenDisplayableAd, _ error: any Error) { print("Failed to display rewarded ad with error: \(error.localizedDescription)") } func onAdClicked(_ ad: any FullScreenDisplayableAd) { print("Rewarded ad clicked.") } func onAdDismissed(_ ad: any FullScreenDisplayableAd) { print("Rewarded ad dismissed") } ``` ## Show a Rewarded Ad To show a rewarded ad call `show(fromRootViewController:)` method on `RewardedAd` instance with an optional root view controller. ```objectivec showLineNumbers if (self.rewardedAd.isReady) { [self.rewardedAd showFromRootViewController:self]; } ``` ```swift showLineNumbers if rewardedAd.isReady { rewardedAd.show() } ``` ## Destroying Rewarded Ad You need to keep a strong reference to the `RewardedAd` instance. You can release it when you are done showing the rewarded ad (Rewarded ad has dismissed) simply by setting it to `nil`. ```objectivec showLineNumbers self.rewardedAd = nil; ``` ```swift showLineNumbers rewardedAd = nil ``` :::info We discourage to create and load new rewarded ad instance in load failed callback `onAdFailedToLoad(_ ad: , _ error:)`. --- ## Supported Networks(1-primairy) BlueStack's mediation feature enables you to deliver advertisements to your app from various sources, including BlueStack itself and third-party ad networks. This section provides guidance on integrating mediation partner SDKs through BlueStack's third-party SDK adapters. :::info **Recommended:** include all mediation adapters by default. Omit an adapter only if you have a specific reason not to ship that demand source. The [Get Started](../../index.md#add-mediation-partners) page shows the full bundle; the sections below cover per-network details and opt-in extras. ::: ***Important Note:*** You must add `BlueStackSDK` to your app's target for using any of the mediation adapters using SPM. ## In-App Bidding In the `Podfile` of your application project add `BlueStackBiddingAdapter` dependency ```ruby pod 'BlueStackBiddingAdapter' ``` Go to your project file --> Package Dependencies --> Add(+) --> Search for the BlueStackBiddingAdapter package ![BlueStackBiddingAdapter Swift Package Search](../../00-images/bluestack-bidding-adapter-spm-integration-1-light.png#gh-light-mode-only)![BlueStackBiddingAdapter Swift Package Search](../../00-images/bluestack-bidding-adapter-spm-integration-1-dark.png#gh-dark-mode-only) Add the `BlueStackBiddingAdapter` to your app's target ![Add BlueStackBiddingAdapter to target](../../00-images/bluestack-bidding-adapter-spm-integration-2-light.png#gh-light-mode-only)![Add BlueStackBiddingAdapter to target](../../00-images/bluestack-bidding-adapter-spm-integration-2-dark.png#gh-dark-mode-only) ## Google Mobile Ads In the `Podfile` of your application project add `BlueStackGoogleAdapter` dependency ```ruby pod 'BlueStackGoogleAdapter' ``` Go to your project file --> Package Dependencies --> Add(+) --> Search for [https://github.com/azerion/BlueStack-Google-Adapter](https://github.com/azerion/BlueStack-Google-Adapter) ![BlueStackGoogleAdapter Swift Package Search](../../00-images/bluestack-google-adapter-spm-integration-1-light.png#gh-light-mode-only)![BlueStackGoogleAdapter Swift Package Search](../../00-images/bluestack-google-adapter-spm-integration-1-dark.png#gh-dark-mode-only) Add the `BlueStackGoogleAdapter` to your app's target ![Add BlueStackGoogleAdapter to target](../../00-images/bluestack-google-adapter-spm-integration-2-light.png#gh-light-mode-only)![Add BlueStackGoogleAdapter to target](../../00-images/bluestack-google-adapter-spm-integration-2-dark.png#gh-dark-mode-only) ***Important Note:*** You must add **GADApplicationIdentifier** in **Info.plist** file of your project. ``` GADApplicationIdentifier YOUR_APP_ID ``` ## Equativ In the `Podfile` of your application project add `BlueStackEquativAdapter` dependency ```ruby pod 'BlueStackEquativAdapter' ``` To get the in app bidding add the BlueStack Bidding adapter dependency to your app's podfile also. ```ruby pod 'BlueStackBiddingAdapter' ``` ## Amazon In-App Bidding Add the BlueStack Amazon publisher service in-app bidding adapter dependency to your app's podfile. ```ruby pod 'BlueStack-SDK', :subspecs=>["BluestackAmazonPublisherServicesAdapter"] ``` Using Swift Package Manager add the `BluestackAmazonPublisherServicesAdapter` to your app's target ![BlueStackSDK mediation adapter list](../../00-images/BlueStackAmazonPublisherServiceAdapter_spm-light.png#gh-light-mode-only)![BlueStackSDK mediation adapter list](../../00-images/BlueStackAmazonPublisherServiceAdapter_spm-dark.png#gh-dark-mode-only) ## Supported Ad Networks | Ad Network | SDK Version | Adapter Version | Supported Ad Formats | |-----------------------|-------------|-----------------|-------------------------------------------------------| | **Google** | | | Banner / MREC, Interstitial, Rewarded Ads, Native Ads | | **Equativ** | | | Banner / MREC, Interstitial | | **BlueStack Bidding** | N/A | | Banner / MREC, Interstitial ### Notes - Ensure all dependencies are included as outlined in each network’s integration guide. - Ad formats may require additional configurations or testing to confirm functionality. --- ## AppLovin(2-secondary) This guide shows you how to integrate our BlueStack mediation adapter of AppLovin MAX SDK with your current Android app and set up additional request parameters. Release notes can be found [here](./02-applovin-changelog.md) ## Supported ad formats - Banner - MREC - Interstitial - Rewarded ## Requirements - Xcode 15.1 or higher - iOS: 13.0 or higher ## AppLovin Configuration When in the Applovin MAX dashboard, navigate to **Manage -> Networks**, at the bottom of the page you have the option to **add a Custom Network**. You'll be directe to a new page. Please use the following details when setting up the custom network: ![img.png](applovin_config.png) ```shell Custom Network Name: Azerion Bluestack iOS Class name: BlueStackAppLovinAdapter.BlueStackMediationAdapter Android Class Name: com.azerion.bluestack.ApplovinAdapter ``` Next navigate to **Manage -> Ad Units**, and select the Ad Unit you would like to have Bluestack added. On the ad unit configuration page, scroll down to **Custom Networks** and click on Azerion Bluestack to show the configuration options. The Bluestack Application ID and the Placement ID's can be configured here. All ID's and CPM configuration will be provided by our publishing team. ![img.png](applovin_placements.png) ## Integrate BlueStackAppLovinAdapter in your application project In the `Podfile` of your application project add `BlueStackAppLovinAdapter` dependency ```shell pod 'BlueStackAppLovinAdapter', '5.1.4.0' ``` # Ad Formats ## Banner ### Supported MAAdDelegate Callback ```swift showLineNumbers func didLoad(_ ad: MAAd) { } func didFailToLoadAd(forAdUnitIdentifier adUnitIdentifier: String, withError error: MAError) { } func didClick(_ ad: MAAd) { } ``` The BlueStacks AppLovin adapter includes the preferred height of the banner adview in the banner load callback using `BlueStackKeys.BANNER_PREFERRED_HEIGHT` key. ```swift showLineNumbers func adsAdapter(_ adsAdapter: AdsAdapter!, bannerDidLoad adView: UIView!, preferredHeight: CGFloat) { self.delegate?.didLoadAd(forAdView: adView, withExtraInfo: [BlueStackKeys.bannerPreferredHeight : preferredHeight]) } ``` You can use the preferred height to resize your banner container. ## Passing Location data to BlueStack Banner Ad ```swift showLineNumbers adView = MAAdView(adUnitIdentifier: "YOUR_AD_UNIT_ID") adView.setLocalExtraParameterForKey(BlueStackKeys.latitude, value: 45.0234) adView.setLocalExtraParameterForKey(BlueStackKeys.longitude, value: 101.1987) adView.setLocalExtraParameterForKey(BlueStackKeys.locationConsentFlag, value: 3) ``` **Note:** You must set the view controller to load the banner ad. ```swift showLineNumbers adView.setLocalExtraParameterForKey(BlueStackKeys.viewController, value: self) ``` ## MREC ### Supported MAAdDelegate Callback ```swift showLineNumbers func didLoad(_ ad: MAAd) { } func didFailToLoadAd(forAdUnitIdentifier adUnitIdentifier: String, withError error: MAError) { } func didClick(_ ad: MAAd) { } ``` The BlueStacks AppLovin adapter includes the preferred height of the MREC adview in the load callback using `BlueStackKeys.BANNER_PREFERRED_HEIGHT` key. ```swift showLineNumbers func adsAdapter(_ adsAdapter: AdsAdapter!, bannerDidLoad adView: UIView!, preferredHeight: CGFloat) { self.delegate?.didLoadAd(forAdView: adView, withExtraInfo: [BlueStackKeys.bannerPreferredHeight : preferredHeight]) } ``` You can use the preferred height to resize your MREC ad container. ## Passing Location data to BlueStack MREC Ad ```swift showLineNumbers adView = MAAdView(adUnitIdentifier: "YOUR_AD_UNIT_ID", adFormat: MAAdFormat.mrec) adView.setLocalExtraParameterForKey(BlueStackKeys.latitude, value: 45.0234) adView.setLocalExtraParameterForKey(BlueStackKeys.longitude, value: 101.1987) adView.setLocalExtraParameterForKey(BlueStackKeys.locationConsentFlag, value: 3) ``` **Note:** You must set the view controller to load the MREC ad. ```swift showLineNumbers adView.setLocalExtraParameterForKey(BlueStackKeys.viewController, value: self) ``` ## Interstitial ### Supported MAAdViewAdDelegate Callback ```swift showLineNumbers func didLoad(_ ad: MAAd) { } func didFailToLoadAd(forAdUnitIdentifier adUnitIdentifier: String, withError error: MAError) { } func didDisplay(_ ad: MAAd) { } func didClick(_ ad: MAAd) { } func didHide(_ ad: MAAd) { } ``` ## Passing Location data to BlueStack Interstitial Ad ```swift showLineNumbers interstitialAd = MAInterstitialAd(adUnitIdentifier: "YOUR_AD_UNIT_ID") interstitialAd.setLocalExtraParameterForKey(BlueStackKeys.latitude, value: 45.0234) interstitialAd.setLocalExtraParameterForKey(BlueStackKeys.longitude, value: 101.1987) interstitialAd.setLocalExtraParameterForKey(BlueStackKeys.locationConsentFlag, value: 3) ``` **Note:** You must set the view controller to load and show the Interstitial ad. ```swift showLineNumbers interstitialAd.setLocalExtraParameterForKey(BlueStackKeys.viewController, value: self) ``` ## Rewarded ### Supported MAAdDelegate Callback ```swift showLineNumbers func didLoad(_ ad: MAAd) { } func didFailToLoadAd(forAdUnitIdentifier adUnitIdentifier: String, withError error: MAError){ } func didDisplay(_ ad: MAAd) { } func didClick(_ ad: MAAd) { } func didHide(_ ad: MAAd) { } func didFail(toDisplay ad: MAAd, withError error: MAError) { } ``` ### Supported MARewardedAdDelegate Callback ```swift showLineNumbers func didRewardUser(for ad: MAAd, with reward: MAReward) { } ``` ## Passing Location data to BlueStack Rewarded Video Ad ```swift showLineNumbers rewardedAd = MARewardedAd.shared(withAdUnitIdentifier: "YOUR_AD_UNIT_ID") rewardedAd.setLocalExtraParameterForKey(BlueStackKeys.latitude, value: 45.0234) rewardedAd.setLocalExtraParameterForKey(BlueStackKeys.longitude, value: 101.1987) rewardedAd.setLocalExtraParameterForKey(BlueStackKeys.locationConsentFlag, value: 3) ``` **Note:** You must set the view controller to load and show the Rewarded ad. ```swift showLineNumbers rewardedAd.setLocalExtraParameterForKey(BlueStackKeys.viewController, value: self) ``` ## Meaning of LOCATION_CONSENT_FLAG - 0 = Not allow to send location. - 1 = When you managed location according to consent value. - 2 and 3 = Allow the SDK to managed location directly in accordance with the consent value use TCF v1 or TCF v2, see with the Azerion team as it depends on your implementation. --- ## AppLovin - Release Notes(2-secondary) ## [5.1.4.0] - 2025-06-30 - Minimum iOS deployment target has been changed to 13.0 - Marketing version updated to 5.1.4 - BlueStackSDK version updated to >= 5.1.1 ## [4.4.8.1] - 2024-09-04 ### Added - SPM (Swift Package Manager) support. ## [4.4.8.0] - 2024-08-30 ### Added - NativeAd support ```ruby pod 'BlueStackAppLovinAdapter', '4.4.8.0' ``` ## [4.3.0.1] - 2024-01-23 ### Changed - OS version requirement downgraded from 13.0 to 12.2 ```ruby pod 'BlueStackAppLovinAdapter', '4.3.0.1' ``` ## [4.3.0.0] - 2024-01-12 ### Added - Initial release based on BluestackSDk/Core 4.3.0 - Banner - MREC - Interstitial - Rewarded ```ruby pod 'BlueStackAppLovinAdapter', '4.3.0.0' ``` --- ## Google Mobile Ads(2-secondary) This guide shows you how to integrate our BlueStack mediation adapter of Google Mobile Ads SDK with your current iOS app and set up additional request parameters. Release notes can be found [here](./04-gma-changelog.md) ## Supported ad formats - Banners - Interstitials - Native Ads - Rewarded Video ## Requirements - Use Xcode 16.0 or higher - Target iOS 13.0 or higher ## Set up Google Ad Manager The following steps are needed to add us as a Demand partner in Ad Manager. These changes need to be set up in [Google Ad Manager](https://admanager.google.com/). ### Add a new Ad Network First you need to add us as an Ad Network in Google Ad Manager 1. Under **Admin**, go to **Companies** 2. Click the _New Company_ button and select **Ad Network** 3. For the name, you can use **BlueStack**, but you are free to enter what you want here 4. For Ad Network, please select **Improve Digital** 5. Don't forget to enable the _Medation_ toggle 6. Other fields can be ignored 7. Press _Save_ ![New ad network](./img/gam/step_1.png) ### Add Yield Groups Next we need to add some yield groups. The basic rule is that for each available format you add one yield group (so one for Banner, one for Interstitial, etc.) If you already have `Yield Groups` set up you can skip this step. 1. Under **Delivery**, go to **Yield Groups** 2. Click the _New Yield Group_ button 3. Insert any name you wish to use 4. Select the correct Ad Format 5. Inventory type should be set to Mobile App 6. For Banner, select at least one size that best fits 7. Please make sure your app's placements are targetted for this Yield Group ![New yield group](./img/gam/step_2.png) ### Add A Yield Partner and Define a custom event Now you have to add us as a `Yield` partner in the `Yield Group` you just created, or on a yield group you already have set before. 1. Open the `Yield Group` you want to add us as a partner 2. Scroll down on the page and click the _Add yield partner_ button 3. As yield partner, choose the company you added in [Add a new Ad Network] 4. Select integration type **Custom Event** 5. Select Platform **iOS** 6. Select Status **Active** 7. Default CPM will be provided by your Azerion representative 8. For Label, use: **BlueStackCustomEvent** 9. For Class Name, use: **BlueStackGoogleMediationAdapter.BlueStackCustomEvent** 10. As parameter, please enter the placement ID provided by your Azerion representative that matches the format you intend to use this yield group for 11. Repeat for each Yield group / format ![Add yield partner](./img/gam/step_3.png) ## Set up BlueStack Mediation adapter in Application ### SDK Integration #### Add BlueStack Core SDK: Please check the [Integration Documentation](../../index.md) of the BlueStack Core SDK to add it to your application. #### Add BlueStack Google Mediation Adapter: ```ruby pod 'BlueStackGoogleMediationAdapter' ``` Go to your project file --> Package Dependencies --> Add(+) --> Search for [https://github.com/azerion/BlueStack-Google-Mediation](https://github.com/azerion/BlueStack-Google-Mediation) ![BlueStackGoogleMediationAdapter Swift Package Search](../../00-images/bluestack-google-mediation-adapter-spm-integration-1-light.png#gh-light-mode-only)![BlueStackGoogleMediationAdapter Swift Package Search](../../00-images/bluestack-google-mediation-adapter-spm-integration-1-dark.png#gh-dark-mode-only) Add the `BlueStackGoogleMediationAdapter` to your app's target ![Add BlueStackGoogleMediationAdapter to target](../../00-images/bluestack-google-mediation-adapter-spm-integration-2-light.png#gh-light-mode-only)![Add BlueStackGoogleMediationAdapter to target](../../00-images/bluestack-google-mediation-adapter-spm-integration-2-dark.png#gh-dark-mode-only) #### Import custom event classes in your source file: ```objectivec showLineNumbers #import @import GoogleMobileAds; ``` ```swift showLineNumbers import BlueStackGoogleMediationAdapter import GoogleMobileAds ``` ### Set up Ad Formats :::info You must initialize `BlueStack SDK` using your **APP_ID** before loading ads of any formats. ::: You may now use BlueStack Google Adapter to show ads the same way it's described in the [Google Ad Manager Documentation]. The adapter code and the setup you did on your Google Ad Manager will allow BlueStack Ads to deliver ads. #### Banner / Square Before you can create custom events, you need to [integrate the banner ad format into your app]. :::info You must pass the ViewController to the GADBannerView ::: ```objectivec showLineNumbers GADBannerView *bannerView; ...................... bannerView.rootViewController = self; ``` ```swift showLineNumbers var bannerView: GADBannerView! bannerView.rootViewController = self ``` #### Interstitial Before you can create custom events, you need to [integrate the Interstitial ad format into your app]. :::info In the Interstitial Ad implementation you must pass the ViewController to the BlueStackCustomEvent ::: ```objectivec showLineNumbers [BlueStackCustomEvent setViewController:self]; ``` ```swift showLineNumbers BlueStackCustomEvent.viewController = self ``` #### Native Ads Before you can create custom events, you need to [integrate the Native ad format into your app]. :::info In the Native Ad implementation you must pass the ViewController to the BlueStackCustomEvent ::: ```objectivec showLineNumbers [BlueStackCustomEvent setViewController:self]; ``` ```swift showLineNumbers BlueStackCustomEvent.viewController = self ``` #### Rewarded video Ads Before you can create custom events, you need to [integrate the Rewarded video ad format into your app]. :::info In the Rewarded video implementation you must pass the ViewController to the BlueStackCustomEvent ::: ```objectivec showLineNumbers [BlueStackCustomEvent setViewController:self]; ``` ```swift showLineNumbers BlueStackCustomEvent.viewController = self ``` ### Custom targeting / Keywords If you need to send your custom key-value pairs. You can specify key-value-targeting and keywords information in the ad request as follows,and you must send your custom key-value pairs also as follows,add the extras to the registerAdNetworkExtras method as follows: ```objectivec showLineNumbers GADRequest* request = [GADRequest request]; BlueStackCustomEventMediationExtras *extras = [[BlueStackCustomEventMediationExtras alloc] initWithKeywords:@"target=mngadsdemo;version=5.1.3" customTargetingBlueStack:@{ @"age" :@"25",@"consent" :@"test",@"gender" :@"male"}]; [request registerAdNetworkExtras:extras]; ``` ```swift showLineNumbers let request = GADRequest() let keyword = "target=mngadsdemo;version=5.1.3" let json = ["age" :"25", "consent" :"test", "gender" :"male"] let extras = BlueStackCustomEventMediationExtras(keywords: keyword, customTargetingBlueStack: json) request.register(extras) ``` [Google Ad Manager Documentation]:https://developers.google.com/ad-manager/mobile-ads-sdk/ios/quick-start [integrate the banner ad format into your app]:https://developers.google.com/ad-manager/mobile-ads-sdk/ios/banner?hl=en [integrate the Interstitial ad format into your app]:https://developers.google.com/ad-manager/mobile-ads-sdk/ios/interstitial [integrate the Native ad format into your app]:https://developers.google.com/ad-manager/mobile-ads-sdk/ios/native/start [integrate the Rewarded video ad format into your app]:https://developers.google.com/ad-manager/mobile-ads-sdk/ios/rewarded --- ## Google Mobile Ads - Release Notes ## Version 6.0.3 ### Release date: July 21th, 2026 **Updated** - Updated **Google SDK** to v13. ## Version 6.0.2 ### Release date: July 3rd, 2026 **Updated** - Updated **BlueStackSDK** to 6.0.2. ## Version 5.4.0 ### Release date: January 12th, 2026 **Changed** - Compatiable with core v5.4.0. ## Version 5.3.7 ### Release date: January 8th, 2026 **Fixed** - Viewability tracking issue. ## Version 5.3.5 ### Release date: December 7th, 2025 **Fixed** - Fixed build issue in xcode 16.4+ ## Version 5.3.4 ### Release date: December 3rd, 2025 **Updated** - Google SDK updated to 12.14.0 ## Version 5.3.3.0 ### Release date: November 20th, 2025 **Updated** - BlueStack core sdk dependency to 5.3.3 - Refactored NativeAd implementation. ## Version 5.1.4.1 ### Release date: June 26th, 2025 **Updated** - Updated marketing version to 5.1.5. ## Version 5.1.4.0 ### Release date: April 24th, 2025 **Updated** - Updated **BlueStackSDK** to 5.1.4. - Updated the version of **Google Mobile Ads SDK** to 11.13.0 ## Version 1.0.0 ### Release date: November 8th, 2023 **Added** - Implemented DFP adapter using Swift. - Introducing xcframework instead of source files - Added missing mediation extras class file. **Removed** - Removed Objective-C implementation files **GADBlueStackBannerRenderer**, **GADBlueStackInterstitialRenderer**, **GADBlueStackMediationAdapter**, **GADBlueStackNativeAdRenderer**, **GADBlueStackRewardedRenderer** **Changed** - Use **BlueStackDFPMediationAdapter.BlueStackCustomEvent** instead of **GADBlueStackMediationAdapter** in google ad manager yield group configuration. - Use **BlueStackCustomEventMediationExtras** instead of **GADBlueStackMediationExtras** while sending extra params. - Integrate using Cocoapods or Swift Package Manager (SPM) ```ruby pod 'BlueStackDFPMediationAdapter', '1.0.0' ``` [BlueStackDFPMediationAdapter SPM integration](https://github.com/azerion/BlueStackDFPMediationAdapter) - Updated the version of BlueStack DFP Mediation SDK to 1.0.0 - Updated the version of Google Ads SDK to 10.10.0 --- ## Unity LevelPlay(2-secondary) This guide shows you how to integrate our BlueStack mediation adapter of Unity LevelPlay with your current iOS app and set up additional request parameters. Release notes can be found [here](./06-unity-levelplay-changelog.md) ## Supported ad formats - Banner - Rewarded - Interstitial ## Requirements - Xcode 15.1 or higher - iOS: 13.0 or higher ## Unity LevelPlay Configuration When in the Unity LevelPlay dashboard, under your app in the left panel navigate to **Setup -> Networks**, in the right panel at the bottom of the page you have the option to **Add custom network**. ![img.png](levelplay_ad_configuration_step_1.png) You'll be directe to a new page. Please enter **15c080481** in the **Network Key** input field and click **Confirm Key**. After confirmation it will show the network name **Improve Digital**. Then click **Save**. ![img.png](levelplay_ad_configuration_step_2.png) ```shell Custom Network Key: 15c080481 Custom Network Name: Improve Digital ``` **Congratulations!!!** You have added our network successfully. Next navigate to **Setup -> Instances** in the left panel, and click **Improve Digital** in the right panel. ![img.png](levelplay_ad_configuration_step_3.png) This will navigate you to a new page where you will add new instances for different types of ads. ![img.png](levelplay_ad_configuration_step_4.png) Click on the button ***+ Add Instance* at the bottom in the right panel, select the type of ad you want to add. ![img.png](levelplay_ad_configuration_step_5.png) Enter **Instance Name**, **placementId**, **Rate**, and select target **Mediation Groups**. Also add your **appId** at the top in the right panel and click **Save**. **Note:** The **placementId**s will be provided by the **Azerion team**. And **Rate** is used for **IronSource** reporting only, it will not be a reflection of the actual money. Rates can be aligned with the **Azerion team** as well. ![img.png](levelplay_ad_configuration_step_6.png) ## Integrate BlueStackLevelPlayMediationAdapter in your application project In the `Podfile` of your application project add `BlueStackLevelPlayMediationAdapter` dependency ```shell pod 'BlueStackLevelPlayMediationAdapter', '5.1.4.1' ``` Go to your project file --> Package Dependencies --> Add(+) --> Search for [https://github.com/azerion/BlueStack-LevelPlay-Mediation.git](https://github.com/azerion/BlueStack-LevelPlay-Mediation.git) ![spm_level_play_mediation_adapter_integration.png](../../00-images/spm_level_play_mediation_adapter_integration.png) #### Import custom event classes in your source file: ```objectivec showLineNumbers @import BlueStackLevelPlayMediationAdapter; @import IronSource; ``` ```swift showLineNumbers import BlueStackLevelPlayMediationAdapter import IronSource ``` # Ad Formats ## Banner ### Supported ISBannerAdDelegate Callback ```objectivec showLineNumbers // Indicates that a banner ad was loaded successfully - (void)adDidLoadWithView:(UIView *) view // The banner ad failed to load. Use ironSource ErrorTypes (No Fill / Other) - (void)adDidFailToLoadWithErrorType:(ISAdapterErrorType)errorType errorCode:(NSInteger)errorCode errorMessage:(nullable NSString*)errorMessage // Indicates an ad was clicked - (void)adDidClick ``` ```swift showLineNumbers // Indicates that a banner ad was loaded successfully func adDidLoad(_ bannerView: UIView) { } // The banner ad failed to load. Use ironSource ErrorTypes (No Fill / Other) func adDidFailToLoadWith(_ errorType: ISAdapterErrorType, errorCode: Int, errorMessage: String!) { } // Indicates an ad was clicked func adDidClick() { } ``` ## Initialization and load Banner Ad ```objectivec showLineNumbers self.adView = [[LPMBannerAdView alloc] initWithAdUnitId:@"YOUR_BANNER_AD_UNIT_ID"]; // 1. recommended - Adaptive ad size that adjusts to the screen width self.bannerSize = [LPMAdSize createAdaptiveAdSize]; // 2. Adaptive ad size using fixed width ad size // self.bannerSize = [LPMAdSize createAdaptiveAdSizeWithWidth:400]; // 3. Specific banner size - BANNER, LARGE, MEDIUM_RECTANGLE // self.bannerSize = [LPMAdSize mediumRectangleSize]; [self.adView setAdSize:self.bannerSize]; [self.bannerAd loadAdWithViewController:YOUR_VIEW_CONTROLLER_INSTANCE]; ``` ```swift showLineNumbers let adView = LPMBannerAdView(adUnitId: "YOUR_BANNER_AD_UNIT_ID") // 1. recommended - Adaptive ad size that adjusts to the screen width bannerSize = LPMAdSize.createAdaptive() // 2. Adaptive ad size using fixed width ad size // bannerSize = LPMAdSize.createAdaptiveAdSize(withWidth: 400) // 3. Specific banner size - BANNER, LARGE, MEDIUM_RECTANGLE // bannerSize = LPMAdSize.mediumRectangle() adView.setAdSize(bannerSize) adView.loadAd(with: YOUR_VIEW_CONTROLLER_INSTANCE) ``` ## Interstitial ### Supported ISInterstitialAdDelegate Callback ```objectivec showLineNumbers // Indicates that the interstitial ad was loaded successfully - (void)adDidLoad; // The interstitial ad failed to load. Use ironSource ErrorTypes (No Fill / Other) - (void)adDidFailToLoadWithErrorType:(ISAdapterErrorType)errorType errorCode:(NSInteger)errorCode errorMessage:(NSString*)errorMessage; // The interstitial ad was displayed successfully to the user. This indicates an impression. - (void)adDidOpen; // User closed the interstitial ad - (void)adDidClose; // The ad could not be displayed - (void)adDidFailToShowWithErrorCode:(NSInteger)errorCode errorMessage:(NSString*)errorMessage; // Indicates the ad was clicked - (void)adDidClick; ``` ```swift showLineNumbers // Indicates that the interstitial ad was loaded successfully func adDidLoad() { } // The interstitial ad failed to load. Use ironSource ErrorTypes (No Fill / Other) func adDidFailToLoadWith(_ errorType: ISAdapterErrorType, errorCode: Int, errorMessage: String!) { } // The interstitial ad was displayed successfully to the user. This indicates an impression. func adDidOpen() { } // User closed the interstitial ad func adDidClose() { } // The ad could not be displayed func adDidFailToShowWithErrorCode(_ errorCode: Int, errorMessage: String!) { } // Indicates the ad was clicked func adDidClick() { } ``` ## Initialization, load, and show Interstitial Ad ```objectivec showLineNumbers self.interstitialAd = [[LPMInterstitialAd alloc] initWithAdUnitId:@"YOUR_INTERSTITIAL_AD_UNIT_ID"]; [self.interstitialAd loadAd]; if ([self.interstitialAd isAdReady]) { // This will present the Interstitial. // Unlike Rewarded Videos there are no placements. [self.interstitialAd showAdWithViewController:YOUR_VIEW_CONTROLLER placementName:NULL]; } ``` ```swift showLineNumbers let interstitialAd = LPMInterstitialAd(adUnitId: "YOUR_INTERSTITIAL_AD_UNIT_ID") interstitialAd.loadAd() if (interstitialAd != nil && self.interstitialAd.isAdReady()) { // This will present the Interstitial. // Unlike Rewarded Videos there are no placements. interstitialAd.showAd(viewController: YOUR_VIEW_CONTROLLER, placementName: nil) } ``` ## Rewarded ### Supported ISRewardedVideoAdDelegate Callback ```objectivec showLineNumbers // Indicates that rewarded video ad was loaded successfully - (void)adDidLoad; // The rewarded video ad failed to load. Use ironSource ErrorTypes (No Fill / Other) - (void)adDidFailToLoadWithErrorType:(ISAdapterErrorType)errorType errorCode:(NSInteger)errorCode errorMessage:(NSString*)errorMessage; // The rewarded video ad was displayed successfully to the user. This indicates an impression. - (void)adDidOpen; // User closed the rewarded video ad - (void)adDidClose; // The ad could not be displayed - (void)adDidFailToShowWithErrorCode:(NSInteger)errorCode errorMessage:(NSString*)errorMessage; // User clicked the rewarded video ad - (void)adDidClick; // User received a reward after watching the ad - (void)adRewarded; ``` ```swift showLineNumbers // Indicates that rewarded video ad was loaded successfully func adDidLoad() { } // The rewarded video ad failed to load. Use ironSource ErrorTypes (No Fill / Other) func adDidFailToLoadWith(_ errorType: ISAdapterErrorType, errorCode: Int, errorMessage: String!) { } // The rewarded video ad was displayed successfully to the user. This indicates an impression. func adDidOpen() { } // User closed the rewarded video ad func adDidClose() { } // The ad could not be displayed func adDidFailToShowWithErrorCode(_ errorCode: Int, errorMessage: String!) { } // User clicked the rewarded video ad func adDidClick() { } // User received a reward after watching the ad func adRewarded() { } ``` ## Initialization, load, and show Rewarded Ad ```objectivec showLineNumbers LPMInitRequestBuilder *requestBuilder = [[LPMInitRequestBuilder alloc] initWithAppKey:kAppKey]; [requestBuilder withLegacyAdFormats:@[IS_REWARDED_VIDEO]]; LPMInitRequest *initRequest = [requestBuilder build]; [LevelPlay initWithRequest:initRequest completion:^(LPMConfiguration *_Nullable config, NSError *_Nullable error){ if(error) { // There was an error on initialization. Take necessary actions or retry return; } // Initialization was successful. You can now load banner ad or perform other tasks }]; if ([IronSource hasRewardedVideo]) { // This will present the Rewarded Video. [IronSource showRewardedVideoWithViewController:self]; } ``` ```swift showLineNumbers let requestBuilder = LPMInitRequestBuilder(appKey: YOUR_LEVEL_PLAY_APP_KEY) .withLegacyAdFormats([IS_REWARDED_VIDEO]) let initRequest = requestBuilder.build() LevelPlay.initWith(initRequest) if IronSource.hasRewardedVideo() { // This will present the Rewarded Video. IronSource.showRewardedVideo(with: self) } ``` ## Set Privacy Settings Change the privacy settings accordingly. By default both are **false**. Please note that you have to set them before initializing the **Iron Source/LevelPlay SDK**. ```objectivec showLineNumbers [PrivacySettings setIsAgeRestrictedUser:YES]; [PrivacySettings setUserOptout:YES]; ``` ```swift showLineNumbers PrivacySettings.setIsAgeRestrictedUser(true) PrivacySettings.setUserOptout(true) ``` ## Passing data for user targeting ```objectivec showLineNumbers ISConfigurations *isConfigurations = [ISConfigurations getConfigurations]; isConfigurations.userAge = USERS_AGE; isConfigurations.userGender = USERS_AGE; isConfigurations.customSegmentParams = @{@"bs_latitude": USERS_LATITUDE, @"bs_longitude": USERS_LONGITUDE, @"bs_consent_flag": LOCATION_CONSENT_FLAG}; ``` ```swift showLineNumbers let isConfigurations = ISConfigurations.getConfigurations() isConfigurations?.userAge = USERS_AGE isConfigurations?.userGender = USERS_GENDER isConfigurations?.customSegmentParams = [ BSLevelPlayAudienceTargetingKey.Latitude: USERS_LATITUDE, BSLevelPlayAudienceTargetingKey.Longitude: USERS_LONGITUDE, BSLevelPlayAudienceTargetingKey.ConsentFlag: LOCATION_CONSENT_FLAG ] ``` ## Meaning of LOCATION_CONSENT_FLAG - 0 = Not allow to send location. - 1 = When you managed location according to consent value. - 2 and 3 = Allow the SDK to managed location directly in accordance with the consent value use TCF v1 or TCF v2, see with the Azerion team as it depends on your implementation. --- ## Unity LevelPlay - Release Notes(2-secondary) ## [5.1.4.1] - 2025-06-26 ### Update - Update marketing version to 5.1.5 ## [5.1.1.0] - 2025-02-24 ### Added - Initial release based on BluestackSDK/Core 5.1.0 and IronSource 8.5.0 - Banner - Interstitial - Rewarded ```ruby pod 'BlueStackLevelPlayMediationAdapter', '5.1.1.0' ``` --- ## F.A.Q. ## Unrecognized selector crash: ![unrecognized selector](../00-images/unrecognized-selector-light.png#gh-light-mode-only)![unrecognized selector](../00-images/unrecognized-selector-dark.png#gh-dark-mode-only) When you integrate BlueStackSDK using Swift Package Manager (SPM) or manually without Cocoapods, you must need to include the `-ObjC` linker flag in "Other Linker Flags" under " Build Settings" of your project target." ![ObjC linker flag](../00-images/linker_flag.png) ## Does BlueStackSDK contains privacy manifest file? Starting from v4.4.1 BlueStackSDK contains privacy manifest. You can export privacy report by selecting your app archive and then right click and select "Generate Privacy Report" from Xcode -> Window -> Organizer. ![Privacy report](../00-images/privacy-report-light.png#gh-light-mode-only)![Privacy report](../00-images/privacy-report-dark.png#gh-dark-mode-only) --- ## Error Handling(30-advanced-topics) When BlueStack SDK encounters an error, it fires the failed callback delegate with an `AdError` object. ## Error codes BlueStack SDK reports with following error codes: | Error Code | Constant | Description | |---|---|---| | 0 | AdErrorWrongPlacement | Wrong placement Id was provided when loading the ad | | -1000 | AdErrorAdServer | An ad server error occurred while loading the ad | | -1001 | AdErrorDataAdServer | The ad response contains invalid or malformed data | | -1002 | AdErrorNoInternet | No internet connection is available to complete the ad request | | -999 | AdErrorSDKUninitialized | The ad was requested before the SDK finished initialization | | -998 | AdErrorCappedRequest | The ad request has been capped | | -997 | AdErrorLockedPlacement | The placement is locked or already in use by another resource | | -996 | AdErrorBusyFactory | Multiple ads are being loaded concurrently using the same placement Id | | -995 | AdErrorBusy | The SDK is busy loading one or more ads | | -994 | AdErrorUnallowedBackgroundRequest | The SDK cannot send a request while the application is in the background | | -993 | AdErrorNoAds | No ad fill is available for the given placement Id | | -992 | AdErrorInterstitialCooldown | The time between the last interstitial dismiss and the new interstitial request is less than 5 seconds | | -991 | AdErrorAlreadyShownInterstitial | Another interstitial ad is already being displayed | | -990 | AdErrorAlreadyShownAppOpen | An app open ad is already being displayed | | -989 | AdErrorRequestTimedOut | The ad request timed out before a response was received | | -988 | AdErrorMissingViewController | No root view controller was provided and the SDK could not find the top-most view controller | | -987 | AdErrorUnableToDisplayAd | The full-screen ad was requested to show before it finished loading | | -986 | AdErrorAdExpired | The ad has expired and can no longer be displayed | | -985 | AdErrorNoAdapterFoundForPlacement | No mediation adapter was found for the given placement | | -984 | AdErrorAdapterClassNotFound | The mediation adapter class could not be found or loaded | | -983 | AdErrorInternal | An internal SDK error occurred | ## Handling the Error Following is an example of handling errors for an Banner Ad. ```objectivec showLineNumbers - (void)bannerView:(BLSBannerView * _Nonnull)bannerView didFailedToLoadWithError:(NSError * _Nonnull)error { switch (error.code) { case AdErrorWrongPlacement: NSLog(@"Wrong placement Id. %@", error.localizedDescription); break; case AdErrorSDKUninitialized: NSLog(@"BlueStack SDK is not initialized. %@", error.localizedDescription); break; default: NSLog(@"Unhandled error"); break; } } ``` ```swift showLineNumbers func onFailedToLoad(_ bannerView: BlueStackSDK.BannerView, _ error: any Error) { guard let error = error as? AdError, let errorCode = AdErrorCode(rawValue: error.code) else { return } switch errorCode { case .wrongPlacement: print("Wrong placement Id. \(error.localizedDescription)") break case .sdkUninitialized: print("BlueStack SDK is not initialized. \(error.localizedDescription)") break default: print("Unhandled error. \(error.localizedDescription)") break } } ``` --- ## Targeting Audiences(30-advanced-topics) In order to take advantage of our targeting campaign, you must use our **RequestOptions** class instance. ```objectivec showLineNumbers RequestOptions *requestOptions = [[RequestOptions alloc] initWithAge:@(25) location:[[CLLocation alloc] initWithLatitude:48.87610 longitude:10.453] gender:GenderMale keyword:@"brand=myBrand;category=sport" contentUrl:@"https://my_content_url.com/"]; ``` ```swift showLineNumbers let requestOptions = RequestOptions(age: 25, location: CLLocation.init(latitude: 48.87610, longitude: 10.453), gender: .male, keyword: "brand=myBrand;category=sport", contentUrl: "https://my_content_url.com/") ``` ## Converting RequestOptions to Preference You can convert `RequestOptions` to `Preference` class instance by using `RequestOptionsToPreferenceTransformer`'s `transform(_ requestOptions:` method. ```objectivec showLineNumbers RequestOptionsToPreferenceTransformer *requestOptionTransformer = [[RequestOptionsToPreferenceTransformer alloc] init]; Preference *preference = [requestOptionTransformer transform:requestOptions]; ``` ```swift showLineNumbers let requestOptionTransformer = RequestOptionsToPreferenceTransformer() let preference = requestOptionTransformer.transform(requestOptions) ``` ## Location Targeting **Our adserver and certain ad can use your user’s location to send more targeted ads by passing Latitude and Longitude.** You must also edit the **plist** by adding **NSLocationAlwaysUsageDescription** key. >your application can be rejected by Apple if you use the device's location only for advertising ```objectivec showLineNumbers if (![APP_DELEGATE sharedLocationManager]) { APP_DELEGATE.sharedLocationManager = [[CLLocationManager alloc]init]; if([[APP_DELEGATE sharedLocationManager]respondsToSelector:@selector(requestWhenInUseAuthorization)]) { [APP_DELEGATE.sharedLocationManager requestWhenInUseAuthorization]; } [APP_DELEGATE.sharedLocationManager startUpdatingLocation]; } preferences.location = [APP_DELEGATE sharedLocationManager].location; ``` >this link can help you to get [device location]. ## Keyword Targeting Keywords allow you to target certain ad requests with user data. Keywords are useless for targeting, if you can provide **dynamic values** per users/devices. To add keyword targeting, you will need to pass these keywords up through the application (They should be formatted as key/value pairs) : - Characters per key: 20 - Characters per value: 40 ```objectivec showLineNumbers preference.keyword = @"page=football;category=sport";//Separator in case of multiple entries is ; key=value ``` ## User demographic Targeting When people are signed in on your app, can you please share **Demographic informations** from their settings with following code : ```objectivec showLineNumbers preference.gender = UserGenderFemale;//or UserGenderMale preference.age = 25;//NSInteger ``` [device location]:http://www.tutorialspoint.com/ios/ios_location_handling.htm --- ## Debugging(30-advanced-topics) Debugging the BlueStack SDK involves configuring and testing to ensure a smooth development experience. Enabling debug mode will provide detailed logs for the BlueStack SDK and all of its adapters for easy troubleshooting. Use the Xcode debug console to analyze runtime logs, verify event triggers, and monitor network calls. For specific issues, [refer to error codes provided in the SDK documentation](./02-error-handling.md). ## Enable Debug Mode To enable debug, you must first register your device as a Test Device in the BlueStack Console. Select _Inventory \> Your App \> Test Devices_ and press the **New +** button to add a device. ![img.png](./images/add_test_device.png) Enter any name for the device that your adding, and it's corresponding IDFA (iOS) or GAID (Android). The device is now registered as a test device and debug mode is automatically enabled . ![img.png](./images/device_added.png) It might take a few minutes before the changes are fully propagated to the SDK, alternatively you can also enable the debug mode programmatically: ```objectivec showLineNumbers [[BLSMobileAds sharedInstance] setDebugModeEnabled:DEBUG_ENABLED]; ``` ```swift showLineNumbers MobileAds.sharedInstance().setDebugMode(enabled: debugEnabled) ``` ## Open Debug Screen Once Debugging has been enabled, you can simply bring up the debug menu by checking your device from left to right. It will present you with a set of buttons, one of which will allow you to clear any local caching from the SDK (config, ads, etc) | Network Infomation | Debug placements | |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------| | On the Mediation settings page you'll be able to see which adapaters have been properly actived. If they indicated a cross they either have been misconfigured, not installed, or perhaps another issue that might have traces of debug information in the debug logs. |The Debug SDK also allows you to inspect the last 3 ad requests made. You'll be able to see the response status for all the networks that have been activated on the placement. The passed Request Options will also be visible for inspection.| | || ## Test Placements A quick way to enable testing is to use the predefined test placements below. They will always deliver ads for a specific format and they will not be reported towards your account. First initialize your SDK instance with the test App id `3167505`: ```objectivec showLineNumbers - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [[BLSMobileAds sharedInstance] initializeWithAppID: @"3167505" completion:^(InitializationStatus * _Nonnull initializationStatus) { }]; return YES; } ``` ```swift showLineNumbers func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { MobileAds.sharedInstance().initialize(appID: "3167505") { initializationStatus in } return true } ``` | Ad format | Test Placement ID | |----------------|-----------------------| | App Open | /3167505/appopen | | Banner | /3167505/banner | | MREC | /3167505/mrec | | Interstitial | /3167505/interstitial | | Rewarded Video | /3167505/rewarded | | Native | /3167505/native | :::warning Please make sure you replace the **App ID** AND **Placement ID's** above with the ones provided by your Azerion representative before going live ::: --- ## App Tracking Transparency Apple's App Tracking Transparency (ATT) framework, introduced with iOS 14.5, is a privacy feature designed to give users greater control over how their data is tracked and shared across apps and websites. It requires apps to obtain explicit user consent before accessing the device's Identifier for Advertisers (IDFA), a unique code used to track user activity for targeted advertising. Through a simple prompt, users can choose whether to allow or deny tracking on a per-app basis. This initiative aims to enhance transparency, safeguard user privacy, and limit the sharing of personal data without explicit permission, fundamentally shifting how advertisers and app developers collect and utilize user information. To display the App Tracking Transparency authorization request for accessing the IDFA, update your Info.plist to add the NSUserTrackingUsageDescription key with a custom message describing your usage. Below is an example description text: ```xml showLineNumbers NSUserTrackingUsageDescription This identifier will be used to deliver personalized ads to you. ``` ```xml showLineNumbers NSUserTrackingUsageDescription Cet identifiant sera utilisé pour vous délivrer des publicités personnalisées. ``` ![editor.png](https://bitbucket.org/repo/aen579/images/4098905308-editor.png) ![Image from iOS.png](https://bitbucket.org/repo/aen579/images/3105148460-Image%20from%20iOS.png) BlueStack-SDK include App Tracking Transparency (ATT) in order to display the App Tracking Transparency authorization request for accessing the IDFA. ### Implement the OS-level ATT authorization request manually. ```objectivec showLineNumbers #import #import ... - (void)requestIDFA { [ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) { // Tracking authorization completed. Start loading ads here. // [self loadAd]; }]; } ``` ```swift showLineNumbers import AppTrackingTransparency import AdSupport ... func requestIDFA() { ATTrackingManager.requestTrackingAuthorization(completionHandler: { status in // Tracking authorization completed. Start loading ads here. // loadAd() }) } ``` For more information about the possible status values, see [ATTrackingManager.AuthorizationStatus]. ## Enable SKAdNetwork to track conversions The BlueStack-SDK supports conversion tracking using [Apple's SKAdNetwork], which means BlueStack is able to attribute an app install even when IDFA is unavailable. To enable this functionality, you will need to update the SKAdNetworkItems key with an additional dictionary in your Info.plist. ```xml SKAdNetworkItems SKAdNetworkIdentifier pd25vrrwzn.skadnetwork SKAdNetworkIdentifier 4pfyvq9l8r.skadnetwork SKAdNetworkIdentifier cstr6suwn9.skadnetwork SKAdNetworkIdentifier 4fzdc2evr5.skadnetwork SKAdNetworkIdentifier 2fnua5tdw4.skadnetwork SKAdNetworkIdentifier ydx93a7ass.skadnetwork SKAdNetworkIdentifier p78axxw29g.skadnetwork SKAdNetworkIdentifier v72qych5uu.skadnetwork SKAdNetworkIdentifier ludvb6z3bs.skadnetwork SKAdNetworkIdentifier cp8zw746q7.skadnetwork SKAdNetworkIdentifier 3sh42y64q3.skadnetwork SKAdNetworkIdentifier c6k4g5qg8m.skadnetwork SKAdNetworkIdentifier s39g8k73mm.skadnetwork SKAdNetworkIdentifier 3qy4746246.skadnetwork SKAdNetworkIdentifier hs6bdukanm.skadnetwork SKAdNetworkIdentifier mlmmfzh3r3.skadnetwork SKAdNetworkIdentifier v4nxqhlyqp.skadnetwork SKAdNetworkIdentifier wzmmz9fp6w.skadnetwork SKAdNetworkIdentifier su67r6k2v3.skadnetwork SKAdNetworkIdentifier yclnxrl5pm.skadnetwork SKAdNetworkIdentifier 7ug5zh24hu.skadnetwork SKAdNetworkIdentifier gta9lk7p23.skadnetwork SKAdNetworkIdentifier vutu7akeur.skadnetwork SKAdNetworkIdentifier y5ghdn5j9k.skadnetwork SKAdNetworkIdentifier v9wttpbfk9.skadnetwork SKAdNetworkIdentifier n38lu8286q.skadnetwork SKAdNetworkIdentifier 47vhws6wlr.skadnetwork SKAdNetworkIdentifier kbd757ywx3.skadnetwork SKAdNetworkIdentifier 9t245vhmpl.skadnetwork SKAdNetworkIdentifier a2p9lx4jpn.skadnetwork SKAdNetworkIdentifier 22mmun2rn5.skadnetwork SKAdNetworkIdentifier 4468km3ulz.skadnetwork SKAdNetworkIdentifier 2u9pt9hc89.skadnetwork SKAdNetworkIdentifier 8s468mfl3y.skadnetwork SKAdNetworkIdentifier ppxm28t8ap.skadnetwork SKAdNetworkIdentifier uw77j35x4d.skadnetwork SKAdNetworkIdentifier pwa73g5rt2.skadnetwork SKAdNetworkIdentifier 578prtvx9j.skadnetwork SKAdNetworkIdentifier 4dzt52r2t5.skadnetwork SKAdNetworkIdentifier tl55sbb4fm.skadnetwork SKAdNetworkIdentifier e5fvkxwrpn.skadnetwork SKAdNetworkIdentifier 8c4e2ghe7u.skadnetwork SKAdNetworkIdentifier 3rd42ekr43.skadnetwork SKAdNetworkIdentifier 3qcr597p9d.skadnetwork ``` [Apple's SKAdNetwork]:https://developer.apple.com/documentation/storekit/skadnetwork [ATTrackingManager.AuthorizationStatus]:https://developer.apple.com/documentation/apptrackingtransparency/attrackingmanager/authorizationstatus --- ## Get Started(Ios) This documentation will guide you through the steps to integrate and initialize the BlueStack SDK into your application. :::info Looking for a working reference? Our public demo app on GitHub — [azerion/azerion-inapp-demo-ios](https://github.com/azerion/azerion-inapp-demo-ios) — showcases the BlueStack iOS SDK across banner, MREC, interstitial, and rewarded ad formats. ::: ## Prerequisites ## Configure your app ### Using CocoaPods Open your `Podfile` and add the `BlueStack-SDK` dependency. Then run `pod install` command in your CLI. ```ruby pod install --repo-update ``` This will add the core BlueStack SDK into your application. Open `.xcworkspace` file using your Xcode. ### Using Swift Package Manager Follow the steps below to integrate BlueStack SDK using SPM: - Select the project (1). - Select Package Dependencies and click on + button (2) and (3). ![SPM](00-images/SPM-1.png) - Enter the following Url (4): ```ruby https://github.com/azerion/BlueStackSDK ``` - Add the package (5). ![SPM](00-images/SPM-2.png) - This will show a list of BlueStack mediation adapters ![BlueStackSDK mediation adapter list](00-images/bluestack-mediation-adapter-list-light.png#gh-light-mode-only)![BlueStackSDK mediation adapter list](00-images/bluestack-mediation-adapter-list-dark.png#gh-dark-mode-only) - Select the mediation adapters that need to be included and add it to the target ![BlueStackSDK mediation adapter list](00-images/bluestack-mediation-adapter-select-light.png#gh-light-mode-only)![BlueStackSDK mediation adapter list](00-images/bluestack-mediation-adapter-select-dark.png#gh-dark-mode-only) - Add `-ObjC` linker flag to the `Other Linker Flags` build settings of your project target. ![SPM](00-images/linker_flag.png) ## Add mediation partners Mediation adapters are added as pods in your `Podfile`. We recommend including all adapters by default so the SDK can serve from every available demand source — omit an adapter only if you have a specific reason not to ship it. :::info **Recommended default:** include every mediation adapter. The snippet below adds the full bundle. ::: For Google Mobile Ads, also add the `GADApplicationIdentifier` key to your `Info.plist` with the ID provided by your Azerion Publisher Representative. For per-partner setup, Swift Package Manager, and the compatibility matrix, see [Supported Networks](./20-mediation/1-primairy/supported-networks.md). ## Initialize the BlueStack SDK Initialize the BlueStack SDK using `MobileAds.sharedInstance` with the appID at the earliest of your application's lifecycle. The initialization should be done before loading any ads. We recommend to do the initialization on your application did finish launching. ```objectivec showLineNumbers - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [[BLSMobileAds sharedInstance] initializeWithAppID: @"YOUR_APP_ID_HERE" completion:^(InitializationStatus * _Nonnull initializationStatus) { }]; return YES; } ``` ```swift showLineNumbers func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { MobileAds.sharedInstance().initialize(appID: "YOUR_APP_ID_HERE") { initializationStatus in } return true } ``` **Note:** If the BlueStack SDK fails to initialize, it will return an InitializationStatus object containing an empty adapter status map. --- ## Privacy and Compliance(Ios) The BlueStack SDK is designed to help publishers meet global privacy regulations, including the General Data Protection Regulation (GDPR) and industry standards like the IAB Transparency and Consent Framework (TCF v2). This ensures that your app can operate responsibly in a privacy-conscious environment while optimizing ad delivery and maintaining user trust. --- ## General Data Protection Regulation (GDPR) The GDPR is a comprehensive data protection law enacted in the European Union. It establishes rules for collecting, processing, and storing personal data while prioritizing user rights such as data access, correction, and erasure. Under GDPR, publishers are required to: 1. Obtain informed consent before processing user data. 2. Provide users with clear information about how their data will be used. 3. Offer users the ability to withdraw consent at any time. --- ## Transparency and Consent Framework (TCF v2) TCF v2, developed by the Interactive Advertising Bureau (IAB), is an industry-standard framework that streamlines how consent and data preferences are shared across the digital advertising ecosystem. It ensures a standardized approach to managing consent signals and enables seamless communication between publishers, advertisers, and ad tech partners. ### Key Benefits of TCF v2 - **Standardized Consent Management**: Ensures all parties in the ad chain are informed about user preferences. - **Greater Transparency**: Provides users with detailed information about data processing purposes, vendors, and partners. - **Flexible Compliance**: Supports different legal bases for processing data, including legitimate interest and consent. The BlueStack SDK is fully compatible with TCF v2 allowing publishers to: - Integrate any consent management platform (CMP) that supports TCF v2. - Manage consent signals efficiently across the ad ecosystem. --- ## Prohibition on Collecting Children's Data, Using the Services for Children, or Targeting Apps Exclusively to Children Starting from BlueStackSDK v6.0.0, `BlueStackPrivacySettings` has been renamed to `PrivacySettings`. You are required to determine whether a user qualifies as a 'child' under applicable laws, such as COPPA, GDPR, and other age-related regulations, as well as the policies of the Apple App Store and Google Play Store. If a user is classified as a 'child,' you must set the age-restricted user flag accordingly before initializing or using the BlueStack SDK. ```objectivec showLineNumbers [PrivacySettings setIsAgeRestrictedUser:YES]; ``` ```swift showLineNumbers PrivacySettings.setIsAgeRestrictedUser(true) ``` If the user does not qualify as a 'child' under applicable laws, ensure the age-restricted user flag is set appropriately before initializing or using the BlueStack SDK. ```objectivec showLineNumbers [PrivacySettings setIsAgeRestrictedUser:NO]; ``` ```swift showLineNumbers PrivacySettings.setIsAgeRestrictedUser(false) ``` --- ## Opt out of displaying user-based advertising You have the option to opt out of displaying ads that are tailored to users’ interests, demographics, or past interactions with advertisers. ```objectivec showLineNumbers [PrivacySettings setUserOptout:YES]; ``` ```swift showLineNumbers PrivacySettings.setUserOptout(true) ``` --- ## Features for Privacy Compliance The BlueStack SDK offers several privacy-centric features to help publishers comply with GDPR, TCF v2, and other regulations: - **Consent Management Integration**: Easily integrate with leading CMPs for seamless consent handling. - **Data Minimization**: Collect only essential data needed for app functionality and ad performance. - **Automated Signals**: Generate and propagate consent signals to ad partners in real time. By implementing these features, the BlueStack SDK empowers publishers to protect user privacy while maintaining effective ad operations. --- ## Best Practices for Publishers To maximize the benefits of the BlueStack SDK and ensure compliance: 1. Use a trusted CMP to manage consent collection and communication. 2. Regularly review your privacy policy to ensure alignment with evolving regulations. 3. Inform users about how their data is used and their rights under GDPR. 4. Test the SDK’s consent mechanisms to verify proper functioning across all scenarios. --- The BlueStack SDK is your trusted solution for navigating the complexities of data privacy while fostering transparency and trust with your users. For more details, refer to our technical documentation or contact our support team. --- ## Release Notes(Ios) ## [6.0.2] - 2026-07-03 ### Fixed - Resolved a crash when a creative contained a invalid click URL. - HTML interstitial ads now render creatives correctly. - The OMID click event now fires for VAST ads in the fullscreen renderer. - HTML interstitial ads now generate the OMID start event correctly. ## [6.0.1] - 2026-06-12 ### Fixed - Fixed a crash that could occur when upgrading from version 5 to version 6. - Passed viewability verification details to the bidding process to support accurate ad viewability tracking. - Improved privacy compliance by falling back to server-provided consent settings when local ones are unavailable. ## [6.0.0] - 2026-03-13 ### Added - New **App Open Ad** format with loading, displaying, and mediation support. - New **AdRenderKit** module — HTML/VAST rendering, viewability tracking, and impression management. ### Changed - Added `BLS` prefix to Objective-C public classes to avoid naming conflicts. - Updated OMSDK to version 1.6.3. ### Breaking Changes - Renamed SDK entry point from `BlueStack` to `MobileAds` (Objective-C: `BLSMobileAds`). - Renamed `BlueStackPrivacySettings` to `PrivacySettings`. - Renamed `BlueStackError` to `AdError` and `BlueStackErrorCode` to `AdErrorCode`. - All `BlueStackError*` and `MAdvertiseError*` error constants renamed to `AdError*` prefix (e.g., `BlueStackErrorWrongPlacement` → `AdErrorWrongPlacement`). - Renamed Native Ad classes: `MNGAdsSDKFactory` → `AdsSDKFactory`, `MNGAdsAdapter` → `AdsAdapter`, `MNGNAtiveObject` → `NativeObject`, `MNGPreference` → `Preference`. - Renamed Native Ad protocols: `MNGAdsAdapterNativeDelegate` → `AdsAdapterNativeDelegate`. - Renamed `MNGDisplayType` to `DisplayType`. - See the [iOS Migration Guide](/blog/bluestack-sdk-6_0_0/migration-guide-ios) for detailed upgrade instructions. ## [5.4.1] - 2026-02-09 - Option for client-side capping to reset on day change ### Fixed - Crash on Native Ad when rendered without cover image - Wrong orientation for some Interstitial Ads ## [5.4.0] - 2026-01-09 ### Added - Seperate bidding adapter from the core. ## [5.3.7] - 2026-01-08 ### Fixed - Viewability tracking issue. ## [5.3.6] - 2025-12-30 ### Added - Adjust constraint in BannerView. ## [5.3.5] - 2025-12-11 ### Added - Width and Height constraint in BannerView. ## [5.3.4] - 2025-11-25 ### Fixed - Fix crash on mediation adapter initialization. ## [5.3.3] - 2025-11-20 ### Added - Exposed MediaView as Generic View for Secondary Native Ad mediation ## [5.3.2] - 2025-10-28 ### Fixed - Calling ad displayed event on RewardedAd. - Fixed issue on internal sdk initialization. ## [5.3.1] - 2025-10-13 ### Fixed - Fixed issue on sending impression tracking for VAST interstitial ad. **Note:** Initialization of BlueStack SDK through `MNGAdsSDKFactory` is no longer supported. ## [5.3.0] - 2025-10-03 ### Added - Global placement timeout settings for RewardedAd and Interstitial. ### Changed - SDK initialization reliability and error handling. - Debugging logs enhancement for troubleshooting ad issues. - Overall SDK stability and performance. ### Fixed - Crash fix while there are no adserver assigned to a placement. ## [5.2.1] - 2025-07-31 ### Changed - Updated list of available adapters in debug menu ### Fixed - Crash when loading Banner Ad ## [5.2.0] - 2025-07-02 ### Added - Preload interstitial ad - Creating and loading multiple interstitial ads - Expire preloaded interstitial ad after certain period - Introduced new error [`BlueStackErrorAdExpired`](https://developers.bluestack.app/ios/advanced-topics/error-handling#error-codes) for reporting interstitial ad expire ### Fixed - Removed Play/Replay button after video ends playing ## [5.1.5] - 2025-06-05 ### Fixed - OMSDK start session issue fix. ## [5.1.4] - 2025-04-21 As mentioned in the changelog we have made some major updates to our adapter setup, please read the [documentation](/ios/mediation/primairy/supported-networks) and [blog](/blog/dependency_updates) about this topic. ### Changed - Minimum iOS Deployment target updated to version 13.0. - Separated BlueStack adapters for Google and Equativ (Formerly Smart) ### Fixed - Initialization issue fixed ## [5.1.3] - 2025-03-12 ### Changed - OMSDK updated to version 1.5.4. ## [5.1.2] - 2025-03-06 ### Changed - Privacy manifest file updated. ## [5.1.1] - 2025-02-07 ### Fixed - Loading user-agent in background thread issue fix. ## [5.1.0] - 2025-02-05 ### Added - Introduced new `BlueStackPrivacySettings` class for setting the age restricted user (COPPA) and user opt-out privacy. ## [5.0.1] - 2024-12-31 ### Fixed - Not initializing if empty provider list provided ## [5.0.0] - 2024-12-04 ### Added - Added `BlueStack` class for initializing the SDK. - Introduced new `BannerView` class for showing banner ads. - Implemented `InterstitialAd` class for loading and showing interstitial ads. - Added `RewardedAd` class for displaying rewarded video ads. - New `RequestOptions` class for sending target specific information. - Added centralized logger. ### Fixed - Interstitial weak reference crash fix. --- ## Integration ## Prerequisites - Android Studio to manage your project. - Android 4.4 (API 19) or above - TargetSdkVersion / compileSdkVersion 31 or above - Kotlin version 1.6.0 or above - For apps that support Android X, use 3.x version or above. ## Set up the SDK ### 1.Dependencies In the build.gradle of to your application module, you can import the Bluestack Location SDK by declaring it in the dependencies section: ```groovy dependencies { implementation 'com.azerion:bluestack-location-sdk:4.1.3' } ``` ### 2.Permissions In order to run properly, the library needs the following permissions : - android.permission.ACCESS_BACKGROUND_LOCATION : Allows an app to access location in the background for android 10 since target 29 - android.permission.ACCESS_FINE_LOCATION : used to get the device last known location - android.permission.ACCESS_COARSE_LOCATION : Allows an app to access approximate location. - android.permission.ACCESS_NETWORK_STATE : used to detect if the device has connectivity - android.permission.INTERNET : used to allow the library to send over the internet the data it has collected - com.google.android.gms.permission.AD_ID : Allows to access the Ad Id,since android 12 and target 31 ```xml ``` You don't need to add those permission to your AndroidManifest.xml unless your application already uses them. **Note:** - Since Android 6.0 (API level 23), this permission is belongs to the "dangerous permissions" and must be requested at run time. - The library does not implement this mechanism as it could interfere with your application's behavior. - **It is therefore your job to ask the user to grant the permission.** This is very well described in the official [Android documentation](https://developer.android.com/training/permissions/requesting.html). Here is an example : ```java showLineNumbers if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION}, LOCATION_REQUEST_CODE); } } else { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, LOCATION_REQUEST_CODE); } } ``` ### 3.Google Play Services Add the following inside the \ tag in your AndroidManifest , if not done already: ```xml showLineNumbers ``` ## That for use in standalone without the mngads SDK Madvertise is built based on Builder creational design pattern. In order to initialize the MadvertiseLocation SDK, you must have an APP_ID value. ```java showLineNumbers private static final String APP_ID = "XXXXXX"; ``` ### 1.Implementation ```java showLineNumbers MadvertiseLocation.configure(getApplicationContext(), APP_ID, CONSENT_FLAG).start(); ``` The configure method takes the following parameters: - the Application Context instance. - the App ID. - the CONSENT_FLAG value (corresponds to a int : 0,1,2 or 3). (since 3.2) - 0 = Not allow to send location. - 1 = When you managed location according to consent value. - 2 and 3 = Allow the SDK to managed location directly in accordance with the consent value use TCF v2, see with the madvertise team it depends on your implementation. ### 2.Stop Service The implementation of this method allows to stop the tracking service ```java showLineNumbers MadvertiseLocation.stop(Context); ``` --- ## Release notes(Android) Change log and release notes for BlueStack Location SDK for Android. ## [4.1.3] - 2023-05-10 ### Fixed - Fixed package renaming issue during obfuscation. ## [4.1.2] - 2023-04-26 ### Updated - Upgrade gradle to 8.0.2 - [google-play-services-location] Updated to 21.0.1 ## [4.1.1] - 2023-03-10 Please consider the following when upgrading: ```groovy // For BlueStack Location SDK implementation 'com.madvertise:location-sdk:4.1.1' ``` ### Changed - Changed the groupId of the BlueStack Location SDK to: **`com.azerion:bluestack-location-sdk`** ### Fixed - IABTCF_SpecialFeaturesOptIns check when using a cmp other than Bluestack CMP. ### Removed - Removed the BlueStack repository from project's gradle file. ## [4.1.0] - 2022-01-13 Please consider the following when upgrading: ```groovy // For BlueStack Location SDK implementation 'com.madvertise:location-sdk:4.1.0' ``` - Update targetSdkVersion / compileSdkVersion to 31 - Update kotlin version to 1.6.0 or above - Added new permission (Apps that target Android SDK 31 or higher should declare: com.google.android.gms.permission.AD_ID in the app manifest to access Ad Id). ```xml ``` ### Added - Support Android 12. ### Fixed - Rare crash occurring in LocationFusedReceiver. ## [4.0.1] - 2021-07-09 ### Fixed - issue with proguard-rules and Kotlin ## [4.0.0] - 2021-04-26 ### Fixed - Migration Java to Kotlin - Improve Android 11 compatibility. - Distinguish foreground and background points. - rewrite workflow/architecture of Receiver (Detect breakpoint, algo optimisations) and Worker (points storage) --- ## Get Started(Location) BlueStack Location SDK is an Android library. The SDK **only** works in conjunction with **CMP** due to **GDPR** regulations --- ## Integration(Ios) ## Prerequisites ### A message that tells the user why the app is requesting access to the user’s location at all times * From iOS 10, set “NSLocationAlwaysUsageDescription” and “NSLocationWhenInUseUsageDescription” in the Info.plist file. * From iOS 11, set “NSLocationAlwaysAndWhenInUseUsageDescription” and “NSLocationWhenInUseUsageDescription” in the Info.plist file. ### Message that tells the user why the app is requesting access to location information while the app is open **(Since madvertiseLocation V1.5)** : * From iOS 10, set “NSLocationWhenInUseUsageDescription” in the Info.plist file. * from IOS 14 prompt for reduced accuracy by default : ```xml showLineNumbers NSLocationDefaultAccuracyReduced<\key> ``` * ## Installation https://bitbucket.org/mngcorp/mngads-demo-ios/wiki/change-log-madvertiselocation * ***CocoaPods installation*** : ## Installation with MNGSDK V2.3.1 (since v2.12.1) You will just need to specify the subspec for Madvertise Location since it s not included by default like so : ```ruby pod "MNGAds",:subspecs => ["MNGAdsFull", "MAdvertiseLocation"] ``` ===> the functions of madevertiselocation will work automatically after the Init of mngads ## Installation Without MNGSDK ```ruby use_frameworks! pod 'MAdvertiseLocation' ``` * ***Manually installation*** : 1. Go to [MAdvertiseLocation-vx](https://bitbucket.org/mngcorp/mngads-demo-ios/downloads/) 2. Select the project file from the project navigator on the left side of the project window. 3. Select the target for where you want to add frameworks in the project settings editor. 4. Select the “Build Phases” tab, and click the small triangle next to “Link Binary With Libraries” to view all of the frameworks in your application. 5. To Add frameworks, click the “+” below the list of frameworks. ## Integration * **Enable Background Mode**: You need to tick the box "Location updates" in the "Capabilities" of your target. ![Capabilities.png](https://bitbucket.org/repo/aen579/images/3460637221-Capabilities.png) ** That for use in standalone without the mngads SDK :** 1.**requestAuthorization** : Ask the user to share their location data (popup) with the +requestAuthorization: function: Put this line where it makes the most sense for your app user experience. This will trigger a permission request popup. If you already asked thoses permissions elsewhere in your app, you don't need to call it (if you do, it will have no impact). ```objectivec showLineNumbers [MAdvertiseLocation madvertiseLocationRequestAuthorization ]; ``` ```swift showLineNumbers MAdvertiseLocation.madvertiseLocationRequestAuthorization() ``` 2.**Initializing MAdvertiseLocation** ```objectivec showLineNumbers MadvertiseBuilder * madvertiseBuilder = [[MadvertiseBuilder alloc] init]; [madvertiseBuilder setWithAppId:madevertiseLocationKey]; ``` ```swift showLineNumbers let madvertiseBuilder:MadvertiseBuilder? = MadvertiseBuilder() madvertiseBuilder?.set(appId: ConfigurationFile.madervertiseLocationAppId) let madvertiseLocationContext: MAdvertiseLocation? = madvertiseBuilder?.build() ``` ***(since v1.9) new attribute Consent flag passed to init madvertiseBuilder :*** * case consentFlag = "0" = user do not allow * case consentFlag = "1" = user provide consent * case consentFlag = "2" = SDK must check consent IAB consent * case consentFlag = "3" = SDK must check Madvertise consent ```objectivec showLineNumbers [madvertiseBuilder setWithConsentFlag:@"0"]; ``` ```swift showLineNumbers madvertiseBuilder?.set(consentFlag: consentFlag) ``` 3.**Start Location tracking position** : Start to collect location data with start function: ```objectivec showLineNumbers [MAdvertiseLocation startWithMadvertiseLocation:madlocation]; ``` ```swift showLineNumbers MAdvertiseLocation.start(madvertiseLocation:madvertiseLocationContext!)Q ``` --- ## Release notes(3) Change log and release notes for BlueStack Location SDK for iOS. ## [3.1.8] - 2024-04-26 ### Added - Privacy manifest file added ### Fixed - Fix crash issue while updating userdefaults data ## [3.1.7] - 2023-05-30 ### Added - Support for apple silicon based iOS simulators. ## [3.1.6] - 2023-04-04 ### Removed - arm64 exclusion from pod targets. ## [3.1.5] - 2023-02-01 ### Added - Exclude arm64 architecture in the project settings ## [3.1.4] - 2022-09-20 ### Fixed - fix an issue concerning a lot of requests sent to the server ## [3.1.3] - 2022-09-20 ### Changed - Swift version 5.5 and Xcode 14 - rename the class **MAdvertiseLocation** with **BlueStackLocation** ## [3.1.2] - 2022-09-14 ### Chagned - Swift version 5.5 and Xcode 14 ## [3.1.1] - 2022-06-15 ### Changed - Swift version 5.5 and Xcode 13.4 ## [3.1.0] - 2022-04-01 ### Changed - Swift version 5.5 and Xcode 13.3 ## [3.0.3] - 2022-02-25 ### Changed - iOS Simulator on Apple Silicon Macs ## [3.0.2] - 2021-09-22 ### Changed - Swift version 5.5 and Xcode 13 ## [3.0.0] - 2021-06-14 ### Changed - Swift version 5.4 and Xcode 12.5 - Distinguish foreground and background points. - Manage ATT (App Tracking Transparency) --- ## Banner Ad For a working implementation of this ad format, see the [bluestack-demo-unity](https://github.com/azerion/bluestack-demo-unity) demo app. ## Integration ### Step 1. Instantiate Banner Ad You can instantiate a `BannerAd` right after the SDK finishes initialization. The constructor you pick determines how the banner is positioned on screen. | Position | value | Definition | | ----------- | :---: | --------------------------------------------------- | | `Custom` | -1 | Banner is positioned at an explicit screen-space coordinate | | `Top` | 0 | Banner is anchored to the top of the screen | | `Bottom` | 1 | Banner is anchored to the bottom of the screen | ```csharp showLineNumbers public class BlueStackAdsController : MonoBehaviour { private BannerAd _bannerAd; void Start() { BlueStackAds.SetDebugMode(true); BlueStackAds.Initialize("app_id", HandleInitCompleteAction); } private void HandleInitCompleteAction(InitializationStatus status) { // Top/Bottom anchored — respects the device safe area by default. _bannerAd = new BannerAd(placementId, AdPosition.Bottom); } } ``` #### Constructor options | Constructor | Description | | --- | --- | | `BannerAd(string placementId, AdPosition adPosition, bool useSafeArea = true)` | Sticky Top/Bottom banner. Pass `useSafeArea: false` to ignore the device safe area (notch / home indicator / status bar). | | `BannerAd(string placementId, Vector2 adPosition)` | Banner positioned at an explicit screen-space coordinate (Unity pixels, bottom-left origin). Custom-positioned banners always ignore the safe area. | | `BannerAd(string placementId, Transform anchor, Camera camera = null)` | Banner follows a GameObject anchor — the SDK adds an `AdPlacementHandler` component to the anchor that re-positions the banner each frame from `anchor.position`. | ```csharp showLineNumbers // Top, ignoring the device safe area _bannerAd = new BannerAd(placementId, AdPosition.Top, useSafeArea: false); // Explicit screen-space position _bannerAd = new BannerAd(placementId, new Vector2(x, y)); // Anchored to a GameObject — banner follows the anchor in screen space _bannerAd = new BannerAd(placementId, anchorTransform, trackingCamera); ``` ### Step 2. Register event listeners `BannerAd` exposes the following events through its lifecycle. | Event | Payload | Definition | | ---------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------ | | `OnAdLoaded` | `PreferredBannerSize` | Ad finished loading. Payload carries the SDK-preferred `Width`/`Height` (in iOS points / Android dp). | | `OnAdFailedToLoad` | `BlueStackError` | The ad failed to load. | | `OnAdDisplayed` | `EventArgs` | Banner became visible on screen. | | `OnAdHidden` | `EventArgs` | Banner was hidden via `Hide()`. The banner can be re-shown with `Show()`. | | `OnAdClicked` | `EventArgs` | The user clicked the banner. | | `OnAdRefreshed` | `EventArgs` | The banner auto-refresh delivered a new creative. | | `OnAdFailedToRefresh` | `BlueStackError` | An auto-refresh attempt failed. | | `OnAdResized` | `PreferredBannerSize` | The banner's preferred size changed after the initial load (e.g. a refresh delivered a different size). | ```csharp showLineNumbers _bannerAd.OnAdLoaded += (sender, size) => { Debug.Log($"OnAdLoaded — preferred size {size.Width}x{size.Height}"); }; _bannerAd.OnAdFailedToLoad += (sender, error) => { Debug.LogError("OnAdFailedToLoad: " + error.Message); }; _bannerAd.OnAdDisplayed += (sender, args) => { Debug.Log("OnAdDisplayed"); }; _bannerAd.OnAdHidden += (sender, args) => { Debug.Log("OnAdHidden"); }; _bannerAd.OnAdClicked += (sender, args) => { Debug.Log("OnAdClicked"); }; _bannerAd.OnAdRefreshed += (sender, args) => { Debug.Log("OnAdRefreshed"); }; _bannerAd.OnAdFailedToRefresh += (sender, error) => { Debug.LogError("OnAdFailedToRefresh: " + error.Message); }; _bannerAd.OnAdResized += (sender, size) => { Debug.Log($"OnAdResized — new size {size.Width}x{size.Height}"); }; ``` :::caution Make sure you only register event listeners once. ::: ### Step 3. Load Banner ad `BannerAd.Load` takes an `AdSize`. An overload also accepts a `Preference` instance for targeting. Supported sizes: | Ad Size | Definition | | -------------------- | ------------------------------------------------------- | | `Banner` | 320x50 | | `DynamicBanner` | dynamic width × 50 — stretches to the container width | | `LargeBanner` | 320x100 | | `FullBanner` | 468x60 | | `Leaderboard` | 728x90 | | `DynamicLeaderboard` | dynamic width × 90 — stretches to the container width | | `MediumRectangle` | 300x250 | - Without `Preference` ```csharp showLineNumbers _bannerAd.Load(AdSize.Banner); ``` - With `Preference` ```csharp showLineNumbers public class BlueStackAdsController : MonoBehaviour { ... private void RequestBannerAd() { Preference _preference = new Preference(); Location myLocation = new Location(Location.NONE_PROVIDER) { Latitude = 35.757866, Longitude = 10.810547 }; _preference.SetAge(25); _preference.SetLanguage("en"); _preference.SetGender(Gender.Male); _preference.SetKeyword("brand=myBrand;category=sport"); _preference.SetLocation(myLocation, 1); _preference.SetContentUrl("https://console.bluestack.app"); _bannerAd.Load(AdSize.Banner, _preference); } ... } ``` **Note:** The `SetLocation` method takes the following parameters: - The `Location` instance. - The CONSENT_FLAG value (corresponds to a int: 0, 1, 2 or 3). - 0 = Do not send location. - 1 = Managed location according to consent value. - 2 and 3 = Allow the SDK to manage location directly in accordance with the consent value (TCF v1 / TCF v2). Check with the Azerion team — behavior depends on your implementation. ### Step 4. Show Banner ad After the banner has loaded, request it to be displayed. ```csharp showLineNumbers _bannerAd.OnAdLoaded += (sender, size) => { _bannerAd?.Show(); }; ``` ### Hide / Show Banner ad A banner can be hidden without releasing native resources and shown again later. ```csharp showLineNumbers _bannerAd.Hide(); // later _bannerAd.Show(); ``` ### Change banner position at runtime ```csharp showLineNumbers // Switch to a sticky position _bannerAd.SetPosition(AdPosition.Top); // Or use an explicit screen-space coordinate _bannerAd.SetPosition(new Vector2(x, y)); // Or follow a GameObject anchor _bannerAd.SetPosition(anchorTransform, trackingCamera); ``` ### Mask (clip) a Banner ad In v6.0.0 you can clip a banner to a UI `RectTransform`. The banner is shown only inside the mask's screen-space bounds; as the mask animates or resizes, the SDK keeps the clipped region in sync. ```csharp showLineNumbers // Apply a mask _bannerAd.SetMask(maskRectTransform); // (optional) The SDK attached an AdMaskHandler component to the mask GameObject. // You can throttle the per-frame update if needed (default is every frame; clamped to [0, 5] seconds). _bannerAd.MaskHandler.UpdateInterval = 0.1f; // 10 Hz // Remove the mask _bannerAd.RemoveMask(); ``` :::note `SetMask` can be called before or after `Load`. If called before, the mask is cached and applied when the banner becomes visible. Custom-positioned banners always ignore the device safe area regardless of the mask state. ::: ### Destroy banner ad Destroy the banner before creating a new one. After `Destroy()` the instance can no longer be used. ```csharp showLineNumbers _bannerAd.Destroy(); ``` --- ## Ad Formats BlueStack Unity SDK supports four types of Ads: 1. [Banner Ads](banner.md) 2. [Interstitial Ads](interstitial.md) 3. [Rewarded Ads](rewarded.md) 4. [Native Ads](nativead.md) --- ## Interstitial Ad For a working implementation of this ad format, see the [bluestack-demo-unity](https://github.com/azerion/bluestack-demo-unity) demo app. ## Integration ### Step 1. Instantiate Interstitial Ad You can instantiate an `InterstitialAd` right after the SDK finishes initialization. Pass the platform-specific interstitial placement id to the `InterstitialAd` constructor. ```csharp showLineNumbers public class BlueStackAdsController : MonoBehaviour { private InterstitialAd _interstitialAd; void Start() { BlueStackAds.SetDebugMode(true); BlueStackAds.Initialize("app_id", HandleInitCompleteAction); } private void HandleInitCompleteAction(InitializationStatus status) { _interstitialAd = new InterstitialAd(placementId); } } ``` ### Step 2. Register event listeners `InterstitialAd` exposes the following events through its lifecycle. | Event | Payload | Definition | | ---------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------ | | `OnAdLoaded` | `EventArgs` | Ad finished loading and is ready to be shown. | | `OnAdFailedToLoad` | `BlueStackError` | The ad failed to load. | | `OnAdDisplayed` | `EventArgs` | The ad has appeared on screen. | | `OnAdFailedToDisplay` | `BlueStackError` | The ad failed to display after `Show()` was called. | | `OnAdClicked` | `EventArgs` | The user clicked the ad. | | `OnAdDismissed` | `EventArgs` | The user dismissed the full-screen ad. This is terminal — the instance cannot be re-shown. | ```csharp showLineNumbers _interstitialAd.OnAdLoaded += (sender, args) => { Debug.Log("OnAdLoaded"); }; _interstitialAd.OnAdFailedToLoad += (sender, error) => { Debug.LogError("OnAdFailedToLoad: " + error.Message); }; _interstitialAd.OnAdDisplayed += (sender, args) => { Debug.Log("OnAdDisplayed"); }; _interstitialAd.OnAdFailedToDisplay += (sender, error) => { Debug.LogError("OnAdFailedToDisplay: " + error.Message); }; _interstitialAd.OnAdClicked += (sender, args) => { Debug.Log("OnAdClicked"); }; _interstitialAd.OnAdDismissed += (sender, args) => { Debug.Log("OnAdDismissed"); }; ``` :::caution Make sure you only register event listeners once. ::: ### Step 3. Load Interstitial ad `InterstitialAd` exposes two `Load` overloads — one with no parameters, and one taking a `Preference` instance. - Without `Preference` ```csharp showLineNumbers _interstitialAd.Load(); ``` - With `Preference` ```csharp showLineNumbers public class BlueStackAdsController : MonoBehaviour { ... private void RequestInterstitialAd() { Preference _preference = new Preference(); Location myLocation = new Location(Location.NONE_PROVIDER) { Latitude = 35.757866, Longitude = 10.810547 }; _preference.SetAge(25); _preference.SetLanguage("en"); _preference.SetGender(Gender.Male); _preference.SetKeyword("brand=myBrand;category=sport"); _preference.SetLocation(myLocation, 1); _preference.SetContentUrl("https://console.bluestack.app"); _interstitialAd.Load(_preference); } ... } ``` **Note:** The `SetLocation` method takes the following parameters: - The `Location` instance. - The CONSENT_FLAG value (corresponds to a int: 0, 1, 2 or 3). - 0 = Do not send location. - 1 = Managed location according to consent value. - 2 and 3 = Allow the SDK to manage location directly in accordance with the consent value (TCF v1 / TCF v2). Check with the Azerion team — behavior depends on your implementation. ### Step 4. Display Interstitial ad After the ad has loaded, request it to be displayed. Check `IsReady` to confirm the ad is loaded before calling `Show()`. ```csharp showLineNumbers if (_interstitialAd.IsReady) { _interstitialAd.Show(); } ``` Equivalently, gate on `OnAdLoaded`: ```csharp showLineNumbers _interstitialAd.OnAdLoaded += (sender, args) => { _interstitialAd.Show(); }; ``` ### Destroy interstitial ad Destroy the interstitial before creating a new one. ```csharp showLineNumbers _interstitialAd.Destroy(); ``` --- ## Native Ads(3) BlueStack native ads allow you to retrieve the metadata of ad campaigns and present the ads yourself, within the context of your app/game, using your own art style. So, it matches the visual design of the app they live within. You are fully responsible for rendering the ad views using the information we supply. :::note Native video ads and native mediation are not supported at present. ::: For a working implementation of this ad format, see the [bluestack-demo-unity](https://github.com/azerion/bluestack-demo-unity) demo app. ## Overview Native ads are created using the `GameObjects`. You get a native ad object when a native ad loads. It contains the assets that you need to construct the ad and display. Before You Start. Make sure that you have correctly integrated the `[BlueStack Unity SDK]` into your application. Check the Examples provided within the SDK for a quick start and better understanding. ## 1. Requesting a native ad Native ads are loaded through the `NativeAdLoader` class. ### Register NativeAdLoader ad events To be notified when a native ad either successfully loads or fails to load, add delegates to the AdLoader class for the events listed below. `OnNativeAdLoaded` is invoked when a native ad is successfully loaded. It has a delegate to access the ad that is loaded. `OnNativeAdFailedToLoad` is invoked when a native ad fails to load. ```csharp showLineNumbers private void RequestNativeAd() { string NativeAdUnitId = "/YOUR_APP_ID/PLACEMENT_ID"; NativeAdLoader nativeAdLoader = new NativeAdLoader(NativeAdUnitId); nativeAdLoader.OnNativeAdLoaded += this.HandleNativeAdLoaded; nativeAdLoader.OnNativeAdFailedToLoad += this.HandleNativeNativeAdFailedToLoad; nativeAdLoader.Load(); } ``` ### Handle ad events #### Handle OnNativeAdFailedToLoad event The `OnNativeAdFailedToLoad` event is of type `EventHandler` and provides the reason for the load failure. ```csharp showLineNumbers private void HandleNativeAdFailedToLoad(object sender, BlueStackError args) { Debug.Log("BlueStack NativeAd failed to load: " + "errorCode: " + args.ErrorCode + " message: " + args.Message); } ``` #### Handle OnNativeAdLoaded event The `OnNativeAdLoaded` event is of type `EventHandler`. The `NativeAd` object can be retrieved from NativeAdEventArgs. ```csharp showLineNumbers private NativeAd nativeAd; private void HandleNativeAdLoaded(object sender, NativeAdEventArgs args) { this.nativeAd = args.nativeAd; } ``` ## 2. Retrieve native ad assets Once the ad has loaded, you can access the ad assets. Text assets are returned as `string` objects and Graphical assets are returned as `Texture2D` objects. Currently, BlueStack Native Ad provides methods to retrieve following assets: ### **Ad Title** - 50 maximum character length string of ad headline - Provide enough space to display the entire length of the Ad Title - method name : **GetTitleText()** ### **Ad Text** - 150 maximum character length string of ad text - Provide enough space to display the entire length of the Ad Text - method name : **GetBodyText()** ### **CallToAction(CTA) Text** - Text for a button - 12 characters maximum - Clickable object - method name : **GetCallToActionText()** ### **Badge Text** - “Ad” text (can be localized) - Badge that says “AD” and should be at least 15x15px (can be localized) - change according ad network - must be inserted on top left - method name : **GetBadgeText()** ### **Icon Image** - Icon for the Ad (usually app icon) - A Texture2D object - method name : **GetIconTexture()** ### **Cover Image** - Main image for the Ad (usually a banner) - A Texture2D object - method name : **GetCoverImageTexture()** ```csharp showLineNumbers void Update() { ... string titleText = this.nativeAd.GetTitleText(); string bodyText = this.nativeAd.GetBodyText(); Texture2D imageTexture = this.nativeAd.GetCoverImageTexture(); ... } ``` :::note Ad assets should only be accessed from the main thread, like, `Update()`, `Start()` methods of a Unity script. ::: ## 3. Construct native ad Construct the Ad using the assets retrieved from the Native Ad object. ![unity-native-ad.png](images/unity-native-ad.png) Here is the list of Methods available to retrieved Native Ad assets, | Methods | Return Type | Definition | | -------------------- | ----------- | ----------------------------------------------------------------------- | | GetTitleText | string | Get the title of the ad. | | GetBodyText | string | Get the body text of the ad. | | GetCallToActionText | string | Get the text for the button of the ad (Example : "Download"). | | GetBadgeText | string | Get the text for the badge of the ad (Example : "AD"). | | GetIconTexture | Texture2D | Get the icon image for the ad (usually app icon). | | GetCoverImageTexture | Texture2D | Get the cover image for the ad (usually a banner). | Following is an example of how to retrieved ad asset and assign it, ```csharp showLineNumbers Texture2D iconTexture = this.nativeAd.GetIconTexture(); if (iconTexture != null) { appIcon.GetComponent().texture = iconTexture; } ``` ## 4. Register ad GameObjects You must register the GameObject for the ad asset to be displayed in your Unity app. If registration is successful, the method used to register the GameObject returns a bool. If registration of an ad asset is unsuccessful, impressions and clicks on the corresponding native ad won't be recognized. Here is the list of Methods to register Native Ad object, | Methods | Definition | | -------------------- | ----------------------------------------------------------------------- | | RegisterTitleTextGameObject | Register the title text GameObject. | | RegisterBodyTextGameObject | Register the body text GameObject. | | RegisterCallToActionTextGameObject | Register the CallToAction text GameObject. | | RegisterBadgeTextGameObject | Register the badge text GameObject. | | RegisterIconImageGameObject | Register the icon image GameObject. | | RegisterCoverImageGameObject | Register the cover image GameObject. | Following is an example of how to register ad object, ```csharp showLineNumbers if (!this.nativeAd.RegisterIconImageGameObject(appIcon)) { Debug.Log("RegisterIconImageGameObject Unsuccessful"); } ``` :::note The registered `GameObject` must have a `Collider` component that represents the size and shape of the `GameObject`. Native ads will not operate correctly, if any `GameObject` registered to ad asset is missing `Collider` component or configured incorrectly. 1. The registered `GameObject` must have a `Collider` component that represents the size and shape of the `GameObject`. It deterdetermines the interactable area of the Native Ad object. Native ads will not operate correctly, if any `GameObject` registered to ad asset is missing `Collider` component or configured incorrectly. 2. If using UI Canvas, make sure the canvas containing the Native ad is at the top, to make the impression and click to work. ::: ![unity-native-ad-collider.png](images/unity-native-ad-collider.png) [BlueStack Unity SDK](https://www.npmjs.com/package/com.azerion.bluestack) --- ## Rewarded Ad :::note In v6.0.0 the class was renamed from `RewardedVideoAd` to `RewardedAd`, and the reward event from `OnUserRewardEarned` to `OnAdRewardEarned`, for naming parity across formats. Existing v3.x code will not compile against v6.0.0 without these renames. ::: For a working implementation of this ad format, see the [bluestack-demo-unity](https://github.com/azerion/bluestack-demo-unity) demo app. ## Integration ### Step 1. Instantiate Rewarded Ad You can instantiate a `RewardedAd` right after the SDK finishes initialization. Pass the platform-specific rewarded placement id to the `RewardedAd` constructor. ```csharp showLineNumbers public class BlueStackAdsController : MonoBehaviour { private RewardedAd _rewardedAd; void Start() { BlueStackAds.SetDebugMode(true); BlueStackAds.Initialize("app_id", HandleInitCompleteAction); } private void HandleInitCompleteAction(InitializationStatus status) { _rewardedAd = new RewardedAd(placementId); } } ``` ### Step 2. Register event listeners `RewardedAd` exposes the following events through its lifecycle. | Event | Payload | Definition | | --------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | `OnAdLoaded` | `EventArgs` | Ad finished loading and is ready to be shown. | | `OnAdFailedToLoad` | `BlueStackError` | The ad failed to load. | | `OnAdDisplayed` | `EventArgs` | The ad has appeared on screen. | | `OnAdFailedToDisplay` | `BlueStackError` | The ad failed to display after `Show()` was called. | | `OnAdClicked` | `EventArgs` | The user clicked the ad. | | `OnAdRewardEarned` | `RewardedItem` | The user earned a reward (typically after watching the video to completion). The payload's `Amount` and `Type` may be unset depending on the mediation network. | | `OnAdDismissed` | `EventArgs` | The user dismissed the ad. Fired whether or not a reward was earned. | ```csharp showLineNumbers _rewardedAd.OnAdLoaded += (sender, args) => { Debug.Log("OnAdLoaded"); }; _rewardedAd.OnAdFailedToLoad += (sender, error) => { Debug.LogError("OnAdFailedToLoad: " + error.Message); }; _rewardedAd.OnAdDisplayed += (sender, args) => { Debug.Log("OnAdDisplayed"); }; _rewardedAd.OnAdFailedToDisplay += (sender, error) => { Debug.LogError("OnAdFailedToDisplay: " + error.Message); }; _rewardedAd.OnAdClicked += (sender, args) => { Debug.Log("OnAdClicked"); }; _rewardedAd.OnAdRewardEarned += (sender, reward) => { Debug.Log("OnAdRewardEarned reward: '" + reward?.Type + "', amount: " + reward?.Amount); }; _rewardedAd.OnAdDismissed += (sender, args) => { Debug.Log("OnAdDismissed"); }; ``` :::caution Make sure you only register event listeners once. ::: ### Step 3. Load Rewarded ad `RewardedAd` exposes two `Load` overloads — one with no parameters, and one taking a `Preference` instance. :::info There must be a delay of at least 5 seconds between each rewarded ad `Load` call. ::: - Without `Preference` ```csharp showLineNumbers _rewardedAd.Load(); ``` - With `Preference` ```csharp showLineNumbers public class BlueStackAdsController : MonoBehaviour { ... private void RequestRewardedAd() { Preference _preference = new Preference(); Location myLocation = new Location(Location.NONE_PROVIDER) { Latitude = 35.757866, Longitude = 10.810547 }; _preference.SetAge(25); _preference.SetLanguage("en"); _preference.SetGender(Gender.Male); _preference.SetKeyword("brand=myBrand;category=sport"); _preference.SetLocation(myLocation, 1); _preference.SetContentUrl("https://console.bluestack.app"); _rewardedAd.Load(_preference); } ... } ``` **Note:** The `SetLocation` method takes the following parameters: - The `Location` instance. - The CONSENT_FLAG value (corresponds to a int: 0, 1, 2 or 3). - 0 = Do not send location. - 1 = Managed location according to consent value. - 2 and 3 = Allow the SDK to manage location directly in accordance with the consent value (TCF v1 / TCF v2). Check with the Azerion team — behavior depends on your implementation. ### Step 4. Display Rewarded ad After the ad has loaded, request it to be displayed. Check `IsReady` to confirm the ad is loaded before calling `Show()`. ```csharp showLineNumbers if (_rewardedAd.IsReady) { _rewardedAd.Show(); } ``` Equivalently, gate on `OnAdLoaded`: ```csharp showLineNumbers _rewardedAd.OnAdLoaded += (sender, args) => { _rewardedAd.Show(); }; ``` ### Destroy Rewarded ad Destroy the rewarded ad before creating a new one. ```csharp showLineNumbers _rewardedAd.Destroy(); ``` --- ## Android Supported Networks :::info **Recommended:** enable all mediation networks by default in `Azerion > BlueStack > Settings`. Omit a network only if you have a specific reason not to ship that demand source. See the [Getting Started](../index.md#select-mediation-networks) page for the in-editor flow. ::: ## Mediation Adapter Compatibility Matrix = 3.4.1"> | Ad Network | SDK Version | Adapter Version | Supported Ad Formats | |-----------------------|-------------|---------------------------------|-------------------------------------------------------| | **Google** | 24.9.0 | 5.4.1.0 | Banner, Interstitial, Rewarded Ads | | **Equativ** | 8.5.2 | 5.2.1.1 | Banner, Interstitial | | **BlueStack Bidding** | N/A | 5.4.0.0 | Banner, Interstitial | = 3.2.0 and < 3.4.1"> | Ad Network | SDK Version | Adapter Version | Supported Ad Formats | |-----------------------|-------------|---------------------------------|-------------------------------------------------------| | **Google** | 24.9.0 | 5.3.0.1 | Banner, Interstitial, Rewarded Ads | | **Amazon** | 6.18.0 | (included in BlueStack Bidding) | Banner, Interstitial, Rewarded Ads | | **Equativ** | 8.5.2 | 5.2.1.1 | Banner, Interstitial | | **BlueStack Bidding** | N/A | 5.3.0.0 | Banner, Interstitial | | Ad Network | SDK Version | Adapter Version | Supported Ad Formats | |-----------------------|-------------|---------------------------------|-------------------------------------------------------| | **Google** | 23.4.0 | 4.4.0.0 | Banner, Interstitial, Rewarded Ads | | **Amazon** | 6.18.0 | (included in BlueStack Bidding) | Banner, Interstitial, Rewarded Ads | | **Equativ** | 7.24.0 | 4.4.1.0 | Banner, Interstitial, Rewarded Ads | | **BlueStack Bidding** | N/A | 4.4.1.0 | Banner, Interstitial, Rewarded Ads | --- ## Mediation & Bidding BlueStack's mediation feature enables you to deliver advertisements to your app from various sources, including BlueStack itself and third-party ad networks. You can select from the list of mediation networks available in [BlueStack Settings](../index.md#settings). All dependencies for these networks are automatically managed by the BlueStack SDK when you select/deselect them from the settings. :::info **Recommended:** enable all mediation networks by default in `Azerion > BlueStack > Settings`. Omit a network only if you have a specific reason not to ship that demand source — EDM4U will resolve the matching native dependencies automatically. ::: ## Supported Networks 1. [Android Mediation Networks](android-mediation-networks.md) 2. [iOS Mediation Networks](ios-mediation-networks.md) --- ## iOS Supported Networks :::info **Recommended:** enable all mediation networks by default in `Azerion > BlueStack > Settings`. Omit a network only if you have a specific reason not to ship that demand source. See the [Getting Started](../index.md#select-mediation-networks) page for the in-editor flow. ::: ## Mediation Adapter Compatibility Matrix = 3.4.1"> | Ad Network | SDK Version | Adapter Version | Supported Ad Formats | |-------------|-------------|-----------------|-------------------------------------------------------| | **Google** | 12.14.0 | 5.4.0 | Banner, Interstitial, Rewarded Ads | | **Equativ** | 8.5.1 | 5.1.8 | Banner, Interstitial | | **BlueStack Bidding** | N/A | 5.4.0 | Banner, Interstitial | | Ad Network | SDK Version | Adapter Version | Supported Ad Formats | |-------------|-------------|-----------------|-------------------------------------------------------| | **Google** | 12.14.0 | 5.3.5 | Banner, Interstitial, Rewarded Ads | | **Amazon** | 4.5.5 | Included in BlueStackSDK as subspec | Banner, Interstitial, Rewarded Ads | | **Equativ** | 8.5.1 | 5.1.8 | Banner, Interstitial | --- ## Knowledge Base :::note This page contains recommendations, requirements that you need to know to resolve potential issues that you might encounter based on the traget platform. :::
Android 12 and 13 targeting requirements Bluestack SDK version 1.1.0 is using the IMA SDK version 2.18.1. If you are targeting Android 13, you must add the com.google.android.gms.permission.AD_ID permission in the AndroidManifest.xml file for the Google Mobile Ads SDK to access the Advertising ID: ```xml ... ``` Reference: https://developers.google.com/interactive-media-ads/docs/sdks/android/dai/android-12
ERROR: java.lang.UnsupportedOperationException: This feature requires ASM7 If you are targeting Android 11-13, you may encounter this error. To fix this. 1. Change the API Level and build 2. Change back to your original API Level and try build again 3. Also try closing and reopening Unity Editor Reference: https://forum.unity.com/threads/this-feature-requires-asm7.1360672/
--- ## Downgrade from v3.2.0 After downgrading from version 3.2.0 or above to a version below 3.2.0, you must delete `BlueStackDependencies.xml` and `BlueStackMediationNetworks.xml` (if present) from the `Assets/Editor` folder. After cleanup, navigate to `Azerion > BlueStack > Settings` and reconfigure the BlueStack Settings. ![downgrade-from-v3.2.0.png](images/downgrade-from-v3.2.0.png) --- ## Migrate from v3.x to v6.0.0 BlueStack Unity SDK v6.0.0 is a major release. The native dependencies have moved to the v6 generation of the BlueStack Core SDK, and a number of public C# APIs have been renamed or have shape changes for naming consistency across ad formats. This guide walks you through the changes you will need to apply to an existing v3.x integration to make it compile and run on v6.0.0. :::info Full per-version notes live in the [Release Notes](../release-notes.md). This page focuses on the mechanical changes a v3.x consumer needs to make. ::: ## At a glance | Area | What changed | Required action | | --------------------------- | ------------------------------------------------------------------ | -------------------------------------------------------------------------- | | Package version | `com.azerion.bluestack` bumped to `6.0.0` | Update `Packages/manifest.json` | | Native dependencies | Core SDK + mediation adapters bumped to v6.0.x | Force-resolve EDM4U (Android) and run `pod install` (iOS) after upgrade | | `BlueStackAds.Initialize` | Signature simplified; `Settings` and split init callbacks removed | Switch to `SetDebugMode(bool)` + single-callback `Initialize` | | `RewardedVideoAd` | Class renamed to `RewardedAd` | Rename class references | | Reward event | `OnUserRewardEarned` → `OnAdRewardEarned` | Rename subscription | | Banner events | Renamed to the unified `OnAd*` convention | Rename subscriptions (see table) | | Banner `OnAdLoaded` payload | Now carries `PreferredBannerSize` | Update handler signature | | Interstitial events | Renamed to the unified `OnAd*` convention | Rename subscriptions (see table) | | Rewarded events | Renamed to the unified `OnAd*` convention | Rename subscriptions (see table) | | Banner with mask | Masked constructor removed | Construct normally, then call `SetMask(RectTransform, Camera)` | --- ## Step 1 — Bump the package version Update `Packages/manifest.json`: ```json showLineNumbers { "dependencies": { "com.azerion.bluestack": "6.0.0" } } ``` After Unity refreshes the package, force-resolve native dependencies: `Assets > External Dependency Manager > Android Resolver > Force Resolve` The resolved AAR dependency line in `Assets/Plugins/Android/mainTemplate.gradle` should now read: ```groovy implementation 'com.azerion:bluestack-sdk-core:6.0.1' ``` After the next Unity iOS export, run `pod install --repo-update` in the exported Xcode project to pull the new `BlueStack-SDK` v6 pod and mediation adapters. --- ## Step 2 — Update SDK initialization The v3.x `Initialize` carried a `Settings` snapshot and two separate callbacks (`SDKInitializationStatus`, `AdaptersInitializationStatus`). In v6.0.0 the `Settings` argument is gone, the debug-mode toggle has its own setter, and a single `Initialize` callback delivers the per-adapter `InitializationStatus`. ### Before (v3.x) ```csharp showLineNumbers using Azerion.BlueStack.API; public class BlueStackAdController : MonoBehaviour { public void Start() { Settings settings = new Settings(isDebugModeEnabled: true); BlueStackAds.Initialize(appId, settings, HandleSDKInitCompleteAction, HandleAdaptersInitCompleteAction); } private void HandleSDKInitCompleteAction(SDKInitializationStatus sdkInitializationStatus) { Debug.Log("SDK init: " + sdkInitializationStatus.IsSuccess); } private void HandleAdaptersInitCompleteAction(AdaptersInitializationStatus adaptersStatus) { foreach (var kv in adaptersStatus.GetAdapterStatusMap()) { Debug.Log($"Adapter {kv.Key}: {kv.Value.InitializationState}"); } } } ``` ### After (v6.0.0) ```csharp showLineNumbers using Azerion.BlueStack.API; public class BlueStackAdController : MonoBehaviour { public void Start() { // Toggle native SDK debug logging at any time. BlueStackAds.SetDebugMode(true); BlueStackAds.Initialize(appId, HandleInitializationComplete); } // Single callback. Runs on the Unity main thread on both iOS and Android in v6.0.0. private void HandleInitializationComplete(InitializationStatus status) { foreach (var kv in status.AdapterStatusMap) { Debug.Log($"Adapter {kv.Key}: {kv.Value.InitializationState} ({kv.Value.Description})"); } // Readiness can now also be checked synchronously at any time: if (BlueStackAds.IsInitialized) { Debug.Log("BlueStack SDK is ready to load ads."); } } } ``` Notes: - **`Settings` is gone** — the class is retained for source compatibility only and is no longer consulted by the SDK. Delete any `new Settings(...)` constructions in your code. - **No more "SDK init success/fail" event** — if you previously branched on `SDKInitializationStatus.IsSuccess`, instead inspect `status.AdapterStatusMap` to check which adapters reached `AdapterState.Ready`. - **Main-thread guarantee** — v6.0.0 dispatches the init callback to Unity's main thread on both iOS and Android. You no longer need to marshal it yourself before touching UnityEngine APIs. --- ## Step 3 — Rename `RewardedVideoAd` to `RewardedAd` The class was renamed for naming parity with `BannerAd` / `InterstitialAd`. The file name in your project also changes (you reference it as `RewardedAd` everywhere now). ### Before ```csharp showLineNumbers private RewardedVideoAd _rewardedVideoAd; _rewardedVideoAd = new RewardedVideoAd(placementId); _rewardedVideoAd.OnUserRewardEarned += (sender, item) => { Debug.Log($"Reward: {item.Amount} {item.Type}"); }; _rewardedVideoAd.Load(); ``` ### After ```csharp showLineNumbers private RewardedAd _rewardedAd; _rewardedAd = new RewardedAd(placementId); _rewardedAd.OnAdRewardEarned += (sender, item) => { Debug.Log($"Reward: {item.Amount} {item.Type}"); }; _rewardedAd.Load(); ``` --- ## Step 4 — Rename ad events Events on all three full-screen formats now follow the unified `OnAdXxx` convention, matching what the native BlueStack SDK delivers internally. ### Banner | v3.x | v6.0.0 | Payload | | ------------------------- | ----------------------- | ------------------------ | | `OnBannerDidLoad` | `OnAdLoaded` | **`PreferredBannerSize`** *(was `EventArgs`)* | | `OnBannerDidFailed` | `OnAdFailedToLoad` | `BlueStackError` | | `OnBannerDisplay` | `OnAdDisplayed` | `EventArgs` | | `OnBannerHide` | `OnAdHidden` | `EventArgs` | | `OnAdClicked` | `OnAdClicked` | `EventArgs` | | `OnBannerDidRefresh` | `OnAdRefreshed` | `EventArgs` | | `OnBannerDidFailToRefresh`| `OnAdFailedToRefresh` | `BlueStackError` | | *(new)* | `OnAdResized` | `PreferredBannerSize` | The `OnAdLoaded` payload type changed — your handler now receives the SDK-preferred banner size: ```csharp showLineNumbers // v3.x _bannerAd.OnBannerDidLoad += (sender, args) => { _bannerAd.Show(); }; // v6.0.0 _bannerAd.OnAdLoaded += (sender, size) => { Debug.Log($"Loaded banner preferred size: {size.Width}x{size.Height}"); _bannerAd.Show(); }; ``` The new `OnAdResized` event lets you react when a refresh delivers a creative of a different size (useful for `DynamicBanner` / `DynamicLeaderboard`): ```csharp showLineNumbers _bannerAd.OnAdResized += (sender, size) => { Debug.Log($"Banner resized: {size.Width}x{size.Height}"); }; ``` ### Interstitial | v3.x | v6.0.0 | Payload | | -------------------------- | ----------------------- | ---------------- | | `OnInterstitialDidLoaded` | `OnAdLoaded` | `EventArgs` | | `OnInterstitialDidFail` | `OnAdFailedToLoad` | `BlueStackError` | | `OnInterstitialClicked` | `OnAdClicked` | `EventArgs` | | `OnInterstitialDidShown` | `OnAdDisplayed` | `EventArgs` | | `OnInterstitialDisappear` | `OnAdDismissed` | `EventArgs` | | *(new)* | `OnAdFailedToDisplay` | `BlueStackError` | ### Rewarded | v3.x | v6.0.0 | Payload | | --------------------------- | ----------------------- | ---------------- | | `OnRewardedVideoAdLoaded` | `OnAdLoaded` | `EventArgs` | | `OnRewardedVideoAdError` | `OnAdFailedToLoad` | `BlueStackError` | | `OnRewardedVideoAdAppeared` | `OnAdDisplayed` | `EventArgs` | | `OnRewardedVideoAdClicked` | `OnAdClicked` | `EventArgs` | | `OnRewardedVideoAdClosed` | `OnAdDismissed` | `EventArgs` | | `OnUserRewardEarned` | `OnAdRewardEarned` | `RewardedItem` | | *(new)* | `OnAdFailedToDisplay` | `BlueStackError` | :::tip A project-wide find-and-replace covers most of the work — the unified `OnAd*` names do not collide between formats since each handler is bound to a specific ad instance. ::: --- ## Step 5 — Verify the build 1. **Editor**: re-open the project. Compilation errors will surface every remaining v3.x identifier; work through them top-down. 2. **Android**: build a debug APK. Force-resolve Android dependencies first (`Assets > External Dependency Manager > Android Resolver > Force Resolve`). 3. **iOS**: export the Xcode project. Delete the `Pods/` and `Podfile.lock` from previous v3.x exports if present, then run `pod install --repo-update`. :::caution After a v3.x → v6.0.0 upgrade, do a clean build of the player. Stale managed assemblies referencing the old class / event names can mask compilation errors until they're flushed. ::: --- ## Recommended v6.0.0 features to adopt These additions are optional but generally simplify common integrations. ### `IsReady` for full-screen ads Both `InterstitialAd` and `RewardedAd` expose an `IsReady` property: ```csharp showLineNumbers if (_interstitialAd.IsReady) _interstitialAd.Show(); if (_rewardedAd.IsReady) _rewardedAd.Show(); ``` This lets you gate `Show()` from a button click without having to wire up `OnAdLoaded` callback state-tracking yourself. ### Anchored banner positioning If you want a banner to follow a UI / world-space GameObject, use the anchor-based constructor — the SDK adds an `AdPlacementHandler` component that tracks `anchor.position` automatically: ```csharp showLineNumbers _bannerAd = new BannerAd(placementId, anchorTransform, trackingCamera); ``` Use `bannerAd.PlacementHandler.UpdateInterval = 0.1f` to throttle anchor tracking (clamped to `[0, 5]` seconds; default is every frame). ### `useSafeArea` opt-out For sticky Top/Bottom banners, pass `useSafeArea: false` to overlap the notch / home indicator / status bar: ```csharp showLineNumbers _bannerAd = new BannerAd(placementId, AdPosition.Bottom, useSafeArea: false); ``` Custom-positioned banners (`Vector2` or `Transform` anchor) always ignore the safe area regardless of this flag. ### Mask `UpdateInterval` throttling If a mask is bound to a heavily animated `RectTransform`, throttle native updates: ```csharp showLineNumbers _bannerAd.SetMask(maskRect); _bannerAd.MaskHandler.UpdateInterval = 0.1f; // 10 Hz ``` --- ## Removed APIs reference If you see compile errors for any of the following symbols, refer to the section linked alongside. | Removed | Replacement | Section | | ----------------------------------------------------------- | ---------------------------------------------------- | ----------------------------- | | `Settings(bool)` argument to `Initialize` | `BlueStackAds.SetDebugMode(bool)` | [Step 2](#step-2--update-sdk-initialization) | | `Action` init callback | `Action` (single callback) | [Step 2](#step-2--update-sdk-initialization) | | `Action` adapters callback | Folded into the single `Action` | [Step 2](#step-2--update-sdk-initialization) | | `class RewardedVideoAd` | `class RewardedAd` | [Step 3](#step-3--rename-rewardedvideoad-to-rewardedad) | | `RewardedAd.OnUserRewardEarned` | `RewardedAd.OnAdRewardEarned` | [Step 3](#step-3--rename-rewardedvideoad-to-rewardedad) | | `BannerAd.OnBannerDidLoad` and family | `BannerAd.OnAd*` | [Step 4 — Banner](#banner) | | `InterstitialAd.OnInterstitial*` | `InterstitialAd.OnAd*` | [Step 4 — Interstitial](#interstitial) | | `RewardedAd.OnRewardedVideoAd*` | `RewardedAd.OnAd*` | [Step 4 — Rewarded](#rewarded) | | `BannerAd(string, Transform, RectTransform, Camera)` | Anchor ctor + `SetMask(RectTransform, Camera)` | [Step 5](#step-5--replace-the-masked-banner-constructor) | --- --- ## Getting Started :::info This section explains how to get started with the BlueStack Unity Plugin. It guides you through the process of adding the BlueStack Unity Plugin to your project. After following this section you’re being able to getting started with the more advanced features of the BlueStack Unity Plugin. ::: :::info Looking for a working reference? Our public demo app on GitHub — [azerion/bluestack-demo-unity](https://github.com/azerion/bluestack-demo-unity) — showcases the BlueStack Unity SDK across banner, interstitial, rewarded, and native ad formats. ::: ## Prerequisites {/* :::info On Android, using `GameActivity` as the application entry point is not yet supported. ::: */} ## Import the package Follow the steps listed below to ensure your project includes the BlueStack SDK. - Add scoped registries in `Edit -> Project Settings -> Package Manager -> Add Scoped Registry` - Add npm registry for BlueStack ```showLineNumbers Name: Azerion URL: http://registry.npmjs.com Scope(s): com.azerion.bluestack ``` - BlueStack Unity package has a indirect dependency of **EDM4U** which need to be resolved using UPM. ```showLineNumbers Name: package.openupm.com URL: https://package.openupm.com Scope(s): com.google.external-dependency-manager ``` - Update `Packages/manifest.json` ## Manage Dependencies BlueStack Unity plugin maintains native BlueStack SDK version compatibility. To accomplish this BlueStack Unity plugin is distributed with the [**EDM4U**](https://github.com/googlesamples/unity-jar-resolver). It provides Unity plugins the ability to declare dependencies(Android specific libraries (e.g., AARs) or iOS CocoaPods), which are then automatically resolved and copied into your Unity project. :::info Note: The BlueStack Unity plugin dependencies are listed in Packages/com.azerion.bluestack/Editor/BlueStackDependencies.xml file. ::: ### 1. Add repository dependencies Open the file `settingsTemplate.gradle` file in `Assets/Plugins/Android`. If you don't have such a file, go to Project `Settings > Player > Publishing Settings` and enable the `Custom Gradle Settings Template` gradle option. ![Custom Gradle Settings Template option enabled in Unity Player Publishing Settings](./00-images/custom_gradle_settings_template_light.png#gh-light-mode-only) ![Custom Gradle Settings Template option enabled in Unity Player Publishing Settings](./00-images/custom_gradle_settings_template_dark.png#gh-dark-mode-only) Add the following repo dependencies. ```groovy showLineNumbers dependencyResolutionManagement { ... repositories { ... google() mavenCentral() maven { url 'https://packagecloud.io/smartadserver/android/maven2' } ... } ... } ``` Open the file `baseProjectTemplate.gradle` file in `Assets/Plugins/Android`. If you don't have such a file, go to Project `Settings > Player > Publishing Settings` and enable the `Custom Base Gradle Template` gradle option. ![Custom Base Gradle Template option enabled in Unity Player Publishing Settings](./00-images/custom_base_gradle_template_light.png#gh-light-mode-only) ![Custom Base Gradle Template option enabled in Unity Player Publishing Settings](./00-images/custom_base_gradle_template_dark.png#gh-dark-mode-only) Add the following repo dependencies. ```groovy showLineNumbers allprojects { ... repositories { ... google() mavenCentral() maven { url 'https://packagecloud.io/smartadserver/android/maven2' } ... } ... } ``` ### 2. Enable the Custom Main Gradle Template Add `mainTemplate.gradle`. Go to `Project Settings > Player > Publishing Settings` and enable the mainTemplate gradle option. ![Custom Main Gradle Template option enabled in Unity Player Publishing Settings](00-images/custom_main_gradle_template_light.png#gh-light-mode-only) ![Custom Main Gradle Template option enabled in Unity Player Publishing Settings](00-images/custom_main_gradle_template_dark.png#gh-dark-mode-only) {/* panels:start */} {/* div:left-panel */} ### 3. Resolve Android Dependencies In the Unity editor, select `Assets > External Dependency Manager > Android Resolver > Resolve`. T he Unity External Dependency Manager library will copy the declared dependencies from `BlueStackDependencies.xml` into the `Assets/Plugins/Android/mainTemplate.gradle` file of your Unity project. {/* div:right-panel */} ```groovy showLineNumbers dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) // Android Resolver Dependencies Start implementation 'com.azerion:bluestack-sdk-core:6.0.0' // Android Resolver Dependencies End **DEPS**} ``` {/* panels:end */} :::caution If EDM4 doesn't copy the dependencies into `mainTemplate.gradle` then you can manually add those dependencies or force EDM4 via `Assets > External Dependency Manager > Android Resolver > Force Resolve` ::: ### Known android build issues and solution - Kotlin module issue ```bash More than one file was found with OS independent path 'META-INF/annotation-experimental_release.kotlin_module' ``` Please add the following config into `launcherTemplate.gradle` ```groovy showLineNumbers android { ... packagingOptions { exclude("META-INF/*.kotlin_module") } ... } ``` ```xml ``` 1. BlueStack iOS framework needs to have minimum iOS version `13.0`. 2. Go to the player settings and set the `Target minimum iOS Version` greater than or equal to `13.0`. ![minimum_iOS_version](00-images/minimum_ios_version_light.png#gh-light-mode-only) ![minimum_iOS_version](00-images/minimum_ios_version_dark.png#gh-dark-mode-only) 3. In the Unity editor, select `File > Build Settings > Build`. The Unity External Dependency Manager library will copy the declared dependencies from `BlueStackDependencies.xml` into the `Podfile` file to the `UnityFramework` target of your Unity-iPhone app. ```bash source 'https://cdn.cocoapods.org/' platform :ios, '13' target 'UnityFramework' do pod 'BlueStack-SDK', '6.0.0' end target 'Unity-iPhone' do end ``` ![ios-resolver-settings](00-images/ios_resolver_settings_light.png#gh-light-mode-only) ![ios-resolver-settings](00-images/ios_resolver_settings_dark.png#gh-dark-mode-only) :::caution 1. It is recommended to remove `:linkage => :static` from the `Podfile` file or uncheck `Link frameworks statically` from **iOS Resolver Settings** of **EDM4U**. 2. If **EDM4U** failed to resolve the dependencies that are in `Podfile` then you need to manually resolve those dependencies by executing `pod install --repo-update` from the terminal. ::: ## Initialize the BlueStack SDK Before loading ads, you need to initialize the BlueStack SDK by calling `BlueStackAds.Initialize()`. This needs to be done only once, ideally at app launch. :::info Note: You will have to register your app in BlueStack console to get an appId for your app. ::: Here's an example of how to call `Initialize()` within the `Start()` method of a script attached to a `GameObject`: ```csharp showLineNumbers using Azerion.BlueStack.API; using Azerion.BlueStack.API.Banner; public class BlueStackAdController : MonoBehaviour { public void Start() { // Optional: enable verbose native SDK logging. // The current state is also exposed via BlueStackAds.IsDebugModeEnabled. BlueStackAds.SetDebugMode(true); // Initialize the BlueStack SDK. BlueStackAds.Initialize(appId, HandleInitializationComplete); } // The callback runs on the Unity main thread once SDK initialization finishes. // InitializationStatus carries the per-adapter readiness state. private void HandleInitializationComplete(InitializationStatus status) { foreach (KeyValuePair entry in status.AdapterStatusMap) { Debug.Log("Adapter Name: " + entry.Value.Name + ", " + "Adapter State: " + entry.Value.InitializationState + ", " + "Adapter Description: " + entry.Value.Description); } if (BlueStackAds.IsInitialized) { Debug.Log("BlueStack SDK is ready to load ads."); } } } ``` :::note **Migrating from v3.x:** The `Initialize` signature has changed in v6.0.0. The `Settings` parameter and the separate `SDKInitializationStatus` callback have been removed. Use `BlueStackAds.SetDebugMode(bool)` to toggle debug logging at any time, and read `BlueStackAds.IsInitialized` to check readiness. The single completion callback now delivers per-adapter `InitializationStatus` directly. ::: ## Mediation Ad Network BlueStack SDK mediation feature enable you to server ads from multiple sources including our Ad Exchange and third-party ad networks. :::info To load ads from third party ad networks, you will have to first configure each ad network for your app on BlueStack console. ::: ## Settings BlueStack setings allows you to configure mediation networks. To open the setings, select `Azerion > BlueStack > Settings` from the menu. ### Configure AdMob App ID - You can insert AdMob App ID here for both iOS and Android platforms. ### Select Mediation Networks - You can choose your prefered mediation networks from the list. Check the [Mediation Networks](./20-mediation/index.md) documentation for more details. :::info **Recommended:** enable all mediation networks by default so the SDK can serve from every available demand source. Omit a network only if you have a specific reason not to ship that demand source. EDM4U will resolve the matching native dependencies automatically. ::: ![BlueStack Settings](00-images/bluestack_settings_light.png#gh-light-mode-only) ![BlueStack Settings](00-images/bluestack_settings_dark.png#gh-dark-mode-only) :::caution You need to resolve or force resolve Android dependencies via EDM4 `Assets > External Dependency Manager > Android Resolver > Force Resolve` every time you add/remove any mediation networks from the Settings. ::: --- ## Release Notes(Unity) ## [6.0.0] - 2026-05-21 This is a major release. Several public APIs have been renamed or have shape changes. See the **Breaking changes** section below before upgrading. :::tip See the dedicated [v3.x → v6.0.0 migration guide](./30-advanced-topics/03-migrate-from-v3-to-v6.md) for a step-by-step walkthrough with full code examples, event-rename tables, and a removed-APIs reference. ::: ### Native SDK upgrades - Android: BlueStack Core SDK upgraded to 6.0.1 - Android: In-App Bidding mediation adapter upgraded to 6.0.0.1 - Android: Google Mobile Ads mediation adapter upgraded to 6.0.0.1 - Android: Equativ mediation adapter upgraded to 6.0.0.2 - iOS: BlueStack Core SDK upgraded to 6.0.0 - iOS: In-App Bidding mediation adapter upgraded to 6.0.2 - iOS: Google Mobile Ads mediation adapter upgraded to 6.0.0 - iOS: Equativ mediation adapter upgraded to 6.0.0 ### Added - `BlueStackAds.SetDebugMode(bool)` for toggling native SDK debug logging at any time, plus `BlueStackAds.IsDebugModeEnabled` to read the cached state. - `BlueStackAds.IsInitialized` property to check native-side readiness. - `InterstitialAd.IsReady` and `RewardedAd.IsReady` properties. - `BannerAd` anchor-based positioning — new constructor `BannerAd(string placementId, Transform anchor, Camera camera = null)`. The banner follows the anchor's screen position automatically via the new `AdPlacementHandler` component. - `BannerAd` custom screen-space position constructor — `BannerAd(string placementId, Vector2 position)`. - Optional `useSafeArea` parameter on `BannerAd(string, AdPosition, bool useSafeArea = true)` to opt out of safe-area insets for sticky Top/Bottom banners. Custom-positioned banners always ignore the safe area. - Banner masking — `BannerAd.SetMask(RectTransform, Camera)` / `BannerAd.RemoveMask()` clip the banner to a UI region. The mask follows the `RectTransform` as it animates or resizes. - `BannerAd.OnAdResized` event delivers a `PreferredBannerSize` (`Width`, `Height`) whenever the SDK reports a post-load size change. - `BannerAd.OnAdLoaded` payload changed to `PreferredBannerSize` carrying the SDK-preferred dimensions (was `EventArgs`). - `RewardedAd.OnAdRewardEarned` (renamed from `OnRewardEarned`) delivers a `RewardedItem`. - `UpdateInterval` API on `AdMaskHandler` and `AdPlacementHandler`, clamped to `[0, 5]` seconds (0 = every frame). ### Changed - **Breaking** — `BlueStackAds.Initialize` signature simplified: - Before: `Initialize(string appId, Settings settings, Action sdkCallback, Action adaptersCallback)` - After: `Initialize(string appId, Action initializationCompleteAction = null)` - The `Settings`-based debug toggle is replaced by `BlueStackAds.SetDebugMode(bool)`. - The separate success/fail `SDKInitializationStatus` callback is removed. The single completion callback now delivers `InitializationStatus.AdapterStatusMap` (per-adapter `AdapterStatus`). - The initialization callback is now consistently dispatched to the Unity main thread on both iOS and Android. - **Breaking** — `RewardedVideoAd` class renamed to `RewardedAd`. - **Breaking** — Banner event names renamed: - `OnBannerDidLoad` → `OnAdLoaded` (payload changed to `PreferredBannerSize`) - `OnBannerDidFailed` → `OnAdFailedToLoad` - `OnBannerDisplay` → `OnAdDisplayed` - `OnBannerHide` → `OnAdHidden` - `OnBannerDidRefresh` → `OnAdRefreshed` - `OnBannerDidFailToRefresh` → `OnAdFailedToRefresh` - **Breaking** — Interstitial event names renamed: - `OnInterstitialDidLoaded` → `OnAdLoaded` - `OnInterstitialDidFail` → `OnAdFailedToLoad` - `OnInterstitialClicked` → `OnAdClicked` - `OnInterstitialDidShown` → `OnAdDisplayed` - `OnInterstitialDisappear` → `OnAdDismissed` - **Breaking** — Rewarded event names renamed: - `OnRewardedVideoAdLoaded` → `OnAdLoaded` - `OnRewardedVideoAdError` → `OnAdFailedToLoad` - `OnRewardedVideoAdAppeared` → `OnAdDisplayed` - `OnRewardedVideoAdClicked` → `OnAdClicked` - `OnRewardedVideoAdClosed` → `OnAdDismissed` - `OnUserRewardEarned` → `OnAdRewardEarned` - Banner ads now show automatically on load (auto-refresh handling moved to the BlueStack console). - Banner container view now wraps the loaded ad's actual rendered size (previously used the requested `AdSize`), reducing layout-padding artifacts for dynamic-width banners. - iOS bridge routes init and SDK-config calls through a `BSUInitializer` Swift façade that caches the debug-mode flag for bridge-side diagnostics. - Android bridge routes init and SDK-config calls through a `UnityMobileAds` Kotlin singleton mirroring the iOS façade. ### Removed - **Breaking** — `Settings` parameter removed from `BlueStackAds.Initialize`. The `Settings` class is retained for source compatibility only and is no longer used by the SDK. ## [3.4.1] - 2026-02-27 ### Changed - Android: BlueStack Core SDK version upgraded to 5.4.1 - Android: In-App Bidding mediation adapter upgraded to 5.4.0.0 - Android: Google Mobile Ads mediation adapter upgraded to 5.4.1.0 - Android: Equativ mediation adapter upgraded to 5.2.1.1 - iOS: BlueStack Core SDK version upgraded to 5.4.1 - iOS: In-App Bidding mediation adapter upgraded to 5.4.0 - iOS: Google Mobile Ads mediation adapter upgraded to 5.4.0 - iOS: Equativ mediation adapter upgraded to 5.1.8 ## [3.3.1] - 2025-12-10 Note: This release includes functional changes originally intended for 3.4.0, without requiring unreleased native dependencies. ### Changed - Android: Equativ mediation adapter upgraded to 5.2.1.0 - Android: Updated Unity bridge calls to execute on the UI thread to prevent crashes and improve stability. - iOS: BlueStack Core SDK upgraded to 5.3.1 - iOS: Removed linker flag -ld64 from PostProcessBuild script, previously added as a temporary fix for Xcode 15.0+ build issue. - iOS: Adjusted banner position to properly respect safe area. - Moved ad prefabs from Resources to Editor folder to avoid loading when not needed. ### Fixed - Android: Fixed extra banner spacing when system already reserves nav bar area (non edge-to-edge). - iOS: Fixed banner view constraint issue caused by height mismatch between `UIView` and `MNGContainerView`. - Replaced deprecated `FindObjectsOfType` with `FindObjectsByType` for Unity 6+ compatibility. ## [3.4.0] – 2025-12-05 (Deprecated) ⚠️ This version is deprecated. It depends on unreleased native SDKs and should not be used. ### Changed - Android: BlueStack Core SDK upgraded to 5.4.0 - Android: In-App Bidding mediation adapter upgraded to 5.4.0.0 - Android: Updated Unity bridge calls to execute on the UI thread to prevent crashes and improve stability. - iOS: BlueStack Core SDK upgraded to 5.3.1 - iOS: Removed linker flag -ld64 from PostProcessBuild script. - iOS: Adjusted banner position to properly respect safe area. - Moved ad prefabs from Resources to Editor folder to avoid loading when not needed ### Fixed - Android: Fixed extra banner spacing when system already reserves nav bar area (non edge-to-edge). - iOS: Fixed banner view constraint issue caused by height mismatch between `UIView` and `MNGContainerView`. - Replaced deprecated `FindObjectsOfType` with `FindObjectsByType` for Unity 6+ compatibility. ## [3.3.0] - 2025-10-09 ### Changed - BlueStack Android Core SDK version upgraded to 5.3.0 - BlueStack iOS Core SDK version upgraded to 5.3.0 - Removed BlueStack core dependency handling from Settings - Updated SDK to ensure compatibility with Unity 6. - Implemented a custom `CrossPlatformInput` system in Unity to support both the legacy `Input Manager` and the new `Input System` - Upgraded Texture loading process for Native ads in Unity by improving URL validation, network and texture creation error handling. - Improved native ad impression validation. ### Fixed - Fixed wrong Android `bluestack-sdk-core` dependency version - Fixed an issue where the banner ad sat under the bottom navigation bar on Android by updating `BannerAdUtils.getSafeInsets` to use `WindowInsetsCompat` `systemBars`, ensuring banners clear the navigation bar. - Fixed Android Native Ad load method to use `MNGPreference` instead of new `RequestOptions` ## [3.2.1] - 2025-06-18 ### Changed - BlueStack Android Core SDK version upgraded to 5.1.5 - BlueStack iOS Core SDK version upgraded to 5.1.5 ## [3.2.0] - 2025-06-16 ### Changed - BlueStack Android Core SDK version upgraded to 5.1.3 - BlueStack iOS Core SDK version upgraded to 5.1.4 - Implemented API changes of native core SDK v5 - iOS dependency management has been updated in the Unity Editor, mediation adapters are now added as separate pods in the Podfile. - A single source of truth is now used for dependency management, removing the need for separate dependency XML files for Mediation networks. ## [3.1.10] - 2024-11-15 ### Changed - Updated the iOS Post Processor to include AdMob App IDs based on the inclusion of AdMob mediation adapters. - onIOSMediationNetworksUpdateEvent will trigger after changes to Settings serializedObject properties are applied. ### Fixed - Fixed the subspec inclusion process in the Podfile to ensure the full subspec string is removed when no adapters are selected and the subspec array is empty. - Fixed a Null Reference issue by updating the Destroy and distractor methods for Native Ads. ## [3.1.9] - 2024-11-07 ### Fixed - Removed unused namespaces and fixed assembly reference missing issue. ## [3.1.8] - 2024-11-07 ### Changed - BlueStackSettings Initialization on AssetPostProcess. ## [3.1.7] - 2024-10-23 ### Changed - Removed `Rewarded` namespace - Renamed the `GetBadge` method to `GetBadgeText` - Renamed the `GetTitle` method to `GetTitleText` - Renamed the `RegisterImageGameObject` method to `RegisterCoverImageGameObject` - Renamed the `RegisterCallToActionGameObject` method to `RegisterCallToActionTextGameObject` ### Fixed - Rerestricted interaction with Native ad objects while any overlay view is on top. ## [3.1.6] - 2024-07-26 ### Changed - BlueStack Android SDK version upgraded to 4.4.0 ### Added - Manage Android mediation networks from the BlueStack Settings ## [3.1.5] - 2024-07-10 ### Added - Method added to refresh Banner in the editor - Native ad Close method added to trigger OnNativeAdClosed event in the editor ### Changed - Unity deprecated methods handled - Readme doc update ### Fixed - Fixed the BlueStack Settings null exception issue when no settings exist - Fixed the issue with saving AdMob App ids - Error handled in Native ads, in case of null or empty image urls. ## [3.1.4] - 2024-07-04 ### Changed - Reward type and amount added in RewardedItem of OnUserRewardEarned event ## [3.1.3] - 2024-04-26 ### Changed - Automatically update all dependencies when SDK version is updated ## [3.1.2] - 2024-04-17 ### Changed - BlueStack iOS SDK version updated to 4.4.6 ## [3.1.1] - 2024-04-09 ### Changed - BlueStack Android SDK version updated to 4.3.2 ### Fixed - iOS runtime error due to Criteo dependency ## [3.1.0] - 2024-04-08 ### Changed - BlueStack iOS SDK version updated to 4.4.4 ### Added - Option to add/remove iOS mediation networks from BlueStack Settings ## [3.0.4] - 2024-03-06 ### Changed - BlueStack Android SDK version updated to 4.3.1 ## [3.0.3] - 2024-02-23 ### Fixed - Platform check in PostProcessBuild ## [3.0.2] - 2024-02-14 ### Changed - BlueStack iOS SDK version updated to 4.4.0 - Google-Mobile-Ads-SDK is included in the package dependencies to be added as a subspec in the pod ## [3.0.1] - 2024-01-24 ### Changed - BlueStack iOS SDK version updated to 4.3.0 - Native Ad support for iOS - Editor Settings: Both iOS and Android admob id fields are together ## [3.0.0] - 2023-10-13 ### Changed - BlueStack iOS SDK version updated to 4.2.9 - BlueStack Android SDK version updated to 4.3.0 ### Added - Native Ad support for Android ## [2.0.2] - 2023-09-21 ### Changed - BlueStack android SDK version updated to 4.2.10 ### Fixed - Android Ad loading error immediately after initialization ## [2.0.1] - 2023-08-10 ### Changed - BlueStack android SDK version updated to 4.2.9 ## [2.0.0] - 2023-06-26 ### Changed - BlueStack android SDK version updated to 4.2.8 - BlueStack iOS SDK version updated to 4.2.7 - Restructured the assembly definition files and namespace - SDK Initialization complete callback ### Added - Adapters Initialization complete callback with status ## [1.1.1] - 2023-05-18 ### Fixed - AdBehaviour namespace issue fixed. ## [1.1.0] - 2023-05-12 ### Changed - BlueStack android SDK version updated to 4.2.6 - BlueStack iOS SDK version updated to 4.2.5 - Interstitial and RewardedVideo ad placeholders (prefabs) are updated in the Unity Editor - Load, Show, Hide methods are improved for the unity editor - "setPosition" method renamed to "SetPosition" ### Added - Added placeholders (prefabs) for for all kinds of banner ads in the Unity Editor - Load, display banner ads based on ad size and display size in the Unity Editor - Change banner position at runtime in the Unity Editor (Using SetPosition method) - OnBannerDidLoad, OnBannerDidFailed, OnBannerDisplay, OnBannerHide events are now functional in the Unity Editor - Added close button actions and Ad click behaviours in the Unity Editor - Load and show interstitial and rewarded ads placeholder based on orientation - Added timer for the Rewarded ad in the Unity Editor ## [1.0.3] - 2023-04-18 ### Changed - BlueStack android SDK version updated to 4.2.5 - BlueStack iOS SDK version updated to 4.2.4 ## [1.0.2] - 2023-03-27 ### Changed - BlueStack android SDK version updated to 4.2.2 - BlueStack iOS SDK version updated to 4.2.2 ## [1.0.1] - 2023-03-14 ### Changed - BlueStack android SDK version updated to 4.2.1 - BlueStack iOS SDK version updated to 4.2.1 ## [1.0.0] 2023-02-23 ### Added - Supported Ad Formats - Banner, Interstitial and RewardedVideo. - Supported Ad Networks - BlueStack, Improve Digital and AdMob. --- ## Banner Ads(3) ## Overview A Banner ad typically appears as a rectangular or square graphic within your app's user interface. Banner ads are usually embedded at the top, bottom, or inline within the scrollable content of a screen. Before You Start. Make sure that you have correctly integrated the BlueStack React Native Plugin into your application. Integration is outlined [here](../index.md). For a working implementation of this ad format, see the [azerion-inapp-demo-reactnative](https://github.com/azerion/azerion-inapp-demo-reactnative) demo app. ## Create a BannerView You need to import the `BannerAdView` component in order to display BlueStack banner ads. ```javascript import { BannerAdView } from "@azerion/bluestack-sdk-react-native"; ``` The SDK module provides a `BannerAdView` component, that must be used to display the ads. Each render of the component loads a single ad, allowing you to display multiple ads at once. It has the following props: | Property | Type | Requirement | Description | | ------------------- | ------------ | ----------- | ------------------------------------------------------------------------------- | | type | BannerAdType | Mandatory | Banner ad type | | placementId | string | Mandatory | ID of impression placement provided in BlueStack console | | shouldLoadWhenReady | boolean | Optional | If `true`, the ad will be automatically loaded as soon as all the props are set | | preference | AdPreference | Optional | To pass additional request preferences before loading an ad | _Here's an example of how to create and auto load a banner ad:_ ```javascript const refBanner = useRef(null); .... ``` ## Load a Banner Ad `BannerAdView` has a `load` method that takes an instance of `AdPreference` as an optional parameter. You can use this method to "manually" load a new ad. :::info Banner `load` call is not required when the `shouldLoadWhenReady` prop is set to `true`. ::: ### Without AdPreference ```javascript // Load a banner ad. refBanner.current?.load(); ``` ### With AdPreference To request a banner ad using [AdPreference](../30-advanced-topics/targeting.md), provide an instance of `AdPreference` in the `BannerAdView`'s `load` method: ```javascript // Load a banner ad with preferences. refBanner.current?.load(preference); ``` :::caution You don't need to set preferences in both the `preference` prop and the `load` method. If preferences are set in both, the one passed to the `load` method will be used. ::: ## Ad events ### Register for banner events `BannerAdView` exposes props for listening to events, allowing you to handle the state of your app: | Methods | Definition | | ------------------- | ------------------------------------------------------------- | | onAdLoaded | Ad is successfully loaded and SDK is ready to display the ad. | | onAdFailedToLoad | The ad failed to load or display. | | onAdClicked | User has clicked the ad. Ad may open a link in browser. | | onAdRefreshed | The banner ad has been refreshed. | | onAdFailedToRefresh | The banner ad has been failed to refresh. | ### Implement banner events _Here's an example of how to use event listeners:_ ```javascript showLineNumbers const [bannerHeight, setBannerHeight] = useState(0); const refBanner = useRef(null); .... // Load a banner ad with preferences. refBanner.current?.load(preference); .... { console.log('Banner Ad Loaded'); setBannerHeight(+object.nativeEvent.size); }} onAdFailedToLoad={(error: any) => { console.log('Banner Ad load failed: ' +error?.nativeEvent?.error); }} onAdRefreshed={() => { console.log('Banner Ad refresh succeed'); }} onAdFailedToRefresh={(error: any) => { console.log('Banner Ad refresh failed: ' + error?.nativeEvent?.error); }} onAdClicked={() => { console.log('Banner Ad Clicked'); }} ref={refBanner} /> ``` ## Show / Hide Banner Ad After loading the banner ad you can hide it using the `hide` method and show it again using the `show` method. ```javascript // Hide banner ad refBanner.current?.hide(); ``` ```javascript // Show banner ad refBanner.current?.show(); ``` ## Destroying Banner Ad You can destroy / remove the banner using the `destroy` method. ```javascript refBanner.current?.destroy(); ``` ## Enable / Disable Banner Refresh You can enable or disable the banner auto refresh using the `toggleRefresh` method. Pass `true` to enable refresh and `false` to disable it. ```javascript refBanner.current?.toggleRefresh(true); ``` ## Banner Ad sizes BlueStack ads provide a variety of pre-defined sizes. See the table below for details about our supported standard banner sizes: | Type | Value | Description | Dimensions in dp (WxH) | | ------------------- | ------------------ | --------------------- | ---------------------------------------------- | | Standard | standard | Standard Banner | 320 x 50 | | Large | large | Large Banner | 320 x 100 | | Full | full | Full Banner | 468 x 60 | | Medium Rectangle | mediumRectangle | Medium Rectangle | 300 x 250 | | Leaderboard | leaderboard | Leaderboard | 728 x 90 | | Dynamic | dynamic | Adjusted Banner | Screen width x 50 | | Dynamic Leaderboard | dynamicLeaderboard | Adjusted Leaderboard | Screen width x 90 | --- ## Interstitial Ads(3) ## Overview Interstitial ads are full-screen ad units that are triggered at natural transition points within the app workflow, such as between levels or during screen transitions. BlueStack Interstitial ads support multiple creative formats, including static assets (images or text) and rich media (e.g., video or interactive content). Before You Start. Make sure that you have correctly integrated the BlueStack React Native Plugin into your application. Integration is outlined [here](../index.md). For a working implementation of this ad format, see the [azerion-inapp-demo-reactnative](https://github.com/azerion/azerion-inapp-demo-reactnative) demo app. ## Create an Interstitial Ad You need to import the `InterstitialAdManager` component in order to display BlueStack interstitial ads. ```javascript import { InterstitialAdManager } from "@azerion/bluestack-sdk-react-native"; ``` ## Load an Interstitial Ad You can load an interstitial ad right after the SDK finishes its initialization. You have to pass the platform specific interstitial placement id to the `loadAd` method. ### Without AdPreference ```javascript InterstitialAdManager.loadAd("/" + appId + "/interstitial", false); ``` ### With AdPreference To request an interstitial ad using [AdPreference](../30-advanced-topics/targeting.md), provide an instance of `AdPreference` in the `loadAd` method: ```javascript /** * Load interstitial ad. * @param: placementId * @param: autoDisplay: chooses if the interstitial will be displayed automatically (optional) * @param: preference (optional) */ InterstitialAdManager.loadAd("/" + appId + "/interstitial", false, preference); ``` ## Ad events ### Register for Interstitial events `InterstitialAdManager` exposes the following events through its lifecycle. Register for interstitial ad events before loading the interstitial ad. | Events | Definition | | --------------- | ------------------------------------------------------------- | | onAdLoaded | Ad is successfully loaded and SDK is ready to display the ad. | | onAdFailedError | The ad failed to load or display. | | onAdClicked | User has clicked the ad. Ad may open a link in browser. | | onAdDisplayed | Ad has appeared on the screen. | | onAdDismissed | The ad has disappeared. | ### Implement Interstitial events _Here's an example of how to register event listeners for interstitial ads:_ ```javascript InterstitialAdManager.addEventListener((event) => { switch (event.interstitialEvent) { case "onAdLoaded": console.log("Interstitial Ad Loaded"); break; case "onAdDismissed": console.log("Interstitial Ad disappeared"); break; case "onAdDisplayed": console.log("Interstitial Ad displayed"); break; case "onAdClicked": console.log("Interstitial Ad did click"); break; case "onAdFailedError": console.log("Interstitial Ad failed:" + event.errorBluestackMessage); break; default: break; } }); ``` _Here's an example of how to remove all event listeners:_ ```javascript InterstitialAdManager.removeAllEventListeners(); ``` :::caution Make sure you only register the event listener once. ::: ## Show an Interstitial Ad After loading the interstitial ad you can request it to be displayed using the `displayAd` method. :::info Listen to interstitial ad events to make sure the ad was successfully loaded before you call the `displayAd` method. ::: ```javascript InterstitialAdManager.displayAd(); ``` --- ## Rewarded Ads(3) ## Overview Rewarded ads are full-screen, non-skippable video ads (15–30 seconds) that users voluntarily watch in exchange for in-app rewards like virtual currency, in-app items or exclusive content. After the video ends, a callback confirms completion so the reward can be granted. Before You Start. Make sure that you have correctly integrated the BlueStack React Native Plugin into your application. Integration is outlined [here](../index.md). For a working implementation of this ad format, see the [azerion-inapp-demo-reactnative](https://github.com/azerion/azerion-inapp-demo-reactnative) demo app. ## Create a Rewarded Ad You need to import the `RewardedAdManager` component in order to display BlueStack rewarded ads. ```javascript import { RewardedAdManager } from "@azerion/bluestack-sdk-react-native"; ``` ## Load a Rewarded Ad You can load a rewarded ad right after the SDK finishes its initialization. You have to pass the platform specific rewarded placement id to the `loadAd` method. ### Without AdPreference ```javascript RewardedAdManager.loadAd("/" + appId + "/rewarded"); ``` ### With AdPreference To request a rewarded ad using [AdPreference](../30-advanced-topics/targeting.md), provide an instance of `AdPreference` in the `loadAd` method: ```javascript /** * Load rewarded ad. * @param: placementId * @param: preference (optional) */ RewardedAdManager.loadAd("/" + appId + "/rewarded", preference); ``` ## Ad events ### Register for Rewarded events `RewardedAdManager` exposes the following events through its lifecycle. Register for rewarded ad events before loading the rewarded ad. | Events | Definition | |-----------------|-------------------------------------------------------------------------------------------------| | onAdLoaded | Ad is successfully loaded and SDK is ready to display the ad. | | onAdFailedError | The ad failed to load or display. | | onAdClicked | User has clicked the ad. Ad may open a link in browser. | | onAdDisplayed | Ad has appeared on the screen. | | onAdDismissed | The ad has disappeared. | | onRewardEarned | SDK will fire this event with `rewardType` and `rewardAmount` depending on mediation ad network | ### Implement Rewarded events _Here's an example of how to register event listeners for rewarded ads:_ ```javascript RewardedAdManager.addEventListener((event) => { switch (event.rewardedEvent) { case "onAdLoaded": console.log(event.rewardedEvent); break; case "onAdDismissed": console.log(event.rewardedEvent); break; case "onAdDisplayed": console.log(event.rewardedEvent); break; case "onAdClicked": console.log(event.rewardedEvent); break; case "onRewardEarned": console.log( "Reward Earned: Type-" + event.rewardType + ", Amount-" + event.rewardAmount ); break; case "onAdFailedError": console.log(event.errorMessage); break; default: break; } }); ``` _Here's an example of how to remove all event listeners:_ ```javascript RewardedAdManager.removeAllEventListeners(); ``` :::caution Make sure you only register the event listener once. ::: ## Show a Rewarded Ad After loading the rewarded ad you can request it to be displayed using the `displayAd` method. :::info Listen to rewarded ad events to make sure the ad was successfully loaded before you call the `displayAd` method. ::: ```javascript RewardedAdManager.displayAd(); ``` --- ## Android Supported Networks(20-mediation) BlueStack's mediation feature enables you to deliver advertisements to your app from various sources, including BlueStack itself and third-party ad networks. This section provides guidance on integrating mediation partner SDKs through BlueStack's third-party SDK adapters. :::info **Recommended:** include all mediation adapters by default. Omit an adapter only if you have a specific reason not to ship that demand source. The [Get Started](../index.md#add-mediation-partners) page shows the full bundle; the per-partner sections below cover details and opt-in extras. ::: :::warning ```shell -keep public class com.azerion.bluestack.mediation.** { *; } ``` Please add the above rule to your proguard file when you encounter errors similar to what you see here: ```shell java.lang.NoSuchMethodException: com.azerion.bluestack.mediation.* ``` ```shell Exception com.azerion.bluestack.error.AdapterNotFoundError: com.azerion.bluestack.mediation.* ``` ::: :::note - Ensure all dependencies are included as outlined in each network’s integration guide. - Ad formats may require additional configurations or testing to confirm functionality. ::: ## In-App Bidding To include the BlueStack In-App Bidding mediation adapter, add the Maven repository address and Gradle dependency below to your app-level build.gradle file :::info In-App-Bidding has a default dependency with the Equativ SDK. ::: ## Google Mobile Ads To include the BlueStack Google Mobile Ads mediation adapter dependency, - Add your Google App ID to your app's AndroidManifest.xml file ```groovy showLineNumbers title="AndroidManifest.xml" ``` - And add the Gradle dependency below to your app-level build.gradle file ## Equativ (previously Smart Ad Server) To include the BlueStack Equativ mediation adapter, add the Maven repository address and Gradle dependency below to your app-level build.gradle file ## Mediation Adapter Compatibility Matrix | Ad Network | SDK Version | Adapter Version | Supported Ad Formats | |---|---|---|---| | **Google** | | | Banner / MREC, Interstitial, Rewarded Ads | | **Equativ** | | | Banner / MREC, Interstitial | | **BlueStack Bidding** | N/A | | Banner / MREC, Interstitial | --- ## iOS Supported Networks(20-mediation) BlueStack's mediation feature enables you to deliver advertisements to your app from various sources, including BlueStack itself and third-party ad networks. This section provides guidance on integrating mediation partner SDKs through BlueStack's third-party SDK adapters. :::info **Recommended:** include all mediation adapters by default. Omit an adapter only if you have a specific reason not to ship that demand source. The [Get Started](../index.md#add-mediation-partners) page shows the full bundle; the per-partner sections below cover details and opt-in extras. ::: :::note You must add `BlueStackSDK` to your app target if you intend to use any mediation adapters via Swift Package Manager (SPM). ::: ## In-App Bidding Add the BlueStack Bidding Adapter dependency to your app's podfile. ```ruby pod 'BlueStackBiddingAdapter' ``` ## Google Mobile Ads To include the BlueStack GMA mediation adapter, Add the `BlueStackGoogleAdapter` dependency to the Podfile of your application project: ```ruby pod 'BlueStackGoogleAdapter' ``` Go to your project file --> Package Dependencies --> Add(+) --> Search for [https://github.com/azerion/BlueStack-Google-Adapter](https://github.com/azerion/BlueStack-Google-Adapter) ![BlueStackGoogleAdapter Swift Package Search](../../../ios/00-images/bluestack-google-adapter-spm-integration-1-light.png#gh-light-mode-only)![BlueStackGoogleAdapter Swift Package Search](../../../ios/00-images/bluestack-google-adapter-spm-integration-1-dark.png#gh-dark-mode-only) Add the `BlueStackGoogleAdapter` to your app's target via Swift Package Manager ![Add BlueStackGoogleAdapter to target](../../../ios/00-images/bluestack-google-adapter-spm-integration-2-light.png#gh-light-mode-only)![Add BlueStackGoogleAdapter to target](../../../ios/00-images/bluestack-google-adapter-spm-integration-2-dark.png#gh-dark-mode-only) ### Add Your AdMob App ID In the `Info.plist` of your app, please add the key `GADApplicationIdentifier` and set its value to the ID you received from your Azerion Publisher Representative. ![ios-gad-application-identifier](../00-images/ios-gad-application-identifier.png) ## Equativ (previously Smart Ad Server) To include the BlueStack Equativ / Smart Ad Server mediation adapter, In the `Podfile` of your application project add `BlueStackEquativAdapter` dependency ```ruby pod 'BlueStackEquativAdapter' ``` To get the in app bidding add the BlueStack Bidding adapter dependency to your app's podfile also. ```ruby pod 'BlueStackBiddingAdapter' ``` ## Amazon In-App Bidding To include the BlueStack Amazon publisher service in-app bidding adapter, Add the `BluestackAmazonPublisherServicesAdapter` adapter dependency as a subspec of `BlueStack-SDK` in your app’s Podfile: ```ruby pod 'BlueStack-SDK', :subspecs=>["BluestackAmazonPublisherServicesAdapter"] ``` Add the `BluestackAmazonPublisherServicesAdapter` to your app's target via Swift Package Manager ![BlueStackSDK mediation adapter list](../../../ios/00-images/BlueStackAmazonPublisherServiceAdapter_spm-light.png#gh-light-mode-only)![BlueStackSDK mediation adapter list](../../../ios/00-images/BlueStackAmazonPublisherServiceAdapter_spm-dark.png#gh-dark-mode-only) ## Mediation Adapter Compatibility Matrix | Ad Network | SDK Version | Adapter Version | Supported Ad Formats | |---|---|---|---| | **Google** | | | Banner / MREC, Interstitial, Rewarded Ads | | **Equativ** | | | Banner / MREC, Interstitial | | **BlueStack Bidding** | N/A | | Banner / MREC, Interstitial | :::note - Ensure all dependencies are included as outlined in each network’s integration guide. - Ad formats may require additional configurations or testing to confirm functionality. ::: --- ## Targeting Audiences(3) In order to take advantage of our targeting campaign, you must pass an instance of the **AdPreference** class in the ad load call. Setting additional preferences helps BlueStack choose better tailored ads from the network. You need to import the `AdPreference` component in order to work with preferences. ```javascript import { AdPreference, ProviderType, GenderType, LocationType, } from "@azerion/bluestack-sdk-react-native"; ``` _Here's an example of how to create preferences for any ad:_ ```javascript const preference = new AdPreference(); preference.setAge(30); preference.setGender("Female"); preference.setLocation( { latitude: 52.2781641, longitude: 4.7482118, provider: "network", }, 1 ); preference.setLanguage("en"); preference.setContentUrl("https://console.bluestack.app"); preference.setKeyword("brand=myBrand;category=sport"); ``` ## Location Targeting **Our ad server and certain ads can use the user's location to send more targeted ads by passing Latitude and Longitude.** ```javascript preference.setLocation( { latitude: 52.2781641, longitude: 4.7482118, provider: "network", }, CONSENT_FLAG ); ``` **Note:** The `setLocation` method takes the following parameters: - The `Location` instance. - The `CONSENT_FLAG` value (corresponds to an int: 0, 1, 2 or 3). - 0 = Do not allow to send location. - 1 = When you manage location according to the consent value. - 2 and 3 = Allow the SDK to manage location directly in accordance with the consent value using TCF v1 or TCF v2. See with the Azerion team as it depends on your implementation. ## Keyword Targeting Keywords allow you to target certain ad requests with user data. Keywords are useless for targeting if you cannot provide **dynamic values** per user/device. To add keyword targeting, pass these keywords up through the application (they should be formatted as key/value pairs): - Characters per key: 20 - Characters per value: 40 ```javascript preference.setKeyword("brand=myBrand;category=sport"); ``` ## User Demographic Targeting When people are signed in on your app, please share **demographic information** from their settings with the following code: ```javascript preference.setAge(25); preference.setGender("Male"); ``` ## Content mapping for apps See [https://support.google.com/adxseller/answer/6270563](https://support.google.com/adxseller/answer/6270563) for more details. ```javascript preference.setContentUrl("https://my_content_url.com/"); ``` --- ## Release notes(React-native) ## [6.0.2] 2026-07-03 ### Fixed - Android: Banner ads no longer relayout on every frame; layout now runs once per change ### Changed - Android: Bumped BlueStack core to version 6.0.6 - iOS: Bumped BlueStack core to version 6.0.2 ## [6.0.1] 2026-06-12 ### Changed - iOS: Bumped BlueStack iOS Core SDK version to 6.0.1 ### Fixed - Android: replaced deprecated usage of React's `RCTEventEmitter` ## [6.0.0] 2026-05-15 ### Changed - iOS: Upgraded BlueStack iOS Core SDK to version 6.0.0 ## [1.4.1] 2026-02-27 ### Fixed - Removed Firebase and Google Service from example app ## [1.4.0] 2026-02-27 ### Changed - iOS: Upgraded BlueStack iOS Core SDK to version 5.4.1 - Android: Upgraded BlueStack Android Core SDK to version 5.4.1 - Updated adapter dependencies in the example app (iOS & Android) to versions compatible with Core SDK v5.4.1 - `react` and `react-native` dependencies moved to peerDependencies in package.json ## [1.3.0] 2025-12-12 ### Changed - Upgraded BlueStack iOS Core SDK to version 5.3.5 - Managed banner height dynamically through the React Native bridge. - iOS: Added constraints to center the Native Banner View within its React Native parent. ## [1.2.2] 2025-11-06 ### Changed - Upgraded BlueStack Android Core SDK to version 5.2.2 - Upgraded Gradle to 8.9 - Upgraded Android Gradle Plugin to 8.2.0 - Upgraded Kotlin plugin to 1.9.22 ## [1.2.1] 2025-08-04 ### Changed - Upgraded BlueStack Android Core SDK to version 5.2.1 - Upgraded BlueStack iOS Core SDK to version 5.2.1 - Updated adapter dependencies in the example app (iOS & Android) to versions compatible with Core SDK v5.2.1 ## [1.2.0] 2025-07-08 ### Added - Added public API BluestackSDK.IsInitialized() to check initialization status anytime. - Added adType validation to handle undefined values safely. ### Changed - Modified SDK initialization process to prevent reinitialization. - Android banner creation delayed to next frame for proper prop update. - Banner show/hide now handled in bridge to avoid native view removal. ## [1.1.1] 2025-07-02 ### Added - Added a custom logger in the native bridge to display debug logs based on Debug Mode. ### Changed - All core API calls and callbacks now run on the main thread. - Upgraded BlueStack Android Core SDK to version 5.1.4. - Upgraded BlueStack iOS Core SDK to version 5.1.5. - Upgraded React to version 19.1.0. - Upgraded React Native to version 0.80.0. ## [1.1.0] 2025-04-16 ### Changed - Native bridges to use New APIs provided in native SDK version 5.1.3 - Android package name to com.azerion.bluestack.react ### Added - BlueStack privacy settings to set OptOut User and Age Restricted User. ## [1.0.2] 2025-03-13 ### Changed - Updated the SDK to use the native Android SDK version 5.1.3 - Updated the SDK to use the native iOS SDK version 5.1.3 ## [1.0.1] 2024-09-30 ### Added - Auto SDK initialization in case the banner load starts before SDK initialization, provided that `shouldLoadWhenReady` is set to true. ### Changed - Updated the SDK to use the native Android SDK version 4.4.3 - Updated the SDK to use the native iOS SDK version 4.4.10 ### Fixed - The issue of Banner load triggering multiple times in case of `shouldLoadWhenReady`. Now, Performing Banner load after all the `BannerAdView` props are set. ## [1.0.0] 2024-06-27 ### Added - Support for Component-Based Banner Ads - Support for Interstitial Ad format - Support for Rewarded Ad format --- ## Get Started(React-native) This documentation will guide you through the steps to integrate and initialize the BlueStack React Native Plugin into your application. BlueStack SDK React Native Plugin provides functionalities for monetizing your mobile application: from premium sales with rich media, video and innovative formats, it facilitates all standard display formats. BlueStack SDK React Native Plugin can be used for both iOS and Android apps. :::info Looking for a working reference? Our public demo app on GitHub — [azerion/azerion-inapp-demo-reactnative](https://github.com/azerion/azerion-inapp-demo-reactnative) — showcases banner, interstitial, rewarded, MREC, native, overlay, and app-open ads against the public test App ID `3167505`. ::: ## Prerequisites ## ExoPlayer / Media3 compatibility (Android) On Android, the BlueStack SDK uses AndroidX Media3 ExoPlayer for video ad playback. React Native apps that also play video — for example via `react-native-video` or any library built on ExoPlayer/Media3 — can run into dependency conflicts. - BlueStack SDK v6.0.0+ bundles `androidx.media3:media3-exoplayer:1.9.2` - If your app (or one of its libraries) uses ExoPlayer/Media3, align it to a compatible version to avoid runtime conflicts - Resolve conflicts by aligning your Media3 version with the SDK's, or by adding a Gradle resolution strategy in `android/app/build.gradle` :::tip If you hit `AbstractMethodError` or similar ExoPlayer-related runtime errors, verify that every ExoPlayer/Media3 dependency in your Android build is aligned to a compatible version. You can inspect the dependency tree with `./gradlew :app:dependencies` from the `android/` folder. ::: ## Configure your app ### Installation Install the BlueStack React Native Plugin from npm: ### Import components ```javascript import { BluestackSDK, BannerAdView, BannerAdType, InterstitialAdManager, RewardedAdManager, } from "@azerion/bluestack-sdk-react-native"; ``` To include targeting options, import the following components: ```javascript import { AdPreference, ProviderType, GenderType, LocationType, } from "@azerion/bluestack-sdk-react-native"; ``` ## Add mediation partners Mediation adapters are added as native dependencies on each platform. We recommend including all adapters by default so the SDK can serve from every available demand source — omit an adapter only if you have a specific reason not to ship it. :::info **Recommended default:** include every mediation adapter. The snippets below add the full bundle for each platform. ::: :::tip Keeping the packages in sync You don't need to track a separate version number for each package. The npm plugin, the Android mediation adapters, and the iOS adapter pods all belong to the same **v6** release line and are designed to work together: - **npm:** `npm install @azerion/bluestack-sdk-react-native` always pulls the latest compatible plugin. - **Android:** the snippet below pins core and each adapter to exact, mutually-compatible versions. - **iOS:** the pods below are pinned to exact, matching versions for a reproducible set. The plugin also ships with the BlueStack **core SDK preconfigured** — if you ever need a specific core build, see [Override the bundled core SDK version](#override-the-bundled-core-sdk-version). ::: Add the repositories and adapter dependencies to your app-level `build.gradle`: For Google Mobile Ads, also add your AdMob App ID to `AndroidManifest.xml`: ```groovy showLineNumbers title="AndroidManifest.xml" ``` Add the adapter pods to your app's `Podfile`: For Google Mobile Ads, also add the `GADApplicationIdentifier` key to your `Info.plist` with the ID provided by your Azerion Publisher Representative. For per-partner setup, Swift Package Manager, ProGuard rules, and the compatibility matrix, see [Android Supported Networks](./20-mediation/android-mediation-networks.md) and [iOS Supported Networks](./20-mediation/ios-mediation-networks.md). ## Override the bundled core SDK version The React Native plugin ships with the BlueStack **core SDK preconfigured** at a fixed version on each native platform, so you don't normally need to set it yourself: - **Android:** the plugin pulls in `com.azerion:bluestack-sdk-core` transitively. - **iOS:** the plugin's podspec depends on the `BlueStack-SDK` pod. If you need a specific core build — for example to align with another native dependency or to pick up a hotfix — you can **override the bundled version from your own project**. Pin the core dependency in the relevant native build file; your app-level declaration takes precedence over the version the plugin brings in. Add an explicit core dependency to your app-level `build.gradle`. Gradle resolves to the highest requested version, so a higher explicit version wins: To force an exact version (including pinning to a lower one), use a resolution strategy: ```groovy showLineNumbers title="android/app/build.gradle" configurations.all { resolutionStrategy { force 'com.azerion:bluestack-sdk-core:6.0.5' } } ``` Pin the core pod explicitly in your `Podfile`. The version must be compatible with the one the plugin's podspec requires: :::tip Only override the core version if you have a specific reason to. Mixing in a core version that isn't compatible with the plugin can cause runtime errors. ::: ## Initialize the BlueStack SDK Before loading any ads, initialize the BlueStack SDK by calling `BluestackSDK.initialize()` with the `appId` parameter. You can optionally pass a second `enableDebug` parameter (defaults to `false`) to enable debug logs. :::info You have to register your app in the BlueStack console to get an App Id for your app. ::: _Here's an example of how to initialize the SDK:_ ```javascript BluestackSDK.initialize(appId, true) .then(() => { console.log("BluestackSdk initialized"); }) .catch((e) => { console.log("BluestackSdk failed to initialize: " + e); }); ``` _Here's an example of how to check the SDK initialization status (bluestack-sdk-react-native >= 1.2.0):_ ```javascript const isInitialized = BluestackSDK.isInitialized(); console.log("SDK is initialized:", isInitialized); ``` **Note:** If the BlueStack SDK fails to initialize, the `initialize` promise will be rejected with the corresponding error. --- ## Banner Ad(10-ad-formats) Banner ads are compact non-sticky ads that can be seamlessly embedded within your existing application, displaying the ad content within that confined space. The BlueStack SDK Flutter Plugin supports multiple banner sizes and provides extensive customization options. ## Integration The SDK provides a `BannerView` widget, that must be used to display the ads. Each render of the widget loads a single ad, allowing you to display multiple ads at once. It has following properties, | Property | Type | Requirement | Description | |---------------------|----------------|-------------|--------------------------------------------------------------------------------------| | key | Key | Optional | Identifier for a banner | | type | BannerAdType | Mandatory | Banner ad type | | placementId | string | Mandatory | ID of impression placement provided in BlueStack console | | shouldLoadWhenReady | boolean | Optional | If `true`, the ad will be automatically loaded as soon as all the properties are set | | options | RequestOptions | Optional | To pass additional request options before loading an ad | #### BlueStack banner ad supports following types | Type | Value | Dimensions in dp (WxH) | |---------------------|--------------------|------------------------------------------------| | Standard | banner | 320x50 | | Large | largeBanner | 320x100 | | Full | fullBanner | 468x60 | | Medium Rectangle | mediumRectangle | 300x250 | | Leaderboard | leaderboard | 728x90 | | Dynamic | dynamicBanner | Screen width x 50 (Adjusted Banner) | | Dynamic Leaderboard | dynamicLeaderboard | Screen width x 90 (Adjusted Banner for tablet) | _Enum representing the BlueStack banner ad sizes._ ```dart showLineNumbers enum BannerAdSize { banner, largeBanner, fullBanner, mediumRectangle, leaderboard, dynamicBanner, dynamicLeaderboard } ``` ### Step 1. Import component You need to import `bluestack_sdk.dart` in order to display BlueStack banner ads. ```dart import 'package:bluestack_sdk_flutter/bluestack_sdk.dart'; ``` ### Step 2. Create an banner ad #### Self-loading banner ad You can create a banner ad with `shouldLoadWhenReady` property set to `true`, which will be automatically loaded as soon as all the mandatory properties are set. _Here's an example of how to create and auto load a banner ad:_ ```dart showLineNumbers // In your widget tree BannerView( type: BannerAdSize.banner, placementId: '/YOUR_APP_ID/banner', shouldLoadWhenReady: true, options: requestOptions, ) ``` #### Banner ad with GlobalKey for manual control ```dart showLineNumbers final bannerViewKey = GlobalKey(); // In your widget tree BannerView( key: bannerViewKey, type: BannerAdSize.banner, placementId: '/YOUR_APP_ID/banner', shouldLoadWhenReady: false, options: requestOptions, ) ``` ### Step 3. Load Banner ad `BannerView` has `load` method that takes instance of `RequestOptions` as an optional parameter. You can use this method to "manually" load a new ad. :::info Banner `load` call is not required when `shouldLoadWhenReady` prop is set to `true` ::: ```dart // Load a banner ad. bannerViewKey.currentState?.load(); ``` ```dart // Load a banner ad with options. bannerViewKey.currentState?.load(options: requestOptions); ``` View the [RequestOptions](../30-advanced-topics/request-options.md) documentation for more details. :::caution You don't need to set `RequestOptions` in both `options` property and in `load` method. If preferences are set in both, the one set in the `load` method will be used. ::: ### Step 4. Register event listeners `BannerView` widget also exposes properties for listening to events, allowing you to respond to ad lifecycle events. | Methods | Definition | |---------------------|---------------------------------------------------------------| | onAdLoaded | Ad is successfully loaded and SDK is ready to display the ad. | | onAdFailedToLoad | The ad failed to load or display. | | onAdClicked | User has clicked the ad. Ad may open a link in browser. | | onAdRefreshed | The banner ad has been refreshed. | | onAdFailedToRefresh | The banner ad has been failed to refresh. | _Here's an example of how to setup event listeners:_ ```dart showLineNumbers // Create a banner view BannerView( type: BannerAdSize.banner, placementId: '/YOUR_APP_ID/banner', shouldLoadWhenReady: true, onAdLoaded: (size) { print('Banner ad loaded with size: $size'); }, onAdFailedToLoad: (error) { print('Banner ad failed to load: ${error.message}'); }, onAdClicked: () { print('Banner ad clicked'); }, onAdRefreshed: () { print('Banner ad refreshed'); }, onAdFailedToRefresh: (error) { print('Banner ad failed to refresh: ${error.message}'); }, ) ``` ### Show/Hide Banner ad After loading the banner ad you can hide or show it using `toggleVisibility` method. ```dart // Control banner visibility bannerViewKey.currentState?.toggleVisibility(true); ``` ### Enable/Disable banner refresh You can enable or disable the banner auto refresh using `toggleRefresh` method. Pass `true` in the method to enable refresh and `false` to disable it. ```dart // Control banner refresh bannerViewKey.currentState?.toggleRefresh(true); ``` ### Destroy banner ad You can destroy/remove the banner using `destroy` method ```dart // Destroy banner when done bannerViewKey.currentState?.destroy(); ``` ## Best Practices 1. **Placement**: Place banner ads where they won't interfere with the user experience 2. **Size Selection**: Choose the appropriate banner size for your layout 3. **Auto-Refresh**: Use auto-refresh properly to balance revenue and user experience 4. **Error Handling**: Always implement error handlers to gracefully handle ad loading failures 5. **Memory Management**: Call `destroy()` when the banner is no longer needed ## Troubleshooting Common issues and solutions: 1. **Ad Not Loading** - Check internet connectivity - Verify placement ID - Ensure SDK is properly initialized 2. **Wrong Size Display** - Verify the selected `BannerAdSize` - Check container constraints --- ## Interstitial Ad(10-ad-formats) Interstitial ads are full-screen advertisements that cover the interface of their host app. They're typically displayed at natural transition points in the flow of an app, such as between activities or during the pause between levels in a app. ## Integration ### Step 1. Import component You need to import `bluestack_sdk.dart` in order to display BlueStack interstitial ads. ```dart import 'package:bluestack_sdk_flutter/bluestack_sdk.dart'; ``` ### Step 2. Create Interstitial ad You have to pass the platform-specific interstitial placement ID while creating an instance of `InterstitialAd` ```dart // Create an interstitial ad instance interstitialAd = InterstitialAd('/YOUR_APP_ID/interstitial'); ``` ### Step 3. Load Interstitial ad You can load a Interstitial ad using `load` method, right after SDK finishes it's initialization. _Here are examples of how to load an interstitial ad:_ ```dart interstitialAd?.load(); ``` _With "RequestOptions":_ ```dart /// [options] Optional parameters for customizing the ad request. interstitialAd?.load(options: requestOptions); ``` View the [RequestOptions](../30-advanced-topics/request-options.md) documentation for more details. ### Step 4. Display Interstitial ad After loading the interstitial ad you can request it to be displayed using `show` method. :::info Listen to interstitial ad events to make sure the ad was successfully loaded before you call `show` method ::: _Here's an example of how to show an interstitial ad:_ ```dart interstitialAd?.show(); ``` ### Step 5. Register event listeners Register for interstitial ad events **before** loading InterstitialAd. #### InterstitialAd exposes the following ad loading events through it's lifecycle. | Events | Definition | |------------------|---------------------------------------------------------------| | onAdLoaded | Ad is successfully loaded and SDK is ready to display the ad. | | onAdFailedToLoad | The ad failed to load. | _Here's an example of how to register load event listeners for interstitial ads:_ ```dart showLineNumbers // Set up load event listeners interstitialAd.setLoadEventListener(InterstitialAdLoadEventListener( onAdLoaded: () { print('Interstitial ad loaded'); }, onAdFailedToLoad: (error) { print('Interstitial ad failed to load: ${error.message}'); }, )); ``` #### InterstitialAd exposes the following ad show events through it's lifecycle. | Events | Definition | | ------------------- | ------------------------------------------------------- | | onAdDisplayed | Ad has appeared on the screen. | | onAdFailedToDisplay | The ad failed to display. | | onAdClicked | User has clicked the ad. Ad may open a link in browser. | | onAdDismissed | The ad has disappeared. | _Here's an example of how to register show event listeners for interstitial ads:_ ```dart showLineNumbers // Set up show event listeners interstitialAd.setShowEventListener(InterstitialAdShowEventListener( onAdDisplayed: () { print('Interstitial ad displayed'); }, onAdFailedToDisplay: (error) { print('Interstitial ad failed to display: ${error.message}'); }, onAdDismissed: () { print('Interstitial ad dismissed'); }, onAdClicked: () { print('Interstitial ad clicked'); }, )); ``` ### Dispose interstitial ad You can dispose the interstitial using `dispose` method ```dart // Don't forget to dispose when done interstitialAd.dispose(); ``` ## Best Practices 3. **Loading**: Pre-load interstitial ads before they're needed 4. **Showing**: Check if interstitial was successfully loaded before trying to show 5. **Event Handling**: Make sure you only register event listeners once. 6. **Error Handling**: Always implement error handlers 7. **Memory Management**: Call `dispose()` when the ad is no longer needed ## Troubleshooting Common issues and solutions: 1. **Ad Not Loading** - Check internet connectivity - Verify placement ID - Ensure SDK is properly initialized 2. **Ad Not Showing** - Verify that `load()` was called successfully - Check if interstitial was successfully loaded before trying to show - Ensure proper timing of `show()` call 3. **Memory Issues** - Always call `dispose()` when done - Don't create multiple instances unnecessarily --- ## Rewarded Ad(10-ad-formats) Rewarded ads are full-screen video ads that users can choose to watch in exchange for in-app rewards. These ads provide a great way to monetize your app while offering value to users. ## Integration ### Step 1. Import component You need to import `bluestack_sdk.dart` in order to display BlueStack rewarded ads. ```dart import 'package:bluestack_sdk_flutter/bluestack_sdk.dart'; ``` ### Step 2. Create Rewarded ad You have to pass the platform-specific rewarded placement ID while creating an instance of `RewardedAd` ```dart // Create an rewarded ad instance rewardedAd = RewardedAd('/YOUR_APP_ID/rewardedVideo'); ``` ### Step 3. Load Rewarded ad You can load a Rewarded ad using `load` method, right after SDK finishes it's initialization. _Here are examples of how to load an rewarded ad:_ ```dart rewardedAd?.load(); ``` _With "RequestOptions":_ ```dart /// [options] Optional parameters for customizing the ad request. rewardedAd?.load(options: requestOptions); ``` View the [RequestOptions](../30-advanced-topics/request-options.md) documentation for more details. ### Step 4. Display Rewarded ad After loading the rewarded ad you can request it to be displayed using `show` method. :::info Listen to rewarded ad events to make sure the ad was successfully loaded before you call `show` method ::: _Here's an example of how to show an rewarded ad:_ ```dart rewardedAd?.show(); ``` ### Step 5. Register event listeners Register for rewarded ad events **before** loading RewardedAd. #### RewardedAd exposes the following ad loading events through it's lifecycle. | Events | Definition | |------------------|---------------------------------------------------------------| | onAdLoaded | Ad is successfully loaded and SDK is ready to display the ad. | | onAdFailedToLoad | The ad failed to load. | _Here's an example of how to register load event listeners for rewarded ads:_ ```dart showLineNumbers // Set up load event listeners rewardedAd.setLoadEventListener(RewardedAdLoadEventListener( onAdLoaded: () { print('Rewarded ad loaded'); }, onAdFailedToLoad: (error) { print('Rewarded ad failed to load: ${error.message}'); }, )); ``` #### RewardedAd exposes the following ad show events through it's lifecycle. | Events | Definition | |---------------------|-------------------------------------------------------------------------------------------------| | onAdDisplayed | Ad has appeared on the screen. | | onAdFailedToDisplay | The ad failed to display. | | onAdClicked | User has clicked the ad. Ad may open a link in browser. | | onAdDismissed | The ad has disappeared. | | onRewardEarned | SDK will fire this event with `rewardType` and `rewardAmount` depending on mediation ad network | _Here's an example of how to register show event listeners for rewarded ads:_ ```dart showLineNumbers // Set up show event listeners rewardedAd.setShowEventListener(RewardedAdShowEventListener( onAdDisplayed: () { print('Rewarded ad displayed'); }, onAdFailedToDisplay: (error) { print('Rewarded ad failed to display: ${error.message}'); }, onAdDismissed: () { print('Rewarded ad dismissed'); }, onAdClicked: () { print('Rewarded ad clicked'); }, onRewarded: (reward) { print('Rewarded: ${reward.type} - ${reward.amount}'); }, )); ``` ### Dispose rewarded ad You can dispose the rewarded using `dispose` method ```dart // Don't forget to dispose when done rewardedAd.dispose(); ``` ## Best Practices 1. **Loading**: Pre-load rewarded ads before they're needed 2. **Showing**: Check if rewarded was successfully loaded before trying to show 3. **Event Handling**: Make sure you only register event listeners once. 4. **Error Handling**: Always implement error handlers 5. **Memory Management**: Call `dispose()` when the ad is no longer needed ## Troubleshooting Common issues and solutions: 1. **Ad Not Loading** - Check internet connectivity - Verify placement ID - Ensure SDK is properly initialized 2. **Ad Not Showing** - Verify that `load()` was called successfully - Check if rewarded was successfully loaded before trying to show - Ensure proper timing of `show()` call 3. **Memory Issues** - Always call `dispose()` when done - Don't create multiple instances unnecessarily --- ## Android Supported Networks(3) BlueStack's mediation feature enables you to deliver advertisements to your app from various sources, including BlueStack itself and third-party ad networks. This section provides guidance on integrating mediation partner SDKs through BlueStack's third-party SDK adapters. ## In-App Bidding To include the BlueStack In-App Bidding mediation adapter, add the Maven repository address and Gradle dependency below to your app-level build.gradle file ```groovy showLineNumbers title="build.gradle" repositories { maven { url 'https://packagecloud.io/smartadserver/android/maven2' } } dependencies { ... implementation 'com.azerion:bluestack-mediation-bidding:5.4.0.0' ... } ``` :::info - For `bluestack_sdk_flutter` >= `2.1.0`: In-App-Bidding has a default dependency with Smart Display SDK. - For `bluestack_sdk_flutter` < `2.1.0`: In-App-Bidding has a default dependency with Amazon and Smart Display SDK. So adding In-App-Bidding will automatically include these two SDKs. For Criteo you will have to add the BlueStack Criteo mediation adapter dependency separately in your project. ::: ## Google Mobile Ads To include the BlueStack Google Mobile Ads mediation adapter dependency, - Add your Google App ID to your app's AndroidManifest.xml file ```groovy showLineNumbers title="AndroidManifest.xml" ``` - And add the Gradle dependency below to your app-level build.gradle file ```groovy showLineNumbers title="build.gradle" repositories { google() mavenCentral() maven { url 'https://packagecloud.io/smartadserver/android/maven2' } } ``` = 2.1.0"> ```groovy showLineNumbers title="build.gradle" dependencies { ... implementation 'com.azerion:bluestack-mediation-google:5.4.1.0' ... } ``` = 1.0.2 and < 2.1.0"> ```groovy showLineNumbers title="build.gradle" dependencies { ... implementation 'com.azerion:bluestack-mediation-google:5.3.0.1' ... } ``` ```groovy showLineNumbers title="build.gradle" dependencies { ... implementation 'com.azerion:bluestack-mediation-gma:4.4.0.0' ... } ``` ## Equativ (previously Smart Ad Server) To include the BlueStack Equativ mediation adapter, add the Maven repository address and Gradle dependency below to your app-level build.gradle file ```groovy showLineNumbers title="build.gradle" repositories { google() mavenCentral() maven { url 'https://packagecloud.io/smartadserver/android/maven2' } } ``` = 1.0.2"> ```groovy showLineNumbers title="build.gradle" dependencies { ... implementation 'com.azerion:bluestack-mediation-equativ:5.2.1.1' ... } ``` ```groovy showLineNumbers title="build.gradle" dependencies { ... implementation 'com.azerion:bluestack-mediation-smartadserver:4.4.1.0' ... } ``` ## Mediation Adapter Compatibility Matrix = 2.1.0"> | Ad Network | SDK Version | Adapter Version | Supported Ad Formats | |-----------------------|-------------|---------------------------------|-------------------------------------------| | **Google** | 24.9.0 | 5.4.1.0 | Banner / MREC, Interstitial, Rewarded Ads | | **Equativ** | 8.5.2 | 5.2.1.1 | Banner / MREC, Interstitial | | **BlueStack Bidding** | N/A | 5.4.0.0 | Banner / MREC, Interstitial | = 1.0.2 and < 2.1.0"> | Ad Network | SDK Version | Adapter Version | Supported Ad Formats | |-----------------------|-------------|---------------------------------|---------------------------------------------| | **Google** | 24.9.0 | 5.3.0.1 | Banner / MREC, Interstitial, Rewarded Ads | | **Amazon** | 6.18.0 | (included in BlueStack Bidding) | Banner / MREC, Interstitial, Rewarded Ads | | **Equativ** | 8.5.2 | 5.2.1.1 | Banner / MREC, Interstitial | | **BlueStack Bidding** | N/A | 5.3.0.0 | Banner / MREC, Interstitial | | Ad Network | SDK Version | Adapter Version | Supported Ad Formats | |-----------------------|-------------|---------------------------------|-------------------------------------------| | **Google** | 23.4.0 | 4.4.0.0 | Banner / MREC, Interstitial, Rewarded Ads | | **Amazon** | 6.18.0 | (included in BlueStack Bidding) | Banner / MREC, Interstitial, Rewarded Ads | | **Equativ** | 7.24.0 | 4.4.1.0 | Banner / MREC, Interstitial, Rewarded Ads | | **BlueStack Bidding** | N/A | 4.4.1.0 | Banner / MREC, Interstitial, Rewarded Ads | :::note - Ensure all dependencies are included as outlined in each network’s integration guide. - Ad formats may require additional configurations or testing to confirm functionality. ::: :::warning ```shell -keep public class com.azerion.bluestack.mediation.** { *; } ``` Please add the above rule to your proguard file when you encounter errors similar to what you see here: ```shell java.lang.NoSuchMethodException: com.azerion.bluestack.mediation.* ``` ```shell Exception com.azerion.bluestack.error.AdapterNotFoundError: com.azerion.bluestack.mediation.* ``` ::: --- ## iOS Supported Networks(3) BlueStack's mediation feature enables you to deliver advertisements to your app from various sources, including BlueStack itself and third-party ad networks. This section provides guidance on integrating mediation partner SDKs through BlueStack's third-party SDK adapters. :::note You must add `BlueStackSDK` to your app target if you intend to use any mediation adapters via Swift Package Manager (SPM). ::: ## In-App Bidding Add the BlueStack Bidding Adapter dependency to your app's podfile. ```ruby pod 'BlueStackBiddingAdapter' ``` ## Google Mobile Ads To include the BlueStack GMA mediation adapter, Add the `BlueStackGoogleAdapter` dependency to the Podfile of your application project: ```ruby pod 'BlueStackGoogleAdapter' ``` Go to your project file -> Package Dependencies -> Add(+) -> Search for [https://github.com/azerion/BlueStack-Google-Adapter](https://github.com/azerion/BlueStack-Google-Adapter) ![BlueStackGoogleAdapter Swift Package Search](../../../ios/00-images/bluestack-google-adapter-spm-integration-1-light.png#gh-light-mode-only)![BlueStackGoogleAdapter Swift Package Search](../../../ios/00-images/bluestack-google-adapter-spm-integration-1-dark.png#gh-dark-mode-only) Add the `BlueStackGoogleAdapter` to your app's target via Swift Package Manager ![Add BlueStackGoogleAdapter to target](../../../ios/00-images/bluestack-google-adapter-spm-integration-2-light.png#gh-light-mode-only)![Add BlueStackGoogleAdapter to target](../../../ios/00-images/bluestack-google-adapter-spm-integration-2-dark.png#gh-dark-mode-only) ### Add Your AdMob App ID In the `Info.plist` of your app, please add the key `GADApplicationIdentifier` and set its value to the ID you received from your Azerion Publisher Representative. ![ios-gad-application-identifier](../00-images/ios-gad-application-identifier.png) ## Equativ To include the BlueStack Equativ mediation adapter, = 2.1.0"> In the `Podfile` of your application project add `BlueStackEquativAdapter` dependency ```ruby pod 'BlueStackEquativAdapter' ``` To get the in app bidding add the BlueStack Bidding adapter dependency to your app's podfile also. ```ruby pod 'BlueStackBiddingAdapter' ``` Add the `BlueStackEquativAdapter` adapter dependency to your app's podfile. ```ruby pod 'BlueStackEquativAdapter' ``` Go to your project file --> Package Dependencies --> Add(+) --> Search for [https://github.com/azerion/BlueStack-Equativ-Adapter](https://github.com/azerion/BlueStack-Equativ-Adapter) ![BlueStackEquativAdapter Swift Package Search](../../../ios/00-images/bluestack-equativ-adapter-spm-integration-1-light.png#gh-light-mode-only)![BlueStackEquativAdapter Swift Package Search](../../../ios/00-images/bluestack-equativ-adapter-spm-integration-1-dark.png#gh-dark-mode-only) Add the `BlueStackEquativAdapter` to your app's target via Swift Package Manager ![Add BlueStackEquativAdapter to target](../../../ios/00-images/bluestack-equativ-adapter-spm-integration-2-light.png#gh-light-mode-only)![Add BlueStackEquativAdapter to target](../../../ios/00-images/bluestack-equativ-adapter-spm-integration-2-dark.png#gh-dark-mode-only) ## Amazon In-App Bidding To include the BlueStack Amazon publisher service in-app bidding adapter, Add the `BluestackAmazonPublisherServicesAdapter` adapter dependency as a subspec of `BlueStack-SDK` in your app’s Podfile: ```ruby pod 'BlueStack-SDK', :subspecs=>["BluestackAmazonPublisherServicesAdapter"] ``` Add the `BluestackAmazonPublisherServicesAdapter` to your app's target via Swift Package Manager ![BlueStackSDK mediation adapter list](../../../ios/00-images/BlueStackAmazonPublisherServiceAdapter_spm-light.png#gh-light-mode-only)![BlueStackSDK mediation adapter list](../../../ios/00-images/BlueStackAmazonPublisherServiceAdapter_spm-dark.png#gh-dark-mode-only) ## Mediation Adapter Compatibility Matrix = 2.1.0"> | Ad Network | SDK Version | Adapter Version | Supported Ad Formats | |-------------|-------------|-----------------|-----------------------------------------------------| | **Google** | 12.14.0 | 5.4.0 | Banner / MREC, Interstitial, Rewarded Ads | | **Equativ** | 8.5.1 | 5.1.8 | Banner / MREC, Interstitial | | **BlueStack Bidding** | N/A | 5.4.0 | Banner / MREC, Interstitial | | Ad Network | SDK Version | Adapter Version | Supported Ad Formats | |-------------|-------------|-----------------|-------------------------------------------------------| | **Google** | 12.14.0 | 5.3.5 | Banner / MREC, Interstitial, Rewarded Ads | | **Amazon** | 4.5.5 | Included in BlueStackSDK as subspec | Banner / MREC, Interstitial, Rewarded Ads | | **Equativ** | 8.5.1 | 5.1.8 | Banner / MREC, Interstitial | :::note - Ensure all dependencies are included as outlined in each network’s integration guide. - Ad formats may require additional configurations or testing to confirm functionality. ::: --- ## RequestOptions The RequestOptions is an optional options object to be sent whilst loading an ad, such as keywords & location. Setting additional options helps BlueStack choose better tailored ads from the network. Following are the full range of options available, _Here's an example of how to create options for any ad:_ ```dart showLineNumbers RequestOptions options = RequestOptions(); options.setAge(25); options.setLanguage('en'); options.setGender(Gender.female); options.setKeyword('testbrand=myBrand;category=sport'); /// [location] The geographical location of the user. /// [consentFlag] The consent status for using location data. options.setLocation( Location( latitude: 37.7749, longitude: -122.4194, provider: LocationProvider.gps), 1); options.setContentUrl('https://developers.bluestack.app/'); ``` _Enum representing the gender of a user:_ ```dart enum Gender { /// Male gender male, /// Female gender female, /// Gender is unknown or not specified unknown, } ``` _Enum representing different location data providers:_ ```dart enum LocationProvider { /// No location data available empty, /// Location from network-based sources network, /// Full location data available full, /// Location from GPS gps, /// Passive location updates passive, } ``` **Note :** The `setLocation` method takes the following parameters: - The Location instance. - The CONSENT_FLAG value (corresponds to a int : 0,1,2 or 3). - 0 = Not allow to send location. - 1 = When you managed location according to consent value. - 2 and 3 = Allow the SDK to managed location directly in accordance with the consent value use TCF v1 or TCF v2, see with the Azerion team as it depends on your implementation. --- ## Release notes(Flutter) ## [2.1.0] 2026-02-27 ### Changed - iOS: Upgraded BlueStack iOS Core SDK to version 5.4.1 - Android: Upgraded BlueStack Android Core SDK to version 5.4.1 - Updated adapter dependencies in the example app (iOS & Android) to versions compatible with Core SDK v5.4.1 ## [2.0.0] 2025-11-05 ### Changed - Renamed bluestack_sdk.dart to bluestack_sdk_flutter.dart to align library name with package name per pub rules. - Upgraded `compileSdkVersion` to 35. - Upgraded Android Gradle to 8.5.0. - Upgraded BlueStack Android Core SDK to v5.3.1. - Upgraded BlueStack iOS Core SDK to v5.3.2. - Updated mediation adapter dependencies in the example app for compatibility with latest Core SDK versions. ## [1.0.2] 2025-07-08 ### Changed - Core API calls and callbacks now run on the main thread for improved stability. - Upgraded Android Gradle to 8.1.0 and Android SDK to API level 34. - Updated BlueStack Android Core SDK to version 5.1.4. - Updated BlueStack iOS Core SDK to version 5.1.5. - Modified the Example App to reflect recent SDK updates and changes. ## [1.0.1] 2025-04-30 ### Changed - Error log in Debug mode. - On iOS `BannerView` operations are now handled on the main thread. ### Fixed - `BlueStackError.java` name issue. - Flutter state issue when calling `dispose()` on `BannerView` ## [1.0.0] 2025-04-23 ### Added - Initial Release: Support for widget based Banner Ads, full screen Interstitial and Rewarded Ad. --- ## Getting Started(Flutter) :::info This section provides a comprehensive guide on integrating the BlueStack Flutter Plugin into your project. Our plugin offers a range of functionalities to monetize your mobile application, including premium sales with rich media, video, and innovative formats, as well as support for all standard display formats. This plugin is compatible with both iOS and Android applications. ::: ## Prerequisites ## Plugin Integration ### Installation Add the plugin to your `pubspec.yaml`: ### Import sdk component ```dart import 'package:bluestack_sdk_flutter/bluestack_sdk_flutter.dart'; ``` ```dart import 'package:bluestack_sdk_flutter/bluestack_sdk.dart'; ``` ## SDK Implementation ### Initialize the SDK in your application You need to initialize SDK using the App Id (depends on the platform), before you request any kind of ads. You need to call `initialize` method of `BlueStackInitializer` with your `appId` to initialize the SDK. You can also set the 2nd parameter (optional) `enableDebug` to `true`, if you want to enable debug option. By default `enableDebug` is `false`. :::info You have to register your app in BlueStack console to get an App Id for your app. ::: _Here's an example of how to initialize the SDK:_ ```dart showLineNumbers void main() { // Initialize the SDK BlueStackInitializer.initialize( appId: "YOUR_APP_ID", enableDebug: true, // Set to false for production ); runApp(MyApp()); } ``` ### Register listeners for SDK initialization callbacks :::warning You must set the listener callbacks **before** calling the `initialize` method; otherwise, you may not receive them. ::: #### `BlueStackInitializer` exposes the following events through its lifecycle. | Events | Definition | | ----------------------- | -------------------------------------------------------------- | | onInitializationSuccess | The SDK has been successfully initialized with `adapterStatus` | | onInitializationFail | The SDK failed to initialize with `error` | _Here's an example of how to register event listeners for SDK initialization:_ ```dart showLineNumbers BlueStackInitializer.setEventListener(InitializationEventListener( onInitializationSuccess: (adapterStatus) { print('BlueStack SDK has been successfully initialized'); }, onInitializationFail: (error) { print('BlueStack SDK failed to initialize: ${error.toString()}'); }, )); ``` --- ## Banner Ad(3) ## Integration ### Step 1. Register the handler A custom control and its handler must be registered with an app, before it can be consumed. This should occur in the `CreateMauiApp` method in the `MauiProgram` class in your app project, which is the cross-platform entry point for the app: ```csharp showLineNumbers using BlueStack.Controls.BannerAdView; using Microsoft.Extensions.Logging; namespace BlueStack.SDK.Maui.DemoApp; public static class MauiProgram { public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder .UseMauiApp() .ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold"); }) .ConfigureMauiHandlers(handlers => { handlers.AddHandler(typeof(BannerAdView), typeof(BannerAdViewHandler)); }); return builder.Build(); } } ``` ### Step 2. Instantiate Banner Ad You can instantiate a Banner ad right after the SDK finishes BlueStack ad network initialization. You can instantiate a `BannerAdView` either through MAUI XAML or programmatically. - Using MAUI XAML `BannerSinglePage.xaml` ```xml showLineNumbers ... ... ``` - Programmatically `BannerSinglePage.xaml` ```xml showLineNumbers ... ``` `BannerSinglePage.xaml.cs` ```csharp showLineNumbers using BlueStack.API; using BlueStack.Controls.BannerAdView; namespace BlueStack.SDK.Maui.DemoApp.banner; public partial class BannerSinglePage : ContentPage { ... private BannerAdView _bannerAdView; public BannerSinglePage() { InitializeComponent(); InitializeNewBanner(); ... } private void InitializeNewBanner() { _bannerAdView = new BannerAdView { PlacementId = "your_ad_placement" }; _bannerAdView.Handler = new BannerAdViewHandler(); ContainerLayout.Add(_bannerAdView); } ... } ``` ### Step 3. Register event listeners To receive banner ad events, implement the `IMauiBannerAdListener` interface and add it during the initialization of `BannerAdView` using `SetBannerAdListener`. ```csharp showLineNumbers using BlueStack.API; using BlueStack.Controls.BannerAdView; namespace BlueStack.SDK.Maui.DemoApp.banner; public partial class BannerSinglePage : ContentPage, IMauiBannerAdListener { private BannerAdView _bannerAdView; public BannerSinglePage() { InitializeComponent(); InitializeNewBanner(); } private void InitializeNewBanner() { ... _bannerAdView.SetBannerAdListener(this); } public void OnBannerDidLoad() { Logger.Debug(TAG, "OnBannerDidLoad"); } public void OnBannerDidFailed(BlueStackError blueStackError) { Logger.Debug(TAG, "OnBannerDidFailed: " + blueStackError.Message); } public void OnAdClicked() { Logger.Debug(TAG, "OnAdClicked"); } public void OnBannerDidRefresh() { Logger.Debug(TAG, "OnBannerDidRefresh"); } public void OnBannerDidFailToRefresh(BlueStackError blueStackError) { Logger.Debug(TAG, "OnBannerDidFailToRefresh: " + blueStackError.Message); } } ``` ### Step 4. Load Banner ad BannerAd takes `AdSize` to load banner. It also have another `Load` method that takes `Preference` instance along with `AdSize`. Banner Ad supports following ad sizes: | Ad Size | Definition | | ----------- | ----------- | | Banner | 320x50| | DynamicBanner | 0x50| | LargeBanner | 320x100| | FullBanner | 468x60| | Leaderboard | 728x90| | DynamicLeaderboard| 0x90| | MediumRectangle | 300x250| - Without Preference ```csharp showLineNumbers public class BannerSinglePage : ContentPage, IMauiBannerAdListener { private BannerAdView _bannerAdView; private AdSize _adSize = AdSize.Banner; ... private void LoadAd() { _bannerAdView.Load(_adSize); } ... } ``` - With Preference ```csharp showLineNumbers public class BannerSinglePage : ContentPage, IMauiBannerAdListener { private BannerAdView _bannerAdView; private AdSize _adSize = AdSize.Banner; ... private void LoadAd() { Preference bsPreference = new Preference(); Location myLocation = new Location(Location.NONE_PROVIDER) { Latitude = 35.757866, Longitude = 10.810547 }; bsPreference.SetAge(25); bsPreference.SetLanguage("en"); bsPreference.SetGender(Gender.Male); bsPreference.SetKeyword("brand=myBrand;category=sport"); bsPreference.SetLocation(myLocation, 3); bsPreference.SetContentUrl("https://console.bluestack.app"); _bannerAdView.Load(_adSize, BlueStackAdPreferenceFactory.create()); } ... } ``` ### Force refresh banner Force refresh will refresh the banner ad immediately. Call this method if you need a force load of banner ad without destroying the existing one. ```csharp showLineNumbers _bannerAd.ForceRefresh(); ``` **Note :** The setLocation method takes the following parameters: - the Location instance. - the CONSENT_FLAG value (corresponds to a int : 0,1,2 or 3). - 0 = Not allow to send location. - 1 = When you managed location according to consent value. - 2 and 3 = Allow the SDK to managed location directly in accordance with the consent value use TCF v1 or TCF v2, see with the Azerion team as it depends on your implementation. ### Destroy banner ad To properly manage resources, make sure to destroy the banner ad when it is no longer in use. ```csharp showLineNumbers _bannerAd.Destroy(); ``` --- ## Interstitial Ad(3) ## Integration ### Step 1. Instantiate Interstitial Ad You can instantiate an Interstitial ad right after the SDK finishes its initialization. You have to pass the platform specific interstitial placement id in `InterstitialAd` constructor. ```csharp showLineNumbers _interstitialAd = new InterstitialAd("your_ad_placement"); ``` ### Step 2. Register event listeners InterstitialAd exposes the following events through it's lifecycle. | Methods | Definition | | ----------- | ----------- | | OnInterstitialDidLoaded | Ad is successfully loaded and SDK is ready to display the ad.| | OnInterstitialDidFail | The ad failed to load or display. An additional error parameter of type `BlueStackError` contains the reason of the failure.| | OnInterstitialClicked | User has clicked the ad. Ad may open a link in browser.| | OnInterstitialDidShown | Ad has appeared on the screen. | | OnInterstitialDisappear | The ad has disappeared. | To register for interstitial ad events, add the following code after instantiating InterstitialAd. ```csharp showLineNumbers _interstitialAd.OnInterstitialDidLoaded += (sender, args) => { }; _interstitialAd.OnInterstitialDidFail += (sender, args) => { }; _interstitialAd.OnInterstitialClicked += (sender, args) => { }; _interstitialAd.OnInterstitialDidShown += (sender, args) => { }; _interstitialAd.OnInterstitialDisappear += (sender, args) => { }; ``` :::caution Make sure you only register event listener once. ::: ### Step 3. Load Interstitial ad InterstitialAd expose two `Load` medthods to load ad. One takes `Preference` instance others emtpy parameters. - Without Preference ```csharp showLineNumbers _interstitialAd.Load(); ``` - With Preference ```csharp showLineNumbers Preference _preference = new Preference(); Location myLocation = new Location(Location.NONE_PROVIDER) { Latitude = 35.757866, Longitude = 10.810547 }; _preference.SetAge(25); _preference.SetLanguage("en"); _preference.SetGender(Gender.Male); _preference.SetKeyword("brand=myBrand;category=sport"); _preference.SetLocation(myLocation, 3); _preference.SetContentUrl("https://console.bluestack.app"); _interstitialAd.Load(_preference); ``` **Note :** The setLocation method takes the following parameters: - the Location instance. - the CONSENT_FLAG value (corresponds to a int : 0,1,2 or 3). - 0 = Not allow to send location. - 1 = When you managed location according to consent value. - 2 and 3 = Allow the SDK to managed location directly in accordance with the consent value use TCF v1 or TCF v2, see with the Azerion team as it depends on your implementation. ### Step 4. Display Interstitial ad After loading the interstitial ad you can request it to be displayed. :::info Register to OnInterstitialDidLoaded to make sure the ad was successfully loaded before you call `Show()` method ::: ```csharp showLineNumbers _interstitialAd.Show(); ``` ### Destroy interstitial ad To properly manage resources, make sure to destroy the banner ad when it is no longer in use. ```csharp showLineNumbers _interstitialAd.Destroy() ``` --- ## Rewarded Video Ad ## Integration ### Step 1. Instantiate RewardedVideo Ad You can instantiate a RewardedVideoAd ad right after the SDK finishes its initialization. You have to pass the platform specific rewarded placement id in `RewardedVideoAd` constructor. ```csharp showLineNumbers _rewardedVideoAd = new RewardedVideoAd("your_ad_placement"); ``` ### Step 2. Register event listeners RewardedVideoAd exposes the following events through it's lifecycle. | Methods | Definition | |---------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------| | OnRewardedVideoAdLoaded | Ad is successfully loaded and SDK is ready to display the ad. | | OnRewardedVideoAdAppeared | The ad has been displayed on the screen. | | OnRewardedVideoAdError | The ad failed to load or display. An additional error parameter of type `BlueStackError` contains the reason of the failure. | | OnRewardedVideoAdClicked | User has clicked the ad. Ad may open a link in browser. | | OnUserRewardEarned | SDK will fire this event with an instance of `RewardedItem` or `null` depends on mediation ad network. Game can use or discard the reward value. | | OnRewardedVideoAdClosed | The ad has been closed by the user. | To register for rewarded ad events, add the following code after instantiating RewardedVideoAd. ```csharp showLineNumbers _rewardedVideoAd.OnRewardedVideoAdLoaded += (sender, args) => { }; _rewardedVideoAd.OnRewardedVideoAdError += (sender, args) => { }; _rewardedVideoAd.OnRewardedVideoAdAppeared += (sender, args) => { }; _rewardedVideoAd.OnRewardedVideoAdClicked += (sender, args) => { }; _rewardedVideoAd.OnUserRewardEarned += (sender, args) => { }; _rewardedVideoAd.OnRewardedVideoAdClosed += (sender, args) => { }; ``` :::caution Make sure you only register event listener once. ::: ### Step 3. Load Rewarded Video ad RewardedVideoAd expose two `Load` medthods to load ad. One takes `Preference` instance others emtpy parameters. - Without Preference ```csharp showLineNumbers _rewardedVideoAd.Load(); ``` - With Preference ```csharp showLineNumbers Preference _preference = new Preference(); Location myLocation = new Location(Location.NONE_PROVIDER) { Latitude = 35.757866, Longitude = 10.810547 }; _preference.SetAge(25); _preference.SetLanguage("en"); _preference.SetGender(Gender.Male); _preference.SetKeyword("brand=myBrand;category=sport"); _preference.SetLocation(myLocation, 3); _preference.SetContentUrl("https://console.bluestack.app"); _rewardedVideoAd.Load(_preference); ``` **Note :** The setLocation method takes the following parameters: - the Location instance. - the CONSENT_FLAG value (corresponds to a int : 0,1,2 or 3). - 0 = Not allow to send location. - 1 = When you managed location according to consent value. - 2 and 3 = Allow the SDK to managed location directly in accordance with the consent value use TCF v1 or TCF v2, see with the Azerion team as it depends on your implementation. ### Step 4. Display Rewarded Video ad After loading the rewarded video you can request it to be displayed. :::info Register to OnRewardedVideoAdLoaded to make sure the ad was successfully loaded before you call `Show()` method ::: ```csharp showLineNumbers _rewardedVideoAd.Show(); ``` ### Destroy Rewarded Video ad To properly manage resources, make sure to destroy the banner ad when it is no longer in use. ```csharp showLineNumbers _rewardedVideoAd.Destroy(); ``` --- ## F.A.Q(20-advanced-topics) This document answers the following frequently asked questions: ## Duplicated classes compile error for Android It can happen that, on compile time, you get error messages regarding duplicated classes. MAUI is more prone for these kind of errors due to the way Android dependencies are managed. These kind of errors look like this: ```bash >: Error JAVA0000 java: Error in obj/Debug/net8.0-android/lp/142/jl/classes.jar:androidx/activity/ActivityViewModelLazyKt$viewModels$1.class: Type androidx.activity.ActivityViewModelLazyKt$viewModels$1 is defined multiple times: obj/Debug/net8.0-android/lp/142/jl/classes.jar:androidx/activity/ActivityViewModelLazyKt$viewModels$1.class, obj/Debug/net8.0-android/lp/175/jl/classes.jar:androidx/activity/ActivityViewModelLazyKt$viewModels$1.class Compilation failed java.lang.RuntimeException: com.android.tools.r8.CompilationFailedException: Compilation failed to complete, origin: obj/Debug/net8.0-android/lp/142/jl/classes.jar androidx/activity/ActivityViewModelLazyKt$viewModels$1.class at com.android.tools.r8.utils.R0.a(R8_8.1.56_756d1f50f618dd1c39c000f11defb367a21e9e866e3401b884be16c0950f6f79:126) at com.android.tools.r8.D8.main(R8_8.1.56_756d1f50f618dd1c39c000f11defb367a21e9e866e3401b884be16c0950f6f79:5) Caused by: com.android.tools.r8.CompilationFailedException: Compilation failed to complete, origin: obj/Debug/net8.0-android/lp/142/jl/classes.jar:androidx/activity/ActivityViewModelLazyKt$viewModels$1.class at Version.fakeStackEntry(Version_8.1.56.java:0) at com.android.tools.r8.M.a(R8_8.1.56_756d1f50f618dd1c39c000f11defb367a21e9e866e3401b884be16c0950f6f79:5) ... snip at com.android.tools.r8.utils.R0.a(R8_8.1.56_756d1f50f618dd1c39c000f11defb367a21e9e866e3401b884be16c0950f6f79:113) ... 1 more Caused by: com.android.tools.r8.utils.b: Type androidx.activity.ActivityViewModelLazyKt$viewModels$1 is defined multiple times: obj/Debug/net8.0-android/lp/142/jl/classes.jar:androidx/activity/ActivityViewModelLazyKt$viewModels$1.class, obj/Debug/net8.0-android/lp/175/jl/classes.jar:androidx/activity/ActivityViewModelLazyKt$viewModels$1.class at com.android.tools.r8.utils.O2.a(R8_8.1.56_756d1f50f618dd1c39c000f11defb367a21e9e866e3401b884be16c0950f6f79:21) ... snip at com.android.tools.r8.utils.R0.a(R8_8.1.56_756d1f50f618dd1c39c000f11defb367a21e9e866e3401b884be16c0950f6f79:28) ... 6 more Directory 'obj/Debug/net8.0-android/lp/142' is from 'androidx.activity.activity.aar'. ``` During compile time, MAUI grabs all classes.dex from the jar/aar files, put them in a numberic folder and then compiles them. You can see in the first part of the error message where these files are located: ```bash obj/Debug/net8.0-android/lp/142/jl/classes.jar ``` At the end of the error message it will tell you from which package the error came: ```bash Directory 'obj/Debug/net8.0-android/lp/142' is from 'androidx.activity.activity.aar'. ``` There are a couple of ways to solve these issues when you encounter them, please check the [Github issue for Xamarin.AndroidX](https://github.com/xamarin/AndroidX/issues/764) --- ## Release notes(Maui) ## [1.1.8] - 2024-07-04 ### Removed - Google Play Service Base depdencies Jar/AAR have been replaced ### Added - Xamarin Google Play Service Base depdencies added ## [1.1.7] - 2024-06-25 ### Removed - Xamarin Google Mobile Ads dependency from BlueStack.SDK.Core.Android.Binding. ### Added - Google Mobile Ads Jar/AAR files into BlueStack.SDK.Core.Android.Binding. ## [1.1.6] - 2024-06-10 ### Fixed - [iOS Core binding] Updated resource bundle targeting iPhoneOS ## [1.1.5] - 2024-06-07 ### Added - [iOS Core binding] Using native Google Mobile Ads version 11.2 instead of old xamarin google mobile ads v8.13.0.3 - [iOS Core binding] Using latest BlueStackSDK version 4.4.8 (that includes OMSDK v1.4.12, Privacy manifest) - [iOS Core binding] Using corresponding BlueStackDFPAdapter - [iOS Bridging binding] Using the latest iOS core binding 1.0.3 - [MAUI] Using the latest iOS bridging binding 4.4.8.0 ## [1.1.4] - 2024-05-31 ### Added - [MAUI] Log version number ## [1.1.3] - 2024-05-30 ### Fixed - [MAUI] Force reload banner ad issue fix - [MAUI] iOS Banner Ad load with preference issue fix - [MAUI] iOS crash issue fix on rewarded video ad destroy ## [1.1.2] - 2024-05-17 ### Added - [Android binding] bluestack-tcf-1.0.2.aar - [Android binding] Xamarin.Android.Glide, Xamarin.Android.Glide.Annotations, Xamarin.AndroidX.Preference and GoogleGson nuget packages. ### Fixed - [Android binding] TCFManager `Java.Lang.ClassNotFoundException`. ## [1.1.1] - 2024-02-07 ### Added - [MAUI] Force refresh banner ad - [MAUI] Limit loading banner ad within 3 sec ### Fixed - [MAUI] Banner crash fix for iOS platform - [iOS bridging] Handle error if initialization fails ## [1.1.0] - 2024-01-16 ### Added - [MAUI] Support for Banner Ad View that can be added from XAML - [MAUI] Support for Interstitial Ad format - [MAUI] Support for Rewarded Ad format - [iOS bridging] Native bridging for supporting BlueStack SDK initialization ad showing Banner, Interstitial, Rewarded ads - [Android bridging] Native bridging for supporting BlueStack SDK initialization ad showing Banner, Interstitial, Rewarded ads - [Demo App] separate tab for individual ad formats. ## [1.0.0] - 2023-11-06 ### Added - [MAUI] Support for Banner Ad format - [iOS binding] Support for BlueStack SDK API definition - [Android binding] Support for BlueStack SDK initializtion and Banner Ad format - [Demo App] Sample for integrating and using BlueStack MAUI plugin. --- ## Getting Started(Maui) :::info This section explains how to get started with the BlueStack MAUI Plugin. It guides you through the process of adding the BlueStack MAUI Plugin to your project. After following this section you’re being able to getting started with the more advanced features of the BlueStack MAUI Plugin. ::: ## Prerequisites ## Integrate the BlueStack SDK BlueStack MAUI support is facilitated through a collection of NuGet packages, strategically divided to uphold a more refined separation of concerns. The segmentation of plugins across multiple packages ensures a streamlined approach to maintenance. Here is the comprehensive list of these packages: - `BlueStack.SDK.Maui` - A MAUI package designed for communication between a MAUI application and the native BlueStack SDK through bridge. - `BlueStack.SDK.Core.iOS.Binding` - `BlueStack.SDK.Bridging.iOS.Binding` - `BlueStack.SDK.Core.Android.Binding` - This package encompasses the Android BlueStack Mediation SDK along with its native dependencies. - `BlueStack.SDK.Core.Android.Bridge.Binding` - Provide android native bridging support for `BlueStack.SDK.Maui`. As a MAUI app developer you will only have to add `BlueStack.SDK.Maui` package in you app. `BlueStack.SDK.Maui` will automatically download the necessary dependencies transitively. ## Initialize the BlueStack SDK Before loading ads, you need to initialize the BlueStack SDK by calling `BlueStackAds.Initialize()`. This needs to be done only once, ideally at app launch. BlueStack initialization process initialize the BlueStack and it's configured mediation ad networks. So `OnAdSDKInitialized` will be called multiple times with newly initialized ad netowrk. ```csharp showLineNumbers public partial class MainPage : ContentPage { public MainPage() { InitializeComponent(); ... Settings settings = new Settings(isDebugModeEnabled:true); BlueStackAds.Initialize(appId, settings, OnAdSDKInitialized); ... } private void OnAdSDKInitialized(InitializationStatus initializationStatus) { foreach (KeyValuePair adapterStateEntry in initializationStatus.GetAdapterStatusMap()) { #if ANDROID Log.Debug("MainPage", "AdapterName: " + adapterStateEntry.Value.Name + " AdapterStatus: " + adapterStateEntry.Value.InitializationState + " Description: " + adapterStateEntry.Value.Description); #endif } ... } } ``` --- ## Seller Reporting API ## Quick Reference All API access is over HTTPS, and accessed via the https://xxx.com domain (ask to your Azerion contact). ### Request Format For **POST** requests, the request body must be JSON, with the *Content-Type* header set to *application/json*. ### Response format The response format for all requests is a JSON object. Whether a request succeeded is indicated by the HTTP status code. A 2xx status code indicates success, whereas a 4xx status code indicates failure. ## POST /auth-reporting : Authentication Service see [auth-reporting] section. ## POST /seller-reporting : Publisher Reporting Service Use the token returned by auth-reporting service when making calls to **/seller-reporting**. You POST the JSON request and get back a report ID. ### Metrics | **Metric** | **Definition** | |---------------|---------------------------------------------------------------------------| | requests | An attempt to MAS adserver to fill an impression. | | displays | Number of times an ad is served and displayed according Viewbility . | | clicks | Number of times an ad is clicked. | | downloads | Number of conversions from Conversion API or Appsfire [Buyer Integration] | | leads | Number of leads from Conversion API or Appsfire [Buyer Integration] | | landings | Number of landings from Conversion API or Appsfire [Buyer Integration] | | revenueEuro | Revenue generated in €. | | revenueDollar | Revenue generated in $. | ### Breakdowns/Dimensions list Optionally, use one of the following options in the breakdowns param to specify which dimension. | **Dimension** | **Definition** | |-------------------|---------------------------------------------------------------------------------------------------------| | DAILY | Provides a breakdown by day (can't be combined with HOURLY, RANGE and DAILY) | | HOURLY | Provides a breakdown by hour (can't be combined with MONTHLY, RANGE and DAILY) | | MONTHLY | Provides a breakdown by Month (can't be combined with HOURLY, RANGE and DAILY) | | RANGE | Provides a breakdown according since and until dates (can't be combined with HOURLY, MONTHLY and DAILY) | | ADNETWORK | Provides a breakdown by our core Ad network (mngperf or appsfire) | | ZONE | Provides a breakdown by placement (banner, Interstitial, NativeAd, ...) | | PUBLISHER | Provides a breakdown by Publisher | | SUBPUBLISHERID | Provides a breakdown by Sub Publisher Id for appsfire adnetwork, arg3 parameter on adrequest | | SUBSUBPUBLISHERID | Provides a breakdown by Sub Publisher Id for appsfire adnetwork, arg4 parameter on adrequest | | APP | Provides a breakdown by App | | BUNDLEID | Provides a breakdown by packageName for android and bundleId for IOS | | FORMAT | Provides a breakdown by format (interstitial, banner, nativeAd ...) | | COUNTRY | Provides a breakdown by country (FR, US,...) | | REGION | Provides a breakdown by Region (Admin Level 1) | | DEPARTMENT | Provides a breakdown by Department (Admin Level 2) | | CITY | Provides a breakdown by City (Paris, ...) | | POSTALCODE | Provides a breakdown by Postal Code | | OS | Provides a breakdown by mobile OS | | DEVICETYPE | Provides a breakdown by DeviceType (phone or tablet) | | DEVICEBRAND | Provides a breakdown by BrandName (e.g Apple) | | DEVICEMODEL | Provides a breakdown by Model (e.g iPhone 7 Plus) | | CARRIER | Provides a breakdown by Carrier (e.g Orange) Mobile carrier name or Wireless carrier | | BUYERNAME | Provides a breakdown by buyer (Vectaury, Adot, Bidswitch,..) connected to our Ad Exchange | | DEALID | Provides a breakdown by deal connected to our Ad Exchange | | DMP | Provides a breakdown by dmp (Adobe, 1px, ...) connected to our Ad Exchange | ### Filters | **Parameter name** | **Required?** | **Format** | **Definition** | |--------------------|---------------|-------------------------------------------------------------------|------------------------------------------------------------------------------------------------| | since | Yes | europe/paris unix timestamp | e.g 1417392000 | | until | Yes | europe/paris unix timestamp | e.g 1420070399 | | breakdowns | No | array of string | see [breakdowns-list section], e.g breakdowns[0]=HOURLY&breakdowns[1]=ZONE | | publisherId | no | array of publishers IDs | For admin only Filter reporting on specific publishers e.g publisherId[0]=1&publisherId[1]=2 | | subPublisherId | no | array of subpublishers IDs | Filter reporting on specific sub publishers e.g subPublisherId[0]=1 | | subSubPublisherId | no | array of subSubPublisherId IDs | Filter reporting on specific sub publishers e.g subSubPublisherId[0]=1 | | platformId | no | array of platformId | Filter reporting on specific platformId e.g platformId[0]=1 | | appId | no | array of apps IDs | Filter reporting on specific apps e.g appId[0]=1&appId[1]=2 | | s | no | array of placements IDs | Filter reporting on a specific placement e.g s[0]=1&s[1]=2 | | osId | no | Array of mobile OS | Filter reporting on a mobile OS see [os-list] e.g osId[0]=1&osId[1]=2 | | formatId | no | Array of format | Filter reporting on a specific placement format [formats-list] e.g formatId[0]=1&formatId[1]=2 | | countryId | no | Array | Filter reporting on a specific placement country based on [geonameid] | | regionId | no | Array | Filter reporting on a specific placement region (admin level 1) based on [geonameid] | | departmentId | no | Array | Filter reporting on a specific placement department (admin level 2) based on [geonameid] | | cityId | no | Array | Filter reporting on a specific placement city based on [geonameid] | | postalcode | no | Array | Filter reporting on a specific placement postalcode based on [geonameid] | | adNetworkId | no | Array | Filter reporting on a specific Ad network | | carrier | no | Array Filter reporting on a specific carrier (free, Orange,...) | | | deviceTypeId | no | Array | Filter reporting on a specific brandId | | deviceBrandId | no | Array | Filter reporting on a specific brandId | | deviceModel | no | Array | Filter reporting on a specific model (iPhone X, ...) | | buyerId | no | Array | Filter reporting on a buyer connected to our Ad Exchange | | dealId | no | Array | Filter reporting on a deal created by our Ad Exchange | | dmpId | no | Array | Filter reporting on a dmp created by our Ad Exchange | | dmpSourceId | no | Array | Filter reporting on a dmp (1px, Adobe, ...) created by our Ad Exchange | ### output ```json showLineNumbers { "response": { "status": "OK", "report_id": "20e5cee104f3ebb0011fbeb8852fafdf9d237113" } } ``` ### Example ```bash $ curl -H 'Authorization: fbe74e915898ee0d560643d0f3dd722eb17bade3' \ -H 'Accept: application/json' \ -H "Content-Type: application/x-www-form-urlencoded" \ -X POST "https://xxx.com/seller-reporting" \ --data 'metrics=requests,displays,clicks,downloads,leads,landings,revenueEuro,eyesTrackings' \ --data 'since=1508485191' \ --data 'until=1508830791' \ --data 'publisherId[0]=5961' \ --data 'breakdowns[1]=PUBLISHER' \ --data 'breakdowns[2]=APP' \ --data 'breakdowns[4]=APPCOUNTRY' ``` ## POST /status-reporting : Request the status of a report Make a POST call with the report ID to retrieve the status of the report. Continue making this call until the status is **ready**. Then use the **/download-reporting** end point to save the reporting data to a file. (This is described in the next step.) ```bash $ curl -H 'Authorization: 1be3546f0bed0e3f03b08673d1a635c81fb55bba' \ -H 'Accept: application/json' \ -H "Content-Type: application/x-www-form-urlencoded" \ -X POST "https://xxx.com/status-reporting" \ --data 'id=7e65da22fca0cad8db34556f11466960b11dba74' \ ``` ```json showLineNumbers { "response": { "status": "progress", "message": "progress", "report_id": "7e65da22fca0cad8db34556f11466960b11dba74" } } ``` ## POST /download-reporting : Retrieve report data To download the report data to a file, make another POST call with the report ID. You can find report ID on previous POST response (/status-reporting), **for ready status only. Must be called when /status-reporting returns **ready** status ```bash $ curl -H 'Authorization: 1be3546f0bed0e3f03b08673d1a635c81fb55bba' \ -H 'Accept: application/json' \ -H "Content-Type: application/x-www-form-urlencoded" \ -X POST "https://xxx.com/download-reporting" \ --data 'id=7e65da22fca0cad8db34556f11466960b11dba74' > /tmp/seller_stats.csv ``` ## Response ```json showLineNumbers { "data": [ { "country": "France", "city": null, "eyesTrackings": 0, "requests": 0, "displays": 59, "platform": "Android", "zoneHash": "/1085857/banner/af", "downloads": 0, "postalcode": null, "appId": "1085857", "subpublisherId": null, "landings": 0, "deviceType": "phone", "os": "Android", "subSubPublisherId": null, "appName": "FR_Mondadori_PleineVie_App_AndroidTab_Madvertise", "adminLevel1": null, "adminLevel2": null, "format": "banner", "bundleId": "com.mondadori.pleinevie", "appOwner": "MBRAND", "encodedSubpublisherId": null, "carrier": "orange", "leads": 0, "publisher": "Mondadori", "clicks": 2, "valueDollar": 0, "deviceModel": "B3-A20", "Adnetwork": "appsFire", "deviceBrand": null, "revenueEuro": 0 }, ... ], "summary": { "since": "2017-10-20T09:39:51+0200", "until": "2017-10-24T09:39:51+0200", "breakdowns": [ "PUBLISHER", "APP", "OS", "FORMAT", "DEVICETYPE", "BUNDLEID", "DEVICEBRAND", "DEVICEMODEL", "COUNTRY", "REGION", "ZONE", "SUBPUBLISHERID", "ADNETWORK", "DEPARTMENT", "CITY", "POSTALCODE", "SUBSUBPUBLISHERID", "PLATFORM", "CARRIER" ], "timezone": "europe/paris" } } ``` [breakdowns-list section]:./01-seller-reporting-api.md#breakdownsdimensions-list [formats-list]:./01-seller-reporting-api.md#breakdownsdimensions-list [os-list]:./01-seller-reporting-api.md#post-seller-reporting--publisher-reporting-service [geonameid]:http://www.geonames.org/ [auth-reporting]:./index.md --- ## Buyer Reporting API ## Quick Reference All API access is over HTTPS, and accessed via the https://xxx.com domain (ask to your Azerion contact). ### Request Format For **POST** requests, the request body must be JSON, with the *Content-Type* header set to *application/json*. ### Response format The response format for all requests is a JSON object. Whether a request succeeded is indicated by the HTTP status code. A 2xx status code indicates success, whereas a 4xx status code indicates failure. ## POST /auth-reporting : Authentication Service see [auth-reporting] section. ## POST /buyer-reporting : Advertiser Reporting Service Use the token returned by auth-reporting service when making calls to **/buyer-reporting**. You POST the JSON request and get back a report ID. ### Metrics | **Metric** | **Definition** | |---------------|-----------------------------------------------------------------------------| | requests | An attempt to MAS adserver to fill an impression. | | displays | Number of times an ad is served and displayed according [Viewbility] . | | clicks | Number of times an ad is clicked. | | downloads | Number of conversions from [Conversion API] or Appsfire [Buyer Integration] | | leads | Number of leads from [Conversion API] or Appsfire [Buyer Integration] | | landings | Number of landings from [Conversion API] or Appsfire [Buyer Integration] | | revenueEuro | Revenue generated in €. | | revenueDollar | Revenue generated in $. | | eyesTrackings | [eyes-tracking] | ### Breakdowns/Dimensions list Optionally, use one of the following options in the breakdowns param to specify which dimension. | **Dimension** | **Definition** | |---------------|---------------------------------------------------------------------------------------------------------| | DAILY | Provides a breakdown by day (can't be combined with HOURLY, RANGE and DAILY) | | HOURLY | Provides a breakdown by hour (can't be combined with MONTHLY, RANGE and DAILY) | | MONTHLY | Provides a breakdown by Month (can't be combined with HOURLY, RANGE and DAILY) | | RANGE | Provides a breakdown according since and until dates (can't be combined with HOURLY, MONTHLY and DAILY) | | ADNETWORK | Provides a breakdown by our core Ad network (mngperf or appsfire) | | CAMPAIGN | Provides a breakdown by campaign | | ADUNIT | Provides a breakdown by Ad | | ADVERTISER | Provides a breakdown by Advertiser | | PUBLISHERID | Provides a breakdown by publisherId | | FORMAT | Provides a breakdown by format (interstitial, banner, nativeAd ...) | | OS | Provides a breakdown by mobile OS | | COUNTRY | Provides a breakdown by country (FR, US,...) | | REGION | Provides a breakdown by Region (Admin Level 1) | | DEPARTMENT | Provides a breakdown by Department (Admin Level 2) | | CITY | Provides a breakdown by City (Paris, ...) | | POSTALCODE | Provides a breakdown by Postal Code | | OS | Provides a breakdown by mobile OS | | DEVICETYPE | Provides a breakdown by DeviceType (phone or tablet) | | DEVICEBRAND | Provides a breakdown by BrandName (e.g Apple) | | DEVICEMODEL | Provides a breakdown by Model (e.g iPhone 7 Plus) | | CARRIER | Provides a breakdown by Carrier (e.g Orange) Mobile carrier name or Wireless carrier | ### Filters | **Parameter name** | **Required?** | **Format** | **Definition** | |--------------------|---------------|-------------------------------------------------------------------|-------------------------------------------------------------------------------------------------| | since | Yes | europe/paris unix timestamp | e.g 1417392000 | | until | Yes | europe/paris unix timestamp | e.g 1420070399 | | breakdowns | No | array of string | see [breakdowns-list section], e.g breakdowns[0]=HOURLY&breakdowns[1]=ZONE | | advertiserId | no | array of advertiser IDs | For admin only Filter reporting on specific advertisers e.g advertiserId[0]=1&advertiserId[1]=2 | | adunitId | no | array of adUnits IDs | Filter reporting on a specific adunitId e.g adunitId[0]=1&adunitId[1]=2 | | campaignId | no | array of campaigns IDs | Filter reporting on a specific campaign e.g campaignId[0]=1&campaignId[1]=2 | | osId | no | Array | Filter reporting on a mobile OS see [os-list] e.g osId[0]=1&osId[1]=2 | | formatId | no | Array | Filter reporting on a specific placement format [formats-list] e.g formatId[0]=1&formatId[1]=2 | | countryId | no | Array | Filter reporting on a specific placement country based on [geonameid] | | regionId | no | Array | Filter reporting on a specific placement region (admin level 1) based on [geonameid] | | departmentId | no | Array | Filter reporting on a specific placement department (admin level 2) based on [geonameid] | | cityId | no | Array | Filter reporting on a specific placement city based on [geonameid] | | postalcode | no | Array | Filter reporting on a specific placement postalcode based on [geonameid] | | adNetworkId | no | Filter reporting on a specific Ad network | | | carrier | no | Array Filter reporting on a specific carrier (free, Orange,...) | | | deviceTypeId | no | Array | Filter reporting on a specific brandId | | deviceBrandId | no | Array | Filter reporting on a specific brandId | | deviceModel | no | Array | Filter reporting on a specific model (iPhone X, ...) | ### Example ```bash $ curl -H 'Authorization: fbe74e915898ee0d560643d0f3dd722eb17bade3' \ -H 'Accept: application/json' \ -H "Content-Type: application/x-www-form-urlencoded" \ -X POST "https://xxx.com/buyer-reporting" \ --data 'metrics=displays,clicks,downloads,leads,landings,revenueEuro,eyesTrackings' \ --data 'since=1508485191' \ --data 'until=1508830791' \ --data 'advertiserId[0]=58' \ --data 'breakdowns[7]=ADUNIT' \ --data 'breakdowns[8]=FORMAT' \ --data 'breakdowns[9]=DEVICETYPE' \ --data 'breakdowns[10]=CAMPAIGN' ``` ## POST /status-reporting : Request the status of a report Make a POST call with the report ID to retrieve the status of the report. Continue making this call until the status is **ready**. Then use the **/download-reporting** end point to save the reporting data to a file. (This is described in the next step.) ```bash $ curl -H 'Authorization: 1be3546f0bed0e3f03b08673d1a635c81fb55bba' \ -H 'Accept: application/json' \ -H "Content-Type: application/x-www-form-urlencoded" \ -X POST "https://xxx.com/status-reporting" \ --data 'id=7e65da22fca0cad8db34556f11466960b11dba74' \ ``` ```json showLineNumbers { "response": { "status": "progress", "message": "progress", "report_id": "7e65da22fca0cad8db34556f11466960b11dba74" } } ``` ## POST /download-reporting : Retrieve report data To download the report data to a file, make another POST call with the report ID. You can find report ID on previous POST response (/status-reporting), **for ready status only. Must be called when /status-reporting returns **ready** status ```bash $ curl -H 'Authorization: 1be3546f0bed0e3f03b08673d1a635c81fb55bba' \ -H 'Accept: application/json' \ -H "Content-Type: application/x-www-form-urlencoded" \ -X POST "https://xxx.com/download-reporting" \ --data 'id=7e65da22fca0cad8db34556f11466960b11dba74' > /tmp/seller_stats.csv ``` ## Response ```json showLineNumbers { "data": [ { "country": null, "city": null, "eyesTrackings": 0, "displays": 1, "billingEntity": "Mbrand3", "platform": "Ios", "creativeId": "0", "adUnitLimitType": "0", "downloads": 0, "postalcode": null, "subpublisherId": null, "landings": 0, "deviceType": "phone", "advertiser": "Mobile Network Group-Advertiser", "adName": "SSP_Appnexus_app_interstitial", "os": null, "campaignId": "1072", "adminLevel1": null, "adminLevel2": null, "bundleId": "com.meteo.meteofrance", "adUnitLimitValue": "0", "carrier": "free", "adId": "4760", "leads": 0, "clicks": 0, "valueDollar": 0, "deviceModel": null, "Adnetwork": "appnexus S2S", "campaignName": "SSP_Appnexus", "deviceBrand": null, "revenueEuro": 0 }, ... ], "summary": { "since": "2017-10-20T09:39:51+0200", "until": "2017-10-24T09:39:51+0200", "breakdowns": [ "OS", "ADUNIT", "DEVICETYPE", "CAMPAIGN", "BUNDLEID", "DEVICEBRAND", "DEVICEMODEL", "COUNTRY", "REGION", "SUBPUBLISHERID", "ADNETWORK", "ADVERTISER", "DEPARTMENT", "CITY", "POSTALCODE", "PLATFORM", "CARRIER", "CREATIVE" ], "timezone": "europe/paris" } } ``` ## GET /campaigns : Campaign List Service Returns all campaigns and adunits for campaigns ### Output ```json showLineNumbers [ { "advertiserId": "56", "advertiserName": "Mobile Network Group-Advertiser", "campaignId": "1", "campaignName": "my campaign", "campaignStartDate": "2018-03-27 00:00:00", "campaignEndDate": "2028-03-27 18:14:00", "adunits": [ { "adunitName": "my campaign 1", "adunitId": "1" } ] }, { "advertiserId": "56", "advertiserName": "Mobile Network Group-Advertiser", "campaignId": "2", "campaignName": "my campaign 2", "campaignStartDate": "2018-03-27 00:00:00", "campaignEndDate": "2028-03-27 18:14:00", "adunits": [ { "adunitName": "my campaign 1", "adunitId": "1" } ] } ] ``` ### Example ```bash curl -H "Authorization: cf45ea76682190d5daca73acc1cc57afa988f546" \ -X GET 'https://xxxx.com/campaigns' ``` [breakdowns-list section]:./01-seller-reporting-api.md#breakdownsdimensions-list [os-list]:01-seller-reporting-api.md#breakdownsdimensions-list [auth-reporting]:./index.md [geonameid]:http://www.geonames.org/y [Buyer Integration]:./02-buyer-reporting-api.md --- ## Mediation Reporting API ## Quick Reference All API access is over HTTPS, and accessed via the https://xxx.com domain (ask to your Azerion contact). ## Request Format For **POST** requests, the request body must be JSON, with the *Content-Type* header set to *application/json*. ## Response format The response format for all requests is a JSON object. Whether a request succeeded is indicated by the HTTP status code. A 2xx status code indicates success, whereas a 4xx status code indicates failure. ## Helpful Tools |json_reformat formats your output nicely without reordering the fields. ## POST /auth-reporting : Authentication Service see [auth-reporting] ## breakdowns list Optionally, use one of the following options in the breakdowns param to specify which metric. | **breakdown** | **comment** | **Default?** | |---------------|---------------------------------------------------------------------------------------------------------|--------------| | DAILY | Provides a breakdown by day (can't be combined with HOURLY, RANGE and DAILY) | No | | HOURLY | Provides a breakdown by hour (can't be combined with MONTHLY, RANGE and DAILY) | No | | MONTHLY | Provides a breakdown by Month (can't be combined with HOURLY, RANGE and DAILY) | No | | RANGE | Provides a breakdown according since and until dates (can't be combined with HOURLY, MONTHLY and DAILY) | No | | ADNETWORK | Provides a breakdown by our core Ad network (mngperf or appsfire) | No | | PUBLISHER | Provides a breakdown by Publisher | No | | APP | Provides a breakdown by App | No | | PLACEMENT | Provides a breakdown by placement (banner, Interstitial, NativeAd, ...) | No | | FORMAT | Provides a breakdown by format (interstitial, banner, nativeAd ...) | No | | APP_MEDIATION | 1 = uur mediation SDK, 0 = our adserving platform | | | APP_STATONLY | 1 = stats from other adnetworks without our mediation SDK | | ## POST /mediation-reporting : Publisher Reporting Service Use the token returned by auth-reporting service when making calls to ### Parameters | **Parameter name** | **Required?** | **Format** | **Description** | |--------------------|---------------|:--------------------------------------------------------------------|------------------------------------------------------------------------------------------------| | since | Yes | europe/paris unix timestamp | e.g 1417392000 | | until | Yes | europe/paris unix timestamp | e.g 1420070399 | | breakdowns | No | array of string | see [breakdowns-list section], e.g breakdowns[0]=HOURLY&breakdowns[1]=ZONE | | publisherId | no | array of publishers IDs | For admin only Filter reporting on specific publishers e.g publisherId[0]=1&publisherId[1]=2 | | appId | no | array of apps IDs | Filter reporting on specific apps e.g appId[0]=1&appId[1]=2 | | placementId | no | array of placements IDs | For admin only Filter reporting on specific placements e.g placementId[0]=1&placementId[1]=2 | | formatId | no | Array of format | Filter reporting on a specific placement format [formats-list] e.g formatId[0]=1&formatId[1]=2 | | adNetwork | no | Filter reporting on a specific Ad networks | e.g smartAdserver, Facebook | | app_statonly | no | 1 = stats from other adnetworks without our mediation SDK || | app_mediation | no | 1 = uur mediation SDK, 0 = our adserving platform || ### Output ```json showLineNumbers { "data": [ { "click": "1695", "request": 181483, "requestMediation": "1639327", "impression": "161294", "breakdowns": { "publisherId": "5746", "publisherName": "xxx", "appName": "xxxxxx", "appOwner": "MBRAND", "appId": "1697824", "date": "2017-10-15" }, "revenue": "27.553169130999997", "revenueEuros": "27.114409424918936", "revenueDollar": "32.0221177026" }, { "click": "31", "request": 45254, "requestMediation": "249237", "impression": "25144", "breakdowns": { "publisherId": "5746", "publisherName": "xxx", "appName": "xxxx", "appOwner": "MBRAND", "appId": "1191726", "date": "2017-10-15" }, "revenue": "2.56965202", "revenueEuros": "2.3561616492127997", "revenueDollar": "2.7826269913" } ], "summary": { "since": "2017-10-15 00:00:00", "until": "2017-10-16 00:00:00", "breakdowns": [ "DAILY", "PUBLISHER", "APP" ], "timezone": "europe/paris" } } ``` ### Example ```bash curl -H 'Authorization: da57722f06c2c4359b03697584a8757c036c1359' -X POST "https://xxxx.com/mediation-reporting" \ --data "since=1508018400&until=1508104800&breakdowns[0]=DAILY&breakdowns[1]=PUBLISHER&breakdowns[2]=APP&publisherId[0]=5746" |json_reformat ``` ### Error ```json showLineNumbers { "response": { "status": "ERROR", "message": "Unknown token, use auth service before" } } ``` [auth-reporting]:./index.md --- ## DSP Reporting API ## Quick Reference All API access is over HTTPS, and accessed via the https://xxx.com domain (ask to your Azerion contact). ### Request Format For **POST** requests, the request body must be JSON, with the *Content-Type* header set to *application/json*. ### Response format The response format for all requests is a JSON object. Whether a request succeeded is indicated by the HTTP status code. A 2xx status code indicates success, whereas a 4xx status code indicates failure. ## POST /auth-reporting : Authentication Service see [auth-reporting] section. ## POST /buyer-reporting : Advertiser Reporting Service Use the token returned by auth-reporting service when making calls to **/buyer-reporting**. You POST the JSON request and get back a report ID. ### Metrics | **Metric** | **Definition** | |---------------|------------------------------------------------------------------------| | displays | Number of times an ad is served and displayed according [Viewbility] . | | revenueEuro | Revenue generated in €. | | revenueDollar | Revenue generated in $. | ### Breakdowns/Dimensions list Optionally, use one of the following options in the breakdowns param to specify which dimension. | **Dimension** | **Definition** | |---------------|---------------------------------------------------------------------------------------------------------| | DAILY | Provides a breakdown by day (can't be combined with HOURLY, RANGE and DAILY) | | HOURLY | Provides a breakdown by hour (can't be combined with MONTHLY, RANGE and DAILY) | | MONTHLY | Provides a breakdown by Month (can't be combined with HOURLY, RANGE and DAILY) | | RANGE | Provides a breakdown according since and until dates (can't be combined with HOURLY, MONTHLY and DAILY) | | BUNDLEID | Provides a breakdown by packageName for android and bundleId for IOS | | FORMAT | Provides a breakdown by format (interstitial, banner, nativeAd ...) | ### Filters | **Parameter name** | **Required?** | **Format** | **Definition** | |--------------------|---------------|----------------------------------|-----------------------------------------------------------------------------| | since | Yes | unix timestamp | e.g 1417392000 | | until | Yes | unix timestamp | e.g 1420070399 | | timezone | no | timezone name [TZ database name] | e.g Europe/Paris | | breakdowns | No | array of string | see [breakdowns-list section], e.g breakdowns[0]=DAILY&breakdowns[1]=FORMAT | ### Example ```bash $ curl -H 'Authorization: fbe74e915898ee0d560643d0f3dd722eb17bade3' \ -H 'Accept: application/json' \ -H "Content-Type: application/x-www-form-urlencoded" \ -X POST "https://xxx.com/buyer-reporting" \ --data 'output=direct' \ --data 'timezone=Europe/Paris' \ --data 'metrics=displays,revenueEuro,revenueDollar' \ --data 'breakdowns[0]=DAILY' \ --data 'since=1547510400' \ --data 'until=1547683200' ``` ## Response If you use Accept: application/json, we will return a json output. If you remove this header, we will return a csv output. ```json showLineNumbers { "data": [ { "date": "2019-01-15", "displays": 1740, "revenueEuro": "0,40", "revenueDollar": "0,46" }, { "date": "2019-01-16", "displays": 1876, "revenueEuro": "0,41", "revenueDollar": "0,46" } ], "summary": { "since": "2019-01-15T01:00:00+0100", "until": "2019-01-17T01:00:00+0100", "breakdowns": [ "DAILY" ], "timezone": "Europe\/Paris" } } ``` [breakdowns-list section]:./01-seller-reporting-api.md#breakdownsdimensions-list [auth-reporting]:./index.md [TZ database name]:https://en.wikipedia.org/wiki/List_of_tz_database_time_zones --- ## Getting Started(Reporting) ## POST ~~/auth-reporting~~ : [DEPRECATED] Previously used authentication method /auth-reporting is deprecated !! ## Authentication Service Before you can make calls to reporting API, you must have to have an authorization token. The token remains active for 7 days once generated, during which you do not need to re-token. Upon using it (while the token is not expired) each time it will extend its validity. To generate a fresh reporting token head to console UI. 1. Click on the arrow icon of the top right corner of the dashboard. 2. Click on the profile after that a modal window will popup with profile details. ![SCREEN-1](./images/screen-1.png) 3. There we will have reporting token & expiry date also there is a button to generate a new one. ![Profile-reporting-token](./images/profile-reporting-token.png) > Double click on token/ the icon to copy to clipboard. ## Available API - [Seller Reporting API] - [Buyer Reporting API] - [Mediation Reporting API] - [SDK tracking API] [Seller Reporting API]:./01-seller-reporting-api.md [Buyer Reporting API]:./02-buyer-reporting-api.md [Mediation Reporting API]:./03-mediation-reporting-api.md [SDK tracking API]:./04-dsp-reporting-api.md --- ## OpenRTB bidRequest API We offer our publishers an [OpenRTB 2.5 bidRequest API] so that integrating with BlueStack ## What do you need to get integrated? * You need to send HTTP POST requests with a JSON ad request body with [OpenRTB 2.5 bidRequest API] format * You should be able to interpret HTTP status codes for the response (200, 204 and 400) * You should already have a [BlueStack] account with an inventory set up. * You should use the inventory **PLACEMENT_CODE** as part of the request URL, which is described below ## How should an RTB Bid Request look like? ### HTTP request headers | **Key** | **Value** | **Required?** | | -------- | -------- | -------- | | **Content-Type** | application/json | Yes | | **x-openrtb-version** | 2.5 | Yes | ### Send your RTB Bid Request to this URL ```bash https://mobile.mng-ads.com/bidrequest/[REPLACE_WITH_YOUR_PLACEMENT_CODE] ``` ### Example of RTB Bid Requests #### Banner Ad ```json showLineNumbers { "id": "1582022328X56X11279X1982X17029X5961X896X50942X0X48X2X2X3012874X0X1X0X2178074X3017382X2968815X2988507X0X0X0H34dfd1fa0f38f8fb1d9d9e4d041cd471f0f9b530", "tmax": 500, "at": 2, "cur": ["EUR"], "regs": { "ext": { "gdpr": 1 } }, "user": { "buyeruid": "89a68102-a116-49f1-be4a-658b2480c3d7", "keywords": "inall=18;inall_hr=10;", "ext": { "consent": "BOs832ZOs832ZAsAZBFRC3-HAAAqkAOQRZiKRoAC0NYBwAADAK4AAAQAAAAALQgAQAYCAEAiAAgAAAAAAAAAAAAAAAgAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAQ" } }, "app": { "id": "2178074", "bundle": "com.madvertise.bluestack", "name": "FR_BlueStack_App_Android_MNG", "storeurl": "https:\/\/play.google.com\/store\/apps\/details?id=com.madvertise.bluestack", "cat": ["IAB6", "IAB7", "IAB9", "IAB10", "IAB12", "IAB14"] }, "device": { "ip": "84.14.10.74", "ua": "Mozilla\/5.0 (Linux; U; Android 9; fr-fr; SM-J600FN Build\/PPR1.180610.011) AppleWebKit\/533.1 (KHTML, like Gecko) Version\/4.0 Mobile Safari\/533.1", "dnt": 0, "lmt": 0, "ifa": "89a68102-a116-49f1-be4a-658b2480c3d7", "make": "Samsung", "model": "SM-J600FN", "os": "Android", "osv": "9.0", "devicetype": 4, "connectiontype": 2, "js": 1, "carrier": "EE", "geo": { "lat": 51.5126912, "lon": -0.1081602, "type": 1, "country": "FRA", "region": "IDF", "city": "Paris", "zip": "75001" } }, "imp": [{ "id": "1", "secure": 1, "tagid": "32266", "bidfloor": 0.5, "bidfloorcur": "EUR", "instl": 0, "banner": { "pos": 4, "w": 320, "h": 50, "api": [5, 1001] } }] } ``` #### VAST Video Ad ```json showLineNumbers { "id": "1582021936X56X12510X1982X18517X5961X5702X51056X8524X48X13X2XX0X1X0X9561622X3017382XXX0X0X0H5944bb9df2ac09617ad13d9effc3c6621942b1e6", "tmax": 500, "at": 2, "cur": ["EUR"], "regs": { "ext": { "gdpr": 1 } }, "user": { "buyeruid": "b879a967-37e7-4564-ab8a-6cc259846116", "ext": { "consent": "BOUA7l2OrUK1-AsAWBFRCxABAAAqkAOQRZiKRoAC0NYBwAADAK4AAAQAAAAALQgAQAYCAEAiAAgAAAAAAAAAAAAAAAgAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAA" } }, "app": { "id": "9561622", "bundle": "com.madvertise.bluestack", "name": "FR_BlueStack_App_Android_MNG", "storeurl": "https:\/\/play.google.com\/store\/apps\/details?id=com.madvertise.bluestack", "cat": ["IAB6", "IAB9", "IAB10", "IAB12", "IAB14", "IAB20"] }, "device": { "ip": "37.166.247.145", "ua": "Mozilla\/5.0 (Linux; U; Android 9; fr-; G8441 Build\/47.2.A.11.228) AppleWebKit\/533.1 (KHTML, like Gecko) Version\/4.0 Mobile Safari\/533.1", "dnt": 0, "lmt": 0, "ifa": "b879a967-37e7-4564-ab8a-6cc259846116", "make": "Sony", "model": "G8441", "os": "Android", "osv": "9.0", "devicetype": 4, "connectiontype": 6, "js": 1, "carrier": "Free", "geo": { "type": 2, "country": "FRA" } }, "imp": [{ "id": "1", "secure": 1, "tagid": "31377", "bidfloor": 3, "bidfloorcur": "EUR", "instl": 0, "video": { "mimes": ["video\/mp4", "video\/3gpp", "application\/javascript"], "w": 320, "h": 480, "minduration": 2, "maxduration": 30, "playbackmethod": [1, 3], "boxingallowed": 0, "protocols": [1, 2, 3, 4, 5, 6], "placement": 4 } }] } ``` #### Native Ad For more info see [RTB Native Ad Request Spec 1.2] ```json showLineNumbers { "id": "1582022531X56X12509X1982X18516X6166X8773X51703X0X48X2X2X2951839X0X1X0X2787907X2921044XX2867714X0X0X0Hbafce55893fc8d49686be4852717c3fd5870fc0c", "tmax": 500, "at": 2, "cur": ["USD"], "regs": { "ext": { "gdpr": 1 } }, "user": { "buyeruid": "c3d3ea59-0d24-4156-9555-712e12f0cbd7", "keywords": "inall=42;inall_hr=10;page=program", "ext": { "consent": null } }, "app": { "id": "2787907", "bundle": "com.madvertise.bluestack", "name": "DE_BlueStack_app_Android_Phone_", "storeurl": "https:\/\/play.google.com\/store\/apps\/details?id=com.madvertise.bluestack" }, "device": { "ip": "89.204.135.74", "ua": "Mozilla\/5.0 (Linux; U; Android 6.0.1; de-de; SM-A300FU Build\/MMB29M) AppleWebKit\/533.1 (KHTML, like Gecko) Version\/4.0 Mobile Safari\/533.1", "dnt": 0, "lmt": 0, "ifa": "c3d3ea59-0d24-4156-9555-712e12f0cbd7", "make": "Samsung", "model": "SM-A300FU", "os": "Android", "osv": "6.0.1", "devicetype": 4, "connectiontype": 6, "js": 1, "carrier": "Tchibo", "geo": { "type": 2, "country": "DEU" } }, "imp": [{ "id": "1", "secure": 1, "tagid": "34656", "bidfloor": 0.3250764, "bidfloorcur": "USD", "native": { "request": "{\"layout\":6,\"vers\":\"1.0\",\"assets\":[{\"id\":1,\"img\":{\"type\":3,\"w\":1200,\"h\":627}},{\"id\":2,\"img\":{\"type\":1,\"w\":50,\"h\":50}},{\"id\":3,\"title\":{\"len\":50}},{\"id\":4,\"data\":{\"type\":2,\"len\":150}},{\"id\":5,\"data\":{\"type\":12,\"len\":12}}]}" } }] } ``` ### What known limitations exist? * Only one "imp" object per Bid Request. * Only in-app Bid Requests. We support only bid requests with the parameter "app", which target applications. * We are using Prebid for web traffic from "site" inventory, see [Header Bidding With Prebid.js For Mobile Web] ## How does an RTB Bid Response look like? ### No Bid Response We send an **HTTP status code 204** (no-content) response when the bid request is valid but BlueStack has "no bid" for it. ### Bid Response #### Invalid Bid Request We respond on any bid request error with the **HTTP status code 400**. The **HTTP status code 500** is reserved for BlueStack internal server errors. The error details are communicated on **x-bluestack-message** HTTP header key ```http HTTP/1.1 400 Bad Request Server: nginx/1.14.2 Date: Tue, 14 Apr 2020 06:48:26 GMT Content-Type: text/html; charset=UTF-8 Connection: close x-bluestack-message: missing raw body ``` #### Valid Bid Request For now, our **Bill notification** is included on **adm** attribute (Ad markup containing) ```json showLineNumbers { "id": "1582022328X56X11279X1982X17029X5961X896X50942X0X48X2X2X3012874X0X1X0X2178074X3017382X2968815X2988507X0X0X0H34dfd1fa0f38f8fb1d9d9e4d041cd471f0f9b530", "bidid": "1587043131X56X37141X10X52700X7697X7849X53919X0X3X2X2X3012874X0X1X0X6030037X3017382X2968815X2988507X0X0X0H76aeb1562c5278c5595034ec87640ce25b649d9b", "cur": "EUR", "seatbid": [{ "bid": [{ "id": "d343d064a31cfd1b1f4e5563f7a71a8d10736df0", "impid": "2", "price": 0.33936, "adm": "...", "cid": "10", "crid": "37141", "adomain": ["improvedigital.com"], "attr": [], "w": 640, "h": 960 }], "seat": "madvertise" }] } ``` ##### Banner Ad adm attribute contains HTML only. ##### VAST Video Ad **adm** attribute contains inline VAST document. We support following protocol: * 1 VAST 1.0 * 2 VAST 2.0 * 3 VAST 3.0 * 4 VAST 1.0 Wrapper * 5 VAST 2.0 Wrapper * 6 VAST 3.0 Wrapper * 7 VAST 4.0 * 8 VAST 4.0 Wrapper Impression contains all tracking url (that include Billing notice URL). However, we can manage **burl** openRTB attribute too. ##### Native Ad We support [RTB Native Ad Request Spec 1.2] ```json showLineNumbers { "native": { "link": { "url": "http://bluestack.app" }, "assets": [ { "id": 1, "required": 1, "title": { "text": "title of nativead" } }, { "id": 2, "required": 1, "img": { "url": "https://creative.mng-ads.com/10/5379-8078.jpg" } }, { "id": 3, "required": 1, "img": { "url": "https://creative.mng-ads.com/10/5379-8079.jpg" } }, { "id": 4, "required": 1, "data": { "value": "CTA" } }, { "id": 5, "required": 1, "data": { "value": "description of nativead." } } ] } } ``` [OpenRTB 2.5 bidRequest API]:https://www.iab.com/wp-content/uploads/2016/03/OpenRTB-API-Specification-Version-2-5-FINAL.pdf [RTB Native Ad Request Spec 1.2]:https://www.iab.com/wp-content/uploads/2018/03/OpenRTB-Native-Ads-Specification-Final-1.2.pdf --- ## Prebid Server Adapter Integrate Prebid and Madvertise to maximize your website's ad revenue effortlessly! ## Prebid Open-source tool for competitive real-time ad bidding. ## Madvertise Powerful platform connecting you with top advertisers using smart targeting. ## Why Combine? - **Maximize Revenue:** Access multiple demand sources and premium advertisers. - **Smart Targeting:** Deliver relevant ads for higher user engagement. ## Get Started 1. Read the simple integration steps in the official Prebid documentation for Madvertise [here](https://docs.prebid.org/dev-docs/pbs-bidders.html#madvertise). 2. Define ad sizes and placements. 3. Install Prebid using their easy guide. 4. Configure Madvertise as a bidder. 5. Test, optimize, and watch your revenue grow! ## Unlock Your Website's Earning Potential Today --- ## Ad Request API Documentation The API accepts simple GET OR POST requests, with optional / required parameters in the query string portion of the URL. The parameters must be encoded as name/value pairs using the standard HTTP URL-encoding principles. ## Response format The response format for all requests is a JSON object. Whether a request succeeded is indicated by the HTTP status code. A 2xx status code indicates success, whereas a 4xx status code indicates failure. ## Ad-Request URL All API access is over HTTPS or HTTP, you'll need to issue a HTTP GET or POST Request to the following URL: ```bash http://mobile.mng-ads.com/?[requiredandoptionalkeyvaluepairs] ``` ### Ad-Request Parameters | **Parameter name** | **Required?** | **Format** | **Description**| |-----------------------------| -------- | -------- | -------- | | **rt** | Yes | string, **api_mediation** or **appsfire-v2-api** |**appsfire-v2-api** for nativead only (app install)| | **u** | Yes | string, URL-encoded User Agent | Pass the url-encoded User Agent of the requesting device in this parameter. e.g. &u=Mozilla%2F5.0+%28iPhone%3B+U%3B+C. | | **s** | Yes | string | Zone code. This parameter should be the unique Publisher ID of your mobile application or website. | | **i** | **Yes for rt=api , rt=appsfire-v2-api, api_mediation only** | string | use **api_mediation** for S2S mediation, **api** for use Madvertise adserving only or **appsfire-v2-api** in order to use appsfire only | | **v** | Yes | string | Version of the client sdk. For logging purpose in the web server logs | | **c_mraid** | No | 0/1 | Specify whether your mobile site / app is able to show MRAID ad responses. | | **c_vast** | No | 0/2 | Specify whether your mobile site / app is able to show VAST2 or VAST3 ad responses. API can return vastinline with c_vast=2 and only external url for c_vast=1 | | **o[idfa]** | No | string | Id(s) of the device / user. iOS | | **o[andadvid]** | No | string | Google Advertising ID | | **o[ip]** | No | string | alias of **i** parameter | | **lat** | Yes, if available | Decimal | The Users Geo-Location (latitude in degrees WGS84). | | **lon** | Yes, if available | Decimal | The Users Geo-Location (longitude in degrees WGS84). | | **connection_type** | No | string | Connection type. One of those values UNKNOWN, WIFI, 3G, 4G | | **seenad[adid1]** |No |Integer for key, Timestamp for value|Use for capping, this array contains ads seen with associated timestamp. `&seenad[adid1]=timestamp1` `&seenad[adid2]=timestamp2` Each **adid** is available response of Ad request | | **gender** | No | string, M/F | Gender of the user. M for male, F for female. | | **age** | No | Integer | The user's age, if available| | **zip** | No | String | The user's zip code, if available| | **jsvar** | Yes if rt=javascript | String | The name of javascript variable, Ad is returned in this variable in order to inject the code on publisher mobile site.| | **w** | No | integer |DThe Width of your Ad Space. This is also the maximum banner size that our servers will return. **Useless for appsfire nativead**| | **h** | No | integer|The Height of your Ad Space. This is also the maximum banner size that our servers will return. **Useless for appsfire nativead**| | **carrier** |No| String |Carrier name of mobile end-user| | **appName** | No | string | Name of app where SDK in used | | **bundleId** | No | string | packageName for android and ituneId for IOS | | **locale** | No | string | language code used on phone (e.g fr) | | **accept_retargeting** | Yes for rt=android_app_json or ios_app_json | int | default value is **1**, it must be set to **0** if the user has limited ad tracking ( [Google AdvertisingIdClient](https://developers.google.com/android/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient.Info)) or [IOS advertisingTrackingEnabled](https://developer.apple.com/library/ios/documentation/AdSupport/Reference/ASIdentifierManager_Ref/#//apple_ref/occ/instp/ASIdentifierManager/advertisingTrackingEnabled)| | **osVersion** | No | string | version of OS e.g 9.1 | | **tgt** | No | urlencoded string | Keyword targeting allows you to display ads only when specific keywords or key/value pairs are passed in the ad request e.g adobeSegmentId%3D1%3Bcat%3Ddemo for adobeSegmentId=1;cat=demo| | **c_video** | No | 0/1 | Specify whether your mobile site / app is able to show video | | **gdpr** | yes | 0/1 | 0: not in GDPR scope or 1: in GDPR scope | | **consent\[0\]\[format\]=** | yes | IAB | We manage IAB consentString only | | **consent\[0\]\[value\]** | yes | BONlRnIONlRnIAAABAENAAAAAAAAoAA | Encoded consent String IAB spec| ## Interstitials ```bash curl http://mobile.mng-ads.com?rt=android_app&v=6.0.0&u=Dalvik%2F1.6.0%20(Linux%3B%20U%3B%20Android%204.3%3B%20GT-I9300%20Build%2FJSS15J)&s=[YOUR_PLACEMENT_ID]&o%5Bandadvid%5D=732503e0-f8df-498b-b9a9-0cbce96394ec&long=5.9889615&lat=43.1377518&age=25&gender=M&c.mraid=1 ``` ### Interstitial Image Ad ```json showLineNumbers { "type": "interstitial", "format": "image", "mraid": false, "vast": false, "content": "", "contentUrl": "http://cdn.mng-ads.com/25b3b31f81909a7584866fbd9f7c7434309.jpg", "clickurl": "http://mobile.mng-ads.com/click/1455397457X56X169X10X309X15X5180317X36X8525X0X2X2X8525X0X1X0X5180317X30Hb2e601d82d4f19a1bdfb2863702fe7063e106f17?o[andadvid]=41c7fa2a-e967-46fd-9520-9869d4a10c9d", "impurl": [ "http://mobile.mng-ads.com/display/1455397457X56X169X10X309X15X5180317X36X8525X0X2X2X8525X0X1X0X5180317X30Hb2e601d82d4f19a1bdfb2863702fe7063e106f17" ], "impscript": [ " " ], "refresh": "0", "clicktype": "inapp", "preload": 0, "autoclose": 0, "adid": "169", "tagline": null, "background": "#FFFFFF", "adspaceWidth": "768", "adspaceHeight": "1024", "orientation": "portrait", "closePosition": "top-right", "closeAppearanceDelay": "0", "duration": "0", "animation": "none" } ``` ### Interstitial Html Ad ```json showLineNumbers { "type": "interstitial", "format": "html", "mraid": false, "vast": false, "content": "
\r\n\r\n \r\n Demo\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n
MacroDescription
mngads:clickour click url
mngads:deviceidId(s) of the device / user. iOS or Google Advertising ID
mngads:userlatThe Users Geo-Location (latitude in degrees WGS84).
mngads:userlonThe Users Geo-Location (longitude in degrees WGS84).
mngads:adidCreative ID (identifier for your banner id associated to the click id)
mngads:localeanguage code used on phone (e.g fr)
mngads:osOS of user
mngads:osVersionversion of OS e.g 9.1
mngads:bundleIdpackageName for android and ituneId for IOS
\r\n \r\n \r\n
", "contentUrl": "", "clickurl": "http://mobile.mng-ads.com/click/1455397844X56X250X10X470X15X5180317X36X8525X0X2X2X8525X0X1X0X5180317X30H0fd023b2cda14df07f97a2c4f13a5ead6377099e?o[andadvid]=41c7fa2a-e967-46fd-9520-9869d4a10c9d", "impurl": [ "http://mobile.mng-ads.com/display/1455397844X56X250X10X470X15X5180317X36X8525X0X2X2X8525X0X1X0X5180317X30H0fd023b2cda14df07f97a2c4f13a5ead6377099e" ], "impscript": [ " " ], "refresh": "0", "clicktype": "inapp", "preload": 0, "autoclose": 0, "adid": "250", "tagline": null, "background": "#FFFFFF", "adspaceWidth": "0", "adspaceHeight": "0", "orientation": "portrait", "closePosition": "top-right", "closeAppearanceDelay": "0", "duration": "0", "animation": "none" } ``` ### Interstitial VAST video For inline VAST contentUrl is empty and xml VAST available on **content** ```json showLineNumbers { type: "interstitial", format: "vast", mraid: false, content: "", contentUrl: "http://ads.stickyadstv.com/www/delivery/swfIndex.php?reqType=AdsSetup&protocolVersion=2.0&zoneId=206813", clickurl: "https://mobile.mng-ads.com/click/...?o%5Bidfa%5D=2CF0B5BB-59AC-4232-8A43-80E050E8B2AF&gender=M&carrier=Free&model=iPhone+6+Plus", impurl: [ "https://mobile.mng-ads.com/display/....?o%5Bidfa%5D=2CF0B5BB-59AC-4232-8A43-80E050E8B2AF&gender=M&carrier=Free&model=iPhone+6+Plus" ], "impscript": [ ], refresh: "0", clicktype: "inapp", preload: 0, autoclose: 0, adid: "4293", publisherid: "3180317", tagline: null, background: "#FFFFFF", adspaceWidth: "0", adspaceHeight: "0", "adchoiceposition": "top-right", "orientation": "portrait", "closePosition": "top-right", "closeAppearanceDelay": "0", "duration": "0", "videosettings": { "autoplay": 1, "audio": "muted", "blur": 1, "radius": "15", "opacity": "0" } } ``` ## Banners ```bash curl http://mobile.mng-ads.com?rt=android_app&v=6.0.0&u=Dalvik%2F1.6.0%20(Linux%3B%20U%3B%20Android%204.3%3B%20GT-I9300%20Build%2FJSS15J)&s=[YOUR_PLACEMENT_ID]&o%5Bandadvid%5D=732503e0-f8df-498b-b9a9-0cbce96394ec&long=5.9889615&lat=43.1377518&age=25&gender=M&c.mraid=1&x=360&y=50 ``` ### Banner Image Ad ```json showLineNumbers { "type": "banner", "format": "image", "mraid": false, "vast": false, "content": "", "contentUrl": "http://cdn.mng-ads.com/10/1308-1858.jpg", "clickurl": "http://mobile.mng-ads.com/click/1455398033X56X1308X10X1858X15X5180317X35X8525X0X2X2X8525X0X1X0X5180317X30H8a28a98565c212c2fe6b4f609b575a032486f651?o[andadvid]=41c7fa2a-e967-46fd-9520-9869d4a10c9d", "impurl": [ "http://mobile.mng-ads.com/display/1455398033X56X1308X10X1858X15X5180317X35X8525X0X2X2X8525X0X1X0X5180317X30H8a28a98565c212c2fe6b4f609b575a032486f651", "http://www.mobilenetworkgroup.com/test" ], "impscript":[ " " ], "refresh": "0", "clicktype": "inapp", "preload": 0, "autoclose": 0, "adid": "1308", "tagline": null, "background": "#FFFFFF", "adspaceWidth": "320", "adspaceHeight": "50" } ``` ### Banner Html Ad ```json showLineNumbers { "type": "banner", "format": "html", "mraid": false, "vast": false, "content": "
", "contentUrl": "", "clickurl": "http://mobile.mng-ads.com/click/1455397972X56X1959X10X2672X15X5180317X35X8525X0X2X2X8525X0X1X0X5180317X30H5fb7db83f5f905eb5e73e2319d9b343208317c2c?o[andadvid]=41c7fa2a-e967-46fd-9520-9869d4a10c9d", "impurl": [ "http://mobile.mng-ads.com/display/1455397972X56X1959X10X2672X15X5180317X35X8525X0X2X2X8525X0X1X0X5180317X30H5fb7db83f5f905eb5e73e2319d9b343208317c2c" ], "refresh": "0", "clicktype": "inapp", "preload": 0, "autoclose": 0, "adid": "1959", "tagline": null, "background": "#FFFFFF", "adspaceWidth": "0", "adspaceHeight": "0" } ``` ### appsfire NativeAd ![appsfire.png](img/appsfire.png) ```bash curl http://mobile.mng-ads.com/?rt=appsfire-v2-api&i=78.232.65.223&u=Mozilla%2F5.0%20(Linux%3B%20U%3B%20Android%205.0.2%3B%20fr-fr%3B%20XT1072%20Build%2FLXB22.99-24.12)%20AppleWebKit%2F533.1%20(KHTML%2C%20like%20Gecko)%20Version%2F4.0%20Mobile%20Safari%2F533.1&s=[YOUR_PLACEMENT_ID]&v=6.0.0&c_mraid=1&o%5Bandadvid%5D=4908b7b8-08aa-4a08-8003-031c2d7f1ae9&accept_retargeting=0&connection_type=WIFI&long=5.9888646&lat=43.1378641&macAddress=e4%3A90%3A7e%3A18%3A01%3Adb&w=360&h=592&carrier=&appName=MNGAdsServer&bundleId=com.mngads.mngadsserver&locale=fr&osVersion=5.0.2 ``` ```json showLineNumbers { "type":"nativeAd", "title":"Star Wars\u2122: Galaxy of Heroes", "description":"May the force be with you!", "categoryid":"JEUX DE R\u00f4LES", "category":"Jeux de r\u00f4les", "iconurl":"https:\/\/lh3.googleusercontent.com\/64RrPLGYj_V9e9Ku8bEemPY_cMEhdKpYkUBA49XczVAQBl1-6B8nEf-4paL2EPXQpbI", "iconsurl":[ ], "bundleid":"com.ea.game.starwarscapital_row", "clickurl":"http:\/\/mobile.mng-ads.com\/click\/1453475776X56X1819X287X2504X15X5180317X1912X8525X0X6X2X8525X0X1X0X5180317X30H82c262207653dc0643fa3cb475daca48d9a75b8c?o[andadvid]=4908b7b8-08aa-4a08-8003-031c2d7f1ae9&pfid={mngadspfid}", "impurl":[ "http:\/\/mobile.mng-ads.com\/display\/1453475776X56X1819X287X2504X15X5180317X1912X8525X0X6X2X8525X0X1X0X5180317X30H82c262207653dc0643fa3cb475daca48d9a75b8c" ], "refresh":"0", "autoclose":0, "price":"0", "adid":"1819", "publisherid": "3180317", "autoplay": 0, "screenshotUrls":[ "https:\/\/lh3.googleusercontent.com\/6vhbqD1-dM9Q4mazbpdiFjgLGgs-YkxkCCOC7XbnPhUFhIsqJpcN68MGGracVpdmgQA=h256-rw", "https:\/\/lh3.googleusercontent.com\/nUnXJUQNuilIXKnZNxrfm4abk5TVzdATASqKBtC-h2tkljULjwT_us_JuY-3Td66UIku=h256-rw", ], "videoUrls": [ "https://creative.mng-ads.com/media/viking.mp4" ], "closePosition":"top-left", "closeAppearanceDelay":"0", "duration":"0", "userRatingCount":"627208", "averageUserRating":4.5885605812073, "contentRating":"PEGI\u00a012" } ``` ### No ad available When there is no ad available for your ad-request, the following JSON is returned: ```json showLineNumbers { error: "No available ad" } ``` ## Appsfire direct clickurl We can provide an appsfire clickurl. This url is used to run a specific appsfire campaign without ad-request API call. This url can be used as: | **Parameter name** | **Required?** | **Format** | **Description** | |--------------------|-----------------|------------|----------------------------------------------------------------------------| | **s** | Yes | string | Zone code. This parameter should be the unique Publisher ID of your | | **adid** | Yes | int | Adunit ID. This parameter is provided by mng/appsfire team per campaign | | **o[idfa]** | No | string | Id(s) of the device / user. iOS | | **o[andadvid]** | No | string | Google Advertising ID | | **arg1** | Custom Value 1 | string | e.g clicid=\{arg1\} if you need to retrieve your clickid | | **arg2** | Custom Value 2 | string | | | **arg3** | Custom Value 3 | string | in most case we use to store your publisherId (sub publisherId for us) | | **arg4** | Custom Value 4 | string | in most case we use to store your publisherId (sub sub publisherId for us) | | **arg5** | Custom Value 5 | string | | | **arg6** | Custom Value 6 | string | | | **arg7** | Custom Value 7 | string | | | **arg8** | Custom Value 8 | string | | | **arg9** | Custom Value 9 | string | | | **arg10** | Custom Value 10 | string | | ```bash https://mobile.mng-ads.com/appsfireclicks/?adid=1&s=/3180317/interstitial/af&o[andadvid]=4908b7b8-&arg1={YOUR_CLICKID}&arg2={YOUR_CUSTOM_VALUE}&arg3={YOUR_PUBLISHERID}&arg4={YOUR_CUSTOM_VALUE} ``` ## Appsfire impression url Impression pixel redirect URL for S2S connected sources that contains parameters similar to appsfireclicks ```bash https://mobile.mng-ads.com/performancedisplay/?adid=1&s=/3180317/interstitial/af&o[andadvid]=4908b7b8-&arg1={YOUR_CLICKID}&arg2={YOUR_CUSTOM_VALUE}&arg3={YOUR_PUBLISHERID}&arg4={YOUR_CUSTOM_VALUE} ``` ## Postback Settings If you want to be notified when conversion occurs fill the URL of your server that our system should call. ```bash http://myserver.com/postback/?clicid={arg1}&campaign={arg2}&revenu={price} ``` Available params for postback url are: | **Macro** | **Description** | **Example** | |-----------------------|--------------------------------------------------------------|----------------------------------------------------------| | **\{unixtimestamp\}** | Number of seconds since the epoch | 1457427080 | | **\{idfa\}** | IDFA (Identifier for advertising - iOS) | 4C8A85C6-AEEE-425B-9DBA-B02C1A9CAB55 | | **\{andadvid\}** | GAID (Google Advertising ID - Android) | 732503e0-f8df-498b-b9a9-0cbce96394ec | | **\{ip\}** | DEVICE IP (IP address of the device) | 216.58.212.78 | | **\{device\}** | Device (Device model) | iPhone 6 | | **\{mngadsclickid\}** | CLICK ID (Internal unique click ID) | 1446566252X5388...cc4bbe9f50fa | | **\{appid\}** | the application id as it appears in Google play or App store | 543864084 or com.baobab.android.grandmatips | | **\{price\}** | revenue generated by postback for you | 3.10 | | **\{arg1\}** | Custom Value 1 added on clickurl | e.g clicid=\{arg1\} if you need to retrieve your clickid | | **\{arg2\}** | Custom Value 2 added on clickurl | | | **\{arg3\}** | Custom Value 3 added on clickurl | | | **\{arg4\}** | Custom Value 4 added on clickurl | | | **\{arg5\}** | Custom Value 5 added on clickurl | | | **\{arg6\}** | Custom Value 6 added on clickurl | | | **\{arg7\}** | Custom Value 7 added on clickurl | | | **\{arg8\}** | Custom Value 8 added on clickurl | | | **\{arg9\}** | Custom Value 9 added on clickurl | | | **\{arg10\}** | Custom Value 10 added on clickurl | | If you need clicid=\{arg1\} on your postback url, you must add on clickurl from ad-request ```bash http://mobile.mng-ads.com/click/1457424571X5101X2020X525X2737X5754X2330955X42914X8525X0X6X2X8525X0X34X0X2330955X30Hf86ba4d0210f2cc68441065df8a75d53656be378?o[andadvid]=4908b7b8-08aa-4a08-8003-031c2d7f1ae9&arg1={yourclickid} ``` --- ## Appsfire/Madvertise Buyers integration ## Tracking URL placeholders |**Macro** | **Description**| **Example**| | -------- | -------- | -------- | |**\{idfa\}** | IDFA (Identifier for advertising - iOS) |4C8A85C6-AEEE-425B-9DBA-B02C1A9CAB55 | |**\{andadvid\}** | GAID (Google Advertising ID - Android) | 732503e0-f8df-498b-b9a9-0cbce96394ec | |**\{ip\}** | DEVICE IP (IP address of the device) | 216.58.212.78 | |**\{device\}** | Device (Device model)|iPhone 6| |**\{devicebrand\}** | Manufacturer (Device manufacturer name)|Apple| |**\{ccode\}** | Country, Country where the ad was delivered.2-letter ISO standard representation| S for United States or FR for France | |**\{useragent\}** | Device User Agent | Dalvik%2F1.6.0%20(Linux%3B%20U%3B%20Android%204.3%3B%20GT-I9300%20Build%2FJSS15J) | |**\{appid\}** | the application id as it appears in Google play or App store | 543864084 or com.baobab.android.grandmatips | |**\{mngadspublisherid\}** |Publisher (id for the publisher app/site on which the banner is displayed) |3180317| |**\{mngadsappname\}** | App Name (id for the application app/site on which the banner is displayed) |my_app_name| |**\{mngadssubpublisherid\}** | Encoded Sub Publisher (id level 2 for the publisher app/site on which the banner is displayed with encoding format from MAS platform) |5555d24c51959e7c2c6d18668ff45dd9055873b4| |**\{mngadsclearsubpublisherid\}** | Sub Publisher (id level 2 for the publisher app/site on which the banner is displayed from MAS platform) |1111| |**\{mngadssub2\}** | Encoded Sub Publisher (id level 3 for the publisher app/site on which the banner is displayed with encoding format from MAS platform) |4902419422969881429f1b896d36df238f3fe33d| |**\{mngadsclearsub2\}** | Sub Publisher (id level 3 for the publisher app/site on which the banner is displayed from MAS platform) |111| |**\{mngadsclickid\}** | CLICK ID (Internal unique click ID) **more than 100 characters**, if you need a limit string use following \{mngadsclickidsplit1\} and \{mngadsclickidsplit2\} |1446566252X5388...cc4bbe9f50fa| |**\{mngadsclickidsplit1\}** | CLICK ID (Internal unique click ID) **substr of our click ID 0 to 50** |1446566252X5388...cc4bbe9f50fa| |**\{mngadsclickidsplit2\}** | CLICK ID (Internal unique click ID) **substr of our click ID 50 until the end** |1446566252X5388...cc4bbe9f50fa| |**\{mngadsadid\}** | Creative ID (identifier for your banner id associated to the click id)|910| |**\{mngadstransfourl\}** | Our post Back url (HTTP)|http://mobile.mng-ads.com/transfo/...| |**\{mngadssecuretransfourl\}** | Our Secure post Back url (HTTPS)|https://mobile.mng-ads.com/transfo/...| |**\{mngadsleadurl\}** | Our post Back url (HTTP)|http://mobile.mng-ads.com/lead/...| |**\{mngadssecureleadurl\}** | Our Secure post Back url (HTTPS)|https://mobile.mng-ads.com/lead/...| |**\{mngadslandingurl\}** | Our post Back url (HTTP)|http://mobile.mng-ads.com/lead/...| |**\{mngadssecurelandingurl\}** | Our Secure post Back url (HTTPS)|https://mobile.mng-ads.com/lead/...| |**\{mngadssecurecustom1url\}** | Our Secure post Back url (HTTPS)|https://mobile.mng-ads.com/custom1/...| |**\{mngadssecurecustom2url\}** | Our Secure post Back url (HTTPS)|https://mobile.mng-ads.com/custom2/...| |**\{mngadsformatid\}** | placement format [formats-list] where the ad was delivered |1| |**\{mngadsadidcost\}** | cost - amount of cost formatted as float |1.2| |**\{mngadsadidcostmodel\}** | predefined values of three cost models: **cpm** - Cost per 1000 Impressions, **cpc** - Cost per Click, **cpi** - Cost per Install |cpi| |**\{mngadsbundleid\}**| bundleId of publisher's app | com.example.mngadsdemo | |**\{mngadsuserlat\}** | The Users Geo-Location (latitude in degrees WGS84). | 5.98892404| |**\{mngadsuserlon\}** | The Users Geo-Location (longitude in degrees WGS84). | 43.13790177 | ## Conversion Post Back Conversion Post Back Url is over HTTPS or HTTP. ### GET /transfo/\{mngadsclickid\} ```bash https://mobile.mng-ads.com/transfo/{mngadsclickid}?o[idfa]=\{idfa\}&o[ip]=\{ip\}&source=\{trackingSystemName\} ``` | Postback URL parameters | **Required?** | **Format** | **Description** | |-------------------------| -------- | -------- | -------- | | **\{clickid\}** | Yes | string | Internal unique click ID which was sent on the click URL | | **o[idfa]** | No | string | Id(s) of the device / user. iOS | | **o[andadvid]** | No | string | Google Advertising ID | | **o[ip]** | No | string | device IP | | **source** | Yes | string | third party tracking conversion name | ### Example https://mobile.mng-ads.com/transfo/1446566252X5388X910X279X0X9X2367011X1493X0XXX0X5430X0X34X0X2367011X30Hbcd83a8a9bfa98767728cc93ab47cc4bbe9f50fa?o[idfa]=4C8A85C6-AEEE-425B-9DBA-B02C1A9CAB55&o[ip]=216.58.212.78 ### Alternative postback ```bash https://mobile.mng-ads.com/transfo/f2?mngadsclickid={mngadsclickid}&o[idfa]={idfa}&o[ip]={ip}&source={trackingSystemName} ``` ## Others Post Back Events ### GET /Landing/\{mngadsclickid\} Landing service ```bash curl https://mobile.mng-ads.com/landing/{mngadsclickid} ``` ### GET /lead/\{mngadsclickid\} Lead service ```bash curl https://mobile.mng-ads.com/lead/{mngadsclickid} ``` ### GET /custom1/\{mngadsclickid\} Custom service You can use custom events. 10 events are available. custom1 to custom10 ```bash curl https://mobile.mng-ads.com/custom1/{mngadsclickid} ``` ## Formats List ```bash #!mysql +-------+---------------------+ | 1 | interstitial | | 2 | banner | | 3 | nativead | | 4 | interstitialOverlay | | 5 | square | +-------+---------------------+ ``` [formats-list]:./04-appsfire-advertiser-integration.md#formats-list --- ## Direct clickUrl The API accepts simple GET OR POST requests, with optional and required parameters in the query string portion of the URL. The parameters must be encoded as name/value pairs using the standard HTTP URL-encoding principles. ## Response format The response format for all requests is a JSON object. Whether a request succeeded is indicated by the HTTP status code. A 2xx status code indicates success, whereas a 4xx status code indicates failure. ## Ad-Request URL All API access is over HTTPS or HTTP, you'll need to issue a HTTP GET or POST Request to the following URL: ```bash http://mobile.mng-ads.com/?[requiredandoptionalkeyvaluepairs] ``` ### Ad-Request Parameters | **Parameter name** | **Required?** | **Format** | **Description** | |-----------------------------| -------- | -------- | -------- | | **rt** | Yes | string, **api_mediation** or **appsfire-v2-api** |**appsfire-v2-api** for nativead only (app install) | | **u** | Yes | string, URL-encoded User Agent | Pass the url-encoded User Agent of the requesting device in this parameter. e.g. &u=Mozilla%2F5.0+%28iPhone%3B+U%3B+C. | | **s** | Yes | string | Zone code. This parameter should be the unique Publisher ID of your mobile application or website. | | **i** | **Yes for rt=api , rt=appsfire-v2-api, api_mediation only** | string | use **api_mediation** for S2S mediation, **api** for use Madvertise adserving only or **appsfire-v2-api** in order to use appsfire only | | **v** | Yes | string | Version of the client sdk. For logging purpose in the web server logs | | **c_mraid** | No | 0/1 | Specify whether your mobile site / app is able to show MRAID ad responses. | | **c_vast** | No | 0/2 | Specify whether your mobile site / app is able to show VAST2 or VAST3 ad responses. API can return vastinline with c_vast=2 and only external url for c_vast=1 | | **o[idfa]** | No | string | Id(s) of the device / user. iOS | | **o[andadvid]** | No | string | Google Advertising ID | | **o[ip]** | No | string | alias of **i** parameter | | **lat** | Yes, if available | Decimal | The Users Geo-Location (latitude in degrees WGS84). | | **lon** | Yes, if available | Decimal | The Users Geo-Location (longitude in degrees WGS84). | | **connection_type** | No | string | Connection type. One of those values UNKNOWN, WIFI, 3G, 4G | | **seenad[adid1]** |No |Integer for key, Timestamp for value|Use for capping, this array contains ads seen with associated timestamp. `&seenad[adid1]=timestamp1` `&seenad[adid2]=timestamp2` Each **adid** is available response of Ad request | | **gender** | No | string, M/F | Gender of the user. M for male, F for female. | | **age** | No | Integer | The user's age, if available| | **zip** | No | String | The user's zip code, if available| | **jsvar** | Yes if rt=javascript | String | The name of javascript variable, Ad is returned in this variable in order to inject the code on publisher mobile site.| | **w** | No | integer |DThe Width of your Ad Space. This is also the maximum banner size that our servers will return. **Useless for appsfire nativead**| | **h** | No | integer|The Height of your Ad Space. This is also the maximum banner size that our servers will return. **Useless for appsfire nativead**| | **carrier** |No| String |Carrier name of mobile end-user| | **appName** | No | string | Name of app where SDK in used | | **bundleId** | No | string | packageName for android and ituneId for IOS | | **locale** | No | string | language code used on phone (e.g fr) | | **accept_retargeting** | Yes for rt=android_app_json or ios_app_json | int | default value is **1**, it must be set to **0** if the user has limited ad tracking ( [Google AdvertisingIdClient](https://developers.google.com/android/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient.Info)) or [IOS advertisingTrackingEnabled](https://developer.apple.com/library/ios/documentation/AdSupport/Reference/ASIdentifierManager_Ref/#//apple_ref/occ/instp/ASIdentifierManager/advertisingTrackingEnabled)| | **osVersion** | No | string | version of OS e.g 9.1 | | **tgt** | No | urlencoded string | Keyword targeting allows you to display ads only when specific keywords or key/value pairs are passed in the ad request e.g adobeSegmentId%3D1%3Bcat%3Ddemo for adobeSegmentId=1;cat=demo| | **c_video** | No | 0/1 | Specify whether your mobile site / app is able to show video | | **gdpr** | yes | 0/1 | 0: not in GDPR scope or 1: in GDPR scope | | **consent\[0\]\[format\]=** | yes | IAB | We manage IAB consentString only | | **consent\[0\]\[value\]** | yes | BONlRnIONlRnIAAABAENAAAAAAAAoAA | Encoded consent String IAB spec| ## Interstitials ```bash curl http://mobile.mng-ads.com?rt=android_app&v=6.0.0&u=Dalvik%2F1.6.0%20(Linux%3B%20U%3B%20Android%204.3%3B%20GT-I9300%20Build%2FJSS15J)&s=[YOUR_PLACEMENT_ID]&o%5Bandadvid%5D=732503e0-f8df-498b-b9a9-0cbce96394ec&long=5.9889615&lat=43.1377518&age=25&gender=M&c.mraid=1 ``` ### Interstitial Image Ad ```json showLineNumbers { "type": "interstitial", "format": "image", "mraid": false, "vast": false, "content": "", "contentUrl": "http://cdn.mng-ads.com/25b3b31f81909a7584866fbd9f7c7434309.jpg", "clickurl": "http://mobile.mng-ads.com/click/1455397457X56X169X10X309X15X5180317X36X8525X0X2X2X8525X0X1X0X5180317X30Hb2e601d82d4f19a1bdfb2863702fe7063e106f17?o[andadvid]=41c7fa2a-e967-46fd-9520-9869d4a10c9d", "impurl": [ "http://mobile.mng-ads.com/display/1455397457X56X169X10X309X15X5180317X36X8525X0X2X2X8525X0X1X0X5180317X30Hb2e601d82d4f19a1bdfb2863702fe7063e106f17" ], "impscript": [ " " ], "refresh": "0", "clicktype": "inapp", "preload": 0, "autoclose": 0, "adid": "169", "tagline": null, "background": "#FFFFFF", "adspaceWidth": "768", "adspaceHeight": "1024", "orientation": "portrait", "closePosition": "top-right", "closeAppearanceDelay": "0", "duration": "0", "animation": "none" } ``` ### Interstitial Html Ad ```json showLineNumbers { "type": "interstitial", "format": "html", "mraid": false, "vast": false, "content": "
\r\n\r\n \r\n Demo\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n
MacroDescription
mngads:clickour click url
mngads:deviceidId(s) of the device / user. iOS or Google Advertising ID
mngads:userlatThe Users Geo-Location (latitude in degrees WGS84).
mngads:userlonThe Users Geo-Location (longitude in degrees WGS84).
mngads:adidCreative ID (identifier for your banner id associated to the click id)
mngads:localeanguage code used on phone (e.g fr)
mngads:osOS of user
mngads:osVersionversion of OS e.g 9.1
mngads:bundleIdpackageName for android and ituneId for IOS
\r\n \r\n \r\n
", "contentUrl": "", "clickurl": "http://mobile.mng-ads.com/click/1455397844X56X250X10X470X15X5180317X36X8525X0X2X2X8525X0X1X0X5180317X30H0fd023b2cda14df07f97a2c4f13a5ead6377099e?o[andadvid]=41c7fa2a-e967-46fd-9520-9869d4a10c9d", "impurl": [ "http://mobile.mng-ads.com/display/1455397844X56X250X10X470X15X5180317X36X8525X0X2X2X8525X0X1X0X5180317X30H0fd023b2cda14df07f97a2c4f13a5ead6377099e" ], "impscript": [ " " ], "refresh": "0", "clicktype": "inapp", "preload": 0, "autoclose": 0, "adid": "250", "tagline": null, "background": "#FFFFFF", "adspaceWidth": "0", "adspaceHeight": "0", "orientation": "portrait", "closePosition": "top-right", "closeAppearanceDelay": "0", "duration": "0", "animation": "none" } ``` ### Interstitial VAST video For inline VAST contentUrl is empty and xml VAST available on **content** ```json showLineNumbers { type: "interstitial", format: "vast", mraid: false, content: "", contentUrl: "http://ads.stickyadstv.com/www/delivery/swfIndex.php?reqType=AdsSetup&protocolVersion=2.0&zoneId=206813", clickurl: "https://mobile.mng-ads.com/click/...?o%5Bidfa%5D=2CF0B5BB-59AC-4232-8A43-80E050E8B2AF&gender=M&carrier=Free&model=iPhone+6+Plus", impurl: [ "https://mobile.mng-ads.com/display/....?o%5Bidfa%5D=2CF0B5BB-59AC-4232-8A43-80E050E8B2AF&gender=M&carrier=Free&model=iPhone+6+Plus" ], "impscript": [ ], refresh: "0", clicktype: "inapp", preload: 0, autoclose: 0, adid: "4293", publisherid: "3180317", tagline: null, background: "#FFFFFF", adspaceWidth: "0", adspaceHeight: "0", "adchoiceposition": "top-right", "orientation": "portrait", "closePosition": "top-right", "closeAppearanceDelay": "0", "duration": "0", "videosettings": { "autoplay": 1, "audio": "muted", "blur": 1, "radius": "15", "opacity": "0" } } ``` ## Banners ```bash curl http://mobile.mng-ads.com?rt=android_app&v=6.0.0&u=Dalvik%2F1.6.0%20(Linux%3B%20U%3B%20Android%204.3%3B%20GT-I9300%20Build%2FJSS15J)&s=[YOUR_PLACEMENT_ID]&o%5Bandadvid%5D=732503e0-f8df-498b-b9a9-0cbce96394ec&long=5.9889615&lat=43.1377518&age=25&gender=M&c.mraid=1&x=360&y=50 ``` ### Banner Image Ad ```json showLineNumbers { "type": "banner", "format": "image", "mraid": false, "vast": false, "content": "", "contentUrl": "http://cdn.mng-ads.com/10/1308-1858.jpg", "clickurl": "http://mobile.mng-ads.com/click/1455398033X56X1308X10X1858X15X5180317X35X8525X0X2X2X8525X0X1X0X5180317X30H8a28a98565c212c2fe6b4f609b575a032486f651?o[andadvid]=41c7fa2a-e967-46fd-9520-9869d4a10c9d", "impurl": [ "http://mobile.mng-ads.com/display/1455398033X56X1308X10X1858X15X5180317X35X8525X0X2X2X8525X0X1X0X5180317X30H8a28a98565c212c2fe6b4f609b575a032486f651", "http://www.mobilenetworkgroup.com/test" ], "impscript": [ " " ], "refresh": "0", "clicktype": "inapp", "preload": 0, "autoclose": 0, "adid": "1308", "tagline": null, "background": "#FFFFFF", "adspaceWidth": "320", "adspaceHeight": "50" } ``` ### Banner Html Ad ```json showLineNumbers { "type": "banner", "format": "html", "mraid": false, "vast": false, "content": "
", "contentUrl": "", "clickurl": "http://mobile.mng-ads.com/click/1455397972X56X1959X10X2672X15X5180317X35X8525X0X2X2X8525X0X1X0X5180317X30H5fb7db83f5f905eb5e73e2319d9b343208317c2c?o[andadvid]=41c7fa2a-e967-46fd-9520-9869d4a10c9d", "impurl": [ "http://mobile.mng-ads.com/display/1455397972X56X1959X10X2672X15X5180317X35X8525X0X2X2X8525X0X1X0X5180317X30H5fb7db83f5f905eb5e73e2319d9b343208317c2c" ], "refresh": "0", "clicktype": "inapp", "preload": 0, "autoclose": 0, "adid": "1959", "tagline": null, "background": "#FFFFFF", "adspaceWidth": "0", "adspaceHeight": "0" } ``` ## appsfire NativeAd ![4193248577-af.png](img/appsfire.png) ```bash curl http://mobile.mng-ads.com/?rt=appsfire-v2-api&i=78.232.65.223&u=Mozilla%2F5.0%20(Linux%3B%20U%3B%20Android%205.0.2%3B%20fr-fr%3B%20XT1072%20Build%2FLXB22.99-24.12)%20AppleWebKit%2F533.1%20(KHTML%2C%20like%20Gecko)%20Version%2F4.0%20Mobile%20Safari%2F533.1&s=[YOUR_PLACEMENT_ID]&v=6.0.0&c_mraid=1&o%5Bandadvid%5D=4908b7b8-08aa-4a08-8003-031c2d7f1ae9&accept_retargeting=0&connection_type=WIFI&long=5.9888646&lat=43.1378641&macAddress=e4%3A90%3A7e%3A18%3A01%3Adb&w=360&h=592&carrier=&appName=MNGAdsServer&bundleId=com.mngads.mngadsserver&locale=fr&osVersion=5.0.2 ``` ```json showLineNumbers { "type": "nativeAd", "title": "Star Wars\u2122: Galaxy of Heroes", "description": "May the force be with you!", "categoryid": "JEUX DE R\u00f4LES", "category": "Jeux de r\u00f4les", "iconurl": "https:\/\/lh3.googleusercontent.com\/64RrPLGYj_V9e9Ku8bEemPY_cMEhdKpYkUBA49XczVAQBl1-6B8nEf-4paL2EPXQpbI", "iconsurl": [ ], "bundleid": "com.ea.game.starwarscapital_row", "clickurl": "http:\/\/mobile.mng-ads.com\/click\/1453475776X56X1819X287X2504X15X5180317X1912X8525X0X6X2X8525X0X1X0X5180317X30H82c262207653dc0643fa3cb475daca48d9a75b8c?o[andadvid]=4908b7b8-08aa-4a08-8003-031c2d7f1ae9&pfid={mngadspfid}", "impurl": [ "http:\/\/mobile.mng-ads.com\/display\/1453475776X56X1819X287X2504X15X5180317X1912X8525X0X6X2X8525X0X1X0X5180317X30H82c262207653dc0643fa3cb475daca48d9a75b8c" ], "refresh": "0", "autoclose": 0, "price": "0", "adid": "1819", "publisherid": "3180317", "autoplay": 0, "screenshotUrls": [ "https:\/\/lh3.googleusercontent.com\/6vhbqD1-dM9Q4mazbpdiFjgLGgs-YkxkCCOC7XbnPhUFhIsqJpcN68MGGracVpdmgQA=h256-rw", "https:\/\/lh3.googleusercontent.com\/nUnXJUQNuilIXKnZNxrfm4abk5TVzdATASqKBtC-h2tkljULjwT_us_JuY-3Td66UIku=h256-rw" ], "videoUrls": [ "https://creative.mng-ads.com/media/viking.mp4" ], "closePosition": "top-left", "closeAppearanceDelay": "0", "duration": "0", "userRatingCount": "627208", "averageUserRating": 4.5885605812073, "contentRating": "PEGI\u00a012" } ``` ## No ad available When there is no ad available for your ad-request, the following JSON is returned: ```json showLineNumbers { error: "No available ad" } ``` ## Appsfire direct clickurl We can provide an appsfire clickurl. This url is used to run a specific appsfire campaign without ad-request API call. This url can be used as : | **Parameter name** | **Required?** | **Format** | **Description** | | -------- | -------- | -------- |----------------------------------------------------------------------------| | **s** | Yes | string | Zone code. This parameter should be the unique Publisher ID of your | | **adid** | Yes | int | Adunit ID. This parameter is provided by mng/appsfire team per campaign | | **o[idfa]** | No | string | Id(s) of the device / user. iOS | | **o[andadvid]** | No | string | Google Advertising ID | |**arg1** | Custom Value 1 |string | e.g clicid=\{arg1\} if you need to retrieve your clickid | |**arg2** | Custom Value 2 | string | | |**arg3** | Custom Value 3 | string | in most case we use to store your publisherId (sub publisherId for us) | |**arg4** | Custom Value 4 | string | in most case we use to store your publisherId (sub sub publisherId for us) | |**arg5** | Custom Value 5 | string | | |**arg6** | Custom Value 6 | string | | |**arg7** | Custom Value 7 | string | | |**arg8** | Custom Value 8 | string | | |**arg9** | Custom Value 9 | string | | |**arg10** | Custom Value 10 | string | | ```bash https://mobile.mng-ads.com/appsfireclicks/?adid=1&s=/3180317/interstitial/af&o[andadvid]=4908b7b8-&arg1={YOUR_CLICKID}&arg2={YOUR_CUSTOM_VALUE}&arg3={YOUR_PUBLISHERID}&arg4={YOUR_CUSTOM_VALUE} ``` ## Appsfire impression url Impression pixel redirect URL for S2S connected sources that contains parameters similar to appsfireclicks ```bash https://mobile.mng-ads.com/performancedisplay/?adid=1&s=/3180317/interstitial/af&o[andadvid]=4908b7b8-&arg1={YOUR_CLICKID}&arg2={YOUR_CUSTOM_VALUE}&arg3={YOUR_PUBLISHERID}&arg4={YOUR_CUSTOM_VALUE} ``` ## Postback Settings If you want to be notified when conversion occurs fill the URL of your server that our system should call. ```bash http://myserver.com/postback/?clicid={arg1}&campaign={arg2}&revenu={price} ``` Available params for postback url are: |**Macro** | **Description**| **Example**| | -------- | -------- | -------- | |**\{unixtimestamp\}** | Number of seconds since the epoch |1457427080 | |**\{idfa\}** | IDFA (Identifier for advertising - iOS) |4C8A85C6-AEEE-425B-9DBA-B02C1A9CAB55 | |**\{andadvid\}** | GAID (Google Advertising ID - Android) | 732503e0-f8df-498b-b9a9-0cbce96394ec | |**\{ip\}** | DEVICE IP (IP address of the device) | 216.58.212.78 | |**\{device\}** | Device (Device model)|iPhone 6| |**\{mngadsclickid\}** | CLICK ID (Internal unique click ID) |1446566252X5388...cc4bbe9f50fa| |**\{appid\}** | the application id as it appears in Google play or App store | 543864084 or com.baobab.android.grandmatips | |**\{price\}** | revenue generated by postback for you | 3.10 | |**\{arg1\}** | Custom Value 1 added on clickurl |e.g clicid=\{arg1\} if you need to retrieve your clickid | |**\{arg2\}** | Custom Value 2 added on clickurl | | |**\{arg3\}** | Custom Value 3 added on clickurl | | |**\{arg4\}** | Custom Value 4 added on clickurl | | |**\{arg5\}** | Custom Value 5 added on clickurl | | |**\{arg6\}** | Custom Value 6 added on clickurl | | |**\{arg7\}** | Custom Value 7 added on clickurl | | |**\{arg8\}** | Custom Value 8 added on clickurl | | |**\{arg9\}** | Custom Value 9 added on clickurl | | |**\{arg10\}** | Custom Value 10 added on clickurl | | If you need clicid=\{arg1\} on your postback url, you must add on clickurl from ad-request ```bash http://mobile.mng-ads.com/click/1457424571X5101X2020X525X2737X5754X2330955X42914X8525X0X6X2X8525X0X34X0X2330955X30Hf86ba4d0210f2cc68441065df8a75d53656be378?o[andadvid]=4908b7b8-08aa-4a08-8003-031c2d7f1ae9&arg1=\{yourclickid\} ``` --- ## Madvertise Ad Exchanges * **We are compliant openRTB 2.3.1 up 2.5.** * **Our data-center is located in Europe (France), therefore data are stored on EU for GDPR and we are calling DSP from EU.** * **Bid TTL** = 300ms * **Bid Auction Strategy**: 2st auction or first price * **We support Content-Encoding:gzip** * **We support HTTP and HTTPS** * **the expiration time of a billed impression is 30 minutes** ## Inventory * Platforms available, we support inApp (ios, android), webMobile * a list of our publishers and sales collerals can be found here: * madvertise Germany, madvertise Italy, madvertise France * Formats available, we support banner, medium rectangle, interstitial, nativeAd * For video we are VAST 2, 3 and 4 and VPAID 2 compliant * We support MRAID 2. * Countries available France, Germany and Italy. ## bidRequest * Data Center on France (Paris Iliad). Allow our IPs range 185.60.93.192/27 and 91.121.133.86 * QPS peak is 3600 * We support EUR and USD price (encrypt or not) * bidRequest sample ```json showLineNumbers { "id": "A518EF01-5CE0-3F8D-7664-AA0AA08B35B7", "tmax": 500, "cur": [ "USD" ], "regs": { "ext": { "gdpr": 0 } }, "app": { "id": "madvertise_1829187", "bundle": "308333134", "name": "FR_NewsWeb_Europ1_Sports_App_iPhone_madvertise", "storeurl": "http://itunes.apple.com/fr/app/id308333134?mt=8", "publisher": { "id": "madvertise_34", "name": "Newsweb" }, "cat": [ "IAB1", "IAB9", "IAB12", "IAB17" ] }, "device": { "ip": "91.xx.66.xx", "ua": "iPhone6,2 10.3.3", "dnt": 0, "lmt": 0, "ifa": "0640224D-40AD-4134-9DC9-D31771987xxxx", "make": "Apple", "model": "iPhone 5S", "os": "iOS", "osv": "10.3.3", "devicetype": 4, "connectiontype": 2, "js": 1, "carrier": "SFR", "geo": { "type": 2, "country": "FRA", "region": "NAQ", "city": "Villedoux", "zip": "17230" } }, "imp": [ { "id": "1", "secure": 1, "tagid": "33290", "bidfloor": 0.577337, "bidfloorcur": "USD", "instl": 0, "banner": { "id": "banner", "pos": 5, "w": 320, "h": 50, "api": [ 5, 1001 ] } } ], "user": { "buyeruid": "buyeruid" }, "is_secure": true } ``` ## Model * We support Second Price Plus Auction Type * We support Deals with 2 = Second Price Plus or 3 = the value passed in bidfloor is the agreed upon deal price. ## bidResponse * We support burl or nurl or pixel on creative for win notification * We support $\{AUCTION_PRICE\} encrypted or not. * Winning price encryption and decrpytion requires two secret, but shared, keys. * An integrity key, and encryption key, referred to as i_key, and e_key respectively. * Both keys are provided at account setup as web-safe base64 strings * We support $\{AUCTION_BID_ID\}, $\{AUCTION_CURRENCY\} * We support webview ## Reporting / Discrepancy * We can provide an access to our reporting API or upload result on your server * We support hourly breakdown and timezone * earnings in USD or EUR ## GDPR We support IAB consentString, we can add consentString on bidRequest (OpenRTB Advisory - GDPR) : ```json "user":{"ext":{"gdpr":1,"consent":"BOUzNm9OUzIv1AsAFBFRBqyAAAAXAAMARAiKQoAAgNAAQAABACIAAAAAAAAAAQgAQAYAAEAiAAAAAAAAAAAAAAAAAAA"}} ``` ## Deal Object Example ```json showLineNumbers "pmp": { "private_auction": 1, "deals": [ { "id": "2210415499731737064", "wseat": [ "16", "165" ], "bidfloor": 0.7, "bidfloorcur": "USD", "at": 3 } ] } ``` ## Video Object Example ```json showLineNumbers { "id": "BEB67BBD-EE88-C17E-5BA4-0C2727BAEDC8", "tmax": 500, "cur": [ "USD" ], "regs": { "ext": { "gdpr": 1 } }, "user": { "keywords": "inall=10;inall_hr=10;", "ext": { "consent": "BOaXNJWOaXNOGAHABAktB5-AAAAid7_______9______9uz_Gv_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur_959__3z3_EA" } }, "app": { "id": "2813727", "bundle": "fr.airweb.ladepeche", "name": "FR_LaDepecheInteractive_LaDepeche_App_Android_Madvertise", "storeurl": "https://play.google.com/store/apps/details?id=fr.airweb.ladepeche", "publisher": { "id": "6928", "name": "FR_LaDepecheInteractive", "ext": { "madvertise": { "placement_id": "36016" }{"id":"BEB67BBD-EE88-C17E-5BA4-0C2727BAEDC8","tmax":500,"cur":["USD"],"regs":{"ext":{"gdpr":1}},"user":{"keywords":"inall=10;inall_hr=10;","ext":{"consent":"BOaXNJWOaXNOGAHABAktB5-AAAAid7_______9______9uz_Gv_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur_959__3z3_EA"}},"app":{"id":"2813727","bundle":"fr.airweb.ladepeche","name":"FR_LaDepecheInteractive_LaDepeche_App_Android_Madvertise","storeurl":"https:\/\/play.google.com\/store\/apps\/details?id=fr.airweb.ladepeche","publisher":{"id":"6928","name":"FR_LaDepecheInteractive","ext":{"madvertise":{"placement_id":"36016"}}},"cat":["IAB1","IAB3","IAB9","IAB12"]},"device":{"ip":"2.6.144.35","ua":"Mozilla\/5.0 (Linux; U; Android 8.1.0; fr-fr; SNE-LX1 Build\/HUAWEISNE-LX1) AppleWebKit\/533.1 (KHTML, like Gecko) Version\/4.0 Mobile Safari\/533.1","dnt":0,"lmt":0,"ifa":"9132e9e8-269a-4dde-b43a-3be9c5a0b642","make":"Huawei","model":"SNE-LX1","os":"Android","osv":"8.1","devicetype":4,"connectiontype":2,"js":1,"carrier":"Orange F","geo":{"lat":43.7181593,"lon":1.4345561,"type":1,"country":"FRA","region":"OCC","city":"Aussonne","zip":"31840"}},"imp":[{"id":"1550660184X56X10993X1982X16600X6928X4134X50703X8526X48X5X2X11071623X0X1X0X2813727X3017382X3013767X3035985X0X0H3dc45306ad14f418fe09335cf7fc0a9ea41d0a56","secure":1,"tagid":"36016","bidfloor":5.667165,"bidfloorcur":"USD","video":{"mimes":["video\/mp4","video\/3gpp","application\/javascript"],"w":320,"h":480,"minduration":2,"maxduration":30,"playbackmethod":[1,3],"boxingallowed":0,"protocols":[1,2,3,4,5,6],"placement":5}}]} } }, "cat": [ "IAB1", "IAB3", "IAB9", "IAB12" ] }, "device": { "ip": "2.6.144.35", "ua": "Mozilla/5.0 (Linux; U; Android 8.1.0; fr-fr; SNE-LX1 Build/HUAWEISNE-LX1) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1", "dnt": 0, "lmt": 0, "ifa": "9132e9e8-269a-4dde-b43a-3be9c5a0b642", "make": "Huawei", "model": "SNE-LX1", "os": "Android", "osv": "8.1", "devicetype": 4, "connectiontype": 2, "js": 1, "carrier": "Orange F", "geo": { "lat": 43.7181593, "lon": 1.4345561, "type": 1, "country": "FRA", "region": "OCC", "city": "Aussonne", "zip": "31840" } }, "imp": [ { "id": "1550660184X56X10993X1982X16600X6928X4134X50703X8526X48X5X2X11071623X0X1X0X2813727X3017382X3013767X3035985X0X0H3dc45306ad14f418fe09335cf7fc0a9ea41d0a56", "secure": 1, "tagid": "36016", "bidfloor": 5.667165, "bidfloorcur": "USD", "video": { "mimes": [ "video/mp4", "video/3gpp", "application/javascript" ], "w": 320, "h": 480, "minduration": 2, "maxduration": 30, "playbackmethod": [ 1, 3 ], "boxingallowed": 0, "protocols": [ 1, 2, 3, 4, 5, 6 ], "placement": 5 } } ] } ``` --- ## Direct VAST We can provide a BlueStack [VAST-compliant] Url. | **Parameter name** | **Required?** | **Format** | **Description** | |-----------------------------| -------- | -------- | -------- | | **s** | Yes | string | Zone code. This parameter should be the unique Publisher ID of your | | **o[idfa]** | No | string | Id(s) of the device / user. iOS | | **o[andadvid]** | No | string | Google Advertising ID | | **lat** | Yes, if available | Decimal | The Users Geo-Location (latitude in degrees WGS84). | | **lon** | Yes, if available | Decimal | The Users Geo-Location (longitude in degrees WGS84). | | **bundleId** | No | string | packageName for android and ituneId for IOS | | **locale** | No | string | language code used on phone (e.g fr) | | **gdpr** | yes | 0/1 | 0: not in GDPR scope or 1: in GDPR scope | | **consent\[0\]\[format\]=** | yes | IAB | IAB TCF v1 or TCF v2 | | **consent\[0\]\[value\]** | yes | BONlRnIONlRnIAAABAENAAAAAAAAoAA | Encoded consent String IAB spec| ```bash https://mobile.mng-ads.com/?rt=api_mediation_vast&c_vast=2&u=iPhone10%2C6%2011.2.6&s=/3509641/vast&v=v3.2.0&o%5Bidfa%5D=01FE2862-4426-42A1-A8B5-065E99BA6455&lat=43.1377887&long=5.9890197&connection_type=WIFI&tgt=&carrier=Free&bundleId=com.mng.ads&locale=fr-FR&gdpr=1&consent[0][format]=IAB&consent[0][value]=COw7M3mOw7M3mGyAAAENAdCAAAAAAAAAAAAAAAAAAAAA.IF0EWSQgCYWgho0QUBzBAIYAfJgSCAMgSAAQIoSkFQISERBAGOiAQHAEQJAAAGBAAkACAAQAoHGBMCQABgAARiRCEQUGIDzNIBIBAggEaYUFAAAVmmkHC3ZCY702yumQ ``` [VAST-compliant]:https://www.iab.com/guidelines/digital-video-ad-serving-template-vast/ --- ## Ad Serving | **BlueStack API** | |--------------------------------------------------------------------------------------------------------------| | [OpenRTB bidRequest API] | | [Prebid Server Adapter] | | [Ad Request API] (server to server integration) | | ![appsfire_small.png](./img/appsfire_small.png) [Buyer Integration] (Install Post Back, Others Post Back Events) | | ![appsfire_small.png](./img/appsfire_small.png) [Direct clickUrl] | | [MadvertiseAdExchanges] (SSP) | | [Direct VAST] | [OpenRTB bidRequest API]:/adserving/open-rtb-bid-request-api/ [Prebid Server Adapter]:/adserving/prebid-server-adapter/ [Ad Request API]:/adserving/ad-request-api-documentation/ [Buyer Integration]:/adserving/appsfire-advertiser-integration/ [Direct clickUrl]:/adserving/appsfire-direct-click-url/ [MadvertiseAdExchanges]:/adserving/madvertise-ad-exchanges/ [Direct VAST]:/adserving/vast/ --- ## BlueStackSDK v4.4.0 for Android ## More Flexibility of Choosing Between Third-party Network Adapters We're thrilled to announce the latest release of our BlueStack SDK, version 4.4.0! This update brings a modular architecture to our ad mediation framework, making it even more flexible and developer-friendly. With a core SDK and individual adapter SDKs for supported ad networks, BlueStackSDK v4.4.0 gives you the option to choose and integrate only what you need, making the ad integration process easier than before. {/* truncate */} ![BlueStackSDK 4.4.0](./bluesatck_android-4_4_0.png#gh-light-mode-only)![BlueStackSDK 4.4.0](./bluesatck_android-4_4_0-dark.png#gh-dark-mode-only) ## What's new? ### 1. Modularization for Enhanced Flexibility In response to developer feedback, we've modularized BlueStack SDK into following components: - BlueStackSDK Core: This core sdk provides public APIs for showing Ads and all the essentials for waterfall logic. - Third-party Ad Network Adapters: Individual SDKs for each supported ad network that handles integration specifics for that network. This modular approach allows for easier updates, maintenance, and customization. ### 2. Adapter Architecture for Simplicity BlueStack adapter architecture simplifies the integration process. Developers can now integrate specific ad network adapters based on their requirements. This also separates the third party ad network implementation from the core, which improves the performance of the BlueStack SDK. ### 3. Improved Flexibility With BlueStackSDK v4.4.0, now you have the power to choose. Integrate only the ad network adapters you need, avoiding unnecessary dependencies. This flexibility gives significant advantage, especially for apps where app size and performance is crucial. ## Why upgrading to v4.4.0 ### 1.Reduced App Size By allowing developers to selectively integrate only the specific ad network adapters they need, unnecessary dependencies are avoided. This will make the app more lightweight. ### 2.Future-Proof Scalability Adding or removing adapters is now a breeze! By updating your app's `build.gradle` dependencies you can easily achieve that. ## Supported Third-party Network Adapters BlueStack supports third-party ad network adapters, which are accessible through the Maven Central Repository. These adapters are published under the group ID `com.azerion`. All adapters use a four-number versioning scheme. The initial three numbers signify the BlueStack core SDK version, while the final number denotes the adapter release. For instance, the first release of the Google Mobile Ads (GMA) adapter, built on BlueStack core `4.4.0`, would be labeled as `4.4.0.0`. Below is a list of BlueStack-supported third-party network adapters, each accompanied by its corresponding Maven artifact ID. Please reach out to our Publishing team when you are in doubt as too inclusion of which Adapter is best for you. | Ad networks | Maven Artifact Id | |-------------------------| ------------- | | GAM / AdMob | bluestack-mediation-gma | | Smart Display / Equativ | bluestack-mediation-smartadserver | | Criteo | bluestack-mediation-criteo | | In-app Bidding | bluestack-mediation-bidding | | Ogury | bluestack-mediation-ogury | | AdColony | bluestack-mediation-adcolony | | huawei | bluestack-mediation-huawei | **Note:** In-App-Bidding has a default dependency with Amazon and Smart Display SDK. So adding In-App-Bidding will automatically include these three SDKs. ## Integrate BlueStackSDK v4.4.0 **Integrate BlueStackSDK Core:** Add BlueStackSDK dependency in your app's `build.gradle` ```groovy dependencies { implementation 'com.azerion:bluestack-sdk-core:4.4.0' } ``` **Integrate Required Adapters:** Choose your required adapters from the above list and add the dependency in your app's `build.gradle` ```groovy dependencies { ... implementation 'com.azerion:bluestack-mediation-gma:4.4.0.0' implementation 'com.azerion:bluestack-mediation-bidding:4.4.0.0' ... } ``` --- ## BlueStackSDK v4.4.0 for iOS ## More Flexibility of Choosing Between Third-party Network Adapters We're thrilled to announce the latest release of our BlueStack SDK, version 4.4.0! This update brings a modular architecture to our ad mediation framework, making it even more flexible and developer-friendly. With a core SDK and individual adapter SDKs for supported ad networks, B lueStackSDK v4.4.0 gives you the option to choose and integrate only what you need, making the ad integration process easier than before. {/* truncate */} Previously this set-up was only supported via SPM, now we are also providing support for this flexibility using [Cocoapods]. ![BlueStackSDK 4.4.0](./BlueStackSDK-4_4_0.light.png#gh-light-mode-only)![BlueStackSDK 4.4.0](./BlueStackSDK-4_4_0.dark.png#gh-dark-mode-only) ## What's new? ### 1. Modularization for Enhanced Flexibility In response to developer feedback, we've modularized BlueStack SDK into following components: - BlueStackSDK Core: This core sdk provides public APIs for showing Ads and all the essentials for waterfall logic. - Third-party Ad Network Adapters: Individual SDKs for each supported ad network that handles integration specifics for that network. This modular approach allows for easier updates, maintenance, and customization. ### 2. Adapter Architecture for Simplicity BlueStack adapter architecture simplifies the integration process. Developers can now integrate specific ad network adapters based on their requirements. This also separates the third party ad network implementation from the core, which improves the performance of the BlueStack SDK. ### 3. Improved Flexibility With BlueStackSDK v4.4.0, now you have the power to choose. Integrate only the ad network adapters you need, avoiding unnecessary dependencies. This flexibility gives significant advantage, especially for apps where app size and performance is crucial. ## Why upgrading to v4.4.0 ### 1.Reduced App Size By allowing developers to selectively integrate only the specific ad network adapters they need, unnecessary dependencies are avoided. This will make the app more lightweight. ### 2.Future-Proof Scalability Adding or removing adapters is now a breeze! By updating the podfile or selecting the dependencies using SPM you can easily achieve that. ## Supported Third-party Network Adapters Below are the BlueStack supported third-party network adapters along with their sub-specs name for integrating using [Cocoapods] Please reach out to our Publishing team when you are in doubt as too inclusion of which Adapter is best for you. | Ad networks | Subspecs | |-------------------------| ------------- | | GAM / AdMob | Google-Mobile-Ads-SDK | | Smart Display / Equativ | Smart-Display-SDK | | Criteo | CriteoPublisherSdk | | Amazon | AmazonPublisherServicesSDK | | In-app Bidding | In-App-Bidding | | ImproveDigital | ImproveDigital | | Ogury | OguryAds | | Madvertise Location | MAdvertiseLocation | **Note:** In-App-Bidding has a default dependency with Criteo, Amazon and Smart Display SDK. So adding In-App-Bidding will automatically include these three SDKs. ## Integrate BlueStackSDK v4.4.0 ### Using Cocoapods **Integrate BlueStackSDK Core:** Add BlueStackSDK to your podfile ```ruby pod 'BlueStackSDK', '4.4.0' ``` **Integrate Required Adapters:** Choose your required adapters from the above list and add the subsepc to your pod file like below ```ruby pod 'BlueStackSDK', '4.4.0', :subspecs=>['Google-Mobile-Ads-SDK', 'In-App-Bidding'] ``` ### Using SPM You will find integration process and requirements through SPM [here](/ios#using-swift-package-manager) [Cocoapods](https://guides.cocoapods.org/using/getting-started.html). --- ## Introducing BlueStack SDK version 5 We’re thrilled to announce the launch of version 5 of the BlueStack Ads SDK! This update brings significant improvements to the SDK's public API for **Interstitial**, **Rewarded Video**, and **Banner** ad formats. These changes are designed to give developers more flexibility and control, while also improving the overall integration process. {/* truncate */} ## Key Improvements in v5 ### 1. Streamlined API for Ad Integration With version 5, we've refined the public API, making it easier than ever to integrate ads into your app. The updated API offers a more intuitive interface for configuring, loading, and displaying ads. Whether you’re working with interstitial, rewarded video, or banner ads, the process is now more straightforward, reducing the amount time required to integrate the SDK. ### 2. Improved Interstitial Ads Interstitial ads are an effective tool for monetizing mobile apps, and in version 5, we've made it even easier to manage their lifecycle. Developers now have enhanced control over the timing and behavior of interstitial ads, allowing for more seamless user experiences without compromising revenue potential. The new API simplifies the handling of ad loading, showing, and dismissing, ensuring better performance across devices. ### 3. Better Rewarded Video Experience Rewarded video ads offer an excellent way to engage users by providing incentives in exchange for watching ads. The improvements in v5 make the rewarded video ad flow smoother similar to the changes we've done to the interstitial ad format. This version also introduces improved error handling, ensuring that any issues are caught and logged, making it easier to debug and improve app performance. ### 4. Optimized Banner Ads Banner ads continue to be a staple for mobile monetization, and in this update, we've improved how they interact with your app’s layout. With version 5, banner ads can now be more easily customized to fit seamlessly into your app’s UI, offering a better user experience while still maximizing ad revenue. The new API also ensures better ad loading performance, reducing delays that could affect your app’s responsiveness. ## Why These Changes Matter Our focus in version 5 was to streamline the ad integration process, improve performance, and enhance user experience across various ad formats. By refining the public API for interstitial, rewarded video, and banner ads, we’ve made it easier for developers to integrate high-performing ads without compromising the quality of the app or the user experience. ## What about BlueStack v4 ? The old Public API as used in version 4 of the SDK is still accessible, but it highly advised to switch to the v5 API when ever possible. In the future we will remove the old usages. We also have made the old documentation available on this site, you can find them here: * [Android](/android/4.x.x/) * [iOS](/ios/4.x.x/) ## Changes to Documentation Apart from updating our SDK, we also simplified our documentation in this version 5 release: * We've added a dedicated [Privacy and Compliance](/android/privacy) section to both native platforms * Release notes have been cleaned up * We created a copy of the old v4 documentation and made it available for you on both native platforms * Error handling and error codes now have a dedicated section as well * Improved the Mediation & Bidding section ## What's Next? As always, we are committed to providing cutting-edge solutions for app monetization. With version 5 of our Mobile Ad SDK, we’ve laid the groundwork for more features and enhancements in future releases. We encourage all Publishers to upgrade to the latest version of the SDK to take advantage of these improvements and continue to deliver the best possible experiences for their users. For more details on the changes in version 5, including the full release notes, check out our official documentation for [Android](/android) and [iOS](/ios). Stay tuned for more exciting updates coming soon! --- ## BlueStackSDK 5.2.0 — Interstitial Preloading Is Here! ## We're releasing BlueStackSDK 5.2.0 We're releasing **BlueStackSDK version 5.2.0** on **Android** and **iOS**, focusing on a significant enhancement to how we handle interstitial ad delivery. This update introduces **preloading** for both static (HTML) and VAST/Video interstitial ad formats. This has been implemented without any changes to our public API. {/* truncate */} ## Understanding the Change In v5.2.0, we've integrated preloading mechanism. This means the SDK now attempts to fetch and prepare interstitial ad assets in the background so that your ad content becomes ready before you show the ad. ## Key Technical Benefits - **Reduced Display Latency:** By having the ad creative and its necessary resources (HTML, video assets, etc.) already downloaded and parsed, the time from calling show to the ad being presented is significantly minimized. This is particularly noticeable for video-based interstitial ads, where buffering can be an issue. - **Enhanced Reliability in Variable Network Conditions:** If a user temporarily experiences a network dip, the ad may already be loaded and ready, preventing a failed display or a prolonged loading spinner. ## Impact on Your Implementation The most important takeaway for developers is that no changes are required to your existing BlueStackSDK integration. Our public APIs remain consistent. You simply need to update your SDK dependency to v5.2.0. The preloading logic is entirely encapsulated within the SDK. BlueStackSDK v5.2.0 is a performance-focused update designed to deliver seamless and reliable ad experience without introducing any integration complexities. --- ## BlueStackSDK 5.3.0 — Android & iOS Release Notes ## We're releasing BlueStackSDK 5.3.0 We're releasing **BlueStackSDK version 5.3.0** for both **Android** and **iOS**. This release includes stability improvements, minor fixes, and compatibility updates across ad formats. {/* truncate */} ## Highlights - **Quality & stability**: Minor bug fixes, logging improvements, and internal refactors - **Mediation readiness**: Validated with our latest mediation adapters > IMPORTANT: If you use Google mediation or the BlueStack Google adapter, > you must update it alongside SDK 5.3.0. See the note below for details. ## Changes - Stability improvements across interstitial, rewarded, and banner flows - Minor fixes to lifecycle and presentation timing for fullscreen formats - Internal dependency and maintenance updates for 5.3.0 parity across Android and iOS ## Critical note about the Google adapter To ensure full compatibility with BlueStackSDK 5.3.0, you must also update the Google adapter packages on both platforms: - Android: `bluestack-google-adapter` (update to a version supporting 5.3.0) - iOS: `BlueStackGoogleAdapter` (update to a version supporting 5.3.0) Refer to our documentation for the latest adapter coordinates and setup: - [Android — Google mediation](https://developers.bluestack.app/android/mediation/secondary/gma) - [iOS — Google mediation](https://developers.bluestack.app/ios/mediation/secondary/gma) If you're migrating from older adapter names, see also: [Mediation Package Updates for Android and iOS](/blog/dependency_updates) ## How to upgrade Update your project dependencies to BlueStackSDK 5.3.0 and the corresponding Google adapter: ```groovy // Android dependencies { implementation "com.azerion:bluestack-sdk-core:5.3.0" implementation 'com.azerion:bluestack-mediation-google:5.3.0.0' implementation 'com.azerion:bluestack-mediation-bidding:5.3.0.0' } ``` ```ruby # iOS pod 'BlueStackSDK', '5.3.0' pod 'BlueStackGoogleAdapter', '>= 5.3.0' ``` For exact artifact names and latest versions, **always** consult the Android and iOS mediation documentation linked above. --- If you encounter issues or have questions, please reach out to us. We'll continue to iterate based on your feedback. --- ## BlueStackSDK 5.4.0 — Android & iOS Release Notes ## BlueStackSDK 5.4.0 Release Notes We're excited to announce the release of **BlueStackSDK version 5.4.0** for both **Android** and **iOS**. This release focuses on enhancing our mediation capabilities and improving the overall adapter architecture. {/* truncate */} ## Highlights - **Enhanced Mediation:** Significant improvements to adapter support and bidding separation for better flexibility. - **Improved Stability:** Continued focus on minor fixes and internal refactors for a more robust SDK. ## Changes ### Android - **Added:** - Introduced `Validator` interface and `ValidationError` data class for improved data validation. - Added necessary classes to support new adapter implementations. - **Updated:** - Enhanced Ad Presenter support for adapters, streamlining ad presentation logic. ### iOS - **Added:** - Separated bidding adapter from the core SDK, allowing for more modular and flexible bidding integrations. ## How to upgrade Update your project dependencies to BlueStackSDK 5.4.0 and the corresponding mediation adapters: ```groovy // Android dependencies { implementation "com.azerion:bluestack-sdk-core:5.4.0" implementation 'com.azerion:bluestack-mediation-bidding:5.4.0.0' // Update other mediation adapters as needed, e.g., Google, Equativ // implementation 'com.azerion:bluestack-mediation-google:X.Y.Z.A' // implementation 'com.azerion:bluestack-mediation-equativ:X.Y.Z.A' } ``` ```ruby # iOS pod 'BlueStackSDK', '5.4.0' pod 'BlueStackBiddingAdapter', '5.4.0' # Update other mediation adapters as needed, e.g., Google, Equativ # pod 'BlueStackGoogleAdapter', '>= X.Y.Z' # pod 'BlueStackEquativAdapter', '>= X.Y.Z' ``` For exact artifact names and latest versions, **always** consult the Android and iOS mediation documentation linked above. --- If you encounter issues or have questions, please reach out to us. We'll continue to iterate based on your feedback. --- ## BlueStack SDK version 6 BlueStack SDK version 6 is now available. This major release focuses on two areas: internal stability improvements across the SDK, and the introduction of **App Open Ads** as a new supported ad format. {/* truncate */} ## What's New in v6 ### App Open Ads Version 6 adds support for **App Open Ads**, a full-screen ad format designed for app launch moments. App open ads are displayed when users open your app or return to it from the background, making use of a natural transition point rather than interrupting an active session. #### Why App Open Ads? App open ads address a monetization window that other formats do not cover well. Unlike interstitial ads, which appear mid-session, app open ads are shown during moments when users already expect a brief pause before content loads. This makes them a practical addition to an existing ad strategy. To get started, check out the full integration guide: - [iOS App Open Ads Documentation](/ios/ad-formats/app-open) - [Android App Open Ads Documentation](/android/ad-formats/app-open) ### Renamed Classes and Constants Version 6 removes the `BlueStack` and `MNG` prefixes from all public API classes and constants. The underlying functionality remains the same — only the names have changed. Here are the key renames: #### iOS | v5 (Old) | v6 (New) | |---|---| | `BlueStack.sharedInstance` | `MobileAds.sharedInstance` | | `BlueStackPrivacySettings` | `PrivacySettings` | | `BlueStackError` / `BlueStackErrorCode` | `AdError` / `AdErrorCode` | | `BlueStackError*` constants | `AdError*` constants | #### Android | v5 (Old) | v6 (New) | |---|---| | `BlueStack.INSTANCE` | `MobileAds.INSTANCE` | | `BlueStackPrivacySettings` | `PrivacySettings` | | `MNGAdsFactory` | `AdsFactory` | | `MNGNativeObject` | `NativeObject` | | `MNGPreference` / `MNGPreferences` | `Preference` / `Preferences` | For a complete list of all renamed classes, constants, and step-by-step migration instructions, see the migration guides: - [iOS Migration Guide](/blog/bluestack-sdk-6_0_0/migration-guide-ios) - [Android Migration Guide](/blog/bluestack-sdk-6_0_0/migration-guide-android) ## What about BlueStack v5 ? The old Public API as used in version 5 of the SDK is still accessible, but it is highly advised to switch to the v6 API whenever possible. In the future we will remove the old usages. We have also made the old documentation available on this site, you can find them here: - [Android](/android/5.x.x/) - [iOS](/ios/5.x.x/) ## What's Next? Version 6 adds App Open Ads and establishes a more consistent API surface as a foundation for further format support in upcoming releases. Publishers are encouraged to migrate to the v6 API at their earliest convenience. For full documentation, visit: - [Android Documentation](/android) - [iOS Documentation](/ios) If you encounter issues or have questions, please reach out to us. Framework integrations such as React Native will also be updated to support version 6 in the near term. Going forward, framework integration packages will follow the same major version scheme as the native SDK, so that version alignment is straightforward and there is no ambiguity about which SDK version a given package targets. --- ## Android Migration Guide — BlueStack SDK v5 to v6 This guide covers the breaking changes introduced in BlueStack SDK v6.0.0 for Android and explains how to update your existing v5 integration. {/* truncate */} ## Overview BlueStack SDK v6 introduces an update that removes the `BlueStack` and `MNG` prefixes from all public API classes and constants. The underlying functionality remains the same — only the names have changed. This makes the SDK more neutral and easier to integrate across different publishing environments. ## SDK Initialization The main entry point for initializing the SDK has been renamed from `BlueStack` to `MobileAds`. ### Before (v5) ```java showLineNumbers import com.azerion.bluestack.BlueStack; import com.azerion.bluestack.initialization.InitializationListener; import com.azerion.bluestack.initialization.InitializationStatus; class MainActivity extends AppCompatActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); BlueStack.INSTANCE.initialize(this, "YOUR_APP_ID", initializationStatus -> { initializationStatus.getAdapterStatusMap().forEach((adNetworkName, adapterStatus) -> Log.d(TAG, "name: " + adapterStatus.getName() + ", state: " + adapterStatus.getState()) ); }); } } ``` ```kotlin showLineNumbers import com.azerion.bluestack.BlueStack import com.azerion.bluestack.initialization.InitializationListener import com.azerion.bluestack.initialization.InitializationStatus class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) BlueStack.initialize(this, "YOUR_APP_ID", object : InitializationListener { override fun onInitialized(status: InitializationStatus) { status.adapterStatusMap.forEach { (adNetworkName, adapterStatus) -> Log.d(TAG, "name: ${adapterStatus.name}, state: ${adapterStatus.state}") } } }) } } ``` ### After (v6) ```java showLineNumbers import com.azerion.bluestack.MobileAds; import com.azerion.bluestack.initialization.InitializationListener; import com.azerion.bluestack.initialization.SDKInitializationStatus; class MainActivity extends AppCompatActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MobileAds.INSTANCE.initialize(this, "YOUR_APP_ID", initializationStatus -> { initializationStatus.getMediationAdapterStatusMap().forEach((adNetworkName, adapterStatus) -> Log.d(TAG, "name: " + adapterStatus.getName() + ", state: " + adapterStatus.getState()) ); }); } } ``` ```kotlin showLineNumbers import com.azerion.bluestack.MobileAds import com.azerion.bluestack.initialization.InitializationListener import com.azerion.bluestack.initialization.SDKInitializationStatus class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) MobileAds.initialize(this, "YOUR_APP_ID", object : InitializationListener { override fun onInitialized(status: SDKInitializationStatus) { status.mediationAdapterStatusMap.forEach { (adNetworkName, adapterStatus) -> Log.d(TAG, "name: ${adapterStatus.name}, state: ${adapterStatus.state}") } } }) } } ``` ## Privacy Settings The privacy settings class has been renamed from `BlueStackPrivacySettings` to `PrivacySettings`. ### Before (v5) ```java showLineNumbers BlueStackPrivacySettings.setIsAgeRestrictedUser(true, context); BlueStackPrivacySettings.setIsUserOptOut(true, context); ``` ```kotlin showLineNumbers BlueStackPrivacySettings.setIsAgeRestrictedUser(true, context) BlueStackPrivacySettings.setIsUserOptOut(true, context) ``` ### After (v6) ```java showLineNumbers PrivacySettings.setIsAgeRestrictedUser(true, context); PrivacySettings.setIsUserOptOut(true, context); ``` ```kotlin showLineNumbers PrivacySettings.setIsAgeRestrictedUser(true, context) PrivacySettings.setIsUserOptOut(true, context) ``` ## Error Handling The error class `AdError` and all error constant names **remain unchanged** in v6. All existing error constants from v5 work exactly the same way in v6. ### Error Constants (Unchanged) | Error Constant | Error Code | Description | |---|---|---| | `AdError.WRONG_PLACEMENT_ERROR` | 0 | Invalid placement ID | | `AdError.NO_INTERNET_ERROR` | 1 | No internet connection | | `AdError.SDK_UNINITIALIZED_ERROR` | 2 | SDK not initialized | | `AdError.CAPPED_REQUEST_ERROR` | 3 | Request limit reached | | `AdError.LOCKED_PLACEMENT_ERROR` | 4 | Placement locked by another factory | | `AdError.BUSY_FACTORY_ERROR` | 5 | Factory is busy | | `AdError.NO_AD_ERROR` | 7 | No ad available | | `AdError.INTERSTITIAL_COOLDOWN_ERROR` | 8 | Interstitial cooldown active | | `AdError.INTERSTITIAL_ALREADY_SHOWN_ERROR` | 9 | Interstitial already shown | | `AdError.TIME_OUT_ERROR` | 10 | Ad request timed out | | `AdError.ADAPTER_NOT_FOUND_ERROR` | 11 | Mediation adapter not found | | `AdError.BLOCKED_BY_GDPR` | 12 | Request blocked by GDPR | | `AdError.AD_EXPIRED` | 13 | Ad has expired | ### New Error in v6 | Error Constant | Error Code | Description | |---|---|---| | `AdError.NO_ADAPTER_FOUND_FOR_PLACEMENT_ID` | 14 | No adapter configured for specific placement ID | :::tip No Migration Needed Since error constants are unchanged, your existing error handling code will work without modifications in v6. ::: ## Native Ads The native ad classes have been renamed to remove the `MNG` prefix. The ad loading, rendering, and interaction logic remains the same. ### Class Renames | v5 (Old) | v6 (New) | |---|---| | `MNGAdsFactory` | `AdsFactory` | | `MNGNativeObject` | `NativeObject` | | `MNGPreference` | `Preference` | | `MNGGender` | `Gender` | ### Factory Initialization #### Before (v5) ```java showLineNumbers MNGAdsFactory mngAdsNativeAdsFactory = new MNGAdsFactory(getActivity()); mngAdsNativeAdsFactory.setPlacementId("/YOUR_APP_ID/PLACEMENT_ID"); ``` ```kotlin showLineNumbers val mngAdsNativeAdsFactory = MNGAdsFactory(activity) mngAdsNativeAdsFactory.setPlacementId("/YOUR_APP_ID/PLACEMENT_ID") ``` #### After (v6) ```java showLineNumbers AdsFactory adsNativeAdsFactory = new AdsFactory(getActivity()); adsNativeAdsFactory.setPlacementId("/YOUR_APP_ID/PLACEMENT_ID"); ``` ```kotlin showLineNumbers val adsNativeAdsFactory = AdsFactory(activity) adsNativeAdsFactory.setPlacementId("/YOUR_APP_ID/PLACEMENT_ID") ``` ### Loading with Preferences #### Before (v5) ```java showLineNumbers MNGPreference mngPreference = new MNGPreference(); mngPreference.setAge(28); mngPreference.setGender(MNGGender.MNGGenderFemale); mngAdsNativeAdsFactory.loadNative(mngPreference); ``` ```kotlin showLineNumbers val mngPreference = MNGPreference() mngPreference.setAge(28) mngPreference.setGender(MNGGender.MNGGenderFemale) mngAdsNativeAdsFactory.loadNative(mngPreference) ``` #### After (v6) ```java showLineNumbers Preference preference = new Preference(); preference.setAge(28); preference.setGender(Gender.GenderFemale); adsNativeAdsFactory.loadNative(preference); ``` ```kotlin showLineNumbers val preference = Preference() preference.setAge(28) preference.setGender(Gender.GenderFemale) adsNativeAdsFactory.loadNative(preference) ``` ### Native Ad Callbacks #### Before (v5) ```java showLineNumbers mngAdsNativeAdsFactory.setNativeListener(new NativeListener() { @Override public void nativeObjectDidLoad(MNGNativeObject nativeObject) { Log.d(TAG, "Native ad loaded"); // Use nativeObject to render your custom ad view } @Override public void nativeObjectDidFail(Exception adsException) { Log.e(TAG, "Native ad failed to load: " + adsException.toString()); } }); ``` ```kotlin showLineNumbers mngAdsNativeAdsFactory.setNativeListener(object : NativeListener { override fun nativeObjectDidLoad(nativeObject: MNGNativeObject) { Log.d(TAG, "Native ad loaded") // Use nativeObject to render your custom ad view } override fun nativeObjectDidFail(adsException: Exception) { Log.e(TAG, "Native ad failed to load: $adsException") } }) ``` #### After (v6) ```java showLineNumbers adsNativeAdsFactory.setNativeListener(new NativeListener() { @Override public void nativeObjectDidLoad(NativeObject nativeObject) { Log.d(TAG, "Native ad loaded"); // Use nativeObject to render your custom ad view } @Override public void nativeObjectDidFail(Exception adsException) { Log.e(TAG, "Native ad failed to load: " + adsException.toString()); } }); ``` ```kotlin showLineNumbers adsNativeAdsFactory.setNativeListener(object : NativeListener { override fun nativeObjectDidLoad(nativeObject: NativeObject) { Log.d(TAG, "Native ad loaded") // Use nativeObject to render your custom ad view } override fun nativeObjectDidFail(adsException: Exception) { Log.e(TAG, "Native ad failed to load: $adsException") } }) ``` ### AdChoice Position #### Before (v5) ```java showLineNumbers mngPreference.setAdChoicePosition(MNGPreference.TOP_LEFT); ``` ```kotlin showLineNumbers mngPreference.setAdChoicePosition(MNGPreference.TOP_LEFT) ``` #### After (v6) ```java showLineNumbers preference.setAdChoicePosition(Preference.TOP_LEFT); ``` ```kotlin showLineNumbers preference.setAdChoicePosition(Preference.TOP_LEFT) ``` ## Other Ad Format Classes The other ad format classes (`InterstitialAd`, `RewardedAd`, `BannerView`) and their listener interfaces remain unchanged in v6. No migration is needed for those formats. ## Quick Find-and-Replace Summary For most projects, the migration can be completed with a few find-and-replace operations: | Find | Replace With | |---|---| | `BlueStack.INSTANCE` (Java) | `MobileAds.INSTANCE` | | `BlueStack.` (Kotlin) | `MobileAds.` | | `BlueStackPrivacySettings` | `PrivacySettings` | | `MNGAdsFactory` | `AdsFactory` | | `MNGNativeObject` | `NativeObject` | | `MNGPreference` | `Preference` | | `MNGGender` | `Gender` | | `MNGGender.MNGGenderUnknown` | `Gender.GenderUnknown` | | `MNGGender.MNGGenderMale` | `Gender.GenderMale` | | `MNGGender.MNGGenderFemale` | `Gender.GenderFemale` | | `AdSize` (banner package) | `BannerAdSize` | :::tip After running find-and-replace, build your project and fix any remaining compiler errors. The Android Studio compiler will flag any references to the old class names that were missed. ::: ## Need Help? If you encounter issues during migration, please reach out to us. The v5 documentation is still available at [Android v5](/android/5.x.x/) for reference. --- ## iOS Migration Guide — BlueStack SDK v5 to v6 This guide covers the breaking changes introduced in BlueStack SDK v6.0.0 for iOS and explains how to update your existing v5 integration. {/* truncate */} ## Overview BlueStack SDK v6 introduces an update that removes the `BlueStack` and `MNG` prefixes from all public API classes and constants. The underlying functionality remains the same — only the names have changed. This makes the SDK more neutral and easier to integrate across different publishing environments. ## SDK Initialization The main entry point for initializing the SDK has been renamed from `BlueStack` to `MobileAds`. ### Before (v5) ```objectivec showLineNumbers - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [[BlueStack sharedInstance] initializeWithAppID:@"YOUR_APP_ID_HERE" completion:^(InitializationStatus * _Nonnull initializationStatus) { }]; return YES; } ``` ```swift showLineNumbers func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { BlueStack.sharedInstance().initialize(appID: "YOUR_APP_ID_HERE") { initializationStatus in } return true } ``` ### After (v6) ```objectivec showLineNumbers - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [[BLSMobileAds sharedInstance] initializeWithAppID:@"YOUR_APP_ID_HERE" completion:^(InitializationStatus * _Nonnull initializationStatus) { }]; return YES; } ``` ```swift showLineNumbers func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { MobileAds.sharedInstance().initialize(appID: "YOUR_APP_ID_HERE") { initializationStatus in } return true } ``` ## Privacy Settings The privacy settings class has been renamed from `BlueStackPrivacySettings` to `PrivacySettings`. ### Before (v5) ```objectivec showLineNumbers [BlueStackPrivacySettings setIsAgeRestrictedUser:YES]; [BlueStackPrivacySettings setUserOptout:YES]; ``` ```swift showLineNumbers BlueStackPrivacySettings.setIsAgeRestrictedUser(true) BlueStackPrivacySettings.setUserOptout(true) ``` ### After (v6) ```objectivec showLineNumbers [PrivacySettings setIsAgeRestrictedUser:YES]; [PrivacySettings setUserOptout:YES]; ``` ```swift showLineNumbers PrivacySettings.setIsAgeRestrictedUser(true) PrivacySettings.setUserOptout(true) ``` ## Error Handling Error classes and constants have been renamed to remove the `BlueStack` prefix. | v5 | v6 | |---|---| | `BlueStackError` | `AdError` | | `BlueStackErrorCode` | `AdErrorCode` | ### Error Constants All error constants have been renamed from the `BlueStack*` prefix to the `AdError*` prefix: | v5 Constant | v6 Constant | |---|---| | `BlueStackErrorWrongPlacement` | `AdErrorWrongPlacement` | | `BlueStackErrorAdServer` | `AdErrorAdServer` | | `BlueStackErrorDataAdServer` | `AdErrorDataAdServer` | | `BlueStackErrorSDKUninitialized` | `AdErrorSDKUninitialized` | | `BlueStackErrorCappedRequest` | `AdErrorCappedRequest` | | `BlueStackErrorLockedPlacement` | `AdErrorLockedPlacement` | | `BlueStackErrorBusyFactory` | `AdErrorBusyFactory` | | `BlueStackErrorBusy` | `AdErrorBusy` | | `BlueStackErrorUnallowedBackgroundRequest` | `AdErrorUnallowedBackgroundRequest` | | `BlueStackErrorNoAds` | `AdErrorNoAds` | | `MAdvertiseErrorInterstitialCooldown` | `AdErrorInterstitialCooldown` | | `BlueStackErrorAlreadyShownInterstitial` | `AdErrorAlreadyShownInterstitial` | | `BlueStackErrorRequestTimedOut` | `AdErrorRequestTimedOut` | | `BlueStackErrorMissingViewController` | `AdErrorMissingViewController` | | `BlueStackErrorUnableToDisplayAd` | `AdErrorUnableToDisplayAd` | | `BlueStackErrorAdExpired` | `AdErrorAdExpired` | ### New Error Codes in v6 The following error codes are new in v6 and do not have v5 equivalents: | Constant | Description | |---|---| | `AdErrorNoInternet` | No internet connection available. | | `AdErrorAlreadyShownAppOpen` | An app open ad is already being displayed. | | `AdErrorNoAdapterFoundForPlacement` | No mediation adapter found for the placement. | | `AdErrorAdapterClassNotFound` | The adapter class could not be found. | | `AdErrorInternal` | An unexpected internal error occurred. | ### Before (v5) ```objectivec showLineNumbers - (void)bannerView:(BannerView * _Nonnull)bannerView didFailedToLoadWithError:(NSError * _Nonnull)error { switch (error.code) { case BlueStackErrorWrongPlacement: NSLog(@"Wrong placement Id. %@", error.localizedDescription); break; case BlueStackErrorSDKUninitialized: NSLog(@"BlueStackSDK is not initialized. %@", error.localizedDescription); break; default: NSLog(@"Unhandled error"); break; } } ``` ```swift showLineNumbers func onFailedToLoad(_ bannerView: BlueStackSDK.BannerView, _ error: any Error) { guard let error = error as? BlueStackError, let errorCode = BlueStackErrorCode(rawValue: error.code) else { return } switch errorCode { case .BlueStackErrorWrongPlacement: print("Wrong placement Id. \(error.localizedDescription)") case .BlueStackErrorSDKUninitialized: print("BlueStackSDK is not initialized. \(error.localizedDescription)") default: print("Unhandled error. \(error.localizedDescription)") } } ``` ### After (v6) ```objectivec showLineNumbers - (void)bannerView:(BLSBannerView * _Nonnull)bannerView didFailedToLoadWithError:(NSError * _Nonnull)error { switch (error.code) { case AdErrorWrongPlacement: NSLog(@"Wrong placement Id. %@", error.localizedDescription); break; case AdErrorSDKUninitialized: NSLog(@"BlueStack SDK is not initialized. %@", error.localizedDescription); break; default: NSLog(@"Unhandled error"); break; } } ``` ```swift showLineNumbers func onFailedToLoad(_ bannerView: BlueStackSDK.BannerView, _ error: any Error) { guard let error = error as? AdError, let errorCode = AdErrorCode(rawValue: error.code) else { return } switch errorCode { case .wrongPlacement: print("Wrong placement Id. \(error.localizedDescription)") case .sdkUninitialized: print("BlueStack SDK is not initialized. \(error.localizedDescription)") default: print("Unhandled error. \(error.localizedDescription)") } } ``` :::info Note that the Swift enum cases have also been simplified. For example, `.BlueStackErrorWrongPlacement` is now `.wrongPlacement`. ::: ## Native Ads The native ad classes and delegate protocols have been renamed to remove the `MNG` prefix. The ad loading, rendering, and interaction logic remains the same. ### Class and Protocol Renames | v5 (Old) | v6 (New) | |---|---| | `MNGAdsSDKFactory` | `AdsSDKFactory` | | `MNGAdsAdapter` | `AdsAdapter` | | `MNGNAtiveObject` | `NativeObject` | | `MNGPreference` | `Preference` | | `MNGAdsAdapterNativeDelegate` | `AdsAdapterNativeDelegate` | | `MNGDisplayType` | `DisplayType` | ### Factory Initialization #### Before (v5) ```objectivec showLineNumbers nativeAdsFactory = [[MNGAdsSDKFactory alloc] init]; nativeAdsFactory.nativeDelegate = self; nativeAdsFactory.placementId = @"/YOUR_APP_ID/PLACEMENT_ID"; ``` ```swift showLineNumbers nativeAdFactory = MNGAdsSDKFactory() nativeAdFactory.nativeDelegate = self nativeAdFactory.placementId = "/YOUR_APP_ID/PLACEMENT_ID" ``` #### After (v6) ```objectivec showLineNumbers nativeAdsFactory = [[AdsSDKFactory alloc] init]; nativeAdsFactory.nativeDelegate = self; nativeAdsFactory.placementId = @"/YOUR_APP_ID/PLACEMENT_ID"; ``` ```swift showLineNumbers nativeAdFactory = AdsSDKFactory() nativeAdFactory.nativeDelegate = self nativeAdFactory.placementId = "/YOUR_APP_ID/PLACEMENT_ID" ``` ### Loading with Preferences #### Before (v5) ```objectivec showLineNumbers MNGPreference *preferences = [[MNGPreference alloc] init]; [nativeAdsFactory loadNativeWithPreferences:preferences]; ``` ```swift showLineNumbers let preferences = MNGPreference() nativeAdFactory.loadNative(withPreferences: preferences) ``` #### After (v6) ```objectivec showLineNumbers Preference *preferences = [[Preference alloc] init]; [nativeAdsFactory loadNativeWithPreferences:preferences]; ``` ```swift showLineNumbers let preferences = Preference() nativeAdFactory.loadNative(withPreferences: preferences) ``` ### Delegate Callbacks #### Before (v5) ```objectivec showLineNumbers - (void)adsAdapter:(MNGAdsAdapter *)adsAdapter nativeObjectDidLoad:(MNGNAtiveObject *)nativeObject { NSLog(@"Native ad loaded"); // Use nativeObject to render your custom ad view } - (void)adsAdapter:(MNGAdsAdapter *)adsAdapter nativeObjectDidFailWithError:(NSError *)error withCover:(BOOL)cover { NSLog(@"Native ad failed to load: %@", error.localizedDescription); } ``` ```swift showLineNumbers func adsAdapter(_ adsAdapter: MNGAdsAdapter!, nativeObjectDidLoad nativeObject: MNGNAtiveObject!) { print("Native ad loaded") // Use nativeObject to render your custom ad view } func adsAdapter(_ adsAdapter: MNGAdsAdapter!, nativeObjectDidFailWithError error: Error!, withCover cover: Bool) { print("Native ad failed to load: \(error.localizedDescription)") } ``` #### After (v6) ```objectivec showLineNumbers - (void)adsAdapter:(AdsAdapter *)adsAdapter nativeObjectDidLoad:(NativeObject *)nativeObject { NSLog(@"Native ad loaded"); // Use nativeObject to render your custom ad view } - (void)adsAdapter:(AdsAdapter *)adsAdapter nativeObjectDidFailWithError:(NSError *)error withCover:(BOOL)cover { NSLog(@"Native ad failed to load: %@", error.localizedDescription); } ``` ```swift showLineNumbers func adsAdapter(_ adsAdapter: AdsAdapter!, nativeObjectDidLoad nativeObject: NativeObject!) { print("Native ad loaded") // Use nativeObject to render your custom ad view } func adsAdapter(_ adsAdapter: AdsAdapter!, nativeObjectDidFailWithError error: Error!, withCover cover: Bool) { print("Native ad failed to load: \(error.localizedDescription)") } ``` ## Other Ad Format Classes The other ad format classes (`InterstitialAd`, `RewardedAd`, `BannerView`) and their delegate protocols remain unchanged in v6. No migration is needed for those formats. ## Quick Find-and-Replace Summary For most projects, the migration can be completed with a few find-and-replace operations: | Find | Replace With | |---|---| | `BlueStack.sharedInstance` | `MobileAds.sharedInstance` | | `BlueStackPrivacySettings` | `PrivacySettings` | | `BlueStackError` (class) | `AdError` | | `BlueStackErrorCode` | `AdErrorCode` | | `BlueStackError` (constant prefix) | `AdError` | | `MAdvertiseError` | `AdError` | | `MNGAdsSDKFactory` | `AdsSDKFactory` | | `MNGAdsAdapter` | `AdsAdapter` | | `MNGNAtiveObject` | `NativeObject` | | `MNGPreference` | `Preference` | | `MNGAdsAdapterNativeDelegate` | `AdsAdapterNativeDelegate` | | `MNGDisplayType` | `DisplayType` | :::tip After running find-and-replace, build your project and fix any remaining compiler errors. The Xcode compiler will flag any references to the old class names that were missed. ::: ## Need Help? If you encounter issues during migration, please reach out to us. The v5 documentation is still available at [iOS v5](/ios/5.x.x/) for reference. --- ## BlueStack SDK for React Native version 6 BlueStack SDK for React Native version 6 is now available. This release brings the React Native plugin in line with [BlueStack SDK v6](/blog/bluestack-sdk-6) on both iOS and Android, and updates all bundled adapter dependencies to their v6 counterparts. {/* truncate */} ## Aligning with the native SDK Until now the React Native plugin followed its own `1.x` version line while the underlying native SDKs were on `5.x`. Going forward, framework integration packages will follow the same major version scheme as the native SDK, so there is no ambiguity about which native SDK version a given plugin release targets. That means the React Native plugin jumps from `1.4.1` straight to `6.0.0` to match the BlueStack iOS and Android Core SDKs. ## What's New in v6 ### Upgraded native cores The plugin now ships against BlueStack Core SDK `6.0.0` on both platforms: - iOS: BlueStack iOS Core SDK upgraded to `6.0.0` - Android: BlueStack Android Core SDK upgraded to `6.0.0` All public ad format APIs (banner, interstitial, rewarded) continue to work exactly as before — the version bump itself does not introduce breaking changes on the React Native surface. ### Updated mediation adapters The example app and documentation have been updated to use the v6 adapter versions across the board: #### iOS | Ad Network | SDK Version | Adapter Version | |-----------------------|-------------|-----------------| | **Google** | 12.14.0 | 6.0.0 | | **Equativ** | 8.5.1 | 6.0.0 | | **BlueStack Bidding** | N/A | 6.0.2 | #### Android | Ad Network | SDK Version | Adapter Version | |-----------------------|-------------|-----------------| | **Google** | 24.9.0 | 6.0.0.1 | | **Equativ** | 8.5.2 | 6.0.0.2 | | **BlueStack Bidding** | N/A | 6.0.0.1 | ### What ships with the native v6 release Because the React Native plugin now sits on top of Core v6, your apps automatically benefit from the work that landed in the v6 native release, like the renamed, prefix-free public APIs on the native side. See the v6 native announcement for the full picture: - [BlueStack SDK version 6](/blog/bluestack-sdk-6) - [iOS Migration Guide](/blog/bluestack-sdk-6_0_0/migration-guide-ios) - [Android Migration Guide](/blog/bluestack-sdk-6_0_0/migration-guide-android) ## Upgrading Update the plugin via npm or yarn: ```bash npm install @azerion/bluestack-sdk-react-native@6 ``` or ```bash yarn add @azerion/bluestack-sdk-react-native@6 ``` Then refresh native dependencies: - iOS: `cd ios && pod install` - Android: re-sync Gradle ### Prerequisites - React Native 0.80 or higher - Android - Target Android API level 21 or higher - JDK 17 or higher - Android Build Tools 35.0.0 or compatible - AndroidX support - iOS - iOS 13.0 or higher - Xcode 15.1 or higher ## What about the 1.x line? The previous `1.x` documentation remains available under the version dropdown for publishers who are not ready to upgrade yet: - [React Native 1.x.x docs](/react-native/1.x.x/) We recommend moving to v6 at your earliest convenience to stay aligned with future native SDK releases. ## What's Next? With the React Native plugin now versioned alongside the native SDK, future releases will track Core SDK changes more directly — including exposing new formats such as App Open Ads to React Native apps in upcoming releases. For the full plugin documentation, visit: - [React Native Documentation](/react-native) If you run into any issues or have questions, please reach out to us. --- ## BlueStack SDK for Unity version 6 BlueStack SDK for Unity version 6 is now available. This release brings the Unity plugin in line with [BlueStack SDK v6](/blog/bluestack-sdk-6) on both iOS and Android, updates all bundled adapter dependencies to their v6 counterparts, and unifies the public C# API surface across ad formats. {/* truncate */} ## Aligning with the native SDK Until now the Unity plugin followed its own `3.x` version line while the underlying native SDKs were on `5.x`. Going forward, framework integration packages will follow the same major version scheme as the native SDK, so there is no ambiguity about which native SDK version a given plugin release targets. That means the Unity plugin jumps from `3.4.1` straight to `6.0.0` to match the BlueStack iOS and Android Core SDKs. ## What's New in v6 ### Upgraded native cores - Android: BlueStack Core SDK upgraded to `6.0.1` - iOS: BlueStack Core SDK upgraded to `6.0.0` - All mediation adapters (In-App Bidding, Google Mobile Ads, Equativ) bumped to their v6 counterparts on both platforms ### A unified, simplified API surface `BlueStackAds.Initialize` no longer takes a `Settings` snapshot or two separate callbacks. The new shape is a single completion callback that delivers per-adapter `InitializationStatus`, with debug logging toggled independently: ```csharp BlueStackAds.SetDebugMode(true); BlueStackAds.Initialize(appId, status => { foreach (var kv in status.AdapterStatusMap) Debug.Log($"Adapter {kv.Key}: {kv.Value.InitializationState}"); }); ``` `RewardedVideoAd` has been renamed to `RewardedAd`, and the per-format ad events now follow a unified `OnAd*` naming convention (`OnAdLoaded`, `OnAdFailedToLoad`, `OnAdDisplayed`, `OnAdDismissed`, …). A project-wide find-and-replace covers most of the migration; the full event-rename tables live in the [migration guide](/unity/advanced-topics/migrate-from-v3-to-v6). ### New banner controls - `BannerAd(string, Transform, Camera)` — anchor-driven positioning that follows a UI or world-space transform automatically - `BannerAd(string, Vector2)` — custom screen-space positioning - `useSafeArea: false` opt-out for sticky Top/Bottom banners - `BannerAd.SetMask(RectTransform, Camera)` / `RemoveMask()` for clipping the banner to an animated UI region - `OnAdResized` event delivering a `PreferredBannerSize` when the SDK reports a post-load size change ### What ships with the native v6 release Because the Unity plugin now sits on top of Core v6, your apps automatically benefit from the work that landed in the v6 native release. See the v6 native announcement for the full picture: - [BlueStack SDK version 6](/blog/bluestack-sdk-6) - [iOS Migration Guide](/blog/bluestack-sdk-6_0_0/migration-guide-ios) - [Android Migration Guide](/blog/bluestack-sdk-6_0_0/migration-guide-android) ## Upgrading Update `Packages/manifest.json`: ```json { "dependencies": { "com.azerion.bluestack": "6.0.0" } } ``` Then force-resolve native dependencies: - Android: `Assets > External Dependency Manager > Android Resolver > Force Resolve` - iOS: after the next Unity export, run `pod install --repo-update` in the exported Xcode project For a step-by-step walkthrough with full code examples and per-event migration tables, see the dedicated guide: [Migrate from v3.x to v6.0.0](/unity/advanced-topics/migrate-from-v3-to-v6). ### Prerequisites - Unity 2020 or higher - Android: minimum API level 21, target API 34 or higher, JDK 17+ - iOS: 13.0+, Xcode 15.3+, CocoaPods ## What about the 3.x line? The previous `3.x` documentation remains available under the version dropdown for publishers who are not ready to upgrade yet. We recommend moving to v6 at your earliest convenience to stay aligned with future native SDK releases. ## See it in action Our public demo app showcases the BlueStack Unity SDK across banner, interstitial, rewarded, and native ad formats: [azerion/bluestack-demo-unity](https://github.com/azerion/bluestack-demo-unity). If you run into any issues or have questions, please reach out to us. --- ## Mediation Package Updates for Android and iOS We’ve made important updates to our mediation SDKs to improve clarity, consistency, and maintainability. This includes renaming key mediation adapter packages and updating dependencies. Below you'll find everything you need to know to migrate smoothly to the latest versions on both Android and iOS. ## Android ### BlueStack Medation: Google **bluestack-mediation-gma** has been renamed to **bluestack-mediation-google**. If you're already using **bluestack-mediation-gma**, simply update your dependencies to **bluestack-mediation-google**, and you're good to go. ### BlueStack Medation: Equativ **bluestack-mediation-smartadserver** has been renamed to **bluestack-mediation-equativ**. If you're already using **bluestack-mediation-smartadserver**, simply update your dependencies to **bluestack-mediation-equativ**, and you're good to go. ### Google mediation Our Google Mediation adapter is used for when Google is your main SDK, it will mediate BlueStack. We've also changed the name of this implementation: **bluestack-gam-adapter** has been renamed to **bluestack-google-adapter**. If you're already using **bluestack-gam-adapter**, simply update your dependencies to **bluestack-google-adapter**, and follow the Yield setup described in our [documentation][android_2] ### Documentation For detailed integration instructions, please refer to our documentation: - [Google Adapter][android_1] - [Equativ Adapter][android_3] - [Google Mediation][android_2] Stay tuned for more updates, and as always, feel free to reach out with any questions. 🚀 [android_1]: https://developers.bluestack.app/android/mediation/primairy/supported-networks#google-mobile-ads [android_2]: https://developers.bluestack.app/android/mediation/secondary/gma [android_3]: https://developers.bluestack.app/android/mediation/primairy/supported-networks#equativ ## iOS ### BlueStack Medation: Google **BlueStackDFPMediationAdapter** has been renamed to **BlueStackGoogleMediationAdapter**. If you're already using **BlueStackDFPMediationAdapter**, simply update your dependencies to **BlueStackGoogleMediationAdapter**, and you're good to go. ### BlueStack Medation: Equativ **BlueStackSASAdapter** and **BlueStackSASBiddingAdapter** have been merged into **BlueStackEquativAdapter**. If you're already using the old adapters, simply replace them with **BlueStackEquativAdapter**, and you're good to go. ### Google mediation Our Google Mediation adapter is used for when Google is your main SDK, it will mediate BlueStack. We've also changed the name of this implementation: **BlueStackGMAAdapter** has been renamed to **BlueStackGoogleAdapter**. If you're already using **BlueStackGMAAdapter**, update your dependencies to **BlueStackGoogleAdapter**, and follow the Yield setup described in our [documentation][ios_2] ### Documentation For detailed integration instructions, please refer to our documentation: - [Google Adapter][ios_1] - [Equativ Adapter][ios_3] - [Google Mediation][ios_2] Stay tuned for more updates, and as always, feel free to reach out with any questions. 🚀 [ios_1]: https://developers.bluestack.app/ios/mediation/primairy/supported-networks#google-mobile-ads [ios_2]: https://developers.bluestack.app/ios/mediation/secondary/gma [ios_3]: https://developers.bluestack.app/ios/mediation/primairy/supported-networks#equativ --- ## Introducing BlueStack SDK for React Native ## Announcing the Release of the React Native BlueStack SDK (v1.0.0): A New Era of Monetization for Your Apps We are thrilled to announce the release of the **React Native BlueStack SDK**! This powerful new React Native plugin allows you to seamlessly integrate BlueStack Ads into your React Native mobile applications. It provides a robust and flexible way to monetize your apps on both iOS and Android platforms: premium sales with rich media, video and innovative formats as well all standard display formats, ensuring a smooth user experience and maximizing your revenue potential. {/* truncate */} ## Why Choose BlueStack Ads? BlueStack Ads are known for their effectiveness and versatility. By using our SDK, you can leverage three distinct ad formats to engage users and drive monetization: 1. **Component-Based Banner Ads**: - Display banner ads seamlessly within your app’s interface. - Easily customizable to match the look and feel of your app. - Ideal for non-intrusive, continuous ad revenue. 2. **Full-Screen Interstitial Ads**: - Engage users with full-screen ads during natural app transitions. - Perfect for maximizing revenue during breaks or level changes. 3. **Full-Screen Rewarded Ads**: - Offer users rewards in exchange for watching ads. - Boost user engagement by providing valuable in-app incentives. ## Key Features - **Cross-Platform Support**: Fully supports both iOS and Android platforms. - **Easy Integration**: Designed for simplicity, making it easy to get started and integrate into existing projects. - **Flexible and Customizable**: Tailor the appearance and behavior of ads to fit your app's design and user experience. - **Comprehensive Documentation**: Step-by-step guides, API references, and example projects to help you get started quickly. ## Getting Started ### Installation To install the React Native BlueStack SDK, you can use either npm or yarn: ```bash npm install @azerion/bluestack-sdk-react-native ``` or ```bash yarn add @azerion/bluestack-sdk-react-native ``` {/* **Linking Native Dependencies** //if requires in future For React Native 0.60 and above, the linking is done automatically. For older versions, you might need to link the native dependencies manually: ```bash npx react-native link react-native-bluestack-module ``` */} ### Usage Here's a quick example to get you started with banner ads: ```jsx import React from "react"; import { View, StyleSheet } from "react-native"; import { BluestackSDK, BannerAdView, } from "@azerion/bluestack-sdk-react-native"; const App = () => { return ( { console.log("Banner Ad Loaded"); }} onAdFailedToLoad={(error: any) => { console.log( "Banner Ad failed to load : " + error?.nativeEvent?.error ); }} ref={refBanner} /> ); }; const styles = StyleSheet.create({ container: { flex: 1, justifyContent: "center", alignItems: "center", }, }); export default App; ``` For interstitial and rewarded ads, the process is just as simple. Check out our [documentation](/react-native) for more detailed examples and comprehensive guides. ## Demo App We are excited to announce that we have also released a Demo App, designed to demonstrate the full capabilities of the newly launched React Native BlueStack SDK. The Demo App serves as a practical example of how to implement and utilize the BlueStack Ads within a React Native project. It highlights the integration process and demonstrates the functionality of the three primary ad formats supported by the SDK. ![BlueStack Demo App](./react-native_demo_app.png) Start by cloning the demo app repository from GitHub: ```bash git clone https://github.com/azerion/bluestack-demo-react-native.git cd bluestack-demo-react-native ``` ## In summary The React Native BlueStack SDK opens up new opportunities for app developers to monetize their applications efficiently and effectively. We can't wait to see the innovative ways you'll use this SDK to enhance your apps and boost your revenue. Get started today and take your app monetization to the next level with BlueStack! Happy coding! [documentation](/react-native)