From 5cc6dac77cf9149acdfaafaf8109ab44af3f0bc9 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Wed, 14 Oct 2020 11:52:19 +0100 Subject: [PATCH] Encapsulate ImaAdsLoader configuration in a class This will reduce the amount of boilerplate required to pass the configuration values around (especially in a planned future change when logic is factored out of ImaAdsLoader). PiperOrigin-RevId: 337058355 --- .../exoplayer2/ext/ima/ImaAdsLoader.java | 128 ++++++++++-------- .../android/exoplayer2/ext/ima/ImaUtil.java | 50 +++++++ 2 files changed, 122 insertions(+), 56 deletions(-) diff --git a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java index 64ad897063..f782443619 100644 --- a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java +++ b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java @@ -347,7 +347,11 @@ public final class ImaAdsLoader @Deprecated public ImaAdsLoader buildForAdTag(Uri adTagUri) { return new ImaAdsLoader( - /* builder= */ this, /* adTagUri= */ adTagUri, /* adsResponse= */ null); + context, + getConfiguration(), + imaFactory, + /* adTagUri= */ adTagUri, + /* adsResponse= */ null); } /** @@ -364,12 +368,31 @@ public final class ImaAdsLoader */ @Deprecated public ImaAdsLoader buildForAdsResponse(String adsResponse) { - return new ImaAdsLoader(/* builder= */ this, /* adTagUri= */ null, adsResponse); + return new ImaAdsLoader( + context, getConfiguration(), imaFactory, /* adTagUri= */ null, adsResponse); } /** Returns a new {@link ImaAdsLoader}. */ public ImaAdsLoader build() { - return new ImaAdsLoader(/* builder= */ this, /* adTagUri= */ null, /* adsResponse= */ null); + return new ImaAdsLoader( + context, getConfiguration(), imaFactory, /* adTagUri= */ null, /* adsResponse= */ null); + } + + // TODO(internal: b/169646419): Remove/hide once the deprecated constructor has been removed. + /* package */ ImaUtil.Configuration getConfiguration() { + return new ImaUtil.Configuration( + adPreloadTimeoutMs, + vastLoadTimeoutMs, + mediaLoadTimeoutMs, + focusSkipButtonWhenAvailable, + playAdBeforeStartPosition, + mediaBitrate, + adUiElements, + companionAdSlots, + adErrorListener, + adEventListener, + videoAdPlayerCallback, + imaSdkSettings); } } @@ -426,20 +449,11 @@ public final class ImaAdsLoader private static final DataSpec EMPTY_AD_TAG_DATA_SPEC = new DataSpec(Uri.EMPTY); + private final ImaUtil.Configuration configuration; private final Context context; + private final ImaUtil.ImaFactory imaFactory; @Nullable private final Uri adTagUri; @Nullable private final String adsResponse; - private final long adPreloadTimeoutMs; - private final int vastLoadTimeoutMs; - private final int mediaLoadTimeoutMs; - private final boolean focusSkipButtonWhenAvailable; - private final boolean playAdBeforeStartPosition; - private final int mediaBitrate; - @Nullable private final Set adUiElements; - @Nullable private final Collection companionAdSlots; - @Nullable private final AdErrorListener adErrorListener; - @Nullable private final AdEventListener adEventListener; - private final ImaUtil.ImaFactory imaFactory; private final ImaSdkSettings imaSdkSettings; private final Timeline.Period period; private final Handler handler; @@ -539,26 +553,27 @@ public final class ImaAdsLoader */ @Deprecated public ImaAdsLoader(Context context, Uri adTagUri) { - this(new Builder(context), adTagUri, /* adsResponse= */ null); + this( + context, + new Builder(context).getConfiguration(), + new DefaultImaFactory(), + adTagUri, + /* adsResponse= */ null); } @SuppressWarnings({"nullness:argument.type.incompatible", "methodref.receiver.bound.invalid"}) - private ImaAdsLoader(Builder builder, @Nullable Uri adTagUri, @Nullable String adsResponse) { - this.context = builder.context.getApplicationContext(); + private ImaAdsLoader( + Context context, + ImaUtil.Configuration configuration, + ImaUtil.ImaFactory imaFactory, + @Nullable Uri adTagUri, + @Nullable String adsResponse) { + this.context = context.getApplicationContext(); + this.configuration = configuration; + this.imaFactory = imaFactory; this.adTagUri = adTagUri; this.adsResponse = adsResponse; - this.adPreloadTimeoutMs = builder.adPreloadTimeoutMs; - this.vastLoadTimeoutMs = builder.vastLoadTimeoutMs; - this.mediaLoadTimeoutMs = builder.mediaLoadTimeoutMs; - this.mediaBitrate = builder.mediaBitrate; - this.focusSkipButtonWhenAvailable = builder.focusSkipButtonWhenAvailable; - this.playAdBeforeStartPosition = builder.playAdBeforeStartPosition; - this.adUiElements = builder.adUiElements; - this.companionAdSlots = builder.companionAdSlots; - this.adErrorListener = builder.adErrorListener; - this.adEventListener = builder.adEventListener; - this.imaFactory = builder.imaFactory; - @Nullable ImaSdkSettings imaSdkSettings = builder.imaSdkSettings; + @Nullable ImaSdkSettings imaSdkSettings = configuration.imaSdkSettings; if (imaSdkSettings == null) { imaSdkSettings = imaFactory.createImaSdkSettings(); if (DEBUG) { @@ -572,8 +587,8 @@ public final class ImaAdsLoader handler = Util.createHandler(getImaLooper(), /* callback= */ null); componentListener = new ComponentListener(); adCallbacks = new ArrayList<>(/* initialCapacity= */ 1); - if (builder.videoAdPlayerCallback != null) { - adCallbacks.add(builder.videoAdPlayerCallback); + if (configuration.applicationVideoAdPlayerCallback != null) { + adCallbacks.add(configuration.applicationVideoAdPlayerCallback); } updateAdProgressRunnable = this::updateAdProgress; adInfoByAdMediaInfo = HashBiMap.create(); @@ -675,8 +690,8 @@ public final class ImaAdsLoader this.adTagDataSpec = adTagDataSpec; pendingAdRequestContext = new Object(); request.setUserRequestContext(pendingAdRequestContext); - if (vastLoadTimeoutMs != TIMEOUT_UNSET) { - request.setVastLoadTimeout(vastLoadTimeoutMs); + if (configuration.vastLoadTimeoutMs != TIMEOUT_UNSET) { + request.setVastLoadTimeout(configuration.vastLoadTimeoutMs); } request.setContentProgressProvider(componentListener); @@ -687,14 +702,14 @@ public final class ImaAdsLoader adDisplayContainer = imaFactory.createAudioAdDisplayContainer(context, /* player= */ componentListener); } - if (companionAdSlots != null) { - adDisplayContainer.setCompanionSlots(companionAdSlots); + if (configuration.companionAdSlots != null) { + adDisplayContainer.setCompanionSlots(configuration.companionAdSlots); } adsLoader = imaFactory.createAdsLoader(context, imaSdkSettings, adDisplayContainer); adsLoader.addAdErrorListener(componentListener); - if (adErrorListener != null) { - adsLoader.addAdErrorListener(adErrorListener); + if (configuration.applicationAdErrorListener != null) { + adsLoader.addAdErrorListener(configuration.applicationAdErrorListener); } adsLoader.addAdsLoadedListener(componentListener); adsLoader.requestAds(request); @@ -819,8 +834,8 @@ public final class ImaAdsLoader if (adsLoader != null) { adsLoader.removeAdsLoadedListener(componentListener); adsLoader.removeAdErrorListener(componentListener); - if (adErrorListener != null) { - adsLoader.removeAdErrorListener(adErrorListener); + if (configuration.applicationAdErrorListener != null) { + adsLoader.removeAdErrorListener(configuration.applicationAdErrorListener); } } imaPausedContent = false; @@ -924,7 +939,7 @@ public final class ImaAdsLoader long adGroupTimeMs = C.usToMs(adPlaybackState.adGroupTimesUs[adGroupIndex]); long contentPositionMs = getContentPeriodPositionMs(player, timeline, period); long timeUntilAdMs = adGroupTimeMs - contentPositionMs; - if (timeUntilAdMs < adPreloadTimeoutMs) { + if (timeUntilAdMs < configuration.adPreloadTimeoutMs) { waitingForPreloadElapsedRealtimeMs = SystemClock.elapsedRealtime(); } } else if (playbackState == Player.STATE_READY) { @@ -974,15 +989,16 @@ public final class ImaAdsLoader AdsRenderingSettings adsRenderingSettings = imaFactory.createAdsRenderingSettings(); adsRenderingSettings.setEnablePreloading(true); adsRenderingSettings.setMimeTypes(supportedMimeTypes); - if (mediaLoadTimeoutMs != TIMEOUT_UNSET) { - adsRenderingSettings.setLoadVideoTimeout(mediaLoadTimeoutMs); + if (configuration.mediaLoadTimeoutMs != TIMEOUT_UNSET) { + adsRenderingSettings.setLoadVideoTimeout(configuration.mediaLoadTimeoutMs); } - if (mediaBitrate != BITRATE_UNSET) { - adsRenderingSettings.setBitrateKbps(mediaBitrate / 1000); + if (configuration.mediaBitrate != BITRATE_UNSET) { + adsRenderingSettings.setBitrateKbps(configuration.mediaBitrate / 1000); } - adsRenderingSettings.setFocusSkipButtonWhenAvailable(focusSkipButtonWhenAvailable); - if (adUiElements != null) { - adsRenderingSettings.setUiElements(adUiElements); + adsRenderingSettings.setFocusSkipButtonWhenAvailable( + configuration.focusSkipButtonWhenAvailable); + if (configuration.adUiElements != null) { + adsRenderingSettings.setUiElements(configuration.adUiElements); } // Skip ads based on the start position as required. @@ -993,7 +1009,7 @@ public final class ImaAdsLoader C.msToUs(contentPositionMs), C.msToUs(contentDurationMs)); if (adGroupForPositionIndex != C.INDEX_UNSET) { boolean playAdWhenStartingPlayback = - playAdBeforeStartPosition + configuration.playAdBeforeStartPosition || adGroupTimesUs[adGroupForPositionIndex] == C.msToUs(contentPositionMs); if (!playAdWhenStartingPlayback) { adGroupForPositionIndex++; @@ -1605,12 +1621,12 @@ public final class ImaAdsLoader private void destroyAdsManager() { if (adsManager != null) { adsManager.removeAdErrorListener(componentListener); - if (adErrorListener != null) { - adsManager.removeAdErrorListener(adErrorListener); + if (configuration.applicationAdErrorListener != null) { + adsManager.removeAdErrorListener(configuration.applicationAdErrorListener); } adsManager.removeAdEventListener(componentListener); - if (adEventListener != null) { - adsManager.removeAdEventListener(adEventListener); + if (configuration.applicationAdEventListener != null) { + adsManager.removeAdEventListener(configuration.applicationAdEventListener); } adsManager.destroy(); adsManager = null; @@ -1636,12 +1652,12 @@ public final class ImaAdsLoader pendingAdRequestContext = null; ImaAdsLoader.this.adsManager = adsManager; adsManager.addAdErrorListener(this); - if (adErrorListener != null) { - adsManager.addAdErrorListener(adErrorListener); + if (configuration.applicationAdErrorListener != null) { + adsManager.addAdErrorListener(configuration.applicationAdErrorListener); } adsManager.addAdEventListener(this); - if (adEventListener != null) { - adsManager.addAdEventListener(adEventListener); + if (configuration.applicationAdEventListener != null) { + adsManager.addAdEventListener(configuration.applicationAdEventListener); } if (player != null) { // If a player is attached already, start playback immediately. diff --git a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaUtil.java b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaUtil.java index e896e1c115..caaf6ae4bf 100644 --- a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaUtil.java +++ b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaUtil.java @@ -21,13 +21,17 @@ import android.view.ViewGroup; import androidx.annotation.Nullable; import com.google.ads.interactivemedia.v3.api.AdDisplayContainer; import com.google.ads.interactivemedia.v3.api.AdError; +import com.google.ads.interactivemedia.v3.api.AdErrorEvent; +import com.google.ads.interactivemedia.v3.api.AdEvent; import com.google.ads.interactivemedia.v3.api.AdsLoader; import com.google.ads.interactivemedia.v3.api.AdsManager; import com.google.ads.interactivemedia.v3.api.AdsRenderingSettings; import com.google.ads.interactivemedia.v3.api.AdsRequest; +import com.google.ads.interactivemedia.v3.api.CompanionAdSlot; import com.google.ads.interactivemedia.v3.api.FriendlyObstruction; import com.google.ads.interactivemedia.v3.api.FriendlyObstructionPurpose; import com.google.ads.interactivemedia.v3.api.ImaSdkSettings; +import com.google.ads.interactivemedia.v3.api.UiElement; import com.google.ads.interactivemedia.v3.api.player.VideoAdPlayer; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.source.ads.AdPlaybackState; @@ -37,7 +41,9 @@ import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.util.Util; import java.io.IOException; import java.util.Arrays; +import java.util.Collection; import java.util.List; +import java.util.Set; /** Utilities for working with IMA SDK and IMA extension data types. */ /* package */ final class ImaUtil { @@ -73,6 +79,50 @@ import java.util.List; Context context, ImaSdkSettings imaSdkSettings, AdDisplayContainer adDisplayContainer); } + /** Stores configuration for ad loading and playback. */ + public static final class Configuration { + + public final long adPreloadTimeoutMs; + public final int vastLoadTimeoutMs; + public final int mediaLoadTimeoutMs; + public final boolean focusSkipButtonWhenAvailable; + public final boolean playAdBeforeStartPosition; + public final int mediaBitrate; + @Nullable public final Set adUiElements; + @Nullable public final Collection companionAdSlots; + @Nullable public final AdErrorEvent.AdErrorListener applicationAdErrorListener; + @Nullable public final AdEvent.AdEventListener applicationAdEventListener; + @Nullable public final VideoAdPlayer.VideoAdPlayerCallback applicationVideoAdPlayerCallback; + @Nullable public final ImaSdkSettings imaSdkSettings; + + public Configuration( + long adPreloadTimeoutMs, + int vastLoadTimeoutMs, + int mediaLoadTimeoutMs, + boolean focusSkipButtonWhenAvailable, + boolean playAdBeforeStartPosition, + int mediaBitrate, + @Nullable Set adUiElements, + @Nullable Collection companionAdSlots, + @Nullable AdErrorEvent.AdErrorListener applicationAdErrorListener, + @Nullable AdEvent.AdEventListener applicationAdEventListener, + @Nullable VideoAdPlayer.VideoAdPlayerCallback applicationVideoAdPlayerCallback, + @Nullable ImaSdkSettings imaSdkSettings) { + this.adPreloadTimeoutMs = adPreloadTimeoutMs; + this.vastLoadTimeoutMs = vastLoadTimeoutMs; + this.mediaLoadTimeoutMs = mediaLoadTimeoutMs; + this.focusSkipButtonWhenAvailable = focusSkipButtonWhenAvailable; + this.playAdBeforeStartPosition = playAdBeforeStartPosition; + this.mediaBitrate = mediaBitrate; + this.adUiElements = adUiElements; + this.companionAdSlots = companionAdSlots; + this.applicationAdErrorListener = applicationAdErrorListener; + this.applicationAdEventListener = applicationAdEventListener; + this.applicationVideoAdPlayerCallback = applicationVideoAdPlayerCallback; + this.imaSdkSettings = imaSdkSettings; + } + } + /** * Returns the IMA {@link FriendlyObstructionPurpose} corresponding to the given {@link * OverlayInfo#purpose}.