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
This commit is contained in:
bachinger 2023-01-10 21:19:55 +00:00 committed by Rohit Singh
parent 9a9da0ab8e
commit 9882a20783
3 changed files with 37 additions and 5 deletions

View File

@ -68,6 +68,9 @@ Release notes
* IMA extension * IMA extension
* Remove player listener of the `ImaServerSideAdInsertionMediaSource` on * Remove player listener of the `ImaServerSideAdInsertionMediaSource` on
the application thread to avoid threading issues. 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. * Bump IMA SDK version to 3.29.0.
### 1.0.0-beta03 (2022-11-22) ### 1.0.0-beta03 (2022-11-22)

View File

@ -57,6 +57,7 @@ import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
import androidx.media3.datasource.TransferListener; import androidx.media3.datasource.TransferListener;
import androidx.media3.exoplayer.drm.DrmSessionManagerProvider; import androidx.media3.exoplayer.drm.DrmSessionManagerProvider;
import androidx.media3.exoplayer.ima.ImaUtil.ServerSideAdInsertionConfiguration;
import androidx.media3.exoplayer.source.CompositeMediaSource; import androidx.media3.exoplayer.source.CompositeMediaSource;
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory; import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
import androidx.media3.exoplayer.source.ForwardingTimeline; import androidx.media3.exoplayer.source.ForwardingTimeline;
@ -193,6 +194,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
@Nullable private AdErrorEvent.AdErrorListener adErrorListener; @Nullable private AdErrorEvent.AdErrorListener adErrorListener;
private State state; private State state;
private ImmutableList<CompanionAdSlot> companionAdSlots; private ImmutableList<CompanionAdSlot> companionAdSlots;
private boolean focusSkipButtonWhenAvailable;
/** /**
* Creates an instance. * Creates an instance.
@ -205,6 +207,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
this.adViewProvider = adViewProvider; this.adViewProvider = adViewProvider;
companionAdSlots = ImmutableList.of(); companionAdSlots = ImmutableList.of();
state = new State(ImmutableMap.of()); state = new State(ImmutableMap.of());
focusSkipButtonWhenAvailable = true;
} }
/** /**
@ -274,6 +277,22 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
return this; 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}. */ /** Returns a new {@link AdsLoader}. */
public AdsLoader build() { public AdsLoader build() {
@Nullable ImaSdkSettings imaSdkSettings = this.imaSdkSettings; @Nullable ImaSdkSettings imaSdkSettings = this.imaSdkSettings;
@ -281,13 +300,14 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
imaSdkSettings = ImaSdkFactory.getInstance().createImaSdkSettings(); imaSdkSettings = ImaSdkFactory.getInstance().createImaSdkSettings();
imaSdkSettings.setLanguage(Util.getSystemLanguageCodes()[0]); imaSdkSettings.setLanguage(Util.getSystemLanguageCodes()[0]);
} }
ImaUtil.ServerSideAdInsertionConfiguration configuration = ServerSideAdInsertionConfiguration configuration =
new ImaUtil.ServerSideAdInsertionConfiguration( new ServerSideAdInsertionConfiguration(
adViewProvider, adViewProvider,
imaSdkSettings, imaSdkSettings,
adEventListener, adEventListener,
adErrorListener, adErrorListener,
companionAdSlots, companionAdSlots,
focusSkipButtonWhenAvailable,
imaSdkSettings.isDebugMode()); imaSdkSettings.isDebugMode());
return new AdsLoader(context, configuration, state); 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 Context context;
private final Map<ImaServerSideAdInsertionMediaSource, MediaSourceResourceHolder> private final Map<ImaServerSideAdInsertionMediaSource, MediaSourceResourceHolder>
mediaSourceResources; mediaSourceResources;
@ -363,7 +383,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
@Nullable private Player player; @Nullable private Player player;
private AdsLoader( private AdsLoader(
Context context, ImaUtil.ServerSideAdInsertionConfiguration configuration, State state) { Context context, ServerSideAdInsertionConfiguration configuration, State state) {
this.context = context.getApplicationContext(); this.context = context.getApplicationContext();
this.configuration = configuration; this.configuration = configuration;
mediaSourceResources = new HashMap<>(); mediaSourceResources = new HashMap<>();
@ -504,6 +524,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
StreamManagerLoadable streamManagerLoadable = StreamManagerLoadable streamManagerLoadable =
new StreamManagerLoadable( new StreamManagerLoadable(
sdkAdsLoader, sdkAdsLoader,
adsLoader.configuration,
streamRequest, streamRequest,
streamPlayer, streamPlayer,
applicationAdErrorListener, applicationAdErrorListener,
@ -932,6 +953,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
implements Loadable, AdsLoadedListener, AdErrorListener { implements Loadable, AdsLoadedListener, AdErrorListener {
private final com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader; private final com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader;
private final ServerSideAdInsertionConfiguration serverSideAdInsertionConfiguration;
private final StreamRequest request; private final StreamRequest request;
private final StreamPlayer streamPlayer; private final StreamPlayer streamPlayer;
@Nullable private final AdErrorListener adErrorListener; @Nullable private final AdErrorListener adErrorListener;
@ -948,11 +970,13 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
/** Creates an instance. */ /** Creates an instance. */
private StreamManagerLoadable( private StreamManagerLoadable(
com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader, com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader,
ServerSideAdInsertionConfiguration serverSideAdInsertionConfiguration,
StreamRequest request, StreamRequest request,
StreamPlayer streamPlayer, StreamPlayer streamPlayer,
@Nullable AdErrorListener adErrorListener, @Nullable AdErrorListener adErrorListener,
int loadVideoTimeoutMs) { int loadVideoTimeoutMs) {
this.adsLoader = adsLoader; this.adsLoader = adsLoader;
this.serverSideAdInsertionConfiguration = serverSideAdInsertionConfiguration;
this.request = request; this.request = request;
this.streamPlayer = streamPlayer; this.streamPlayer = streamPlayer;
this.adErrorListener = adErrorListener; this.adErrorListener = adErrorListener;
@ -1029,6 +1053,8 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
AdsRenderingSettings adsRenderingSettings = AdsRenderingSettings adsRenderingSettings =
ImaSdkFactory.getInstance().createAdsRenderingSettings(); ImaSdkFactory.getInstance().createAdsRenderingSettings();
adsRenderingSettings.setLoadVideoTimeout(loadVideoTimeoutMs); adsRenderingSettings.setLoadVideoTimeout(loadVideoTimeoutMs);
adsRenderingSettings.setFocusSkipButtonWhenAvailable(
serverSideAdInsertionConfiguration.focusSkipButtonWhenAvailable);
// After initialization completed the streamUri will be reported to the streamPlayer. // After initialization completed the streamUri will be reported to the streamPlayer.
streamManager.init(adsRenderingSettings); streamManager.init(adsRenderingSettings);
this.streamManager = streamManager; this.streamManager = streamManager;
@ -1261,7 +1287,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
private static StreamDisplayContainer createStreamDisplayContainer( private static StreamDisplayContainer createStreamDisplayContainer(
ImaSdkFactory imaSdkFactory, ImaSdkFactory imaSdkFactory,
ImaUtil.ServerSideAdInsertionConfiguration config, ServerSideAdInsertionConfiguration config,
StreamPlayer streamPlayer) { StreamPlayer streamPlayer) {
StreamDisplayContainer container = StreamDisplayContainer container =
ImaSdkFactory.createStreamDisplayContainer( ImaSdkFactory.createStreamDisplayContainer(

View File

@ -166,6 +166,7 @@ import java.util.Set;
@Nullable public final AdEvent.AdEventListener applicationAdEventListener; @Nullable public final AdEvent.AdEventListener applicationAdEventListener;
@Nullable public final AdErrorEvent.AdErrorListener applicationAdErrorListener; @Nullable public final AdErrorEvent.AdErrorListener applicationAdErrorListener;
public final ImmutableList<CompanionAdSlot> companionAdSlots; public final ImmutableList<CompanionAdSlot> companionAdSlots;
public final boolean focusSkipButtonWhenAvailable;
public final boolean debugModeEnabled; public final boolean debugModeEnabled;
public ServerSideAdInsertionConfiguration( public ServerSideAdInsertionConfiguration(
@ -174,12 +175,14 @@ import java.util.Set;
@Nullable AdEvent.AdEventListener applicationAdEventListener, @Nullable AdEvent.AdEventListener applicationAdEventListener,
@Nullable AdErrorEvent.AdErrorListener applicationAdErrorListener, @Nullable AdErrorEvent.AdErrorListener applicationAdErrorListener,
List<CompanionAdSlot> companionAdSlots, List<CompanionAdSlot> companionAdSlots,
boolean focusSkipButtonWhenAvailable,
boolean debugModeEnabled) { boolean debugModeEnabled) {
this.imaSdkSettings = imaSdkSettings; this.imaSdkSettings = imaSdkSettings;
this.adViewProvider = adViewProvider; this.adViewProvider = adViewProvider;
this.applicationAdEventListener = applicationAdEventListener; this.applicationAdEventListener = applicationAdEventListener;
this.applicationAdErrorListener = applicationAdErrorListener; this.applicationAdErrorListener = applicationAdErrorListener;
this.companionAdSlots = ImmutableList.copyOf(companionAdSlots); this.companionAdSlots = ImmutableList.copyOf(companionAdSlots);
this.focusSkipButtonWhenAvailable = focusSkipButtonWhenAvailable;
this.debugModeEnabled = debugModeEnabled; this.debugModeEnabled = debugModeEnabled;
} }
} }