package com.google.ads.mediation.nend; import android.app.Activity; import android.content.Context; import android.content.res.Resources; import android.os.Bundle; import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Log; import android.view.Gravity; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.webkit.WebView; import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.google.android.gms.ads.AdRequest; import com.google.android.gms.ads.AdSize; import com.google.android.gms.ads.MediationUtils; import com.google.android.gms.ads.mediation.MediationAdRequest; import com.google.android.gms.ads.mediation.MediationBannerAdapter; import com.google.android.gms.ads.mediation.MediationBannerListener; import com.google.android.gms.ads.mediation.MediationInterstitialAdapter; import com.google.android.gms.ads.mediation.MediationInterstitialListener; import com.google.android.gms.ads.mediation.OnContextChangedListener; import java.lang.ref.WeakReference; import java.util.ArrayList; import net.nend.android.NendAdInformationListener; import net.nend.android.NendAdInterstitial; import net.nend.android.NendAdInterstitial.NendAdInterstitialClickType; import net.nend.android.NendAdInterstitial.NendAdInterstitialShowResult; import net.nend.android.NendAdInterstitial.NendAdInterstitialStatusCode; import net.nend.android.NendAdInterstitial.OnCompletionListener; import net.nend.android.NendAdInterstitialVideo; import net.nend.android.NendAdVideo; import net.nend.android.NendAdVideoListener; import net.nend.android.NendAdView; import net.nend.android.NendAdView.NendError; /** The {@link NendAdapter} class is used to load and show Nend interstitial and banner ads. */ @SuppressWarnings("unused") public class NendAdapter extends NendMediationAdapter implements MediationBannerAdapter, MediationInterstitialAdapter, NendAdInformationListener, OnContextChangedListener { private NendAdView mNendAdView; private MediationBannerListener mListener; private NendAdInterstitialVideo mNendAdInterstitialVideo; private MediationInterstitialListener mListenerInterstitial; private FrameLayout bannerContainerView; static final String KEY_INTERSTITIAL_TYPE = "key_interstitial_type"; public enum InterstitialType { TYPE_VIDEO, TYPE_NORMAL } private enum InterstitialVideoStatus { PLAYING, PLAYING_WHEN_CLICKED, STOPPED } private InterstitialVideoStatus mInterstitialVideoStatus = InterstitialVideoStatus.PLAYING; private WeakReference<Activity> mActivityWeakReference; private boolean mIsDetached = false; private boolean mIsRequireLoadAd = false; private boolean mIsRequestBannerAd = false; private boolean mIsPausingWebView = false; private View.OnAttachStateChangeListener mAttachStateChangeListener = new View.OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View view) { mNendAdView.setListener(NendAdapter.this); if (mIsRequireLoadAd) { mNendAdView.loadAd(); mIsRequireLoadAd = false; } mIsDetached = false; } @Override public void onViewDetachedFromWindow(View view) { mIsDetached = true; } }; private void requireLoadAd() { if (mIsDetached && !mIsRequireLoadAd) { mIsRequireLoadAd = true; } } @Override public void onDestroy() { bannerContainerView = null; mNendAdView = null; mListener = null; mListenerInterstitial = null; if (mActivityWeakReference != null) { mActivityWeakReference.clear(); mActivityWeakReference = null; } if (mNendAdInterstitialVideo != null) { mNendAdInterstitialVideo.releaseAd(); mNendAdInterstitialVideo = null; } } @Override public void onPause() { if (mNendAdView != null) { if (mNendAdView.getChildAt(0) instanceof WebView) { mIsPausingWebView = true; } mNendAdView.pause(); requireLoadAd(); } } @Override public void onResume() { if (mNendAdView != null) { if (mIsPausingWebView) { mNendAdView.resume(); } requireLoadAd(); mIsPausingWebView = false; } } @Override public void requestInterstitialAd( Context context, MediationInterstitialListener listener, Bundle serverParameters, MediationAdRequest mediationAdRequest, Bundle mediationExtras) { mListenerInterstitial = listener; if (!(context instanceof Activity)) { Log.w(TAG, "Failed to request ad from Nend: Context not an Activity."); adFailedToLoad(AdRequest.ERROR_CODE_INVALID_REQUEST); return; } AdUnitMapper mapper = AdUnitMapper.createAdUnitMapper(serverParameters); if (mapper == null) { Log.w(TAG, "Failed to request ad from Nend: Invalid Spot ID or API Key."); adFailedToLoad(AdRequest.ERROR_CODE_INVALID_REQUEST); return; } mActivityWeakReference = new WeakReference<>((Activity) context); if (mediationExtras != null) { final InterstitialType type = (InterstitialType) mediationExtras.getSerializable(KEY_INTERSTITIAL_TYPE); if (type == InterstitialType.TYPE_VIDEO) { requestNendInterstialVideo( context, mapper.apiKey, mapper.spotId, mediationExtras.getString(KEY_USER_ID, ""), mediationAdRequest); return; } } requestNendInterstitial(context, mapper.apiKey, mapper.spotId); } private void requestNendInterstitial(Context context, String apikey, int spotId) { NendAdInterstitial.loadAd(context, apikey, spotId); NendAdInterstitial.isAutoReloadEnabled = false; NendAdInterstitial.setListener( new OnCompletionListener() { @Override public void onCompletion(NendAdInterstitialStatusCode status) { switch (status) { case SUCCESS: adLoaded(); break; case FAILED_AD_DOWNLOAD: adFailedToLoad(AdRequest.ERROR_CODE_INVALID_REQUEST); break; case FAILED_AD_REQUEST: adFailedToLoad(AdRequest.ERROR_CODE_INVALID_REQUEST); break; case INVALID_RESPONSE_TYPE: adFailedToLoad(AdRequest.ERROR_CODE_INTERNAL_ERROR); break; } } }); } private void requestNendInterstialVideo( Context context, String apikey, int spotId, String userId, MediationAdRequest mediationAdRequest) { mNendAdInterstitialVideo = new NendAdInterstitialVideo(context, spotId, apikey); mNendAdInterstitialVideo.setMediationName(MEDIATION_NAME_ADMOB); if (!TextUtils.isEmpty(userId)) { mNendAdInterstitialVideo.setUserId(userId); } mNendAdInterstitialVideo.setAdListener( new NendAdVideoListener() { @Override public void onLoaded(@NonNull NendAdVideo nendAdVideo) { adLoaded(); } @Override public void onFailedToLoad(@NonNull NendAdVideo nendAdVideo, int errorCode) { adFailedToLoad(ErrorUtil.convertErrorCodeFromNendVideoToAdMob(errorCode)); } @Override public void onFailedToPlay(@NonNull NendAdVideo nendAdVideo) { Log.w(TAG, "Interstitial video ad failed to play..."); } @Override public void onShown(@NonNull NendAdVideo nendAdVideo) { adOpened(); } @Override public void onClosed(@NonNull NendAdVideo nendAdVideo) { adClosed(); if (mInterstitialVideoStatus == InterstitialVideoStatus.PLAYING_WHEN_CLICKED) { adLeftApplication(); } } @Override public void onStarted(@NonNull NendAdVideo nendAdVideo) { mInterstitialVideoStatus = InterstitialVideoStatus.PLAYING; } @Override public void onStopped(@NonNull NendAdVideo nendAdVideo) { if (mInterstitialVideoStatus != InterstitialVideoStatus.PLAYING_WHEN_CLICKED) { mInterstitialVideoStatus = InterstitialVideoStatus.STOPPED; } } @Override public void onCompleted(@NonNull NendAdVideo nendAdVideo) { mInterstitialVideoStatus = InterstitialVideoStatus.STOPPED; } @Override public void onAdClicked(@NonNull NendAdVideo nendAdVideo) { adClicked(); switch (mInterstitialVideoStatus) { case PLAYING: case PLAYING_WHEN_CLICKED: mInterstitialVideoStatus = InterstitialVideoStatus.PLAYING_WHEN_CLICKED; break; default: adLeftApplication(); break; } } @Override public void onInformationClicked(@NonNull NendAdVideo nendAdVideo) { adLeftApplication(); } }); mNendAdInterstitialVideo.loadAd(); } @Override public void showInterstitial() { if (mNendAdInterstitialVideo != null) { showNendAdInterstitialVideo(); } else { showNendAdInterstitial(); } } private void showNendAdInterstitial() { if (mActivityWeakReference == null || mActivityWeakReference.get() == null) { Log.e( TAG, "Failed to show Nend Interstitial ad: " + "An activity context is required to show Nend ads."); return; } NendAdInterstitialShowResult result = NendAdInterstitial.showAd( mActivityWeakReference.get(), new NendAdInterstitial.OnClickListener() { @Override public void onClick(NendAdInterstitialClickType clickType) { switch (clickType) { case CLOSE: adClosed(); break; case DOWNLOAD: adClicked(); adLeftApplication(); adClosed(); break; case INFORMATION: adLeftApplication(); adClosed(); break; default: break; } } }); switch (result) { case AD_SHOW_SUCCESS: adOpened(); break; case AD_SHOW_ALREADY: break; case AD_FREQUENCY_NOT_REACHABLE: break; case AD_REQUEST_INCOMPLETE: break; case AD_LOAD_INCOMPLETE: // Request is not start yet or requesting now. break; case AD_DOWNLOAD_INCOMPLETE: break; default: break; } } private void showNendAdInterstitialVideo() { if (mNendAdInterstitialVideo.isLoaded()) { if (mActivityWeakReference != null && mActivityWeakReference.get() != null) { mNendAdInterstitialVideo.showAd(mActivityWeakReference.get()); } else { Log.e( TAG, "Failed to show Nend Interstitial ad: " + "An activity context is required to show Nend ads."); } } else { Log.w(TAG, "Nend Interstitial video ad is not ready."); } } @Override public View getBannerView() { return bannerContainerView; } @Override public void requestBannerAd( final Context context, MediationBannerListener listener, Bundle serverParameters, AdSize adSize, MediationAdRequest mediationAdRequest, Bundle mediationExtras) { final AdSize supportedAdSize = getSupportedAdSize(context, adSize); if (supportedAdSize == null) { Log.w(TAG, "Failed to request ad, Unsupported ad size: " + adSize.toString()); listener.onAdFailedToLoad(this, AdRequest.ERROR_CODE_INVALID_REQUEST); return; } AdUnitMapper mapper = AdUnitMapper.createAdUnitMapper(serverParameters); if (mapper == null) { Log.w(TAG, "Failed to request ad from Nend: Invalid Spot ID or API Key."); listener.onAdFailedToLoad(this, AdRequest.ERROR_CODE_INTERNAL_ERROR); return; } mNendAdView = new NendAdView(context, mapper.spotId, mapper.apiKey); bannerContainerView = new FrameLayout(context); FrameLayout.LayoutParams containerViewParams = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); bannerContainerView.setLayoutParams(containerViewParams); int adViewParamHeight = adSize.getHeightInPixels(context); if (adViewParamHeight <= 0) { adViewParamHeight = LayoutParams.WRAP_CONTENT; } FrameLayout.LayoutParams adViewParams = new FrameLayout.LayoutParams(adSize.getWidthInPixels(context), adViewParamHeight); adViewParams.gravity = Gravity.CENTER; bannerContainerView.addView(mNendAdView, adViewParams); mListener = listener; // NOTE: Use the reload function of AdMob mediation instead of NendAdView. // So, reload function of NendAdView should be stopped. mNendAdView.pause(); mNendAdView.setListener(this); mNendAdView.addOnAttachStateChangeListener(mAttachStateChangeListener); mNendAdView.loadAd(); mIsRequestBannerAd = true; } // region Banner ad utility methods. @Nullable private AdSize getSupportedAdSize(@NonNull Context context, @NonNull AdSize adSize) { // Check if the specified adSize is a Smart banner since nend supports any Smart banner with at // least a height of 50. if (adSize.getWidth() == AdSize.FULL_WIDTH && adSize.getHeight() == AdSize.AUTO_HEIGHT) { DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics(); final int heightPixel = adSize.getHeightInPixels(context); final int heightDip = Math.round(heightPixel / displayMetrics.density); if (heightDip >= 50) { return adSize; } } /* Supported Sizes: https://github.com/fan-ADN/nendSDK-Android/wiki/About-Ad-Sizes#available-ad-sizes-are-listed-below. 320 × 50 320 × 100 300 × 100 300 × 250 728 × 90 */ ArrayList<AdSize> potentials = new ArrayList<>(); potentials.add(AdSize.BANNER); potentials.add(AdSize.LARGE_BANNER); potentials.add(new AdSize(300, 100)); potentials.add(AdSize.MEDIUM_RECTANGLE); potentials.add(AdSize.LEADERBOARD); return MediationUtils.findClosestSize(context, adSize, potentials); } // endregion // region NendAdListener callbacks. @Override public void onReceiveAd(@NonNull NendAdView adView) { if (mListener != null && mIsRequestBannerAd) { // New request or auto reload from AdMob network. mListener.onAdLoaded(this); mIsRequestBannerAd = false; } else { // This case is not need to send onAdLoaded to AdMob network. Log.d(TAG, "This ad is auto reloading by nend network."); } } @Override public void onFailedToReceiveAd(@NonNull NendAdView adView) { if (!mIsRequestBannerAd) { // This case is not need to call listener function to AdMob network. return; } mIsRequestBannerAd = false; NendError nendError = adView.getNendError(); if (mListener != null) { switch (nendError) { case INVALID_RESPONSE_TYPE: mListener.onAdFailedToLoad(this, AdRequest.ERROR_CODE_INTERNAL_ERROR); break; case FAILED_AD_DOWNLOAD: mListener.onAdFailedToLoad(this, AdRequest.ERROR_CODE_INVALID_REQUEST); break; case FAILED_AD_REQUEST: mListener.onAdFailedToLoad(this, AdRequest.ERROR_CODE_INVALID_REQUEST); break; case AD_SIZE_TOO_LARGE: mListener.onAdFailedToLoad(this, AdRequest.ERROR_CODE_INTERNAL_ERROR); break; case AD_SIZE_DIFFERENCES: mListener.onAdFailedToLoad(this, AdRequest.ERROR_CODE_INTERNAL_ERROR); break; } } } @Override public void onClick(@NonNull NendAdView adView) { if (mListener != null) { mListener.onAdClicked(this); mListener.onAdOpened(this); mListener.onAdLeftApplication(this); } } @Override public void onDismissScreen(@NonNull NendAdView adView) { if (mListener != null) { mListener.onAdClosed(this); } } @Override public void onInformationButtonClick(@NonNull NendAdView adView) { if (mListener != null) { mListener.onAdLeftApplication(this); } } // endregion // region MediationInterstitialListener callbacks. public void adLeftApplication() { if (mListenerInterstitial != null) { mListenerInterstitial.onAdLeftApplication(this); } } public void adClicked() { if (mListenerInterstitial != null) { mListenerInterstitial.onAdClicked(this); } } public void adClosed() { if (mListenerInterstitial != null) { mListenerInterstitial.onAdClosed(this); } } public void adFailedToLoad(int errorCode) { if (mListenerInterstitial != null) { mListenerInterstitial.onAdFailedToLoad(this, errorCode); } } public void adLoaded() { if (mListenerInterstitial != null) { mListenerInterstitial.onAdLoaded(this); } } public void adOpened() { if (mListenerInterstitial != null) { mListenerInterstitial.onAdOpened(this); } } // endregion // region OnContextChangedListener implementation @Override public void onContextChanged(Context context) { if (!(context instanceof Activity)) { Log.w(TAG, "Nend Ads require an Activity context to show ads."); return; } mActivityWeakReference = new WeakReference<>((Activity) context); } // endregion }