From e25a8b34da0428382c7efdd81796ee53affbc013 Mon Sep 17 00:00:00 2001 From: jenkins Date: Fri, 5 Apr 2024 15:48:27 -0700 Subject: [PATCH] Moloco/2.1.1.0 --- Moloco/.gitignore | 2 + Moloco/CHANGELOG.md | 5 + Moloco/README.md | 1 + Moloco/build.gradle.kts | 65 ++ Moloco/proguard-rules.pro | 17 + .../adapters/MolocoMediationAdapter.java | 907 ++++++++++++++++++ 6 files changed, 997 insertions(+) create mode 100644 Moloco/.gitignore create mode 100644 Moloco/CHANGELOG.md create mode 100644 Moloco/README.md create mode 100644 Moloco/build.gradle.kts create mode 100644 Moloco/proguard-rules.pro create mode 100644 Moloco/src/main/java/com/applovin/mediation/adapters/MolocoMediationAdapter.java diff --git a/Moloco/.gitignore b/Moloco/.gitignore new file mode 100644 index 0000000000..82448ccd92 --- /dev/null +++ b/Moloco/.gitignore @@ -0,0 +1,2 @@ +/target +/build diff --git a/Moloco/CHANGELOG.md b/Moloco/CHANGELOG.md new file mode 100644 index 0000000000..845de3844b --- /dev/null +++ b/Moloco/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## 2.1.1.0 +* Initial commit. +* Minimum AppLovin MAX SDK version 12.3.0. diff --git a/Moloco/README.md b/Moloco/README.md new file mode 100644 index 0000000000..c31ba92b92 --- /dev/null +++ b/Moloco/README.md @@ -0,0 +1 @@ +# Moloco Adapter diff --git a/Moloco/build.gradle.kts b/Moloco/build.gradle.kts new file mode 100644 index 0000000000..7386ef556d --- /dev/null +++ b/Moloco/build.gradle.kts @@ -0,0 +1,65 @@ +plugins { + id("signing") + id("maven-publish") +} + +private val versionMajor = 2 +private val versionMinor = 1 +private val versionPatch = 1 +private val versionAdapterPatch = 0 + +val libraryVersionName by extra("${versionMajor}.${versionMinor}.${versionPatch}.${versionAdapterPatch}") +val libraryVersionCode by extra((versionMajor * 1000000) + (versionMinor * 10000) + (versionPatch * 100) + versionAdapterPatch) + +val libraryArtifactId by extra("moloco-adapter") +val libraryGroupId by extra("com.applovin.mediation") + +var libraryVersions = rootProject.extra["versions"] as Map<*, *> + +android.namespace = "com.applovin.mediation.adapters.moloco" +android.defaultConfig.versionCode = libraryVersionCode +android.defaultConfig.versionName = libraryVersionName +android.defaultConfig.minSdk = 21 + +dependencies { + implementation("com.moloco.sdk:moloco-sdk:${libraryVersions["moloco"]}") + compileOnly("androidx.annotation:annotation:${libraryVersions["annotation"]}") +} + +publishing { + publications { + create(extra["publicationName"] as String) { + // The publication doesn't know about our dependencies, so we have to manually add them to the pom + pom.withXml { + asNode().apply { + appendNode("name", libraryArtifactId) + appendNode("description", "Moloco adapter for AppLovin MAX mediation") + appendNode("url", "https://www.applovin.com/") + appendNode("licenses") + .appendNode("license").apply { + appendNode("name", "AppLovin Corporation Mediation Adapter EULA") + appendNode("url", "https://www.applovin.com/eula") + } + appendNode("scm").apply { + appendNode("connection", "scm:git:github.com/AppLovin/AppLovin-MAX-SDK-Android.git") + appendNode("developerConnection", "scm:git:ssh://github.com/AppLovin/AppLovin-MAX-SDK-Android.git") + appendNode("url", "https://github.com/AppLovin/AppLovin-MAX-SDK-Android") + } + appendNode("developers") + .appendNode("developer").apply { + appendNode("name", "AppLovin") + appendNode("url", "https://www.applovin.com") + } + // Add Moloco network to list of dependencies. + appendNode("dependencies") + .appendNode("dependency").apply { + appendNode("groupId", "com.moloco.sdk") + appendNode("artifactId", "moloco-sdk") + appendNode("version", libraryVersions["moloco"]) + appendNode("scope", "compile") + } + } + } + } + } +} diff --git a/Moloco/proguard-rules.pro b/Moloco/proguard-rules.pro new file mode 100644 index 0000000000..07138e3920 --- /dev/null +++ b/Moloco/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/basil/Library/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/Moloco/src/main/java/com/applovin/mediation/adapters/MolocoMediationAdapter.java b/Moloco/src/main/java/com/applovin/mediation/adapters/MolocoMediationAdapter.java new file mode 100644 index 0000000000..601132f753 --- /dev/null +++ b/Moloco/src/main/java/com/applovin/mediation/adapters/MolocoMediationAdapter.java @@ -0,0 +1,907 @@ +package com.applovin.mediation.adapters; + +import android.app.Activity; +import android.content.Context; +import android.net.Uri; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; + +import com.applovin.impl.sdk.utils.BundleUtils; +import com.applovin.impl.sdk.utils.ImageViewUtils; +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.MaxNativeAdAdapter; +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.MaxNativeAdAdapterListener; +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.mediation.adapters.moloco.BuildConfig; +import com.applovin.mediation.nativeAds.MaxNativeAd; +import com.applovin.mediation.nativeAds.MaxNativeAdView; +import com.applovin.sdk.AppLovinSdk; +import com.applovin.sdk.AppLovinSdkUtils; +import com.moloco.sdk.publisher.AdLoad; +import com.moloco.sdk.publisher.Banner; +import com.moloco.sdk.publisher.BannerAdShowListener; +import com.moloco.sdk.publisher.Initialization; +import com.moloco.sdk.publisher.InterstitialAd; +import com.moloco.sdk.publisher.InterstitialAdShowListener; +import com.moloco.sdk.publisher.MediationInfo; +import com.moloco.sdk.publisher.Moloco; +import com.moloco.sdk.publisher.MolocoAd; +import com.moloco.sdk.publisher.MolocoAdError; +import com.moloco.sdk.publisher.NativeAdForMediation; +import com.moloco.sdk.publisher.RewardedInterstitialAd; +import com.moloco.sdk.publisher.RewardedInterstitialAdShowListener; +import com.moloco.sdk.publisher.init.MolocoInitParams; +import com.moloco.sdk.publisher.privacy.MolocoPrivacy; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public class MolocoMediationAdapter + extends MediationAdapterBase + implements MaxSignalProvider, MaxInterstitialAdapter, MaxRewardedAdapter, MaxAdViewAdapter, MaxNativeAdAdapter +{ + private static final AtomicBoolean initialized = new AtomicBoolean(); + private static InitializationStatus initializationStatus; + + private InterstitialAd interstitialAd; + private RewardedInterstitialAd rewardedAd; + private Banner adView; + private NativeAdForMediation nativeAd; + + private InterstitialAdListener interstitialAdListener; + private RewardedAdListener rewardedAdListener; + + public MolocoMediationAdapter(final AppLovinSdk sdk) { super( sdk ); } + + @Override + public void initialize(final MaxAdapterInitializationParameters parameters, final Activity activity, final OnCompletionListener onCompletionListener) + { + if ( initialized.compareAndSet( false, true ) ) + { + initializationStatus = InitializationStatus.INITIALIZING; + + final Bundle serverParameters = parameters.getServerParameters(); + + final String appKey = serverParameters.getString( "app_key" ); + final MediationInfo mediationInfo = new MediationInfo( "MAX" ); + + final MolocoInitParams initParams = new MolocoInitParams( getContext( activity ), appKey, mediationInfo ); + Moloco.initialize( initParams, initStatus -> { + + if ( initStatus.getInitialization() == Initialization.SUCCESS ) + { + log( "Moloco SDK initialized" ); + initializationStatus = InitializationStatus.INITIALIZED_SUCCESS; + onCompletionListener.onCompletion( initializationStatus, null ); + } + else + { + log( "Moloco SDK failed to initialize with error: " + initStatus.getDescription() ); + initializationStatus = InitializationStatus.INITIALIZED_FAILURE; + onCompletionListener.onCompletion( initializationStatus, initStatus.getDescription() ); + } + } ); + } + else + { + log( "Moloco SDK already initialized" ); + onCompletionListener.onCompletion( initializationStatus, null ); + } + } + + @Override + public String getSdkVersion() + { + return getVersionString( com.moloco.sdk.BuildConfig.class, "SDK_VERSION_NAME" ); + } + + @Override + public String getAdapterVersion() + { + return BuildConfig.VERSION_NAME; + } + + @Override + public boolean isBeta() + { + return true; + } + + @Override + public void onDestroy() + { + if ( interstitialAd != null ) + { + interstitialAd.destroy(); + interstitialAd = null; + interstitialAdListener = null; + } + + if ( rewardedAd != null ) + { + rewardedAd.destroy(); + rewardedAd = null; + rewardedAdListener = null; + } + + if ( adView != null ) + { + adView.destroy(); + adView = null; + } + + if ( nativeAd != null ) + { + nativeAd.destroy(); + nativeAd = null; + } + } + + //region MAX Signal Provider Methods + + @Override + public void collectSignal(final MaxAdapterSignalCollectionParameters parameters, final Activity activity, final MaxSignalCollectionListener callback) + { + log( "Collecting signal" ); + + updatePrivacyPreferences( parameters ); + + Moloco.getBidToken( (signal, errorType) -> { + + if ( errorType == null ) + { + log( "Signal collection successful" ); + callback.onSignalCollected( signal ); + } + else + { + log( "Signal collection failed: " + errorType.getDescription() ); + callback.onSignalCollectionFailed( errorType.getDescription() ); + } + } ); + } + + //endregion + + //region MAX Interstitial Adapter Methods + + @Override + public void loadInterstitialAd(final MaxAdapterResponseParameters parameters, final Activity activity, final MaxInterstitialAdapterListener listener) + { + final String placementId = parameters.getThirdPartyAdPlacementId(); + + log( "Loading interstitial ad: " + placementId ); + + if ( activity == null ) + { + log( "Interstitial ad load failed: Activity is null" ); + + final MaxAdapterError error = new MaxAdapterError( MaxAdapterError.ERROR_CODE_MISSING_ACTIVITY, "Missing Activity" ); + listener.onInterstitialAdLoadFailed( error ); + + return; + } + + updatePrivacyPreferences( parameters ); + + interstitialAd = Moloco.createInterstitial( activity, placementId ); + if ( interstitialAd == null ) + { + listener.onInterstitialAdLoadFailed( MaxAdapterError.INVALID_CONFIGURATION ); + return; + } + + interstitialAdListener = new InterstitialAdListener( listener ); + interstitialAd.load( parameters.getBidResponse(), interstitialAdListener ); + } + + @Override + public void showInterstitialAd(final MaxAdapterResponseParameters parameters, final Activity activity, final MaxInterstitialAdapterListener listener) + { + log( "Showing interstitial ad: " + parameters.getThirdPartyAdPlacementId() ); + + if ( !interstitialAd.isLoaded() ) + { + log( "Unable to show interstitial - ad not ready" ); + listener.onInterstitialAdDisplayFailed( MaxAdapterError.AD_NOT_READY ); + + return; + } + + interstitialAd.show( interstitialAdListener ); + } + + //endregion + + //region MAX Rewarded Adapter Methods + + @Override + public void loadRewardedAd(final MaxAdapterResponseParameters parameters, final Activity activity, final MaxRewardedAdapterListener listener) + { + final String placementId = parameters.getThirdPartyAdPlacementId(); + + log( "Loading rewarded ad: " + placementId ); + + if ( activity == null ) + { + log( "Rewarded ad load failed: Activity is null" ); + + final MaxAdapterError error = new MaxAdapterError( MaxAdapterError.ERROR_CODE_MISSING_ACTIVITY, "Missing Activity" ); + listener.onRewardedAdLoadFailed( error ); + + return; + } + + updatePrivacyPreferences( parameters ); + + rewardedAd = Moloco.createRewardedInterstitial( activity, placementId ); + if ( rewardedAd == null ) + { + listener.onRewardedAdLoadFailed( MaxAdapterError.INVALID_CONFIGURATION ); + return; + } + + rewardedAdListener = new RewardedAdListener( listener ); + rewardedAd.load( parameters.getBidResponse(), rewardedAdListener ); + } + + @Override + public void showRewardedAd(final MaxAdapterResponseParameters parameters, final Activity activity, final MaxRewardedAdapterListener listener) + { + log( "Showing rewarded ad: " + parameters.getThirdPartyAdPlacementId() ); + + if ( !rewardedAd.isLoaded() ) + { + log( "Unable to show rewarded ad - ad not ready" ); + listener.onRewardedAdDisplayFailed( MaxAdapterError.AD_NOT_READY ); + + return; + } + + configureReward( parameters ); + + rewardedAd.show( rewardedAdListener ); + } + + //endregion + + //region MAX Ad View Adapter Methods + + @Override + public void loadAdViewAd(final MaxAdapterResponseParameters parameters, final MaxAdFormat adFormat, final Activity activity, final MaxAdViewAdapterListener listener) + { + final String placementId = parameters.getThirdPartyAdPlacementId(); + final boolean isNative = parameters.getServerParameters().getBoolean( "is_native" ); + + log( "Loading " + ( isNative ? "native " : "" ) + adFormat.getLabel() + " ad: " + placementId ); + + if ( activity == null ) + { + log( adFormat.getLabel() + " ad load failed: Activity is null" ); + + final MaxAdapterError error = new MaxAdapterError( MaxAdapterError.ERROR_CODE_MISSING_ACTIVITY, "Missing Activity" ); + listener.onAdViewAdLoadFailed( error ); + + return; + } + + updatePrivacyPreferences( parameters ); + + if ( isNative ) + { + nativeAd = Moloco.createNativeAd( activity, placementId ); + if ( nativeAd == null ) + { + listener.onAdViewAdLoadFailed( MaxAdapterError.INVALID_CONFIGURATION ); + return; + } + + final NativeAdViewListener nativeAdViewListener = new NativeAdViewListener( adFormat, parameters, getContext( activity ), listener ); + nativeAd.load( parameters.getBidResponse(), nativeAdViewListener ); + nativeAd.setInteractionListener( nativeAdViewListener ); + } + else + { + if ( adFormat == MaxAdFormat.BANNER ) + { + adView = Moloco.createBanner( activity, placementId ); + } + else if ( adFormat == MaxAdFormat.LEADER ) + { + adView = Moloco.createBannerTablet( activity, placementId ); + } + else if ( adFormat == MaxAdFormat.MREC ) + { + adView = Moloco.createMREC( activity, placementId ); + } + else + { + throw new IllegalArgumentException( "Unsupported ad format: " + adFormat ); + } + + if ( adView == null ) + { + listener.onAdViewAdLoadFailed( MaxAdapterError.INVALID_CONFIGURATION ); + return; + } + + final AdViewAdListener adViewAdListener = new AdViewAdListener( listener ); + adView.setAdShowListener( adViewAdListener ); + adView.load( parameters.getBidResponse(), adViewAdListener ); + } + } + + //endregion + + //region MAX Native Ad Adapter Methods + + @Override + public void loadNativeAd(final MaxAdapterResponseParameters parameters, final Activity activity, final MaxNativeAdAdapterListener listener) + { + final String placementId = parameters.getThirdPartyAdPlacementId(); + + log( "Loading native ad: " + placementId ); + + if ( activity == null ) + { + log( "Native ad load failed: Activity is null" ); + + final MaxAdapterError error = new MaxAdapterError( MaxAdapterError.ERROR_CODE_MISSING_ACTIVITY, "Missing Activity" ); + listener.onNativeAdLoadFailed( error ); + + return; + } + + updatePrivacyPreferences( parameters ); + + nativeAd = Moloco.createNativeAd( activity, placementId ); + if ( nativeAd == null ) + { + listener.onNativeAdLoadFailed( MaxAdapterError.INVALID_CONFIGURATION ); + return; + } + + final NativeAdListener nativeAdListener = new NativeAdListener( parameters, getContext( activity ), listener ); + nativeAd.setInteractionListener( nativeAdListener ); + nativeAd.load( parameters.getBidResponse(), nativeAdListener ); + } + + //region Helper Methods + + private MaxAdapterError toMaxError(final MolocoAdError molocoAdError) + { + MaxAdapterError adapterError = MaxAdapterError.UNSPECIFIED; + switch ( molocoAdError.getErrorType() ) + { + case UNKNOWN: + break; + case SDK_INIT_ERROR: + case AD_LOAD_FAILED_SDK_NOT_INIT: + adapterError = MaxAdapterError.NOT_INITIALIZED; + break; + case SDK_PERSISTENT_HTTP_REQUEST_FAILED_TO_INIT: + adapterError = MaxAdapterError.BAD_REQUEST; + break; + case SDK_INVALID_CONFIGURATION: + adapterError = MaxAdapterError.INVALID_CONFIGURATION; + break; + case AD_LOAD_FAILED: + adapterError = MaxAdapterError.NO_FILL; + break; + case AD_LOAD_TIMEOUT_ERROR: + adapterError = MaxAdapterError.TIMEOUT; + break; + case AD_LOAD_LIMIT_REACHED: + adapterError = MaxAdapterError.AD_FREQUENCY_CAPPED; + break; + case AD_SHOW_ERROR: + case AD_SHOW_ERROR_ALREADY_DISPLAYING: + adapterError = MaxAdapterError.AD_DISPLAY_FAILED; + break; + case AD_SHOW_ERROR_NOT_LOADED: + adapterError = MaxAdapterError.AD_NOT_READY; + break; + case AD_BID_PARSE_ERROR: + adapterError = MaxAdapterError.INVALID_LOAD_STATE; + break; + case AD_SIGNAL_COLLECTION_FAILED: + adapterError = MaxAdapterError.SIGNAL_COLLECTION_TIMEOUT; + break; + } + + return new MaxAdapterError( adapterError, + molocoAdError.getErrorType().getErrorCode(), + molocoAdError.getDescription() ); + } + + private void updatePrivacyPreferences(final MaxAdapterParameters parameters) + { + final Boolean hasUserConsent = parameters.hasUserConsent(); + final Boolean isAgeRestrictedUser = parameters.isAgeRestrictedUser(); + final Boolean isDoNotSell = parameters.isDoNotSell(); + + final MolocoPrivacy.PrivacySettings privacySettings = new MolocoPrivacy.PrivacySettings( hasUserConsent, isAgeRestrictedUser, isDoNotSell ); + MolocoPrivacy.INSTANCE.setPrivacy( privacySettings ); + } + + private List getClickableViews(final MaxNativeAdView maxNativeAdView) + { + final List clickableViews = new ArrayList<>( 6 ); + if ( maxNativeAdView.getTitleTextView() != null ) clickableViews.add( maxNativeAdView.getTitleTextView() ); + if ( maxNativeAdView.getAdvertiserTextView() != null ) clickableViews.add( maxNativeAdView.getAdvertiserTextView() ); + if ( maxNativeAdView.getBodyTextView() != null ) clickableViews.add( maxNativeAdView.getBodyTextView() ); + if ( maxNativeAdView.getCallToActionButton() != null ) clickableViews.add( maxNativeAdView.getCallToActionButton() ); + if ( maxNativeAdView.getIconImageView() != null ) clickableViews.add( maxNativeAdView.getIconImageView() ); + if ( maxNativeAdView.getMediaContentViewGroup() != null ) clickableViews.add( maxNativeAdView.getMediaContentViewGroup() ); + + return clickableViews; + } + + private Context getContext(@Nullable final Activity activity) + { + return ( activity != null ) ? activity.getApplicationContext() : getApplicationContext(); + } + + //endregion + + private class InterstitialAdListener + implements AdLoad.Listener, InterstitialAdShowListener + { + private final MaxInterstitialAdapterListener listener; + + public InterstitialAdListener(final MaxInterstitialAdapterListener listener) + { + this.listener = listener; + } + + @Override + public void onAdLoadSuccess(@NonNull final MolocoAd molocoAd) + { + log( "Interstitial ad loaded" ); + listener.onInterstitialAdLoaded(); + } + + @Override + public void onAdLoadFailed(@NonNull final MolocoAdError molocoAdError) + { + final MaxAdapterError adapterError = toMaxError( molocoAdError ); + log( "Interstitial ad failed to load with error: " + adapterError ); + listener.onInterstitialAdLoadFailed( adapterError ); + } + + @Override + public void onAdShowSuccess(@NonNull final MolocoAd molocoAd) + { + log( "Interstitial ad displayed" ); + listener.onInterstitialAdDisplayed(); + } + + @Override + public void onAdShowFailed(@NonNull final MolocoAdError molocoAdError) + { + final MaxAdapterError adapterError = toMaxError( molocoAdError ); + log( "Interstitial ad failed to display with error: " + adapterError ); + listener.onInterstitialAdDisplayFailed( adapterError ); + } + + @Override + public void onAdClicked(@NonNull final MolocoAd molocoAd) + { + log( "Interstitial ad clicked" ); + listener.onInterstitialAdClicked(); + } + + @Override + public void onAdHidden(@NonNull final MolocoAd molocoAd) + { + log( "Interstitial ad hidden" ); + listener.onInterstitialAdHidden(); + } + } + + private class RewardedAdListener + implements AdLoad.Listener, RewardedInterstitialAdShowListener + { + private final MaxRewardedAdapterListener listener; + + private boolean hasGrantedReward; + + public RewardedAdListener(final MaxRewardedAdapterListener listener) + { + this.listener = listener; + } + + @Override + public void onAdLoadSuccess(@NonNull final MolocoAd molocoAd) + { + log( "Rewarded ad loaded" ); + listener.onRewardedAdLoaded(); + } + + @Override + public void onAdLoadFailed(@NonNull final MolocoAdError molocoAdError) + { + final MaxAdapterError adapterError = toMaxError( molocoAdError ); + log( "Rewarded ad failed to load with error: " + adapterError ); + listener.onRewardedAdLoadFailed( adapterError ); + } + + @Override + public void onAdShowSuccess(@NonNull final MolocoAd molocoAd) + { + log( "Rewarded ad displayed" ); + listener.onRewardedAdDisplayed(); + } + + @Override + public void onAdShowFailed(@NonNull final MolocoAdError molocoAdError) + { + final MaxAdapterError adapterError = toMaxError( molocoAdError ); + log( "Rewarded ad failed to display error: " + adapterError ); + listener.onRewardedAdDisplayFailed( adapterError ); + } + + @Override + public void onAdClicked(@NonNull final MolocoAd molocoAd) + { + log( "Rewarded ad clicked" ); + listener.onRewardedAdClicked(); + } + + @Override + public void onRewardedVideoStarted(@NonNull final MolocoAd molocoAd) + { + log( "Rewarded video started" ); + } + + @Override + public void onRewardedVideoCompleted(@NonNull final MolocoAd molocoAd) + { + log( "Rewarded video completed" ); + } + + @Override + public void onUserRewarded(@NonNull final MolocoAd molocoAd) + { + log( "Rewarded video granted reward" ); + hasGrantedReward = true; + } + + @Override + public void onAdHidden(@NonNull final MolocoAd molocoAd) + { + if ( hasGrantedReward || shouldAlwaysRewardUser() ) + { + final MaxReward reward = getReward(); + log( "Rewarded user with reward: " + reward ); + listener.onUserRewarded( reward ); + } + + log( "Rewarded ad hidden" ); + listener.onRewardedAdHidden(); + } + } + + private class AdViewAdListener + implements AdLoad.Listener, BannerAdShowListener + { + private final MaxAdViewAdapterListener listener; + + public AdViewAdListener(final MaxAdViewAdapterListener listener) + { + this.listener = listener; + } + + @Override + public void onAdLoadSuccess(@NonNull final MolocoAd molocoAd) + { + log( "AdView ad loaded" ); + listener.onAdViewAdLoaded( adView ); + } + + @Override + public void onAdLoadFailed(@NonNull final MolocoAdError molocoAdError) + { + final MaxAdapterError adapterError = toMaxError( molocoAdError ); + log( "AdView ad failed to load with error: " + adapterError ); + listener.onAdViewAdLoadFailed( adapterError ); + } + + @Override + public void onAdShowSuccess(@NonNull final MolocoAd molocoAd) + { + log( "AdView ad displayed" ); + listener.onAdViewAdDisplayed(); + } + + @Override + public void onAdShowFailed(@NonNull final MolocoAdError molocoAdError) + { + final MaxAdapterError adapterError = toMaxError( molocoAdError ); + log( "AdView ad failed to display with error: " + adapterError ); + listener.onAdViewAdDisplayFailed( adapterError ); + } + + @Override + public void onAdClicked(@NonNull final MolocoAd molocoAd) + { + log( "AdView ad clicked" ); + listener.onAdViewAdClicked(); + } + + @Override + public void onAdHidden(@NonNull final MolocoAd molocoAd) { } + } + + private class NativeAdViewListener + implements AdLoad.Listener, NativeAdForMediation.InteractionListener + { + private final MaxAdFormat adFormat; + private final Bundle serverParameters; + private final Context context; + private final MaxAdViewAdapterListener listener; + + NativeAdViewListener(final MaxAdFormat adFormat, final MaxAdapterResponseParameters parameters, final Context context, final MaxAdViewAdapterListener listener) + { + this.adFormat = adFormat; + this.serverParameters = parameters.getServerParameters(); + this.context = context; + this.listener = listener; + } + + @Override + public void onAdLoadSuccess(@NonNull final MolocoAd molocoAd) + { + if ( nativeAd == null ) + { + e( "Native " + adFormat.getLabel() + " ad is null" ); + return; + } + + log( "Native " + adFormat.getLabel() + " ad loaded" ); + + if ( TextUtils.isEmpty( nativeAd.getTitle() ) ) + { + e( "Native " + adFormat.getLabel() + " ad (" + nativeAd + ") does not have required assets." ); + listener.onAdViewAdLoadFailed( MaxAdapterError.MISSING_REQUIRED_NATIVE_AD_ASSETS ); + + return; + } + + final MaxNativeAd.Builder builder = new MaxNativeAd.Builder() + .setAdFormat( adFormat ) + .setTitle( nativeAd.getTitle() ) + .setBody( nativeAd.getDescription() ) + .setAdvertiser( nativeAd.getSponsorText() ) + .setCallToAction( nativeAd.getCallToActionText() ) + .setStarRating( nativeAd.getRating() != null ? nativeAd.getRating().doubleValue() : null ); + + if ( nativeAd.getIconUri() != null ) + { + // getIconUri() returns a Uri string without the "file://" prefix. + final Uri iconUri = Uri.fromFile( new File( nativeAd.getIconUri() ) ); + builder.setIcon( new MaxNativeAd.MaxNativeAdImage( iconUri ) ); + } + + if ( nativeAd.getVideo() != null ) + { + builder.setMediaView( nativeAd.getVideo() ); + } + else if ( nativeAd.getMainImageUri() != null ) + { + // getMainImageUri() returns a Uri string without the "file://" prefix. + final Uri mainImageUri = Uri.fromFile( new File( nativeAd.getMainImageUri() ) ); + + final ImageView imageView = new ImageView( context ); + ImageViewUtils.setImageUri( imageView, mainImageUri, null ); + + builder.setMediaView( imageView ); + builder.setMainImage( new MaxNativeAd.MaxNativeAdImage( mainImageUri ) ); + } + + final MaxMolocoNativeAd maxMolocoNativeAd = new MaxMolocoNativeAd( builder ); + final String templateName = BundleUtils.getString( "template", "", serverParameters ); + final MaxNativeAdView maxNativeAdView = new MaxNativeAdView( maxMolocoNativeAd, + AppLovinSdkUtils.isValidString( templateName ) ? templateName : "media_banner_template", + getApplicationContext() ); + + maxMolocoNativeAd.prepareForInteraction( getClickableViews( maxNativeAdView ), maxNativeAdView ); + listener.onAdViewAdLoaded( maxNativeAdView ); + + nativeAd.handleImpression(); + } + + @Override + public void onAdLoadFailed(@NonNull final MolocoAdError molocoAdError) + { + final MaxAdapterError adapterError = toMaxError( molocoAdError ); + log( "Native " + adFormat.getLabel() + " ad failed to load with error: " + adapterError ); + listener.onAdViewAdLoadFailed( adapterError ); + } + + @Override + public void onImpressionHandled() + { + log( "Native " + adFormat.getLabel() + " ad displayed" ); + listener.onAdViewAdDisplayed(); + } + + @Override + public void onGeneralClickHandled() + { + log( "Native " + adFormat.getLabel() + " ad clicked" ); + listener.onAdViewAdClicked(); + } + } + + private class NativeAdListener + implements AdLoad.Listener, NativeAdForMediation.InteractionListener + { + private final Bundle serverParameters; + private final Context context; + private final MaxNativeAdAdapterListener listener; + + public NativeAdListener(final MaxAdapterResponseParameters parameters, final Context context, final MaxNativeAdAdapterListener listener) + { + this.serverParameters = parameters.getServerParameters(); + this.context = context; + this.listener = listener; + } + + @Override + public void onAdLoadSuccess(@NonNull final MolocoAd molocoAd) + { + if ( nativeAd == null ) + { + e( "Native ad is null" ); + return; + } + + log( "Native ad loaded" ); + + final String templateName = BundleUtils.getString( "template", "", serverParameters ); + final boolean isTemplateAd = AppLovinSdkUtils.isValidString( templateName ); + + if ( isTemplateAd && TextUtils.isEmpty( nativeAd.getTitle() ) ) + { + e( "Native ad (" + nativeAd + ") does not have required assets." ); + listener.onNativeAdLoadFailed( MaxAdapterError.MISSING_REQUIRED_NATIVE_AD_ASSETS ); + + return; + } + + final MaxNativeAd.Builder builder = new MaxNativeAd.Builder() + .setAdFormat( MaxAdFormat.NATIVE ) + .setTitle( nativeAd.getTitle() ) + .setBody( nativeAd.getDescription() ) + .setAdvertiser( nativeAd.getSponsorText() ) + .setCallToAction( nativeAd.getCallToActionText() ) + .setStarRating( nativeAd.getRating() != null ? nativeAd.getRating().doubleValue() : null ); + + if ( nativeAd.getIconUri() != null ) + { + // getIconUri() returns a Uri string without the "file://" prefix. + final Uri iconUri = Uri.fromFile( new File( nativeAd.getIconUri() ) ); + builder.setIcon( new MaxNativeAd.MaxNativeAdImage( iconUri ) ); + } + + if ( nativeAd.getVideo() != null ) + { + builder.setMediaView( nativeAd.getVideo() ); + } + else if ( nativeAd.getMainImageUri() != null ) + { + // getMainImageUri() returns a Uri string without the "file://" prefix. + final Uri mainImageUri = Uri.fromFile( new File( nativeAd.getMainImageUri() ) ); + + final ImageView imageView = new ImageView( context ); + ImageViewUtils.setImageUri( imageView, mainImageUri, null ); + + builder.setMediaView( imageView ); + builder.setMainImage( new MaxNativeAd.MaxNativeAdImage( mainImageUri ) ); + } + + final MaxNativeAd maxNativeAd = new MaxMolocoNativeAd( builder ); + listener.onNativeAdLoaded( maxNativeAd, null ); + } + + @Override + public void onAdLoadFailed(@NonNull final MolocoAdError molocoAdError) + { + final MaxAdapterError adapterError = toMaxError( molocoAdError ); + log( "Native ad failed to load with error: " + adapterError ); + listener.onNativeAdLoadFailed( adapterError ); + } + + @Override + public void onImpressionHandled() + { + log( "Native ad displayed" ); + listener.onNativeAdDisplayed( null ); + } + + @Override + public void onGeneralClickHandled() + { + log( "Native ad clicked" ); + listener.onNativeAdClicked(); + } + } + + private class MaxMolocoNativeAd + extends MaxNativeAd + { + public MaxMolocoNativeAd(final Builder builder) + { + super( builder ); + } + + @Override + public void prepareViewForInteraction(final MaxNativeAdView maxNativeAdView) + { + prepareForInteraction( MolocoMediationAdapter.this.getClickableViews( maxNativeAdView ), maxNativeAdView ); + } + + @Override + public boolean prepareForInteraction(final List clickableViews, final ViewGroup container) + { + final NativeAdForMediation nativeAd = MolocoMediationAdapter.this.nativeAd; + if ( nativeAd == null ) + { + e( "Failed to register native ad view: native ad is null." ); + return false; + } + + d( "Preparing views for interaction: " + clickableViews + " with container: " + container ); + + for ( final View clickableView : clickableViews ) + { + clickableView.setOnClickListener( view -> { + + if ( view instanceof ImageView ) + { + nativeAd.handleIconClick(); + } + else + { + nativeAd.handleGeneralAdClick(); + } + } ); + } + + final View mediaView = getMediaView(); + if ( mediaView != null ) + { + mediaView.setOnClickListener( view -> nativeAd.handleMainImageClick() ); + } + + if ( getFormat() == MaxAdFormat.NATIVE ) + { + nativeAd.handleImpression(); + } + + return true; + } + } +}