From 9882a207836bdc089796bde7238f5357b0c23e76 Mon Sep 17 00:00:00 2001 From: bachinger Date: Tue, 10 Jan 2023 21:19:55 +0000 Subject: [PATCH] Add focusSkipButtonWhenAvailable to focus UI on ATV For TV devices the skip button needs to have the focus to be accessible with the remote control. This property makes this configurable while being set to true by default. PiperOrigin-RevId: 501077608 --- RELEASENOTES.md | 3 ++ .../ImaServerSideAdInsertionMediaSource.java | 36 ++++++++++++++++--- .../media3/exoplayer/ima/ImaUtil.java | 3 ++ 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 8bdc2462fe..740af1cf88 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -68,6 +68,9 @@ Release notes * IMA extension * Remove player listener of the `ImaServerSideAdInsertionMediaSource` on the application thread to avoid threading issues. + * Add a property `focusSkipButtonWhenAvailable` to the + `ImaServerSideAdInsertionMediaSource.AdsLoader.Builder` to request + focusing the skip button on TV devices and set it to true by default. * Bump IMA SDK version to 3.29.0. ### 1.0.0-beta03 (2022-11-22) diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java index 70fccc7655..831bf183be 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java @@ -57,6 +57,7 @@ import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; import androidx.media3.datasource.TransferListener; import androidx.media3.exoplayer.drm.DrmSessionManagerProvider; +import androidx.media3.exoplayer.ima.ImaUtil.ServerSideAdInsertionConfiguration; import androidx.media3.exoplayer.source.CompositeMediaSource; import androidx.media3.exoplayer.source.DefaultMediaSourceFactory; import androidx.media3.exoplayer.source.ForwardingTimeline; @@ -193,6 +194,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou @Nullable private AdErrorEvent.AdErrorListener adErrorListener; private State state; private ImmutableList companionAdSlots; + private boolean focusSkipButtonWhenAvailable; /** * Creates an instance. @@ -205,6 +207,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou this.adViewProvider = adViewProvider; companionAdSlots = ImmutableList.of(); state = new State(ImmutableMap.of()); + focusSkipButtonWhenAvailable = true; } /** @@ -274,6 +277,22 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou return this; } + /** + * Sets whether to focus the skip button (when available) on Android TV devices. The default + * setting is {@code true}. + * + * @param focusSkipButtonWhenAvailable Whether to focus the skip button (when available) on + * Android TV devices. + * @return This builder, for convenience. + * @see AdsRenderingSettings#setFocusSkipButtonWhenAvailable(boolean) + */ + @CanIgnoreReturnValue + public AdsLoader.Builder setFocusSkipButtonWhenAvailable( + boolean focusSkipButtonWhenAvailable) { + this.focusSkipButtonWhenAvailable = focusSkipButtonWhenAvailable; + return this; + } + /** Returns a new {@link AdsLoader}. */ public AdsLoader build() { @Nullable ImaSdkSettings imaSdkSettings = this.imaSdkSettings; @@ -281,13 +300,14 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou imaSdkSettings = ImaSdkFactory.getInstance().createImaSdkSettings(); imaSdkSettings.setLanguage(Util.getSystemLanguageCodes()[0]); } - ImaUtil.ServerSideAdInsertionConfiguration configuration = - new ImaUtil.ServerSideAdInsertionConfiguration( + ServerSideAdInsertionConfiguration configuration = + new ServerSideAdInsertionConfiguration( adViewProvider, imaSdkSettings, adEventListener, adErrorListener, companionAdSlots, + focusSkipButtonWhenAvailable, imaSdkSettings.isDebugMode()); return new AdsLoader(context, configuration, state); } @@ -354,7 +374,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou } } - private final ImaUtil.ServerSideAdInsertionConfiguration configuration; + private final ServerSideAdInsertionConfiguration configuration; private final Context context; private final Map mediaSourceResources; @@ -363,7 +383,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou @Nullable private Player player; private AdsLoader( - Context context, ImaUtil.ServerSideAdInsertionConfiguration configuration, State state) { + Context context, ServerSideAdInsertionConfiguration configuration, State state) { this.context = context.getApplicationContext(); this.configuration = configuration; mediaSourceResources = new HashMap<>(); @@ -504,6 +524,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou StreamManagerLoadable streamManagerLoadable = new StreamManagerLoadable( sdkAdsLoader, + adsLoader.configuration, streamRequest, streamPlayer, applicationAdErrorListener, @@ -932,6 +953,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou implements Loadable, AdsLoadedListener, AdErrorListener { private final com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader; + private final ServerSideAdInsertionConfiguration serverSideAdInsertionConfiguration; private final StreamRequest request; private final StreamPlayer streamPlayer; @Nullable private final AdErrorListener adErrorListener; @@ -948,11 +970,13 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou /** Creates an instance. */ private StreamManagerLoadable( com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader, + ServerSideAdInsertionConfiguration serverSideAdInsertionConfiguration, StreamRequest request, StreamPlayer streamPlayer, @Nullable AdErrorListener adErrorListener, int loadVideoTimeoutMs) { this.adsLoader = adsLoader; + this.serverSideAdInsertionConfiguration = serverSideAdInsertionConfiguration; this.request = request; this.streamPlayer = streamPlayer; this.adErrorListener = adErrorListener; @@ -1029,6 +1053,8 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou AdsRenderingSettings adsRenderingSettings = ImaSdkFactory.getInstance().createAdsRenderingSettings(); adsRenderingSettings.setLoadVideoTimeout(loadVideoTimeoutMs); + adsRenderingSettings.setFocusSkipButtonWhenAvailable( + serverSideAdInsertionConfiguration.focusSkipButtonWhenAvailable); // After initialization completed the streamUri will be reported to the streamPlayer. streamManager.init(adsRenderingSettings); this.streamManager = streamManager; @@ -1261,7 +1287,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou private static StreamDisplayContainer createStreamDisplayContainer( ImaSdkFactory imaSdkFactory, - ImaUtil.ServerSideAdInsertionConfiguration config, + ServerSideAdInsertionConfiguration config, StreamPlayer streamPlayer) { StreamDisplayContainer container = ImaSdkFactory.createStreamDisplayContainer( diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaUtil.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaUtil.java index 9c24e62009..2e900e7c6d 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaUtil.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaUtil.java @@ -166,6 +166,7 @@ import java.util.Set; @Nullable public final AdEvent.AdEventListener applicationAdEventListener; @Nullable public final AdErrorEvent.AdErrorListener applicationAdErrorListener; public final ImmutableList companionAdSlots; + public final boolean focusSkipButtonWhenAvailable; public final boolean debugModeEnabled; public ServerSideAdInsertionConfiguration( @@ -174,12 +175,14 @@ import java.util.Set; @Nullable AdEvent.AdEventListener applicationAdEventListener, @Nullable AdErrorEvent.AdErrorListener applicationAdErrorListener, List companionAdSlots, + boolean focusSkipButtonWhenAvailable, boolean debugModeEnabled) { this.imaSdkSettings = imaSdkSettings; this.adViewProvider = adViewProvider; this.applicationAdEventListener = applicationAdEventListener; this.applicationAdErrorListener = applicationAdErrorListener; this.companionAdSlots = ImmutableList.copyOf(companionAdSlots); + this.focusSkipButtonWhenAvailable = focusSkipButtonWhenAvailable; this.debugModeEnabled = debugModeEnabled; } }