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 11aab906e0a..b6d92805794 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 @@ -20,6 +20,7 @@ import android.os.SystemClock; import android.util.Log; import android.view.ViewGroup; +import android.webkit.WebView; import com.google.ads.interactivemedia.v3.api.Ad; import com.google.ads.interactivemedia.v3.api.AdDisplayContainer; import com.google.ads.interactivemedia.v3.api.AdErrorEvent; @@ -112,6 +113,14 @@ public final class ImaAdsLoader implements Player.EventListener, VideoAdPlayer, */ private static final long END_OF_CONTENT_POSITION_THRESHOLD_MS = 5000; + /** + * The "Skip ad" button rendered in the IMA WebView does not gain focus by default and cannot be + * clicked via a keypress event. Workaround this issue by calling focus() on the HTML element in + * the WebView directly when an ad starts. See [Internal: b/62371030]. + */ + private static final String FOCUS_SKIP_BUTTON_WORKAROUND_JS = "javascript:" + + "try{ document.getElementsByClassName(\"videoAdUiSkipButton\")[0].focus(); } catch (e) {}"; + private final Uri adTagUri; private final Timeline.Period period; private final List adCallbacks; @@ -121,6 +130,7 @@ public final class ImaAdsLoader implements Player.EventListener, VideoAdPlayer, private EventListener eventListener; private Player player; + private ViewGroup adUiViewGroup; private VideoProgressUpdate lastContentProgress; private VideoProgressUpdate lastAdProgress; @@ -249,6 +259,7 @@ public ImaAdsLoader(Context context, Uri adTagUri, ImaSdkSettings imaSdkSettings ViewGroup adUiViewGroup) { this.player = player; this.eventListener = eventListener; + this.adUiViewGroup = adUiViewGroup; lastAdProgress = null; lastContentProgress = null; adDisplayContainer.setAdContainer(adUiViewGroup); @@ -278,6 +289,7 @@ public ImaAdsLoader(Context context, Uri adTagUri, ImaSdkSettings imaSdkSettings player.removeListener(this); player = null; eventListener = null; + adUiViewGroup = null; } /** @@ -363,6 +375,11 @@ public void onAdEvent(AdEvent adEvent) { imaPausedContent = true; pauseContentInternal(); break; + case STARTED: + if (ad.isSkippable()) { + focusSkipButton(); + } + break; case TAPPED: if (eventListener != null) { eventListener.onAdTapped(); @@ -727,4 +744,13 @@ private static long[] getAdGroupTimesUs(List cuePoints) { return adGroupTimesUs; } + private void focusSkipButton() { + if (playingAd && adUiViewGroup != null && adUiViewGroup.getChildCount() > 0 + && adUiViewGroup.getChildAt(0) instanceof WebView) { + WebView webView = (WebView) (adUiViewGroup.getChildAt(0)); + webView.requestFocus(); + webView.loadUrl(FOCUS_SKIP_BUTTON_WORKAROUND_JS); + } + } + } diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java index a4fb5391754..de28eb2f934 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java @@ -515,6 +515,13 @@ public void setUseController(boolean useController) { @Override public boolean dispatchKeyEvent(KeyEvent event) { + if (player != null && player.isPlayingAd()) { + // Focus any overlay UI now, in case it's provided by a WebView whose contents may update + // dynamically. This is needed to make the "Skip ad" button focused on Android TV when using + // IMA [Internal: b/62371030]. + overlayFrameLayout.requestFocus(); + return super.dispatchKeyEvent(event); + } maybeShowController(true); return dispatchMediaKeyEvent(event) || super.dispatchKeyEvent(event); }