Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package com.google.ads.mediation.unity;

import android.app.Activity;
import android.content.Context;
import com.unity3d.services.banners.BannerView;
import com.unity3d.services.banners.UnityBannerSize;

/** A factory to create UnityAds {@link BannerView} for Banner Ads */
class UnityBannerViewFactory {
UnityBannerViewWrapper createBannerView(
Activity activity, String placementId, UnityBannerSize bannerSize) {
BannerView bannerView = new BannerView(activity, placementId, bannerSize);
Context context, String placementId, UnityBannerSize bannerSize) {
BannerView bannerView = new BannerView(context, placementId, bannerSize);
return new UnityBannerViewWrapper(bannerView);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -211,13 +211,11 @@ public void showAd(Context context) {
"Unity Ads received call to show before successfully loading an ad.");
}

Activity activity = context instanceof Activity ? (Activity) context : null;
UnityAdsShowOptions unityAdsShowOptions =
unityAdsLoader.createUnityAdsShowOptionsWithId(objectId);
unityAdsShowOptions.set(KEY_WATERMARK, watermark);
// UnityAds can handle a null placement ID so show is always called here.
// Note: Context here is the activity that the publisher passed to GMA SDK's show() method
// (https://developers.google.com/admob/android/reference/com/google/android/gms/ads/appopen/AppOpenAd#show(android.app.Activity)).
// So, this is guaranteed to be an activity context.
unityAdsLoader.show((Activity) context, placementId, unityAdsShowOptions, this);
unityAdsLoader.show(activity, placementId, unityAdsShowOptions, this);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import static com.google.ads.mediation.unity.UnityAdsAdapterUtils.createSDKError;
import static com.google.ads.mediation.unity.UnityAdsAdapterUtils.getAdFormat;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
Expand All @@ -44,6 +43,7 @@
import com.google.android.gms.ads.mediation.rtb.RtbSignalData;
import com.google.android.gms.ads.mediation.rtb.SignalCallbacks;
import com.unity3d.ads.IUnityAdsInitializationListener;
import com.unity3d.ads.IUnityAdsTokenListener;
import com.unity3d.ads.TokenConfiguration;
import com.unity3d.ads.UnityAds;
import java.lang.annotation.Retention;
Expand Down Expand Up @@ -82,12 +82,12 @@ public class UnityMediationAdapter extends RtbAdapter {
ERROR_PLACEMENT_STATE_NO_FILL,
ERROR_PLACEMENT_STATE_DISABLED,
ERROR_NULL_CONTEXT,
ERROR_CONTEXT_NOT_ACTIVITY,
ERROR_AD_NOT_READY,
ERROR_UNITY_ADS_NOT_SUPPORTED,
ERROR_FINISH,
ERROR_BANNER_SIZE_MISMATCH,
ERROR_INITIALIZATION_FAILURE
ERROR_INITIALIZATION_FAILURE,
ERROR_TOKEN_GENERATION_FAILED
})
Comment thread
vishaldhiman-unity marked this conversation as resolved.
@interface AdapterError {

Expand All @@ -113,9 +113,6 @@ public class UnityMediationAdapter extends RtbAdapter {
*/
static final int ERROR_NULL_CONTEXT = 104;

/** Tried to load or show an ad with a non-Activity context. */
static final int ERROR_CONTEXT_NOT_ACTIVITY = 105;

/**
* Tried to show an ad that's not ready to be shown.
*/
Expand All @@ -141,12 +138,10 @@ public class UnityMediationAdapter extends RtbAdapter {
*/
static final int ERROR_INITIALIZATION_FAILURE = 111;

static final String ERROR_MSG_MISSING_PARAMETERS = "Missing or invalid server parameters.";

static final String ERROR_MSG_NON_ACTIVITY =
"Unity Ads requires an Activity context to load ads.";
/** UnityAds returned no usable bidding token; routed to onFailure so the bidder skips Unity. */

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

super nit: format this line

static final int ERROR_TOKEN_GENERATION_FAILED = 112;

static final String ERROR_MSG_CONTEXT_NULL = "Activity context is null.";
static final String ERROR_MSG_MISSING_PARAMETERS = "Missing or invalid server parameters.";

static final String ERROR_MSG_INITIALIZATION_FAILURE = "Unity Ads initialization failed: [%s] %s";

Expand Down Expand Up @@ -208,18 +203,6 @@ public void collectSignals(
AdFormat adFormat = getAdFormat(rtbSignalData);
com.unity3d.ads.AdFormat unityAdFormat = null;

// For banner ad format, Unity Ads SDK requires an activity context to load the banner ad. So,
// fail here so that Unity bidder will not bid if the ad request was made with a non-activity
// context.
if (adFormat == AdFormat.BANNER && !(rtbSignalData.getContext() instanceof Activity)) {
signalCallbacks.onFailure(
new AdError(
ERROR_CONTEXT_NOT_ACTIVITY,
"Unity Ads RTB Banner ads require activity context",
ADAPTER_ERROR_DOMAIN));
return;
}

if (adFormat == AdFormat.BANNER) {
unityAdFormat = com.unity3d.ads.AdFormat.BANNER;
} else if (adFormat == AdFormat.REWARDED || adFormat == AdFormat.REWARDED_INTERSTITIAL) {
Expand All @@ -230,24 +213,35 @@ public void collectSignals(
Log.w(TAG, "Unsupported ad format for Unity Ads: " + adFormat);
}

IUnityAdsTokenListener listener = new RoutingTokenListener(signalCallbacks);
if (unityAdFormat != null) {
TokenConfiguration tokenConfiguration = new TokenConfiguration(unityAdFormat);
unityAdsWrapper.getToken(
tokenConfiguration,
token -> {
if (token == null) {
token = "";
}
signalCallbacks.onSuccess(token);
});
unityAdsWrapper.getToken(tokenConfiguration, listener);
} else {
unityAdsWrapper.getToken(
token -> {
if (token == null) {
token = "";
}
signalCallbacks.onSuccess(token);
});
unityAdsWrapper.getToken(listener);
}
}

/** Routes a null or empty token to onFailure and a non-empty token to onSuccess. */
@VisibleForTesting
static final class RoutingTokenListener implements IUnityAdsTokenListener {
private final SignalCallbacks signalCallbacks;

RoutingTokenListener(@NonNull SignalCallbacks signalCallbacks) {
this.signalCallbacks = signalCallbacks;
}

@Override
public void onUnityAdsTokenReady(String token) {
if (TextUtils.isEmpty(token)) {
signalCallbacks.onFailure(
new AdError(
ERROR_TOKEN_GENERATION_FAILED,
"Unity Ads returned a null or empty bidding token.",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the other errors have a named constant for an error message. should we do the same?

ADAPTER_ERROR_DOMAIN));
return;
}
signalCallbacks.onSuccess(token);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,8 @@
import static com.google.ads.mediation.unity.UnityAdsAdapterUtils.getMediationErrorCode;
import static com.google.ads.mediation.unity.UnityMediationAdapter.ADAPTER_ERROR_DOMAIN;
import static com.google.ads.mediation.unity.UnityMediationAdapter.ERROR_MSG_MISSING_PARAMETERS;
import static com.google.ads.mediation.unity.UnityMediationAdapter.ERROR_MSG_NON_ACTIVITY;
import static com.google.ads.mediation.unity.UnityMediationAdapter.KEY_WATERMARK;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
Expand Down Expand Up @@ -176,18 +174,6 @@ public void loadAd(
return;
}

if (!(context instanceof Activity)) {
AdError adError =
new AdError(
UnityMediationAdapter.ERROR_CONTEXT_NOT_ACTIVITY,
ERROR_MSG_NON_ACTIVITY,
ADAPTER_ERROR_DOMAIN);
Log.w(UnityMediationAdapter.TAG, adError.toString());
mediationBannerAdLoadCallback.onFailure(adError);
return;
}
final Activity activity = (Activity) context;

final String adMarkup = mediationBannerAdConfiguration.getBidResponse();

// It is RTB if adMarkup is not empty.
Expand Down Expand Up @@ -224,7 +210,7 @@ public void onInitializationComplete() {
if (unityBannerViewWrapper == null) {
unityBannerViewWrapper =
unityBannerViewFactory.createBannerView(
activity, bannerPlacementId, unityBannerSize);
context, bannerPlacementId, unityBannerSize);
}

unityBannerViewWrapper.setListener(UnityMediationBannerAd.this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,8 @@

import static com.google.ads.mediation.unity.UnityAdsAdapterUtils.createSDKError;
import static com.google.ads.mediation.unity.UnityMediationAdapter.ADAPTER_ERROR_DOMAIN;
import static com.google.ads.mediation.unity.UnityMediationAdapter.ERROR_CONTEXT_NOT_ACTIVITY;
import static com.google.ads.mediation.unity.UnityMediationAdapter.ERROR_INVALID_SERVER_PARAMETERS;
import static com.google.ads.mediation.unity.UnityMediationAdapter.ERROR_MSG_MISSING_PARAMETERS;
import static com.google.ads.mediation.unity.UnityMediationAdapter.ERROR_MSG_NON_ACTIVITY;
import static com.google.ads.mediation.unity.UnityMediationAdapter.KEY_WATERMARK;
import static com.google.ads.mediation.unity.UnityMediationAdapter.TAG;

Expand Down Expand Up @@ -139,22 +137,12 @@ public void loadAd(MediationRewardedAdConfiguration mediationRewardedAdConfigura

@Override
public void showAd(@NonNull Context context) {
if (!(context instanceof Activity)) {
AdError showError =
new AdError(ERROR_CONTEXT_NOT_ACTIVITY, ERROR_MSG_NON_ACTIVITY, ADAPTER_ERROR_DOMAIN);
Log.e(TAG, showError.toString());
if (mediationRewardedAdCallback != null) {
mediationRewardedAdCallback.onAdFailedToShow(showError);
}
return;
}
Activity activity = (Activity) context;

// Check if the placement is ready before showing
if (placementId == null) {
Log.w(TAG, "Unity Ads received call to show before successfully loading an ad.");
}

Activity activity = context instanceof Activity ? (Activity) context : null;
UnityAdsShowOptions unityAdsShowOptions =
unityAdsLoader.createUnityAdsShowOptionsWithId(objectId);
unityAdsShowOptions.set(KEY_WATERMARK, watermark);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.google.ads.mediation.unity

import android.app.Activity
import androidx.core.os.bundleOf
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.ads.mediation.unity.UnityAdsAdapterUtils.getMediationErrorCode
import com.google.ads.mediation.unity.UnityMediationAdapter.SDK_ERROR_DOMAIN
Expand All @@ -21,10 +22,12 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.eq
import org.mockito.kotlin.isNull
import org.mockito.kotlin.mock
import org.mockito.kotlin.notNull
import org.mockito.kotlin.spy
Expand Down Expand Up @@ -133,6 +136,17 @@ class UnityInterstitialAdTest {
assertThat(capturedError.domain).isEqualTo(SDK_ERROR_DOMAIN)
}

@Test
fun showAd_withNonActivityContext_callsShowWithNullActivity() {
val unityAdsShowOptions: UnityAdsShowOptions = mock()
whenever(unityAdsLoader.createUnityAdsShowOptionsWithId(anyOrNull())) doReturn unityAdsShowOptions
unityInterstitialAd.onUnityAdsAdLoaded(PLACEMENT_ID)

unityInterstitialAd.showAd(ApplicationProvider.getApplicationContext())

verify(unityAdsLoader).show(isNull(), any(), any(), any())
}

@Test
fun showAd_invokesUnityAdsShow() {
doAnswer { invocation ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@ import com.google.ads.mediation.unity.UnityInitializer.KEY_ADAPTER_VERSION
import com.google.ads.mediation.unity.UnityInterstitialAd.ERROR_MSG_INTERSTITIAL_INITIALIZATION_FAILED
import com.google.ads.mediation.unity.UnityMediationAdapter.ADAPTER_ERROR_DOMAIN
import com.google.ads.mediation.unity.UnityMediationAdapter.ERROR_BANNER_SIZE_MISMATCH
import com.google.ads.mediation.unity.UnityMediationAdapter.ERROR_CONTEXT_NOT_ACTIVITY
import com.google.ads.mediation.unity.UnityMediationAdapter.ERROR_INVALID_SERVER_PARAMETERS
import com.google.ads.mediation.unity.UnityMediationAdapter.ERROR_MSG_INITIALIZATION_FAILURE
import com.google.ads.mediation.unity.UnityMediationAdapter.ERROR_MSG_MISSING_PARAMETERS
import com.google.ads.mediation.unity.UnityMediationAdapter.ERROR_MSG_NON_ACTIVITY
import com.google.ads.mediation.unity.UnityMediationAdapter.ERROR_TOKEN_GENERATION_FAILED
import com.google.ads.mediation.unity.UnityMediationAdapter.SDK_ERROR_DOMAIN
import com.google.ads.mediation.unity.UnityMediationBannerAd.ERROR_MSG_INITIALIZATION_FAILED_FOR_GAME_ID
import com.google.ads.mediation.unity.UnityMediationBannerAd.ERROR_MSG_NO_MATCHING_AD_SIZE
Expand Down Expand Up @@ -218,7 +217,13 @@ class UnityMediationAdapterTest {
}

@Test
fun collectSignals_forBannerFormatAndNonActivityContext_fails() {
fun collectSignals_forBannerFormatAndNonActivityContext_invokesSignalCallbacks() {
whenever(unityAdsWrapper.getToken(any(), any())) doAnswer
{ invocation ->
val callback = invocation.arguments[1] as IUnityAdsTokenListener
callback.onUnityAdsTokenReady(TEST_TOKEN)
}

val rtbSignalData =
RtbSignalData(
nonActivityContext,
Expand All @@ -229,11 +234,7 @@ class UnityMediationAdapterTest {

unityMediationAdapter.collectSignals(rtbSignalData, signalCallbacks)

val adErrorCaptor = argumentCaptor<AdError>()
verify(signalCallbacks).onFailure(adErrorCaptor.capture())
val adError = adErrorCaptor.firstValue
assertThat(adError.code).isEqualTo(ERROR_CONTEXT_NOT_ACTIVITY)
assertThat(adError.domain).isEqualTo(ADAPTER_ERROR_DOMAIN)
verify(signalCallbacks).onSuccess(TEST_TOKEN)
verifyNoMoreInteractions(signalCallbacks)
}
Comment thread
vishaldhiman-unity marked this conversation as resolved.

Expand Down Expand Up @@ -340,6 +341,54 @@ class UnityMediationAdapterTest {
verifyNoMoreInteractions(signalCallbacks)
}

@Test
fun collectSignals_whenSdkReturnsNullToken_routesToOnFailure() {
// Legacy SDK path: only onUnityAdsTokenReady is called, with null.
whenever(unityAdsWrapper.getToken(any(), any())) doAnswer
{ invocation ->
val callback = invocation.arguments[1] as IUnityAdsTokenListener
callback.onUnityAdsTokenReady(null)
}

val rtbSignalData =
RtbSignalData(
activity,
listOf(MediationConfiguration(AdFormat.INTERSTITIAL, /* serverParameters= */ bundleOf())),
/* networkExtras= */ bundleOf(),
null,
)

unityMediationAdapter.collectSignals(rtbSignalData, signalCallbacks)

val adErrorCaptor = argumentCaptor<AdError>()
verify(signalCallbacks).onFailure(adErrorCaptor.capture())
assertEquals(ERROR_TOKEN_GENERATION_FAILED, adErrorCaptor.firstValue.code)
assertEquals(ADAPTER_ERROR_DOMAIN, adErrorCaptor.firstValue.domain)
verifyNoMoreInteractions(signalCallbacks)
}

@Test
fun collectSignals_whenSdkReturnsEmptyToken_routesToOnFailure() {
whenever(unityAdsWrapper.getToken(any(), any())) doAnswer
{ invocation ->
val callback = invocation.arguments[1] as IUnityAdsTokenListener
callback.onUnityAdsTokenReady("")
}

val rtbSignalData =
RtbSignalData(
activity,
listOf(MediationConfiguration(AdFormat.INTERSTITIAL, /* serverParameters= */ bundleOf())),
/* networkExtras= */ bundleOf(),
null,
)

unityMediationAdapter.collectSignals(rtbSignalData, signalCallbacks)

verify(signalCallbacks).onFailure(any())
verify(signalCallbacks, never()).onSuccess(any())
}

@Test
fun collectSignals_forUnsupportedFormat_invokesSignalCallbacks() {
whenever(unityAdsWrapper.getToken(any())) doAnswer
Expand Down Expand Up @@ -451,20 +500,17 @@ class UnityMediationAdapterTest {
}

@Test
fun loadBannerAd_withNonActivityContext_failsWithAdError() {
mediationBannerAdConfiguration = initializeBannerAd(ApplicationProvider.getApplicationContext())
fun loadBannerAd_withNonActivityContext_callsInitializeUnityAds() {
mediationBannerAdConfiguration = initializeBannerAd(nonActivityContext)
whenever(mediationUtils.findClosestSize(eq(nonActivityContext), eq(AdSize.BANNER), any())) doReturn
AdSize.BANNER

unityMediationAdapter.loadBannerAd(
mediationBannerAdConfiguration,
mediationBannerAdLoadCallback,
)

val adErrorCaptor = argumentCaptor<AdError>()
verify(mediationBannerAdLoadCallback).onFailure(adErrorCaptor.capture())
val capturedError = adErrorCaptor.firstValue
assertThat(capturedError.code).isEqualTo(ERROR_CONTEXT_NOT_ACTIVITY)
assertThat(capturedError.message).isEqualTo(ERROR_MSG_NON_ACTIVITY)
assertThat(capturedError.domain).isEqualTo(ADAPTER_ERROR_DOMAIN)
verify(unityInitializer).initializeUnityAds(any(), any(), any())
}

@Test
Expand Down
Loading
Loading