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
65d8ff80
authored
May 19, 2021
by
christosts
Committed by
Oliver Woodman
May 19, 2021
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
ForwardingPlayer only forwards Player operations
PiperOrigin-RevId: 374621615
parent
41afb6ac
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
226 additions
and
662 deletions
library/common/src/main/java/com/google/android/exoplayer2/ForwardingPlayer.java
library/common/src/test/java/com/google/android/exoplayer2/ForwardingPlayerTest.java
library/common/src/main/java/com/google/android/exoplayer2/ForwardingPlayer.java
View file @
65d8ff80
/*
/*
* Copyright
(C)
2021 The Android Open Source Project
* Copyright 2021 The Android Open Source Project
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* you may not use this file except in compliance with the License.
...
@@ -15,74 +15,32 @@
...
@@ -15,74 +15,32 @@
*/
*/
package
com
.
google
.
android
.
exoplayer2
;
package
com
.
google
.
android
.
exoplayer2
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkState
;
import
android.os.Looper
;
import
android.os.Looper
;
import
android.view.Surface
;
import
android.view.Surface
;
import
android.view.SurfaceHolder
;
import
android.view.SurfaceHolder
;
import
android.view.SurfaceView
;
import
android.view.SurfaceView
;
import
android.view.TextureView
;
import
android.view.TextureView
;
import
androidx.annotation.Nullable
;
import
androidx.annotation.Nullable
;
import
androidx.annotation.VisibleForTesting
;
import
com.google.android.exoplayer2.audio.AudioAttributes
;
import
com.google.android.exoplayer2.audio.AudioAttributes
;
import
com.google.android.exoplayer2.device.DeviceInfo
;
import
com.google.android.exoplayer2.device.DeviceInfo
;
import
com.google.android.exoplayer2.metadata.Metadata
;
import
com.google.android.exoplayer2.metadata.Metadata
;
import
com.google.android.exoplayer2.source.TrackGroupArray
;
import
com.google.android.exoplayer2.source.TrackGroupArray
;
import
com.google.android.exoplayer2.text.Cue
;
import
com.google.android.exoplayer2.text.Cue
;
import
com.google.android.exoplayer2.trackselection.TrackSelectionArray
;
import
com.google.android.exoplayer2.trackselection.TrackSelectionArray
;
import
com.google.android.exoplayer2.util.Clock
;
import
com.google.android.exoplayer2.util.ListenerSet
;
import
com.google.android.exoplayer2.video.VideoSize
;
import
com.google.android.exoplayer2.video.VideoSize
;
import
java.util.List
;
import
java.util.List
;
/**
/**
* A {@link Player} that forwards operations to another {@link Player}. Applications can use this
* A {@link Player} that forwards operations to another {@link Player}. Applications can use this
* class to suppress or modify specific operations, by overriding the respective methods.
* class to suppress or modify specific operations, by overriding the respective methods.
*
* <p>An application can {@link #setDisabledCommands disable available commands}. When the wrapped
* player advertises available commands, either with {@link Player#isCommandAvailable(int)} or with
* {@link Listener#onAvailableCommandsChanged}, the disabled commands will be filtered out.
*/
*/
public
class
ForwardingPlayer
implements
Player
{
public
class
ForwardingPlayer
implements
Player
{
private
final
Player
player
;
private
final
Clock
clock
;
@Nullable
private
ForwardingListener
forwardingListener
;
private
Commands
disabledCommands
;
private
final
Player
player
;
@Nullable
private
Commands
unfilteredCommands
;
@Nullable
private
Commands
filteredCommands
;
/** Creates a new instance that forwards all operations to {@code player}. */
/** Creates a new instance that forwards all operations to {@code player}. */
public
ForwardingPlayer
(
Player
player
)
{
public
ForwardingPlayer
(
Player
player
)
{
this
(
player
,
Clock
.
DEFAULT
);
}
@VisibleForTesting
/* package */
ForwardingPlayer
(
Player
player
,
Clock
clock
)
{
this
.
player
=
player
;
this
.
player
=
player
;
this
.
clock
=
clock
;
this
.
disabledCommands
=
Commands
.
EMPTY
;
}
/**
* Sets the disabled {@link Commands}.
*
* <p>When querying for available commands with {@link #isCommandAvailable(int)}, or when the
* wrapped player advertises available commands with {@link Listener#isCommandAvailable}, disabled
* commands will be filtered out.
*/
public
void
setDisabledCommands
(
Commands
commands
)
{
checkState
(
player
.
getApplicationLooper
().
equals
(
Looper
.
myLooper
()));
disabledCommands
=
commands
;
filteredCommands
=
null
;
if
(
forwardingListener
!=
null
)
{
forwardingListener
.
maybeAdvertiseAvailableCommands
();
}
}
/** Returns the disabled commands. */
public
Commands
getDisabledCommands
()
{
return
disabledCommands
;
}
}
@Override
@Override
...
@@ -91,35 +49,25 @@ public class ForwardingPlayer implements Player {
...
@@ -91,35 +49,25 @@ public class ForwardingPlayer implements Player {
}
}
@Override
@Override
@SuppressWarnings
(
"deprecation"
)
// Implementing deprecated method.
public
void
addListener
(
EventListener
listener
)
{
public
void
addListener
(
EventListener
listener
)
{
addListener
(
new
EventListenerWrapper
(
listener
));
player
.
addListener
(
new
ForwardingEventListener
(
this
,
listener
));
}
}
@Override
@Override
public
void
addListener
(
Listener
listener
)
{
public
void
addListener
(
Listener
listener
)
{
if
(
forwardingListener
==
null
)
{
player
.
addListener
(
new
ForwardingListener
(
this
,
listener
));
forwardingListener
=
new
ForwardingListener
(
this
);
}
if
(!
forwardingListener
.
isRegistered
())
{
forwardingListener
.
registerTo
(
player
);
}
forwardingListener
.
addListener
(
listener
);
}
}
@Override
@Override
@SuppressWarnings
(
"deprecation"
)
// Implementing deprecated method.
public
void
removeListener
(
EventListener
listener
)
{
public
void
removeListener
(
EventListener
listener
)
{
removeListener
(
new
EventListenerWrapper
(
listener
));
player
.
removeListener
(
new
ForwardingEventListener
(
this
,
listener
));
}
}
@Override
@Override
public
void
removeListener
(
Listener
listener
)
{
public
void
removeListener
(
Listener
listener
)
{
if
(
forwardingListener
==
null
)
{
player
.
removeListener
(
new
ForwardingListener
(
this
,
listener
));
return
;
}
forwardingListener
.
removeListener
(
listener
);
if
(!
forwardingListener
.
hasListeners
())
{
forwardingListener
.
unregisterFrom
(
player
);
}
}
}
@Override
@Override
...
@@ -200,17 +148,12 @@ public class ForwardingPlayer implements Player {
...
@@ -200,17 +148,12 @@ public class ForwardingPlayer implements Player {
@Override
@Override
public
boolean
isCommandAvailable
(
@Command
int
command
)
{
public
boolean
isCommandAvailable
(
@Command
int
command
)
{
return
!
disabledCommands
.
contains
(
command
)
&&
player
.
isCommandAvailable
(
command
);
return
player
.
isCommandAvailable
(
command
);
}
}
@Override
@Override
public
Commands
getAvailableCommands
()
{
public
Commands
getAvailableCommands
()
{
Commands
commands
=
player
.
getAvailableCommands
();
return
player
.
getAvailableCommands
();
if
(
filteredCommands
==
null
||
!
commands
.
equals
(
unfilteredCommands
))
{
filteredCommands
=
filterCommands
(
commands
,
disabledCommands
);
unfilteredCommands
=
commands
;
}
return
filteredCommands
;
}
}
@Override
@Override
...
@@ -602,456 +545,233 @@ public class ForwardingPlayer implements Player {
...
@@ -602,456 +545,233 @@ public class ForwardingPlayer implements Player {
player
.
setDeviceMuted
(
muted
);
player
.
setDeviceMuted
(
muted
);
}
}
/**
@SuppressWarnings
(
"deprecation"
)
// Use of deprecated type for backwards compatibility.
* Wraps a {@link Listener} and intercepts {@link Listener#onAvailableCommandsChanged} in order to
private
static
class
ForwardingEventListener
implements
EventListener
{
* filter disabled commands. All other operations are forwarded to the wrapped {@link Listener}.
*/
private
static
class
ForwardingListener
implements
Listener
{
private
final
ForwardingPlayer
player
;
private
final
ListenerSet
<
Listener
>
listeners
;
private
boolean
registered
;
private
Commands
lastReceivedCommands
;
private
Commands
lastAdvertisedCommands
;
public
ForwardingListener
(
ForwardingPlayer
forwardingPlayer
)
{
this
.
player
=
forwardingPlayer
;
listeners
=
new
ListenerSet
<>(
forwardingPlayer
.
player
.
getApplicationLooper
(),
forwardingPlayer
.
clock
,
(
listener
,
flags
)
->
listener
.
onEvents
(
forwardingPlayer
,
new
Events
(
flags
)));
lastReceivedCommands
=
Commands
.
EMPTY
;
lastAdvertisedCommands
=
Commands
.
EMPTY
;
}
public
void
registerTo
(
Player
player
)
{
checkState
(!
registered
);
player
.
addListener
(
this
);
lastReceivedCommands
=
player
.
getAvailableCommands
();
lastAdvertisedCommands
=
lastReceivedCommands
;
registered
=
true
;
}
public
void
unregisterFrom
(
Player
player
)
{
checkState
(
registered
);
player
.
removeListener
(
this
);
registered
=
false
;
}
public
boolean
isRegistered
()
{
return
registered
;
}
public
void
addListener
(
Listener
listener
)
{
listeners
.
add
(
listener
);
}
public
void
removeListener
(
Listener
listener
)
{
listeners
.
remove
(
listener
);
}
public
boolean
hasListeners
()
{
return
listeners
.
size
()
>
0
;
}
// VideoListener callbacks
@Override
public
void
onVideoSizeChanged
(
VideoSize
videoSize
)
{
listeners
.
sendEvent
(
C
.
INDEX_UNSET
,
listener
->
listener
.
onVideoSizeChanged
(
videoSize
));
}
@Override
@SuppressWarnings
(
"deprecation"
)
// Forwarding to deprecated method.
public
void
onVideoSizeChanged
(
int
width
,
int
height
,
int
unappliedRotationDegrees
,
float
pixelWidthHeightRatio
)
{
listeners
.
sendEvent
(
C
.
INDEX_UNSET
,
listener
->
listener
.
onVideoSizeChanged
(
width
,
height
,
unappliedRotationDegrees
,
pixelWidthHeightRatio
));
}
@Override
public
void
onSurfaceSizeChanged
(
int
width
,
int
height
)
{
listeners
.
sendEvent
(
C
.
INDEX_UNSET
,
listener
->
listener
.
onSurfaceSizeChanged
(
width
,
height
));
}
@Override
public
void
onRenderedFirstFrame
()
{
listeners
.
sendEvent
(
C
.
INDEX_UNSET
,
Listener:
:
onRenderedFirstFrame
);
}
// AudioListener callbacks
@Override
private
final
ForwardingPlayer
forwardingPlayer
;
public
void
onAudioSessionIdChanged
(
int
audioSessionId
)
{
private
final
EventListener
eventListener
;
listeners
.
sendEvent
(
C
.
INDEX_UNSET
,
listener
->
listener
.
onAudioSessionIdChanged
(
audioSessionId
));
}
@Override
public
void
onAudioAttributesChanged
(
AudioAttributes
audioAttributes
)
{
listeners
.
sendEvent
(
C
.
INDEX_UNSET
,
listener
->
listener
.
onAudioAttributesChanged
(
audioAttributes
));
}
@Override
public
void
onVolumeChanged
(
float
volume
)
{
listeners
.
sendEvent
(
C
.
INDEX_UNSET
,
listener
->
listener
.
onVolumeChanged
(
volume
));
}
@Override
public
void
onSkipSilenceEnabledChanged
(
boolean
skipSilenceEnabled
)
{
listeners
.
sendEvent
(
C
.
INDEX_UNSET
,
listener
->
listener
.
onSkipSilenceEnabledChanged
(
skipSilenceEnabled
));
}
// TextOutput callbacks
@Override
public
void
onCues
(
List
<
Cue
>
cues
)
{
listeners
.
sendEvent
(
C
.
INDEX_UNSET
,
listener
->
listener
.
onCues
(
cues
));
}
// MetadataOutput callbacks
@Override
public
void
onMetadata
(
Metadata
metadata
)
{
listeners
.
sendEvent
(
C
.
INDEX_UNSET
,
listener
->
listener
.
onMetadata
(
metadata
));
}
// DeviceListener callbacks
@Override
public
void
onDeviceInfoChanged
(
DeviceInfo
deviceInfo
)
{
listeners
.
sendEvent
(
C
.
INDEX_UNSET
,
listener
->
listener
.
onDeviceInfoChanged
(
deviceInfo
));
}
@Override
private
ForwardingEventListener
(
public
void
onDeviceVolumeChanged
(
int
volume
,
boolean
muted
)
{
ForwardingPlayer
forwardingPlayer
,
EventListener
eventListener
)
{
listeners
.
sendEvent
(
C
.
INDEX_UNSET
,
listener
->
listener
.
onDeviceVolumeChanged
(
volume
,
muted
));
this
.
forwardingPlayer
=
forwardingPlayer
;
this
.
eventListener
=
eventListener
;
}
}
// EventListener callbacks
@Override
@Override
public
void
onTimelineChanged
(
Timeline
timeline
,
@TimelineChangeReason
int
reason
)
{
public
void
onTimelineChanged
(
Timeline
timeline
,
@TimelineChangeReason
int
reason
)
{
listeners
.
sendEvent
(
eventListener
.
onTimelineChanged
(
timeline
,
reason
);
EVENT_TIMELINE_CHANGED
,
listener
->
listener
.
onTimelineChanged
(
timeline
,
reason
));
}
}
@Override
@Override
public
void
onMediaItemTransition
(
public
void
onMediaItemTransition
(
@Nullable
MediaItem
mediaItem
,
@MediaItemTransitionReason
int
reason
)
{
@Nullable
MediaItem
mediaItem
,
@MediaItemTransitionReason
int
reason
)
{
listeners
.
sendEvent
(
eventListener
.
onMediaItemTransition
(
mediaItem
,
reason
);
EVENT_MEDIA_ITEM_TRANSITION
,
listener
->
listener
.
onMediaItemTransition
(
mediaItem
,
reason
));
}
}
@Override
@Override
public
void
onTracksChanged
(
TrackGroupArray
trackGroups
,
TrackSelectionArray
trackSelections
)
{
public
void
onTracksChanged
(
TrackGroupArray
trackGroups
,
TrackSelectionArray
trackSelections
)
{
listeners
.
sendEvent
(
eventListener
.
onTracksChanged
(
trackGroups
,
trackSelections
);
EVENT_TRACKS_CHANGED
,
listener
->
listener
.
onTracksChanged
(
trackGroups
,
trackSelections
));
}
}
@Override
@Override
public
void
onStaticMetadataChanged
(
List
<
Metadata
>
metadataList
)
{
public
void
onStaticMetadataChanged
(
List
<
Metadata
>
metadataList
)
{
listeners
.
sendEvent
(
eventListener
.
onStaticMetadataChanged
(
metadataList
);
EVENT_STATIC_METADATA_CHANGED
,
listener
->
listener
.
onStaticMetadataChanged
(
metadataList
));
}
}
@Override
@Override
public
void
onMediaMetadataChanged
(
MediaMetadata
mediaMetadata
)
{
public
void
onMediaMetadataChanged
(
MediaMetadata
mediaMetadata
)
{
listeners
.
sendEvent
(
eventListener
.
onMediaMetadataChanged
(
mediaMetadata
);
EVENT_MEDIA_METADATA_CHANGED
,
listener
->
listener
.
onMediaMetadataChanged
(
mediaMetadata
));
}
}
@Override
@Override
public
void
onIsLoadingChanged
(
boolean
isLoading
)
{
public
void
onIsLoadingChanged
(
boolean
isLoading
)
{
listeners
.
sendEvent
(
eventListener
.
onIsLoadingChanged
(
isLoading
);
EVENT_IS_LOADING_CHANGED
,
listener
->
listener
.
onIsLoadingChanged
(
isLoading
));
}
}
@Override
@Override
@SuppressWarnings
(
"deprecation"
)
// Forwarding to deprecated method.
public
void
onLoadingChanged
(
boolean
isLoading
)
{
public
void
onLoadingChanged
(
boolean
isLoading
)
{
listeners
.
sendEvent
(
eventListener
.
onIsLoadingChanged
(
isLoading
);
EVENT_IS_LOADING_CHANGED
,
listener
->
listener
.
onLoadingChanged
(
isLoading
));
}
}
@Override
@Override
public
void
onAvailableCommandsChanged
(
Commands
availableCommands
)
{
public
void
onAvailableCommandsChanged
(
Commands
availableCommands
)
{
lastReceivedCommands
=
availableCommands
;
eventListener
.
onAvailableCommandsChanged
(
availableCommands
);
maybeAdvertiseAvailableCommands
();
}
}
@Override
@Override
@SuppressWarnings
(
"deprecation"
)
// Forwarding to deprecated method.
public
void
onPlayerStateChanged
(
boolean
playWhenReady
,
@State
int
playbackState
)
{
public
void
onPlayerStateChanged
(
boolean
playWhenReady
,
@State
int
playbackState
)
{
listeners
.
sendEvent
(
eventListener
.
onPlayerStateChanged
(
playWhenReady
,
playbackState
);
C
.
INDEX_UNSET
,
listener
->
listener
.
onPlayerStateChanged
(
playWhenReady
,
playbackState
));
}
}
@Override
@Override
public
void
onPlaybackStateChanged
(
@State
int
state
)
{
public
void
onPlaybackStateChanged
(
@State
int
state
)
{
listeners
.
sendEvent
(
eventListener
.
onPlaybackStateChanged
(
state
);
EVENT_PLAYBACK_STATE_CHANGED
,
listener
->
listener
.
onPlaybackStateChanged
(
state
));
}
}
@Override
@Override
public
void
onPlayWhenReadyChanged
(
boolean
playWhenReady
,
@State
int
reason
)
{
public
void
onPlayWhenReadyChanged
(
listeners
.
sendEvent
(
boolean
playWhenReady
,
@PlayWhenReadyChangeReason
int
reason
)
{
EVENT_PLAY_WHEN_READY_CHANGED
,
eventListener
.
onPlayWhenReadyChanged
(
playWhenReady
,
reason
);
listener
->
listener
.
onPlayWhenReadyChanged
(
playWhenReady
,
reason
));
}
}
@Override
@Override
public
void
onPlaybackSuppressionReasonChanged
(
public
void
onPlaybackSuppressionReasonChanged
(
@PlaybackSuppressionReason
int
playbackSuppressionReason
)
{
@PlayWhenReadyChangeReason
int
playbackSuppressionReason
)
{
listeners
.
sendEvent
(
eventListener
.
onPlaybackStateChanged
(
playbackSuppressionReason
);
EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED
,
listener
->
listener
.
onPlaybackSuppressionReasonChanged
(
playbackSuppressionReason
));
}
}
@Override
@Override
public
void
onIsPlayingChanged
(
boolean
isPlaying
)
{
public
void
onIsPlayingChanged
(
boolean
isPlaying
)
{
listeners
.
sendEvent
(
eventListener
.
onIsPlayingChanged
(
isPlaying
);
EVENT_IS_PLAYING_CHANGED
,
listener
->
listener
.
onIsPlayingChanged
(
isPlaying
));
}
}
@Override
@Override
public
void
onRepeatModeChanged
(
@RepeatMode
int
repeatMode
)
{
public
void
onRepeatModeChanged
(
@RepeatMode
int
repeatMode
)
{
listeners
.
sendEvent
(
eventListener
.
onRepeatModeChanged
(
repeatMode
);
EVENT_REPEAT_MODE_CHANGED
,
listener
->
listener
.
onRepeatModeChanged
(
repeatMode
));
}
}
@Override
@Override
public
void
onShuffleModeEnabledChanged
(
boolean
shuffleModeEnabled
)
{
public
void
onShuffleModeEnabledChanged
(
boolean
shuffleModeEnabled
)
{
listeners
.
sendEvent
(
eventListener
.
onShuffleModeEnabledChanged
(
shuffleModeEnabled
);
EVENT_SHUFFLE_MODE_ENABLED_CHANGED
,
listener
->
listener
.
onShuffleModeEnabledChanged
(
shuffleModeEnabled
));
}
}
@Override
@Override
public
void
onPlayerError
(
ExoPlaybackException
error
)
{
public
void
onPlayerError
(
ExoPlaybackException
error
)
{
listeners
.
sendEvent
(
EVENT_PLAYER_ERROR
,
listener
->
listener
.
onPlayerError
(
error
)
);
eventListener
.
onPlayerError
(
error
);
}
}
@Override
@Override
@SuppressWarnings
(
"deprecation"
)
// Forwarding to deprecated method.
public
void
onPositionDiscontinuity
(
@DiscontinuityReason
int
reason
)
{
public
void
onPositionDiscontinuity
(
@DiscontinuityReason
int
reason
)
{
listeners
.
sendEvent
(
eventListener
.
onPositionDiscontinuity
(
reason
);
EVENT_POSITION_DISCONTINUITY
,
listener
->
listener
.
onPositionDiscontinuity
(
reason
));
}
}
@Override
@Override
public
void
onPositionDiscontinuity
(
public
void
onPositionDiscontinuity
(
PositionInfo
oldPosition
,
PositionInfo
newPosition
,
@DiscontinuityReason
int
reason
)
{
PositionInfo
oldPosition
,
PositionInfo
newPosition
,
@DiscontinuityReason
int
reason
)
{
listeners
.
sendEvent
(
eventListener
.
onPositionDiscontinuity
(
oldPosition
,
newPosition
,
reason
);
EVENT_POSITION_DISCONTINUITY
,
listener
->
listener
.
onPositionDiscontinuity
(
oldPosition
,
newPosition
,
reason
));
}
}
@Override
@Override
public
void
onPlaybackParametersChanged
(
PlaybackParameters
playbackParameters
)
{
public
void
onPlaybackParametersChanged
(
PlaybackParameters
playbackParameters
)
{
listeners
.
sendEvent
(
eventListener
.
onPlaybackParametersChanged
(
playbackParameters
);
EVENT_PLAYBACK_PARAMETERS_CHANGED
,
listener
->
listener
.
onPlaybackParametersChanged
(
playbackParameters
));
}
}
@Override
@Override
@SuppressWarnings
(
"deprecation"
)
// Forwarding to deprecated method.
public
void
onSeekProcessed
()
{
public
void
onSeekProcessed
()
{
listeners
.
sendEvent
(
C
.
INDEX_UNSET
,
EventListener:
:
onSeekProcessed
);
eventListener
.
onSeekProcessed
(
);
}
}
@Override
@Override
public
void
onEvents
(
Player
player
,
Events
events
)
{
public
void
onEvents
(
Player
player
,
Events
events
)
{
//
Do nothing, individual callbacks will trigger this event on behalf of the forwarding
//
Replace player with forwarding player.
// player.
eventListener
.
onEvents
(
forwardingPlayer
,
events
);
}
}
public
void
maybeAdvertiseAvailableCommands
()
{
@Override
Commands
commandsToAdvertise
=
filterCommands
(
lastReceivedCommands
,
player
.
disabledCommands
);
public
boolean
equals
(
@Nullable
Object
o
)
{
if
(
!
commandsToAdvertise
.
equals
(
lastAdvertisedCommands
)
)
{
if
(
this
==
o
)
{
lastAdvertisedCommands
=
commandsToAdvertis
e
;
return
tru
e
;
listeners
.
sendEvent
(
}
EVENT_AVAILABLE_COMMANDS_CHANGED
,
if
(!(
o
instanceof
ForwardingEventListener
))
{
listener
->
listener
.
onAvailableCommandsChanged
(
commandsToAdvertise
))
;
return
false
;
}
}
}
}
/**
* Wraps an {@link EventListener} as a {@link Listener} so that it can be used by the {@link
* ForwardingListener}.
*/
private
static
class
EventListenerWrapper
implements
Listener
{
private
final
EventListener
listener
;
/** Wraps an {@link EventListener}. */
public
EventListenerWrapper
(
EventListener
listener
)
{
this
.
listener
=
listener
;
}
// EventListener callbacks
@Override
ForwardingEventListener
that
=
(
ForwardingEventListener
)
o
;
public
void
onTimelineChanged
(
Timeline
timeline
,
@TimelineChangeReason
int
reason
)
{
listener
.
onTimelineChanged
(
timeline
,
reason
);
}
@Override
if
(!
forwardingPlayer
.
equals
(
that
.
forwardingPlayer
))
{
public
void
onMediaItemTransition
(
return
false
;
@Nullable
MediaItem
mediaItem
,
@MediaItemTransitionReason
int
reason
)
{
}
listener
.
onMediaItemTransition
(
mediaItem
,
reason
);
return
eventListener
.
equals
(
that
.
eventListener
);
}
}
@Override
@Override
public
void
onTracksChanged
(
TrackGroupArray
trackGroups
,
TrackSelectionArray
trackSelections
)
{
public
int
hashCode
()
{
listener
.
onTracksChanged
(
trackGroups
,
trackSelections
);
int
result
=
forwardingPlayer
.
hashCode
();
result
=
31
*
result
+
eventListener
.
hashCode
();
return
result
;
}
}
}
@Override
private
static
final
class
ForwardingListener
extends
ForwardingEventListener
public
void
onStaticMetadataChanged
(
List
<
Metadata
>
metadataList
)
{
implements
Listener
{
listener
.
onStaticMetadataChanged
(
metadataList
);
}
@Override
private
final
Listener
listener
;
public
void
onMediaMetadataChanged
(
MediaMetadata
mediaMetadata
)
{
listener
.
onMediaMetadataChanged
(
mediaMetadata
);
}
@Override
public
ForwardingListener
(
ForwardingPlayer
forwardingPlayer
,
Listener
listener
)
{
public
void
onIsLoadingChanged
(
boolean
isLoading
)
{
super
(
forwardingPlayer
,
listener
);
listener
.
onIsLoadingChanged
(
isLoading
)
;
this
.
listener
=
listener
;
}
}
@Override
// VideoListener methods.
@SuppressWarnings
(
"deprecation"
)
// Forwarding to deprecated method.
public
void
onLoadingChanged
(
boolean
isLoading
)
{
listener
.
onLoadingChanged
(
isLoading
);
}
@Override
@Override
public
void
on
AvailableCommandsChanged
(
Commands
availableCommands
)
{
public
void
on
VideoSizeChanged
(
VideoSize
videoSize
)
{
listener
.
on
AvailableCommandsChanged
(
availableCommands
);
listener
.
on
VideoSizeChanged
(
videoSize
);
}
}
@Override
@Override
@SuppressWarnings
(
"deprecation"
)
// Forwarding to deprecated method.
@SuppressWarnings
(
"deprecation"
)
// Forwarding to deprecated method.
public
void
onPlayerStateChanged
(
boolean
playWhenReady
,
@State
int
playbackState
)
{
public
void
onVideoSizeChanged
(
listener
.
onPlayerStateChanged
(
playWhenReady
,
playbackState
);
int
width
,
int
height
,
int
unappliedRotationDegrees
,
float
pixelWidthHeightRatio
)
{
}
listener
.
onVideoSizeChanged
(
width
,
height
,
unappliedRotationDegrees
,
pixelWidthHeightRatio
);
@Override
public
void
onPlaybackStateChanged
(
@State
int
state
)
{
listener
.
onPlaybackStateChanged
(
state
);
}
@Override
public
void
onPlayWhenReadyChanged
(
boolean
playWhenReady
,
@PlayWhenReadyChangeReason
int
reason
)
{
listener
.
onPlayWhenReadyChanged
(
playWhenReady
,
reason
);
}
@Override
public
void
onPlaybackSuppressionReasonChanged
(
@PlaybackSuppressionReason
int
playbackSuppressionReason
)
{
listener
.
onPlaybackSuppressionReasonChanged
(
playbackSuppressionReason
);
}
}
@Override
@Override
public
void
on
IsPlayingChanged
(
boolean
isPlaying
)
{
public
void
on
SurfaceSizeChanged
(
int
width
,
int
height
)
{
listener
.
on
IsPlayingChanged
(
isPlaying
);
listener
.
on
SurfaceSizeChanged
(
width
,
height
);
}
}
@Override
@Override
public
void
onRe
peatModeChanged
(
@RepeatMode
int
repeatMode
)
{
public
void
onRe
nderedFirstFrame
(
)
{
listener
.
onRe
peatModeChanged
(
repeatMode
);
listener
.
onRe
nderedFirstFrame
(
);
}
}
@Override
// AudioListener methods
public
void
onShuffleModeEnabledChanged
(
boolean
shuffleModeEnabled
)
{
listener
.
onShuffleModeEnabledChanged
(
shuffleModeEnabled
);
}
@Override
@Override
public
void
on
PlayerError
(
ExoPlaybackException
error
)
{
public
void
on
AudioSessionIdChanged
(
int
audioSessionId
)
{
listener
.
on
PlayerError
(
error
);
listener
.
on
AudioSessionIdChanged
(
audioSessionId
);
}
}
@Override
@Override
@SuppressWarnings
(
"deprecation"
)
// Forwarding to deprecated method.
public
void
onAudioAttributesChanged
(
AudioAttributes
audioAttributes
)
{
public
void
onPositionDiscontinuity
(
@DiscontinuityReason
int
reason
)
{
listener
.
onAudioAttributesChanged
(
audioAttributes
);
listener
.
onPositionDiscontinuity
(
reason
);
}
}
@Override
@Override
public
void
onPositionDiscontinuity
(
public
void
onVolumeChanged
(
float
volume
)
{
PositionInfo
oldPosition
,
PositionInfo
newPosition
,
@DiscontinuityReason
int
reason
)
{
listener
.
onVolumeChanged
(
volume
);
listener
.
onPositionDiscontinuity
(
oldPosition
,
newPosition
,
reason
);
}
}
@Override
@Override
public
void
on
PlaybackParametersChanged
(
PlaybackParameters
playbackParameters
)
{
public
void
on
SkipSilenceEnabledChanged
(
boolean
skipSilenceEnabled
)
{
listener
.
on
PlaybackParametersChanged
(
playbackParameters
);
listener
.
on
SkipSilenceEnabledChanged
(
skipSilenceEnabled
);
}
}
@Override
// TextOutput methods.
@SuppressWarnings
(
"deprecation"
)
// Forwarding to deprecated method.
public
void
onSeekProcessed
()
{
listener
.
onSeekProcessed
();
}
@Override
@Override
public
void
on
Events
(
Player
player
,
Events
event
s
)
{
public
void
on
Cues
(
List
<
Cue
>
cue
s
)
{
listener
.
on
Events
(
player
,
event
s
);
listener
.
on
Cues
(
cue
s
);
}
}
//
Other Listener callbacks, they should never be invoked on this wrapper
.
//
MetadataOutput methods
.
@Override
@Override
public
void
onMetadata
(
Metadata
metadata
)
{
public
void
onMetadata
(
Metadata
metadata
)
{
throw
new
IllegalStateException
(
);
listener
.
onMetadata
(
metadata
);
}
}
@Override
// DeviceListener callbacks
public
void
onCues
(
List
<
Cue
>
cues
)
{
throw
new
IllegalStateException
();
}
@Override
@Override
public
boolean
equals
(
@Nullable
Object
o
)
{
public
void
onDeviceInfoChanged
(
DeviceInfo
deviceInfo
)
{
if
(
this
==
o
)
{
listener
.
onDeviceInfoChanged
(
deviceInfo
);
return
true
;
}
if
(!(
o
instanceof
EventListenerWrapper
))
{
return
false
;
}
EventListenerWrapper
that
=
(
EventListenerWrapper
)
o
;
return
listener
.
equals
(
that
.
listener
);
}
}
@Override
@Override
public
int
hashCode
()
{
public
void
onDeviceVolumeChanged
(
int
volume
,
boolean
muted
)
{
return
listener
.
hashCode
();
listener
.
onDeviceVolumeChanged
(
volume
,
muted
);
}
}
/** Returns the remaining available commands after removing disabled commands. */
private
static
Commands
filterCommands
(
Commands
availableCommands
,
Commands
disabledCommands
)
{
if
(
disabledCommands
.
size
()
==
0
)
{
return
availableCommands
;
}
Commands
.
Builder
builder
=
new
Commands
.
Builder
();
for
(
int
i
=
0
;
i
<
availableCommands
.
size
();
i
++)
{
int
command
=
availableCommands
.
get
(
i
);
builder
.
addIf
(
command
,
!
disabledCommands
.
contains
(
command
));
}
}
return
builder
.
build
();
}
}
}
}
library/common/src/test/java/com/google/android/exoplayer2/ForwardingPlayerTest.java
View file @
65d8ff80
/*
/*
* Copyright
(C)
2021 The Android Open Source Project
* Copyright 2021 The Android Open Source Project
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* you may not use this file except in compliance with the License.
...
@@ -15,26 +15,18 @@
...
@@ -15,26 +15,18 @@
*/
*/
package
com
.
google
.
android
.
exoplayer2
;
package
com
.
google
.
android
.
exoplayer2
;
import
static
com
.
google
.
android
.
exoplayer2
.
Player
.
COMMAND_PLAY_PAUSE
;
import
static
com
.
google
.
android
.
exoplayer2
.
Player
.
EVENT_IS_PLAYING_CHANGED
;
import
static
com
.
google
.
android
.
exoplayer2
.
Player
.
COMMAND_PREPARE_STOP
;
import
static
com
.
google
.
android
.
exoplayer2
.
Player
.
EVENT_MEDIA_ITEM_TRANSITION
;
import
static
com
.
google
.
android
.
exoplayer2
.
Player
.
COMMAND_SEEK_TO_MEDIA_ITEM
;
import
static
com
.
google
.
android
.
exoplayer2
.
Player
.
EVENT_TIMELINE_CHANGED
;
import
static
com
.
google
.
android
.
exoplayer2
.
Player
.
EVENT_AVAILABLE_COMMANDS_CHANGED
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkArgument
;
import
static
com
.
google
.
android
.
exoplayer2
.
Player
.
EVENT_PLAYBACK_STATE_CHANGED
;
import
static
com
.
google
.
android
.
exoplayer2
.
Player
.
STATE_READY
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkState
;
import
static
com
.
google
.
common
.
truth
.
Truth
.
assertThat
;
import
static
com
.
google
.
common
.
truth
.
Truth
.
assertThat
;
import
static
org
.
mockito
.
ArgumentMatchers
.
argThat
;
import
static
org
.
mockito
.
ArgumentMatchers
.
same
;
import
static
org
.
mockito
.
ArgumentMatchers
.
same
;
import
static
org
.
mockito
.
Mockito
.
inOrder
;
import
static
org
.
mockito
.
Mockito
.
mock
;
import
static
org
.
mockito
.
Mockito
.
mock
;
import
static
org
.
mockito
.
Mockito
.
spy
;
import
static
org
.
mockito
.
Mockito
.
verify
;
import
static
org
.
mockito
.
Mockito
.
verify
;
import
static
org
.
mockito
.
Mockito
.
verifyNoMoreInteractions
;
import
android.os.Looper
;
import
androidx.annotation.Nullable
;
import
androidx.test.ext.junit.runners.AndroidJUnit4
;
import
androidx.test.ext.junit.runners.AndroidJUnit4
;
import
com.google.android.exoplayer2.testutil.StubExoPlayer
;
import
com.google.android.exoplayer2.testutil.StubExoPlayer
;
import
com.google.android.exoplayer2.util.ExoFlags
;
import
java.lang.reflect.Method
;
import
java.lang.reflect.Method
;
import
java.lang.reflect.Modifier
;
import
java.lang.reflect.Modifier
;
import
java.util.ArrayDeque
;
import
java.util.ArrayDeque
;
...
@@ -46,220 +38,112 @@ import java.util.Queue;
...
@@ -46,220 +38,112 @@ import java.util.Queue;
import
java.util.Set
;
import
java.util.Set
;
import
org.junit.Test
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.junit.runner.RunWith
;
import
org.mockito.ArgumentMatcher
;
import
org.mockito.ArgumentCaptor
;
import
org.mockito.InOrder
;
import
org.robolectric.shadows.ShadowLooper
;
/** Unit test for {@link ForwardingPlayer}. */
/** Unit test
s
for {@link ForwardingPlayer}. */
@RunWith
(
AndroidJUnit4
.
class
)
@RunWith
(
AndroidJUnit4
.
class
)
public
class
ForwardingPlayerTest
{
public
class
ForwardingPlayerTest
{
@Test
public
void
getAvailableCommands_withDisabledCommands_filtersDisabledCommands
()
{
Player
player
=
new
FakePlayer
(
COMMAND_PLAY_PAUSE
,
COMMAND_PREPARE_STOP
);
ForwardingPlayer
forwardingPlayer
=
new
ForwardingPlayer
(
player
);
forwardingPlayer
.
setDisabledCommands
(
buildCommands
(
COMMAND_PREPARE_STOP
));
assertThat
(
forwardingPlayer
.
getAvailableCommands
())
.
isEqualTo
(
buildCommands
(
COMMAND_PLAY_PAUSE
));
}
@Test
public
void
getAvailableCommands_playerAvailableCommandsChanged_returnsFreshCommands
()
{
FakePlayer
player
=
new
FakePlayer
(
COMMAND_PLAY_PAUSE
,
COMMAND_PREPARE_STOP
);
ForwardingPlayer
forwardingPlayer
=
new
ForwardingPlayer
(
player
);
forwardingPlayer
.
setDisabledCommands
(
buildCommands
(
COMMAND_PREPARE_STOP
));
assertThat
(
forwardingPlayer
.
getAvailableCommands
())
.
isEqualTo
(
buildCommands
(
COMMAND_PLAY_PAUSE
));
player
.
setAvailableCommands
(
buildCommands
(
COMMAND_PLAY_PAUSE
,
COMMAND_PREPARE_STOP
,
COMMAND_SEEK_TO_MEDIA_ITEM
));
assertThat
(
forwardingPlayer
.
getAvailableCommands
())
.
isEqualTo
(
buildCommands
(
COMMAND_PLAY_PAUSE
,
COMMAND_SEEK_TO_MEDIA_ITEM
));
}
@Test
public
void
isCommandAvailable_withDisabledCommands_filtersDisabledCommands
()
{
Player
player
=
new
FakePlayer
(
COMMAND_PLAY_PAUSE
,
COMMAND_PREPARE_STOP
);
ForwardingPlayer
forwardingPlayer
=
new
ForwardingPlayer
(
player
);
forwardingPlayer
.
setDisabledCommands
(
buildCommands
(
COMMAND_PREPARE_STOP
));
assertThat
(
forwardingPlayer
.
isCommandAvailable
(
COMMAND_PLAY_PAUSE
)).
isTrue
();
assertThat
(
forwardingPlayer
.
isCommandAvailable
(
COMMAND_PREPARE_STOP
)).
isFalse
();
}
@Test
@Test
public
void
setDisabledCommands_triggersOnCommandsAvailableChanged
()
{
public
void
addListener_addsForwardingListener
()
{
Player
player
=
new
FakePlayer
(
COMMAND_PLAY_PAUSE
,
COMMAND_PREPARE_STOP
);
FakePlayer
player
=
new
FakePlayer
();
Player
.
Listener
listener
=
mock
(
Player
.
Listener
.
class
);
Player
.
Listener
listener1
=
mock
(
Player
.
Listener
.
class
);
ForwardingPlayer
forwardingPlayer
=
new
ForwardingPlayer
(
player
);
Player
.
Listener
listener2
=
mock
(
Player
.
Listener
.
class
);
forwardingPlayer
.
addListener
(
listener
);
forwardingPlayer
.
setDisabledCommands
(
buildCommands
(
COMMAND_PREPARE_STOP
));
ShadowLooper
.
idleMainLooper
();
InOrder
inOrder
=
inOrder
(
listener
);
inOrder
.
verify
(
listener
).
onAvailableCommandsChanged
(
buildCommands
(
COMMAND_PLAY_PAUSE
));
inOrder
.
verify
(
listener
)
.
onEvents
(
same
(
forwardingPlayer
),
argThat
(
new
EventsMatcher
(
EVENT_AVAILABLE_COMMANDS_CHANGED
)));
inOrder
.
verifyNoMoreInteractions
();
}
@Test
public
void
setDisabledCommands_withoutChangingAvailableCommands_noCallbackTriggered
()
{
Player
player
=
new
FakePlayer
(
COMMAND_PLAY_PAUSE
,
COMMAND_PREPARE_STOP
);
Player
.
Listener
listener
=
mock
(
Player
.
Listener
.
class
);
ForwardingPlayer
forwardingPlayer
=
new
ForwardingPlayer
(
player
);
ForwardingPlayer
forwardingPlayer
=
new
ForwardingPlayer
(
player
);
forwardingPlayer
.
addListener
(
listener
);
forwardingPlayer
.
addListener
(
listener1
);
// Add listener1 again.
forwardingPlayer
.
addListener
(
listener1
);
forwardingPlayer
.
addListener
(
listener2
);
forwardingPlayer
.
setDisabledCommands
(
buildCommands
(
COMMAND_SEEK_TO_MEDIA_ITEM
));
assertThat
(
player
.
listeners
).
hasSize
(
2
);
ShadowLooper
.
idleMainLooper
();
verifyNoMoreInteractions
(
listener
);
}
}
@Test
@Test
public
void
setDisabledCommands_multipleTimes_availableCommandsUpdated
()
{
@SuppressWarnings
(
"deprecation"
)
// Testing backwards compatibility with deprecated method.
Player
player
=
new
FakePlayer
(
COMMAND_PLAY_PAUSE
,
COMMAND_PREPARE_STOP
);
public
void
addEventListener_addsForwardingListener
()
{
ForwardingPlayer
forwardingPlayer
=
new
ForwardingPlayer
(
player
);
FakePlayer
player
=
new
FakePlayer
();
Player
.
EventListener
listener1
=
mock
(
Player
.
EventListener
.
class
);
forwardingPlayer
.
setDisabledCommands
(
buildCommands
(
COMMAND_SEEK_TO_MEDIA_ITEM
));
Player
.
EventListener
listener2
=
mock
(
Player
.
EventListener
.
class
);
assertThat
(
forwardingPlayer
.
getAvailableCommands
())
.
isEqualTo
(
buildCommands
(
COMMAND_PLAY_PAUSE
,
COMMAND_PREPARE_STOP
));
forwardingPlayer
.
setDisabledCommands
(
buildCommands
(
COMMAND_PREPARE_STOP
,
COMMAND_SEEK_TO_MEDIA_ITEM
));
assertThat
(
forwardingPlayer
.
getAvailableCommands
())
.
isEqualTo
(
buildCommands
(
COMMAND_PLAY_PAUSE
));
}
@Test
public
void
onCommandsAvailableChanged_listenerChangesCommandsRecursively_secondCallbackCalled
()
{
FakePlayer
player
=
new
FakePlayer
(
COMMAND_PLAY_PAUSE
,
COMMAND_PREPARE_STOP
);
ForwardingPlayer
forwardingPlayer
=
new
ForwardingPlayer
(
player
);
ForwardingPlayer
forwardingPlayer
=
new
ForwardingPlayer
(
player
);
Player
.
Listener
listener
=
forwardingPlayer
.
addListener
(
listener1
);
spy
(
// Add listener1 again.
new
Player
.
Listener
()
{
forwardingPlayer
.
addListener
(
listener1
);
@Override
forwardingPlayer
.
addListener
(
listener2
);
public
void
onAvailableCommandsChanged
(
Player
.
Commands
availableCommands
)
{
// The callback changes the forwarding player's disabled commands triggering
// exactly one more callback.
forwardingPlayer
.
setDisabledCommands
(
buildCommands
(
COMMAND_PREPARE_STOP
));
}
});
forwardingPlayer
.
addListener
(
listener
);
Player
.
Commands
updatedCommands
=
buildCommands
(
COMMAND_PLAY_PAUSE
,
COMMAND_PREPARE_STOP
,
COMMAND_SEEK_TO_MEDIA_ITEM
);
player
.
setAvailableCommands
(
updatedCommands
);
player
.
forwardingListener
.
onAvailableCommandsChanged
(
updatedCommands
);
ShadowLooper
.
idleMainLooper
();
InOrder
inOrder
=
inOrder
(
listener
);
assertThat
(
player
.
eventListeners
).
hasSize
(
2
);
inOrder
.
verify
(
listener
)
.
onAvailableCommandsChanged
(
buildCommands
(
COMMAND_PLAY_PAUSE
,
COMMAND_PREPARE_STOP
,
COMMAND_SEEK_TO_MEDIA_ITEM
));
inOrder
.
verify
(
listener
)
.
onAvailableCommandsChanged
(
buildCommands
(
COMMAND_PLAY_PAUSE
,
COMMAND_SEEK_TO_MEDIA_ITEM
));
inOrder
.
verify
(
listener
)
.
onEvents
(
same
(
forwardingPlayer
),
argThat
(
new
EventsMatcher
(
EVENT_AVAILABLE_COMMANDS_CHANGED
)));
inOrder
.
verifyNoMoreInteractions
();
}
}
@Test
@Test
public
void
public
void
removeListener_removesForwardingListener
()
{
interceptingOnAvailableCommandsChanged_withDisabledCommands_filtersDisabledCommands
()
{
FakePlayer
player
=
new
FakePlayer
();
FakePlayer
player
=
new
FakePlayer
();
Player
.
Listener
listener
=
mock
(
Player
.
Listener
.
class
);
Player
.
Listener
listener1
=
mock
(
Player
.
Listener
.
class
);
Player
.
Listener
listener2
=
mock
(
Player
.
Listener
.
class
);
ForwardingPlayer
forwardingPlayer
=
new
ForwardingPlayer
(
player
);
ForwardingPlayer
forwardingPlayer
=
new
ForwardingPlayer
(
player
);
forwardingPlayer
.
addListener
(
listener
);
forwardingPlayer
.
addListener
(
listener1
);
forwardingPlayer
.
addListener
(
listener2
);
forwardingPlayer
.
setDisabledCommands
(
buildCommands
(
COMMAND_PREPARE_STOP
));
forwardingPlayer
.
removeListener
(
listener1
);
ShadowLooper
.
idleMainLooper
();
assertThat
(
player
.
listeners
).
hasSize
(
1
);
// Setting the disabled commands did not affect the available commands, hence no callback was
// Remove same listener again.
// triggered.
forwardingPlayer
.
removeListener
(
listener1
);
verifyNoMoreInteractions
(
listener
);
assertThat
(
player
.
listeners
).
hasSize
(
1
);
forwardingPlayer
.
removeListener
(
listener2
);
// The wrapped player advertises new available commands.
assertThat
(
player
.
listeners
).
isEmpty
();
Player
.
Commands
updatedCommands
=
buildCommands
(
COMMAND_PLAY_PAUSE
,
COMMAND_PREPARE_STOP
);
player
.
setAvailableCommands
(
updatedCommands
);
player
.
forwardingListener
.
onAvailableCommandsChanged
(
updatedCommands
);
ShadowLooper
.
idleMainLooper
();
verify
(
listener
).
onAvailableCommandsChanged
(
buildCommands
(
COMMAND_PLAY_PAUSE
));
verify
(
listener
)
.
onEvents
(
same
(
forwardingPlayer
),
argThat
(
new
EventsMatcher
(
EVENT_AVAILABLE_COMMANDS_CHANGED
)));
verifyNoMoreInteractions
(
listener
);
}
}
@Test
@Test
public
void
@SuppressWarnings
(
"deprecation"
)
// Testing backwards compatibility with deprecated method.
interceptingOnAvailableCommandsChanged_withDisabledCommandsButAvailableCommandsNotChanged_doesNotForwardCallback
()
{
public
void
removeEventListener_removesForwardingListener
()
{
FakePlayer
player
=
new
FakePlayer
(
COMMAND_PLAY_PAUSE
,
COMMAND_PREPARE_STOP
);
FakePlayer
player
=
new
FakePlayer
();
Player
.
Listener
listener
=
mock
(
Player
.
Listener
.
class
);
Player
.
EventListener
listener1
=
mock
(
Player
.
EventListener
.
class
);
Player
.
EventListener
listener2
=
mock
(
Player
.
EventListener
.
class
);
ForwardingPlayer
forwardingPlayer
=
new
ForwardingPlayer
(
player
);
ForwardingPlayer
forwardingPlayer
=
new
ForwardingPlayer
(
player
);
forwardingPlayer
.
addListener
(
listener
);
forwardingPlayer
.
addListener
(
listener1
);
forwardingPlayer
.
addListener
(
listener2
);
// Disable commands that do not affect the available commands.
forwardingPlayer
.
removeListener
(
listener1
);
forwardingPlayer
.
setDisabledCommands
(
buildCommands
(
COMMAND_SEEK_TO_MEDIA_ITEM
));
assertThat
(
player
.
eventListeners
).
hasSize
(
1
);
ShadowLooper
.
idleMainLooper
();
// Remove same listener again.
verifyNoMoreInteractions
(
listener
);
forwardingPlayer
.
removeListener
(
listener1
);
assertThat
(
player
.
eventListeners
).
hasSize
(
1
);
// The wrapped player advertises new available commands which, after filtering the disabled
forwardingPlayer
.
removeListener
(
listener2
);
// commands, do not change the available commands.
assertThat
(
player
.
eventListeners
).
isEmpty
();
Player
.
Commands
updatedCommands
=
buildCommands
(
COMMAND_PLAY_PAUSE
,
COMMAND_PREPARE_STOP
,
COMMAND_SEEK_TO_MEDIA_ITEM
);
player
.
setAvailableCommands
(
updatedCommands
);
player
.
forwardingListener
.
onAvailableCommandsChanged
(
updatedCommands
);
ShadowLooper
.
idleMainLooper
();
verifyNoMoreInteractions
(
listener
);
}
}
@Test
@Test
public
void
removeListener_removesListenerFromPlayer
()
{
public
void
onEvents_passesForwardingPlayerAsArgument
()
{
FakePlayer
player
=
new
FakePlayer
(
COMMAND_PLAY_PAUSE
,
COMMAND_PREPARE_STOP
);
FakePlayer
player
=
new
FakePlayer
();
Player
.
Listener
listener
=
mock
(
Player
.
Listener
.
class
);
Player
.
Listener
listener
=
mock
(
Player
.
Listener
.
class
);
ForwardingPlayer
forwardingPlayer
=
new
ForwardingPlayer
(
player
);
ForwardingPlayer
forwardingPlayer
=
new
ForwardingPlayer
(
player
);
forwardingPlayer
.
addListener
(
listener
);
forwardingPlayer
.
addListener
(
listener
);
assertThat
(
player
.
forwardingListener
).
isNotNull
();
Player
.
Listener
forwardingListener
=
player
.
listeners
.
iterator
().
next
();
forwardingPlayer
.
removeListener
(
listener
);
assertThat
(
player
.
forwardingListener
).
isNull
();
forwardingListener
.
onEvents
(
}
player
,
new
Player
.
Events
(
@Test
new
ExoFlags
.
Builder
()
public
void
addEventListener_forwardsEventListenerEvents
()
{
.
addAll
(
FakePlayer
player
=
new
FakePlayer
(
COMMAND_PLAY_PAUSE
,
COMMAND_PREPARE_STOP
);
EVENT_TIMELINE_CHANGED
,
EVENT_MEDIA_ITEM_TRANSITION
,
EVENT_IS_PLAYING_CHANGED
)
Player
.
EventListener
eventListener
=
mock
(
Player
.
EventListener
.
class
);
.
build
()));
ForwardingPlayer
forwardingPlayer
=
new
ForwardingPlayer
(
player
);
ArgumentCaptor
<
Player
.
Events
>
eventsArgumentCaptor
=
forwardingPlayer
.
addListener
(
eventListener
);
ArgumentCaptor
.
forClass
(
Player
.
Events
.
class
);
player
.
forwardingListener
.
onPlaybackStateChanged
(
STATE_READY
);
verify
(
listener
).
onEvents
(
same
(
forwardingPlayer
),
eventsArgumentCaptor
.
capture
());
ShadowLooper
.
idleMainLooper
();
Player
.
Events
receivedEvents
=
eventsArgumentCaptor
.
getValue
();
assertThat
(
receivedEvents
.
size
()).
isEqualTo
(
3
);
InOrder
inOrder
=
inOrder
(
eventListener
);
assertThat
(
receivedEvents
.
contains
(
EVENT_TIMELINE_CHANGED
)).
isTrue
();
inOrder
.
verify
(
eventListener
).
onPlaybackStateChanged
(
STATE_READY
);
assertThat
(
receivedEvents
.
contains
(
EVENT_MEDIA_ITEM_TRANSITION
)).
isTrue
();
inOrder
assertThat
(
receivedEvents
.
contains
(
EVENT_IS_PLAYING_CHANGED
)).
isTrue
();
.
verify
(
eventListener
)
.
onEvents
(
same
(
forwardingPlayer
),
argThat
(
new
EventsMatcher
(
EVENT_PLAYBACK_STATE_CHANGED
)));
inOrder
.
verifyNoMoreInteractions
();
}
}
@Test
@Test
public
void
forwardingPlayer_overridesAllPlayerMethods
()
throws
Exception
{
public
void
forwardingPlayer_overridesAllPlayerMethods
()
throws
Exception
{
// Check with reflection that ForwardingPlayer overrides all Player methods.
// Check with reflection that ForwardingPlayer overrides all Player methods.
List
<
Method
>
playerM
ethods
=
getPublicMethods
(
Player
.
class
);
List
<
Method
>
m
ethods
=
getPublicMethods
(
Player
.
class
);
for
(
int
i
=
0
;
i
<
playerM
ethods
.
size
();
i
++)
{
for
(
int
i
=
0
;
i
<
m
ethods
.
size
();
i
++)
{
Method
method
=
playerM
ethods
.
get
(
i
);
Method
method
=
m
ethods
.
get
(
i
);
assertThat
(
assertThat
(
ForwardingPlayer
.
class
.
getDeclaredMethod
(
ForwardingPlayer
.
class
.
getDeclaredMethod
(
method
.
getName
(),
method
.
getParameterTypes
()))
method
.
getName
(),
method
.
getParameterTypes
()))
...
@@ -268,13 +152,13 @@ public class ForwardingPlayerTest {
...
@@ -268,13 +152,13 @@ public class ForwardingPlayerTest {
}
}
@Test
@Test
public
void
forwardingListener_overridesAllListenerMethods
()
throws
Exception
{
@SuppressWarnings
(
"deprecation"
)
// Testing backwards compatibility with deprecated type.
// Check with reflection that ForwardingListener in ForwardingPlayer overrides all Listener
public
void
forwardingEventListener_overridesAllEventListenerMethods
()
throws
Exception
{
// methods.
//
Check with reflection that ForwardingListener overrides all Listener
methods.
Class
<?>
forwardingListenerClass
=
get
NestedClass
(
"Forwarding
Listener"
);
Class
<?>
forwardingListenerClass
=
get
InnerClass
(
"ForwardingEvent
Listener"
);
List
<
Method
>
publicListenerMethods
=
getPublicMethods
(
Player
.
Listener
.
class
);
List
<
Method
>
methods
=
getPublicMethods
(
Player
.
Event
Listener
.
class
);
for
(
int
i
=
0
;
i
<
publicListenerM
ethods
.
size
();
i
++)
{
for
(
int
i
=
0
;
i
<
m
ethods
.
size
();
i
++)
{
Method
method
=
publicListenerM
ethods
.
get
(
i
);
Method
method
=
m
ethods
.
get
(
i
);
assertThat
(
assertThat
(
forwardingListenerClass
.
getDeclaredMethod
(
forwardingListenerClass
.
getDeclaredMethod
(
method
.
getName
(),
method
.
getParameterTypes
()))
method
.
getName
(),
method
.
getParameterTypes
()))
...
@@ -283,99 +167,20 @@ public class ForwardingPlayerTest {
...
@@ -283,99 +167,20 @@ public class ForwardingPlayerTest {
}
}
@Test
@Test
public
void
eventListenerWrapper_overridesAllEventListenerMethods
()
throws
Exception
{
public
void
forwardingListener_overridesAllListenerMethods
()
throws
Exception
{
// Check with reflection that EventListenerWrapper in ForwardingPlayer overrides all
// Check with reflection that ForwardingListener overrides all Listener methods.
// EventListener methods.
Class
<?>
forwardingListenerClass
=
getInnerClass
(
"ForwardingListener"
);
Class
<?>
listenerWrapperClass
=
getNestedClass
(
"EventListenerWrapper"
);
List
<
Method
>
methods
=
getPublicMethods
(
Player
.
Listener
.
class
);
List
<
Method
>
publicListenerMethods
=
getPublicMethods
(
Player
.
EventListener
.
class
);
for
(
int
i
=
0
;
i
<
methods
.
size
();
i
++)
{
for
(
int
i
=
0
;
i
<
publicListenerMethods
.
size
();
i
++)
{
Method
method
=
methods
.
get
(
i
);
Method
method
=
publicListenerMethods
.
get
(
i
);
assertThat
(
forwardingListenerClass
.
getMethod
(
method
.
getName
(),
method
.
getParameterTypes
()))
assertThat
(
listenerWrapperClass
.
getDeclaredMethod
(
method
.
getName
(),
method
.
getParameterTypes
()))
.
isNotNull
();
.
isNotNull
();
}
}
}
}
private
static
class
FakePlayer
extends
StubExoPlayer
{
/** Returns all the public methods of a Java interface. */
private
Commands
availableCommands
;
/**
* Supports up to 1 registered listener, named deliberately forwardingListener to emphasize its
* purpose.
*/
@Nullable
private
Listener
forwardingListener
;
public
FakePlayer
()
{
this
.
availableCommands
=
Commands
.
EMPTY
;
}
public
FakePlayer
(
@Command
int
...
commands
)
{
this
.
availableCommands
=
new
Commands
.
Builder
().
addAll
(
commands
).
build
();
}
@Override
public
void
addListener
(
Listener
listener
)
{
checkState
(
this
.
forwardingListener
==
null
);
this
.
forwardingListener
=
listener
;
}
@Override
public
void
removeListener
(
Listener
listener
)
{
checkState
(
this
.
forwardingListener
.
equals
(
listener
));
this
.
forwardingListener
=
null
;
}
@Override
public
Commands
getAvailableCommands
()
{
return
availableCommands
;
}
@Override
public
Looper
getApplicationLooper
()
{
return
Looper
.
getMainLooper
();
}
public
void
setAvailableCommands
(
Commands
availableCommands
)
{
this
.
availableCommands
=
availableCommands
;
}
}
private
static
Player
.
Commands
buildCommands
(
@Player
.
Command
int
...
commands
)
{
return
new
Player
.
Commands
.
Builder
().
addAll
(
commands
).
build
();
}
private
Class
<?>
getNestedClass
(
String
className
)
{
for
(
Class
<?>
declaredClass
:
ForwardingPlayer
.
class
.
getDeclaredClasses
())
{
if
(
declaredClass
.
getSimpleName
().
equals
(
className
))
{
return
declaredClass
;
}
}
throw
new
IllegalStateException
();
}
private
static
class
EventsMatcher
implements
ArgumentMatcher
<
Player
.
Events
>
{
private
final
int
[]
events
;
private
EventsMatcher
(
int
...
events
)
{
this
.
events
=
events
;
}
@Override
public
boolean
matches
(
Player
.
Events
argument
)
{
if
(
events
.
length
!=
argument
.
size
())
{
return
false
;
}
for
(
int
event
:
events
)
{
if
(!
argument
.
contains
(
event
))
{
return
false
;
}
}
return
true
;
}
}
/** Returns all the methods of Java interface. */
private
static
List
<
Method
>
getPublicMethods
(
Class
<?>
anInterface
)
{
private
static
List
<
Method
>
getPublicMethods
(
Class
<?>
anInterface
)
{
assertThat
(
anInterface
.
isInterface
()).
isTrue
(
);
checkArgument
(
anInterface
.
isInterface
()
);
// Run a BFS over all extended interfaces to inspect them all.
// Run a BFS over all extended interfaces to inspect them all.
Queue
<
Class
<?>>
interfacesQueue
=
new
ArrayDeque
<>();
Queue
<
Class
<?>>
interfacesQueue
=
new
ArrayDeque
<>();
interfacesQueue
.
add
(
anInterface
);
interfacesQueue
.
add
(
anInterface
);
...
@@ -398,4 +203,43 @@ public class ForwardingPlayerTest {
...
@@ -398,4 +203,43 @@ public class ForwardingPlayerTest {
return
list
;
return
list
;
}
}
private
static
Class
<?>
getInnerClass
(
String
className
)
{
for
(
Class
<?>
innerClass
:
ForwardingPlayer
.
class
.
getDeclaredClasses
())
{
if
(
innerClass
.
getSimpleName
().
equals
(
className
))
{
return
innerClass
;
}
}
throw
new
IllegalStateException
();
}
private
static
class
FakePlayer
extends
StubExoPlayer
{
@SuppressWarnings
(
"deprecation"
)
// Use of deprecated type for backwards compatibility.
private
final
Set
<
EventListener
>
eventListeners
=
new
HashSet
<>();
private
final
Set
<
Listener
>
listeners
=
new
HashSet
<>();
@Override
@SuppressWarnings
(
"deprecation"
)
// Implementing deprecated method.
public
void
addListener
(
EventListener
listener
)
{
eventListeners
.
add
(
listener
);
}
@Override
public
void
addListener
(
Listener
listener
)
{
listeners
.
add
(
listener
);
}
@Override
@SuppressWarnings
(
"deprecation"
)
// Implementing deprecated method.
public
void
removeListener
(
EventListener
listener
)
{
eventListeners
.
remove
(
listener
);
}
@Override
public
void
removeListener
(
Listener
listener
)
{
listeners
.
remove
(
listener
);
}
}
}
}
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