Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
SDK
/
exoplayer
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
acc84536
authored
Aug 10, 2020
by
andrewlewis
Committed by
kim-vde
Aug 17, 2020
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Add support for audio-only ad display containers
Issue: #7689 PiperOrigin-RevId: 325752377
parent
f2866a49
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
80 additions
and
16 deletions
RELEASENOTES.md
extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java
extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoaderTest.java
library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsLoader.java
RELEASENOTES.md
View file @
acc84536
...
...
@@ -266,6 +266,10 @@
*
Migrate to new 'friendly obstruction' IMA SDK APIs, and allow apps to
register a purpose and detail reason for overlay views via
`AdsLoader.AdViewProvider`
.
*
Add support for audio-only ads display containers by returning
`null`
from
`AdsLoader.AdViewProvider.getAdViewGroup`
, and allow skipping
audio-only ads via
`ImaAdsLoader.skipAd`
(
[
#7689
](
https://github.com/google/ExoPlayer/issues/7689
)
).
*
Add
`ImaAdsLoader.Builder.setCompanionAdSlots`
so it's possible to set
companion ad slots without accessing the
`AdDisplayContainer`
.
*
Add missing notification of
`VideoAdPlayerCallback.onLoaded`
.
...
...
extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java
View file @
acc84536
...
...
@@ -609,15 +609,21 @@ public final class ImaAdsLoader
* called, so it is only necessary to call this method if you want to request ads before preparing
* the player.
*
* @param adViewGroup A {@link ViewGroup} on top of the player that will show any ad UI.
* @param adViewGroup A {@link ViewGroup} on top of the player that will show any ad UI, or {@code
* null} if playing audio-only ads.
*/
public
void
requestAds
(
ViewGroup
adViewGroup
)
{
public
void
requestAds
(
@Nullable
ViewGroup
adViewGroup
)
{
if
(
hasAdPlaybackState
||
adsManager
!=
null
||
pendingAdRequestContext
!=
null
)
{
// Ads have already been requested.
return
;
}
adDisplayContainer
=
imaFactory
.
createAdDisplayContainer
(
adViewGroup
,
/* player= */
componentListener
);
if
(
adViewGroup
!=
null
)
{
adDisplayContainer
=
imaFactory
.
createAdDisplayContainer
(
adViewGroup
,
/* player= */
componentListener
);
}
else
{
adDisplayContainer
=
imaFactory
.
createAudioAdDisplayContainer
(
context
,
/* player= */
componentListener
);
}
if
(
companionAdSlots
!=
null
)
{
adDisplayContainer
.
setCompanionSlots
(
companionAdSlots
);
}
...
...
@@ -639,6 +645,19 @@ public final class ImaAdsLoader
adsLoader
.
requestAds
(
request
);
}
/**
* Skips the current ad.
*
* <p>This method is intended for apps that play audio-only ads and so need to provide their own
* UI for users to skip skippable ads. Apps showing video ads should not call this method, as the
* IMA SDK provides the UI to skip ads in the ad view group passed via {@link AdViewProvider}.
*/
public
void
skipAd
()
{
if
(
adsManager
!=
null
)
{
adsManager
.
skip
();
}
}
// com.google.android.exoplayer2.source.ads.AdsLoader implementation.
@Override
...
...
@@ -1582,6 +1601,8 @@ public final class ImaAdsLoader
* non-linear ads, and slots for companion ads.
*/
AdDisplayContainer
createAdDisplayContainer
(
ViewGroup
container
,
VideoAdPlayer
player
);
/** Creates an {@link AdDisplayContainer} to hold the player for audio ads. */
AdDisplayContainer
createAudioAdDisplayContainer
(
Context
context
,
VideoAdPlayer
player
);
/**
* Creates a {@link FriendlyObstruction} to describe an obstruction considered "friendly" for
* viewability measurement purposes.
...
...
@@ -1817,6 +1838,11 @@ public final class ImaAdsLoader
return
ImaSdkFactory
.
createAdDisplayContainer
(
container
,
player
);
}
@Override
public
AdDisplayContainer
createAudioAdDisplayContainer
(
Context
context
,
VideoAdPlayer
player
)
{
return
ImaSdkFactory
.
createAudioAdDisplayContainer
(
context
,
player
);
}
// The reasonDetail parameter to createFriendlyObstruction is annotated @Nullable but the
// annotation is not kept in the obfuscated dependency.
@SuppressWarnings
(
"nullness:argument.type.incompatible"
)
...
...
extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoaderTest.java
View file @
acc84536
...
...
@@ -15,6 +15,7 @@
*/
package
com
.
google
.
android
.
exoplayer2
.
ext
.
ima
;
import
static
androidx
.
test
.
core
.
app
.
ApplicationProvider
.
getApplicationContext
;
import
static
com
.
google
.
common
.
truth
.
Truth
.
assertThat
;
import
static
org
.
mockito
.
ArgumentMatchers
.
any
;
import
static
org
.
mockito
.
ArgumentMatchers
.
anyDouble
;
...
...
@@ -31,7 +32,6 @@ import android.view.View;
import
android.view.ViewGroup
;
import
android.widget.FrameLayout
;
import
androidx.annotation.Nullable
;
import
androidx.test.core.app.ApplicationProvider
;
import
androidx.test.ext.junit.runners.AndroidJUnit4
;
import
com.google.ads.interactivemedia.v3.api.Ad
;
import
com.google.ads.interactivemedia.v3.api.AdDisplayContainer
;
...
...
@@ -117,6 +117,7 @@ public final class ImaAdsLoaderTest {
private
ViewGroup
adViewGroup
;
private
AdsLoader
.
AdViewProvider
adViewProvider
;
private
AdsLoader
.
AdViewProvider
audioAdsAdViewProvider
;
private
AdEvent
.
AdEventListener
adEventListener
;
private
ContentProgressProvider
contentProgressProvider
;
private
VideoAdPlayer
videoAdPlayer
;
...
...
@@ -127,8 +128,8 @@ public final class ImaAdsLoaderTest {
@Before
public
void
setUp
()
{
setupMocks
();
adViewGroup
=
new
FrameLayout
(
ApplicationProvider
.
getApplicationContext
());
View
adOverlayView
=
new
View
(
ApplicationProvider
.
getApplicationContext
());
adViewGroup
=
new
FrameLayout
(
getApplicationContext
());
View
adOverlayView
=
new
View
(
getApplicationContext
());
adViewProvider
=
new
AdsLoader
.
AdViewProvider
()
{
@Override
...
...
@@ -142,6 +143,18 @@ public final class ImaAdsLoaderTest {
new
AdsLoader
.
OverlayInfo
(
adOverlayView
,
AdsLoader
.
OverlayInfo
.
PURPOSE_CLOSE_AD
));
}
};
audioAdsAdViewProvider
=
new
AdsLoader
.
AdViewProvider
()
{
@Override
public
ViewGroup
getAdViewGroup
()
{
return
null
;
}
@Override
public
ImmutableList
<
AdsLoader
.
OverlayInfo
>
getAdOverlayInfos
()
{
return
ImmutableList
.
of
();
}
};
}
@After
...
...
@@ -165,10 +178,22 @@ public final class ImaAdsLoaderTest {
imaAdsLoader
.
start
(
adsLoaderListener
,
adViewProvider
);
verify
(
mockImaFactory
,
atLeastOnce
()).
createAdDisplayContainer
(
adViewGroup
,
videoAdPlayer
);
verify
(
mockImaFactory
,
never
()).
createAudioAdDisplayContainer
(
any
(),
any
());
verify
(
mockAdDisplayContainer
).
registerFriendlyObstruction
(
mockFriendlyObstruction
);
}
@Test
public
void
startForAudioOnlyAds_createsAudioOnlyAdDisplayContainer
()
{
setupPlayback
(
CONTENT_TIMELINE
,
PREROLL_CUE_POINTS_SECONDS
);
imaAdsLoader
.
start
(
adsLoaderListener
,
audioAdsAdViewProvider
);
verify
(
mockImaFactory
,
atLeastOnce
())
.
createAudioAdDisplayContainer
(
getApplicationContext
(),
videoAdPlayer
);
verify
(
mockImaFactory
,
never
()).
createAdDisplayContainer
(
any
(),
any
());
verify
(
mockAdDisplayContainer
,
never
()).
registerFriendlyObstruction
(
any
());
}
@Test
public
void
start_withPlaceholderContent_initializedAdsLoader
()
{
Timeline
placeholderTimeline
=
new
PlaceholderTimeline
(
MediaItem
.
fromUri
(
Uri
.
EMPTY
));
setupPlayback
(
placeholderTimeline
,
PREROLL_CUE_POINTS_SECONDS
);
...
...
@@ -470,7 +495,7 @@ public final class ImaAdsLoaderTest {
setupPlayback
(
CONTENT_TIMELINE
,
cuePoints
,
new
ImaAdsLoader
.
Builder
(
ApplicationProvider
.
getApplicationContext
())
new
ImaAdsLoader
.
Builder
(
getApplicationContext
())
.
setPlayAdBeforeStartPosition
(
false
)
.
setImaFactory
(
mockImaFactory
)
.
setImaSdkSettings
(
mockImaSdkSettings
)
...
...
@@ -502,7 +527,7 @@ public final class ImaAdsLoaderTest {
setupPlayback
(
CONTENT_TIMELINE
,
cuePoints
,
new
ImaAdsLoader
.
Builder
(
ApplicationProvider
.
getApplicationContext
())
new
ImaAdsLoader
.
Builder
(
getApplicationContext
())
.
setPlayAdBeforeStartPosition
(
false
)
.
setImaFactory
(
mockImaFactory
)
.
setImaSdkSettings
(
mockImaSdkSettings
)
...
...
@@ -534,7 +559,7 @@ public final class ImaAdsLoaderTest {
setupPlayback
(
CONTENT_TIMELINE
,
cuePoints
,
new
ImaAdsLoader
.
Builder
(
ApplicationProvider
.
getApplicationContext
())
new
ImaAdsLoader
.
Builder
(
getApplicationContext
())
.
setPlayAdBeforeStartPosition
(
false
)
.
setImaFactory
(
mockImaFactory
)
.
setImaSdkSettings
(
mockImaSdkSettings
)
...
...
@@ -570,7 +595,7 @@ public final class ImaAdsLoaderTest {
setupPlayback
(
CONTENT_TIMELINE
,
cuePoints
,
new
ImaAdsLoader
.
Builder
(
ApplicationProvider
.
getApplicationContext
())
new
ImaAdsLoader
.
Builder
(
getApplicationContext
())
.
setPlayAdBeforeStartPosition
(
false
)
.
setImaFactory
(
mockImaFactory
)
.
setImaSdkSettings
(
mockImaSdkSettings
)
...
...
@@ -609,7 +634,7 @@ public final class ImaAdsLoaderTest {
setupPlayback
(
CONTENT_TIMELINE
,
cuePoints
,
new
ImaAdsLoader
.
Builder
(
ApplicationProvider
.
getApplicationContext
())
new
ImaAdsLoader
.
Builder
(
getApplicationContext
())
.
setPlayAdBeforeStartPosition
(
false
)
.
setImaFactory
(
mockImaFactory
)
.
setImaSdkSettings
(
mockImaSdkSettings
)
...
...
@@ -696,7 +721,7 @@ public final class ImaAdsLoaderTest {
setupPlayback
(
contentTimeline
,
cuePoints
,
new
ImaAdsLoader
.
Builder
(
ApplicationProvider
.
getApplicationContext
())
new
ImaAdsLoader
.
Builder
(
getApplicationContext
())
.
setImaFactory
(
mockImaFactory
)
.
setImaSdkSettings
(
mockImaSdkSettings
)
.
buildForAdTag
(
TEST_URI
));
...
...
@@ -765,6 +790,13 @@ public final class ImaAdsLoaderTest {
})
.
when
(
mockImaFactory
)
.
createAdDisplayContainer
(
any
(),
any
());
doAnswer
(
invocation
->
{
videoAdPlayer
=
invocation
.
getArgument
(
1
);
return
mockAdDisplayContainer
;
})
.
when
(
mockImaFactory
)
.
createAudioAdDisplayContainer
(
any
(),
any
());
when
(
mockImaFactory
.
createAdsRenderingSettings
()).
thenReturn
(
mockAdsRenderingSettings
);
when
(
mockImaFactory
.
createAdsRequest
()).
thenReturn
(
mockAdsRequest
);
when
(
mockImaFactory
.
createAdsLoader
(
any
(),
any
(),
any
())).
thenReturn
(
mockAdsLoader
);
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsLoader.java
View file @
acc84536
...
...
@@ -80,10 +80,12 @@ public interface AdsLoader {
interface
AdViewProvider
{
/**
* Returns the {@link ViewGroup} on top of the player that will show any ad UI. Any views on top
* of the returned view group must be described by {@link OverlayInfo OverlayInfos} returned by
* {@link #getAdOverlayInfos()}, for accurate viewability measurement.
* Returns the {@link ViewGroup} on top of the player that will show any ad UI, or {@code null}
* if playing audio-only ads. Any views on top of the returned view group must be described by
* {@link OverlayInfo OverlayInfos} returned by {@link #getAdOverlayInfos()}, for accurate
* viewability measurement.
*/
@Nullable
ViewGroup
getAdViewGroup
();
/** @deprecated Use {@link #getAdOverlayInfos()} instead. */
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment