Created
March 23, 2020 20:54
-
-
Save thomasmso/029525c04fe808110829d0dac67e06f1 to your computer and use it in GitHub Desktop.
MAX Facebook Adapter - Android
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.applovin.mediation.adapters; | |
import android.app.Activity; | |
import android.os.Bundle; | |
import android.text.TextUtils; | |
import com.applovin.mediation.MaxAdFormat; | |
import com.applovin.mediation.MaxReward; | |
import com.applovin.mediation.adapter.MaxAdViewAdapter; | |
import com.applovin.mediation.adapter.MaxAdapterError; | |
import com.applovin.mediation.adapter.MaxInterstitialAdapter; | |
import com.applovin.mediation.adapter.MaxRewardedAdapter; | |
import com.applovin.mediation.adapter.MaxSignalProvider; | |
import com.applovin.mediation.adapter.listeners.MaxAdViewAdapterListener; | |
import com.applovin.mediation.adapter.listeners.MaxInterstitialAdapterListener; | |
import com.applovin.mediation.adapter.listeners.MaxRewardedAdapterListener; | |
import com.applovin.mediation.adapter.listeners.MaxSignalCollectionListener; | |
import com.applovin.mediation.adapter.parameters.MaxAdapterInitializationParameters; | |
import com.applovin.mediation.adapter.parameters.MaxAdapterParameters; | |
import com.applovin.mediation.adapter.parameters.MaxAdapterResponseParameters; | |
import com.applovin.mediation.adapter.parameters.MaxAdapterSignalCollectionParameters; | |
import com.applovin.sdk.AppLovinSdk; | |
import com.facebook.ads.Ad; | |
import com.facebook.ads.AdError; | |
import com.facebook.ads.AdListener; | |
import com.facebook.ads.AdSettings; | |
import com.facebook.ads.AdSize; | |
import com.facebook.ads.AdView; | |
import com.facebook.ads.AudienceNetworkAds; | |
import com.facebook.ads.BidderTokenProvider; | |
import com.facebook.ads.InterstitialAd; | |
import com.facebook.ads.InterstitialAdExtendedListener; | |
import com.facebook.ads.InterstitialAdListener; | |
import com.facebook.ads.RewardedVideoAd; | |
import com.facebook.ads.RewardedVideoAdExtendedListener; | |
import java.util.Arrays; | |
import java.util.List; | |
import java.util.concurrent.atomic.AtomicBoolean; | |
/** | |
* Copyright © 2019 AppLovin Corporation. All rights reserved. | |
*/ | |
public class FacebookMediationAdapter | |
extends MediationAdapterBase | |
implements MaxInterstitialAdapter, MaxRewardedAdapter, MaxAdViewAdapter, MaxSignalProvider | |
{ | |
private static final AtomicBoolean INITIALIZED = new AtomicBoolean(); | |
private static InitializationStatus sStatus; | |
private AdView mAdView; | |
private InterstitialAd mInterstitialAd; | |
private RewardedVideoAd mRewardedVideoAd; | |
private AtomicBoolean onInterstitialAdHiddenCalled = new AtomicBoolean(); | |
private AtomicBoolean onRewardedAdVideoCompletedCalled = new AtomicBoolean(); | |
private AtomicBoolean onRewardedAdHiddenCalled = new AtomicBoolean(); | |
// Explicit default constructor declaration | |
public FacebookMediationAdapter(final AppLovinSdk sdk) { super( sdk ); } | |
@Override | |
public void initialize(final MaxAdapterInitializationParameters parameters, final Activity activity, final OnCompletionListener onCompletionListener) | |
{ | |
// Check existence of SDK classes | |
checkExistence( InterstitialAd.class, InterstitialAdListener.class, AdSettings.class ); | |
// Update ad settings | |
updateAdSettings( parameters ); | |
if ( INITIALIZED.compareAndSet( false, true ) ) | |
{ | |
if ( AppLovinSdk.VERSION_CODE >= 90800 ) // 9.8.0+ | |
{ | |
sStatus = InitializationStatus.INITIALIZING; | |
} | |
final List<String> placementIds = parameters.getServerParameters().getStringArrayList( "placement_ids" ); | |
final AudienceNetworkAds.InitListener initListener = new AudienceNetworkAds.InitListener() | |
{ | |
@Override | |
public void onInitialized(final AudienceNetworkAds.InitResult initResult) | |
{ | |
if ( initResult.isSuccess() ) | |
{ | |
log( "Facebook SDK successfully finished initialization: " + initResult.getMessage() ); | |
if ( AppLovinSdk.VERSION_CODE >= 90800 ) // 9.8.0+ | |
{ | |
sStatus = InitializationStatus.INITIALIZED_SUCCESS; | |
onCompletionListener.onCompletion( sStatus, null ); | |
} | |
else | |
{ | |
onCompletionListener.onCompletion(); | |
} | |
} | |
else | |
{ | |
log( "Facebook SDK failed to finished initialization: " + initResult.getMessage() ); | |
if ( AppLovinSdk.VERSION_CODE >= 90800 ) // 9.8.0+ | |
{ | |
sStatus = InitializationStatus.INITIALIZED_FAILURE; | |
onCompletionListener.onCompletion( sStatus, initResult.getMessage() ); | |
} | |
else | |
{ | |
onCompletionListener.onCompletion(); | |
} | |
} | |
} | |
}; | |
if ( parameters.isTesting() ) | |
{ | |
AdSettings.setDebugBuild( true ); | |
} | |
log( "Initializing Facebook SDK with placements: " + placementIds ); | |
AudienceNetworkAds.buildInitSettings( activity ) | |
.withMediationService( getMediationIdentifier() ) | |
.withPlacementIds( placementIds ) | |
.withInitListener( initListener ) | |
.initialize(); | |
} | |
else | |
{ | |
log( "Facebook attempted initialization already - marking initialization as completed" ); | |
if ( AppLovinSdk.VERSION_CODE >= 90800 ) // 9.8.0+ | |
{ | |
onCompletionListener.onCompletion( sStatus, null ); | |
} | |
else | |
{ | |
onCompletionListener.onCompletion(); | |
} | |
} | |
} | |
@Override | |
public String getSdkVersion() | |
{ | |
return getVersionString( com.facebook.ads.BuildConfig.class, "VERSION_NAME" ); | |
} | |
@Override | |
public String getAdapterVersion() | |
{ | |
return com.applovin.mediation.adapters.facebook.BuildConfig.VERSION_NAME; | |
} | |
@Override | |
public void loadInterstitialAd(final MaxAdapterResponseParameters parameters, final Activity activity, final MaxInterstitialAdapterListener listener) | |
{ | |
final String placementId = parameters.getThirdPartyAdPlacementId(); | |
log( "Loading interstitial ad: " + placementId + "..." ); | |
updateAdSettings( parameters ); | |
mInterstitialAd = new InterstitialAd( activity.getApplicationContext(), placementId ); | |
InterstitialAd.InterstitialAdLoadConfigBuilder adLoadConfigBuilder = mInterstitialAd.buildLoadAdConfig() | |
.withAdListener( new InterstitialAdExtendedListener() | |
{ | |
@Override | |
public void onAdLoaded(final Ad ad) | |
{ | |
log( "Interstitial ad loaded: " + placementId ); | |
listener.onInterstitialAdLoaded(); | |
} | |
@Override | |
public void onError(final Ad ad, final AdError adError) | |
{ | |
log( "Interstitial ad (" + placementId + ") failed to load with error code (" + adError.getErrorCode() + ") and message: " + adError.getErrorMessage() ); | |
listener.onInterstitialAdLoadFailed( toMaxError( adError ) ); | |
} | |
@Override | |
public void onAdClicked(final Ad ad) | |
{ | |
log( "Interstitial ad clicked: " + placementId ); | |
listener.onInterstitialAdClicked(); | |
} | |
@Override | |
public void onLoggingImpression(final Ad ad) | |
{ | |
// Max does its own impression tracking | |
log( "Interstitial ad logging impression: " + placementId ); | |
listener.onInterstitialAdDisplayed(); | |
} | |
@Override | |
public void onInterstitialDisplayed(final Ad ad) | |
{ | |
log( "Interstitial ad displayed: " + placementId ); | |
// listener.onInterstitialAdDisplayed(); | |
} | |
@Override | |
public void onInterstitialDismissed(final Ad ad) | |
{ | |
log( "Interstitial ad hidden: " + placementId ); | |
if ( onInterstitialAdHiddenCalled.compareAndSet( false, true ) ) | |
{ | |
listener.onInterstitialAdHidden(); | |
} | |
} | |
@Override | |
public void onInterstitialActivityDestroyed() | |
{ | |
log( "Interstitial ad Activity destroyed: " + placementId ); | |
// | |
// This may be due to launching from app icon and having the `android:launchMode="singleTask"` flag | |
// | |
if ( onInterstitialAdHiddenCalled.compareAndSet( false, true ) ) | |
{ | |
listener.onInterstitialAdHidden(); | |
} | |
} | |
@Override | |
public void onRewardedAdCompleted() { } | |
@Override | |
public void onRewardedAdServerSucceeded() { } | |
@Override | |
public void onRewardedAdServerFailed() { } | |
} ); | |
if ( mInterstitialAd.isAdLoaded() && !mInterstitialAd.isAdInvalidated() ) | |
{ | |
log( "An interstitial ad has been loaded already" ); | |
listener.onInterstitialAdLoaded(); | |
} | |
else | |
{ | |
// If there is no bid response | |
if ( TextUtils.isEmpty( parameters.getBidResponse() ) ) | |
{ | |
log( "Loading mediated interstitial ad..." ); | |
mInterstitialAd.loadAd( adLoadConfigBuilder.build() ); | |
} | |
// Otherwise (we have a bid response) | |
else | |
{ | |
log( "Loading bidding interstitial ad..." ); | |
mInterstitialAd.loadAd( adLoadConfigBuilder.withBid( parameters.getBidResponse() ).build() ); | |
} | |
} | |
} | |
@Override | |
public void showInterstitialAd(final MaxAdapterResponseParameters parameters, final Activity activity, final MaxInterstitialAdapterListener listener) | |
{ | |
log( "Showing interstitial ad: " + parameters.getThirdPartyAdPlacementId() + "..." ); | |
if ( mInterstitialAd != null && mInterstitialAd.isAdLoaded() ) | |
{ | |
// Check if ad is already expired or invalidated, and do not show ad if that is the case. You will not get paid to show an invalidated ad. | |
if ( !mInterstitialAd.isAdInvalidated() ) | |
{ | |
mInterstitialAd.show(); | |
} | |
else | |
{ | |
log( "Unable to show interstitial - ad expired..." ); | |
listener.onInterstitialAdDisplayFailed( MaxAdapterError.AD_EXPIRED ); | |
} | |
} | |
else | |
{ | |
log( "Unable to show interstitial - no ad loaded..." ); | |
listener.onInterstitialAdDisplayFailed( MaxAdapterError.AD_NOT_READY ); | |
} | |
} | |
@Override | |
public void loadRewardedAd(final MaxAdapterResponseParameters parameters, final Activity activity, final MaxRewardedAdapterListener listener) | |
{ | |
final String placementId = parameters.getThirdPartyAdPlacementId(); | |
log( "Loading rewarded: " + placementId + "..." ); | |
updateAdSettings( parameters ); | |
mRewardedVideoAd = new RewardedVideoAd( activity.getApplicationContext(), placementId ); | |
RewardedVideoAd.RewardedVideoAdLoadConfigBuilder adLoadConfigBuilder = mRewardedVideoAd.buildLoadAdConfig() | |
.withAdListener( new RewardedVideoAdExtendedListener() | |
{ | |
private boolean hasGrantedReward; | |
@Override | |
public void onAdLoaded(final Ad ad) | |
{ | |
log( "Rewarded ad loaded: " + placementId ); | |
listener.onRewardedAdLoaded(); | |
} | |
@Override | |
public void onError(final Ad ad, final AdError adError) | |
{ | |
log( "Rewarded ad (" + placementId + ") failed to load with error code (" + adError.getErrorCode() + ") and message: " + adError.getErrorMessage() ); | |
listener.onRewardedAdLoadFailed( toMaxError( adError ) ); | |
} | |
@Override | |
public void onAdClicked(final Ad ad) | |
{ | |
log( "Rewarded ad clicked: " + placementId ); | |
listener.onRewardedAdClicked(); | |
} | |
@Override | |
public void onRewardedVideoClosed() | |
{ | |
log( "Rewarded ad hidden: " + placementId ); | |
if ( onRewardedAdHiddenCalled.compareAndSet( false, true ) ) | |
{ | |
if ( hasGrantedReward || shouldAlwaysRewardUser() ) | |
{ | |
final MaxReward reward = getReward(); | |
log( "Rewarded user with reward: " + reward ); | |
listener.onUserRewarded( reward ); | |
} | |
listener.onRewardedAdHidden(); | |
} | |
} | |
@Override | |
public void onRewardedVideoCompleted() | |
{ | |
log( "Rewarded ad video completed: " + placementId ); | |
if ( onRewardedAdVideoCompletedCalled.compareAndSet( false, true ) ) | |
{ | |
listener.onRewardedAdVideoCompleted(); | |
hasGrantedReward = true; | |
} | |
} | |
@Override | |
public void onLoggingImpression(final Ad ad) | |
{ | |
log( "Rewarded ad logging impression: " + placementId ); | |
listener.onRewardedAdDisplayed(); | |
listener.onRewardedAdVideoStarted(); | |
} | |
@Override | |
public void onRewardedVideoActivityDestroyed() | |
{ | |
log( "Rewarded ad Activity destroyed: " + placementId ); | |
// | |
// We will not reward the user if Activity is destroyed - this may be due to launching from app icon and having the `android:launchMode="singleTask"` flag | |
// | |
if ( onRewardedAdVideoCompletedCalled.compareAndSet( false, true ) ) | |
{ | |
listener.onRewardedAdVideoCompleted(); | |
} | |
if ( onRewardedAdHiddenCalled.compareAndSet( false, true ) ) | |
{ | |
listener.onRewardedAdHidden(); | |
} | |
} | |
} ); | |
if ( mRewardedVideoAd.isAdLoaded() && !mRewardedVideoAd.isAdInvalidated() ) | |
{ | |
log( "A rewarded ad has been loaded already" ); | |
listener.onRewardedAdLoaded(); | |
} | |
else | |
{ | |
// If there is no bid response | |
if ( TextUtils.isEmpty( parameters.getBidResponse() ) ) | |
{ | |
log( "Loading mediated rewarded ad..." ); | |
mRewardedVideoAd.loadAd( adLoadConfigBuilder.build() ); | |
} | |
// Otherwise (we have a bid response) | |
else | |
{ | |
log( "Loading bidding rewarded ad..." ); | |
mRewardedVideoAd.loadAd( adLoadConfigBuilder.withBid( parameters.getBidResponse() ).build() ); | |
} | |
} | |
} | |
@Override | |
public void showRewardedAd(final MaxAdapterResponseParameters parameters, final Activity activity, final MaxRewardedAdapterListener listener) | |
{ | |
log( "Showing rewarded ad: " + parameters.getThirdPartyAdPlacementId() + "..." ); | |
if ( mRewardedVideoAd != null && mRewardedVideoAd.isAdLoaded() ) | |
{ | |
// Check if ad is already expired or invalidated, and do not show ad if that is the case. You will not get paid to show an invalidated ad. | |
if ( !mRewardedVideoAd.isAdInvalidated() ) | |
{ | |
// Configure userReward from server. | |
configureReward( parameters ); | |
mRewardedVideoAd.show(); | |
} | |
else | |
{ | |
log( "Unable to show rewarded ad - ad expired..." ); | |
listener.onRewardedAdDisplayFailed( MaxAdapterError.AD_EXPIRED ); | |
} | |
} | |
else | |
{ | |
log( "Unable to show rewarded ad - no ad loaded..." ); | |
listener.onRewardedAdDisplayFailed( MaxAdapterError.AD_NOT_READY ); | |
} | |
} | |
@Override | |
public void collectSignal(final MaxAdapterSignalCollectionParameters parameters, final Activity activity, final MaxSignalCollectionListener callback) | |
{ | |
log( "Collecting signal..." ); | |
// Must be ran on bg thread | |
String signal = BidderTokenProvider.getBidderToken( activity ); | |
callback.onSignalCollected( signal ); | |
} | |
@Override | |
public void loadAdViewAd(final MaxAdapterResponseParameters parameters, final MaxAdFormat adFormat, final Activity activity, final MaxAdViewAdapterListener listener) | |
{ | |
final String placementId = parameters.getThirdPartyAdPlacementId(); | |
log( "Loading banner ad: " + placementId + "..." ); | |
updateAdSettings( parameters ); | |
mAdView = new AdView( activity, placementId, toAdSize( adFormat ) ); | |
AdView.AdViewLoadConfigBuilder adViewLoadConfigBuilder = mAdView.buildLoadAdConfig() | |
.withAdListener( new AdListener() | |
{ | |
@Override | |
public void onAdLoaded(final Ad ad) | |
{ | |
log( "Banner ad loaded: " + placementId ); | |
listener.onAdViewAdLoaded( mAdView ); | |
} | |
@Override | |
public void onError(final Ad ad, final AdError adError) | |
{ | |
log( "Banner ad (" + placementId + ") failed to load with error code (" + adError.getErrorCode() + ") and message: " + adError.getErrorMessage() ); | |
listener.onAdViewAdLoadFailed( toMaxError( adError ) ); | |
} | |
@Override | |
public void onAdClicked(final Ad ad) | |
{ | |
log( "Banner ad clicked: " + placementId ); | |
listener.onAdViewAdClicked(); | |
} | |
@Override | |
public void onLoggingImpression(final Ad ad) | |
{ | |
log( "Banner ad displayed: " + placementId ); | |
listener.onAdViewAdDisplayed(); | |
} | |
} ); | |
// If there is no bid response | |
if ( TextUtils.isEmpty( parameters.getBidResponse() ) ) | |
{ | |
log( "Loading mediated banner ad..." ); | |
mAdView.loadAd( adViewLoadConfigBuilder.build() ); | |
} | |
// Otherwise (we have a bid response) | |
else | |
{ | |
log( "Loading bidding banner ad..." ); | |
mAdView.loadAd( adViewLoadConfigBuilder.withBid( parameters.getBidResponse() ).build() ); | |
} | |
} | |
@Override | |
public void onDestroy() | |
{ | |
if ( mInterstitialAd != null ) | |
{ | |
mInterstitialAd.destroy(); | |
mInterstitialAd = null; | |
} | |
if ( mRewardedVideoAd != null ) | |
{ | |
mRewardedVideoAd.destroy(); | |
mRewardedVideoAd = null; | |
} | |
if ( mAdView != null ) | |
{ | |
mAdView.destroy(); | |
mAdView = null; | |
} | |
} | |
private AdSize toAdSize(final MaxAdFormat adFormat) | |
{ | |
if ( adFormat == MaxAdFormat.BANNER ) | |
{ | |
return AdSize.BANNER_HEIGHT_50; | |
} | |
else if ( adFormat == MaxAdFormat.LEADER ) | |
{ | |
return AdSize.BANNER_HEIGHT_90; | |
} | |
else if ( adFormat == MaxAdFormat.MREC ) | |
{ | |
return AdSize.RECTANGLE_HEIGHT_250; | |
} | |
else | |
{ | |
throw new IllegalArgumentException( "Invalid ad format: " + adFormat ); | |
} | |
} | |
private void updateAdSettings(final MaxAdapterParameters parameters) | |
{ | |
final Bundle serverParameters = parameters.getServerParameters(); | |
if ( serverParameters.containsKey( "video_autoplay" ) ) | |
{ | |
final boolean videoAutoplay = serverParameters.getBoolean( "video_autoplay" ); | |
AdSettings.setVideoAutoplay( videoAutoplay ); | |
} | |
AdSettings.setMixedAudience( parameters.isAgeRestrictedUser() ); | |
final String testDevicesString = serverParameters.getString( "test_device_ids", null ); | |
if ( !TextUtils.isEmpty( testDevicesString ) ) | |
{ | |
final List<String> testDeviceList = Arrays.asList( testDevicesString.split( "," ) ); | |
AdSettings.addTestDevices( testDeviceList ); | |
} | |
// Update mediation service | |
AdSettings.setMediationService( getMediationIdentifier() ); | |
} | |
private static MaxAdapterError toMaxError(final AdError adError) | |
{ | |
final int facebookErrorCode = adError.getErrorCode(); | |
final int maxErrorCode; | |
// Facebook's SDK sometimes creates new instances of their pre-defined enums, so we should extract the raw int, and not do pointer equality | |
if ( facebookErrorCode == AdError.NETWORK_ERROR.getErrorCode() ) // 1000 | |
{ | |
maxErrorCode = MaxAdapterError.ERROR_CODE_NO_CONNECTION; // -5207 | |
} | |
else if ( facebookErrorCode == AdError.NO_FILL.getErrorCode() ) // 1001 | |
{ | |
maxErrorCode = MaxAdapterError.ERROR_CODE_NO_FILL; // 204 | |
} | |
else if ( facebookErrorCode == AdError.LOAD_TOO_FREQUENTLY.getErrorCode() ) // 1002 | |
{ | |
maxErrorCode = MaxAdapterError.ERROR_CODE_INVALID_LOAD_STATE; // -5201 | |
} | |
else if ( facebookErrorCode == AdError.SERVER_ERROR.getErrorCode() ) // 2000 | |
{ | |
maxErrorCode = MaxAdapterError.ERROR_CODE_SERVER_ERROR; // -5208 | |
} | |
else if ( facebookErrorCode == AdError.INTERNAL_ERROR.getErrorCode() ) // 2001 - it's actually a timeout event... | |
{ | |
maxErrorCode = MaxAdapterError.ERROR_CODE_TIMEOUT; // -5209 | |
} | |
else | |
{ | |
maxErrorCode = MaxAdapterError.ERROR_CODE_UNSPECIFIED; // -5200 | |
} | |
return new MaxAdapterError( maxErrorCode, facebookErrorCode ); | |
} | |
private String getMediationIdentifier() | |
{ | |
return "APPLOVIN_" + AppLovinSdk.VERSION + ":" + getAdapterVersion(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment