From 67b94a72a55ba1e1e50665bbede907a0e3fc749e Mon Sep 17 00:00:00 2001 From: olly Date: Wed, 13 Dec 2017 08:32:28 -0800 Subject: [PATCH] Update SingleSampleMediaSource with factory/listener changes - Convert the Builder into a Factory - Have it use MediaSourceEventListener - Also made some misc related fixes to other sources ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=178906521 --- .../source/ExtractorMediaPeriod.java | 17 +- .../source/ExtractorMediaSource.java | 4 +- .../source/SingleSampleMediaPeriod.java | 99 +++--- .../source/SingleSampleMediaSource.java | 282 +++++++++++++----- .../exoplayer2/source/hls/HlsMediaSource.java | 4 +- 5 files changed, 280 insertions(+), 126 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java index f557d4ac97..f907dc6229 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java @@ -431,9 +431,6 @@ import java.util.Arrays; @Override public void onLoadCanceled(ExtractingLoadable loadable, long elapsedRealtimeMs, long loadDurationMs, boolean released) { - if (released) { - return; - } eventDispatcher.loadCanceled( loadable.dataSpec, C.DATA_TYPE_MEDIA, @@ -446,12 +443,14 @@ import java.util.Arrays; elapsedRealtimeMs, loadDurationMs, loadable.bytesLoaded); - copyLengthFromLoader(loadable); - for (SampleQueue sampleQueue : sampleQueues) { - sampleQueue.reset(); - } - if (enabledTrackCount > 0) { - callback.onContinueLoadingRequested(this); + if (!released) { + copyLengthFromLoader(loadable); + for (SampleQueue sampleQueue : sampleQueues) { + sampleQueue.reset(); + } + if (enabledTrackCount > 0) { + callback.onContinueLoadingRequested(this); + } } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java index e787c34a9c..3b650482f5 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java @@ -195,7 +195,7 @@ public final class ExtractorMediaSource implements MediaSource, ExtractorMediaPe * @param uri The {@link Uri}. * @return The new {@link ExtractorMediaSource}. */ - public MediaSource createMediaSource(Uri uri) { + public ExtractorMediaSource createMediaSource(Uri uri) { return createMediaSource(uri, null, null); } @@ -208,7 +208,7 @@ public final class ExtractorMediaSource implements MediaSource, ExtractorMediaPe * @return The new {@link ExtractorMediaSource}. */ @Override - public MediaSource createMediaSource( + public ExtractorMediaSource createMediaSource( Uri uri, @Nullable Handler eventHandler, @Nullable MediaSourceEventListener eventListener) { isCreateCalled = true; if (extractorsFactory == null) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java b/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java index 6101c79b7f..5069a2d633 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java @@ -15,13 +15,11 @@ */ package com.google.android.exoplayer2.source; -import android.net.Uri; -import android.os.Handler; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.decoder.DecoderInputBuffer; -import com.google.android.exoplayer2.source.SingleSampleMediaSource.EventListener; +import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSpec; @@ -43,14 +41,14 @@ import java.util.Arrays; */ private static final int INITIAL_SAMPLE_SIZE = 1024; - private final Uri uri; + private final DataSpec dataSpec; private final DataSource.Factory dataSourceFactory; private final int minLoadableRetryCount; - private final Handler eventHandler; - private final EventListener eventListener; - private final int eventSourceId; + private final EventDispatcher eventDispatcher; private final TrackGroupArray tracks; private final ArrayList sampleStreams; + private final long durationUs; + // Package private to avoid thunk methods. /* package */ final Loader loader; /* package */ final Format format; @@ -62,16 +60,20 @@ import java.util.Arrays; /* package */ int sampleSize; private int errorCount; - public SingleSampleMediaPeriod(Uri uri, DataSource.Factory dataSourceFactory, Format format, - int minLoadableRetryCount, Handler eventHandler, EventListener eventListener, - int eventSourceId, boolean treatLoadErrorsAsEndOfStream) { - this.uri = uri; + public SingleSampleMediaPeriod( + DataSpec dataSpec, + DataSource.Factory dataSourceFactory, + Format format, + long durationUs, + int minLoadableRetryCount, + EventDispatcher eventDispatcher, + boolean treatLoadErrorsAsEndOfStream) { + this.dataSpec = dataSpec; this.dataSourceFactory = dataSourceFactory; this.format = format; + this.durationUs = durationUs; this.minLoadableRetryCount = minLoadableRetryCount; - this.eventHandler = eventHandler; - this.eventListener = eventListener; - this.eventSourceId = eventSourceId; + this.eventDispatcher = eventDispatcher; this.treatLoadErrorsAsEndOfStream = treatLoadErrorsAsEndOfStream; tracks = new TrackGroupArray(new TrackGroup(format)); sampleStreams = new ArrayList<>(); @@ -125,7 +127,9 @@ import java.util.Arrays; if (loadingFinished || loader.isLoading()) { return false; } - loader.startLoading(new SourceLoadable(uri, dataSourceFactory.createDataSource()), this, + loader.startLoading( + new SourceLoadable(dataSpec, dataSourceFactory.createDataSource()), + this, minLoadableRetryCount); return true; } @@ -158,6 +162,18 @@ import java.util.Arrays; @Override public void onLoadCompleted(SourceLoadable loadable, long elapsedRealtimeMs, long loadDurationMs) { + eventDispatcher.loadCompleted( + loadable.dataSpec, + C.DATA_TYPE_MEDIA, + C.TRACK_TYPE_UNKNOWN, + format, + C.SELECTION_REASON_UNKNOWN, + /* trackSelectionData= */ null, + /* mediaStartTimeUs= */ 0, + durationUs, + elapsedRealtimeMs, + loadDurationMs, + loadable.sampleSize); sampleSize = loadable.sampleSize; sampleData = loadable.sampleData; loadingFinished = true; @@ -167,34 +183,46 @@ import java.util.Arrays; @Override public void onLoadCanceled(SourceLoadable loadable, long elapsedRealtimeMs, long loadDurationMs, boolean released) { - // Do nothing. + eventDispatcher.loadCanceled( + loadable.dataSpec, + C.DATA_TYPE_MEDIA, + C.TRACK_TYPE_UNKNOWN, + /* trackFormat= */ null, + C.SELECTION_REASON_UNKNOWN, + /* trackSelectionData= */ null, + /* mediaStartTimeUs= */ 0, + durationUs, + elapsedRealtimeMs, + loadDurationMs, + loadable.sampleSize); } @Override public int onLoadError(SourceLoadable loadable, long elapsedRealtimeMs, long loadDurationMs, IOException error) { - notifyLoadError(error); errorCount++; - if (treatLoadErrorsAsEndOfStream && errorCount >= minLoadableRetryCount) { + boolean cancel = treatLoadErrorsAsEndOfStream && errorCount >= minLoadableRetryCount; + eventDispatcher.loadError( + loadable.dataSpec, + C.DATA_TYPE_MEDIA, + C.TRACK_TYPE_UNKNOWN, + format, + C.SELECTION_REASON_UNKNOWN, + /* trackSelectionData= */ null, + /* mediaStartTimeUs= */ 0, + durationUs, + elapsedRealtimeMs, + loadDurationMs, + loadable.sampleSize, + error, + /* wasCanceled= */ cancel); + if (cancel) { loadingFinished = true; return Loader.DONT_RETRY; } return Loader.RETRY; } - // Internal methods. - - private void notifyLoadError(final IOException e) { - if (eventHandler != null && eventListener != null) { - eventHandler.post(new Runnable() { - @Override - public void run() { - eventListener.onLoadError(eventSourceId, e); - } - }); - } - } - private final class SampleStreamImpl implements SampleStream { private static final int STREAM_STATE_SEND_FORMAT = 0; @@ -259,14 +287,15 @@ import java.util.Arrays; /* package */ static final class SourceLoadable implements Loadable { - private final Uri uri; + public final DataSpec dataSpec; + private final DataSource dataSource; private int sampleSize; private byte[] sampleData; - public SourceLoadable(Uri uri, DataSource dataSource) { - this.uri = uri; + public SourceLoadable(DataSpec dataSpec, DataSource dataSource) { + this.dataSpec = dataSpec; this.dataSource = dataSource; } @@ -286,7 +315,7 @@ import java.util.Arrays; sampleSize = 0; try { // Create and open the input. - dataSource.open(new DataSpec(uri)); + dataSource.open(dataSpec); // Load the sample data. int result = 0; while (result != C.RESULT_END_OF_INPUT) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java index 2aa8ccc712..3b0a5a16c8 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java @@ -17,11 +17,14 @@ package com.google.android.exoplayer2.source; import android.net.Uri; import android.os.Handler; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Timeline; +import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.util.Assertions; import java.io.IOException; @@ -32,7 +35,10 @@ public final class SingleSampleMediaSource implements MediaSource { /** * Listener of {@link SingleSampleMediaSource} events. + * + * @deprecated Use {@link MediaSourceEventListener}. */ + @Deprecated public interface EventListener { /** @@ -45,35 +51,23 @@ public final class SingleSampleMediaSource implements MediaSource { } - /** - * Builder for {@link SingleSampleMediaSource}. Each builder instance can only be used once. - */ - public static final class Builder { + /** Factory for {@link SingleSampleMediaSource}. */ + public static final class Factory { - private final Uri uri; private final DataSource.Factory dataSourceFactory; - private final Format format; - private final long durationUs; private int minLoadableRetryCount; - private Handler eventHandler; - private EventListener eventListener; - private int eventSourceId; private boolean treatLoadErrorsAsEndOfStream; - private boolean isBuildCalled; + private boolean isCreateCalled; /** - * @param uri The {@link Uri} of the media stream. + * Creates a factory for {@link SingleSampleMediaSource}s. + * * @param dataSourceFactory The factory from which the {@link DataSource} to read the media will * be obtained. - * @param format The {@link Format} associated with the output track. - * @param durationUs The duration of the media stream in microseconds. */ - public Builder(Uri uri, DataSource.Factory dataSourceFactory, Format format, long durationUs) { - this.uri = uri; - this.dataSourceFactory = dataSourceFactory; - this.format = format; - this.durationUs = durationUs; + public Factory(DataSource.Factory dataSourceFactory) { + this.dataSourceFactory = Assertions.checkNotNull(dataSourceFactory); this.minLoadableRetryCount = DEFAULT_MIN_LOADABLE_RETRY_COUNT; } @@ -82,37 +76,15 @@ public final class SingleSampleMediaSource implements MediaSource { * {@link #DEFAULT_MIN_LOADABLE_RETRY_COUNT}. * * @param minLoadableRetryCount The minimum number of times to retry if a loading error occurs. - * @return This builder. + * @return This factory, for convenience. + * @throws IllegalStateException If one of the {@code create} methods has already been called. */ - public Builder setMinLoadableRetryCount(int minLoadableRetryCount) { + public Factory setMinLoadableRetryCount(int minLoadableRetryCount) { + Assertions.checkState(!isCreateCalled); this.minLoadableRetryCount = minLoadableRetryCount; return this; } - /** - * Sets the listener to respond to events and the handler to deliver these events. - * - * @param eventHandler A handler for events. - * @param eventListener A listener of events. - * @return This builder. - */ - public Builder setEventListener(Handler eventHandler, EventListener eventListener) { - this.eventHandler = eventHandler; - this.eventListener = eventListener; - return this; - } - - /** - * Sets an identifier that gets passed to {@code eventListener} methods. The default value is 0. - * - * @param eventSourceId An identifier that gets passed to {@code eventListener} methods. - * @return This builder. - */ - public Builder setEventSourceId(int eventSourceId) { - this.eventSourceId = eventSourceId; - return this; - } - /** * Sets whether load errors will be treated as end-of-stream signal (load errors will not be * propagated). The default value is false. @@ -120,27 +92,53 @@ public final class SingleSampleMediaSource implements MediaSource { * @param treatLoadErrorsAsEndOfStream If true, load errors will not be propagated by sample * streams, treating them as ended instead. If false, load errors will be propagated * normally by {@link SampleStream#maybeThrowError()}. - * @return This builder. + * @return This factory, for convenience. + * @throws IllegalStateException If one of the {@code create} methods has already been called. */ - public Builder setTreatLoadErrorsAsEndOfStream(boolean treatLoadErrorsAsEndOfStream) { + public Factory setTreatLoadErrorsAsEndOfStream(boolean treatLoadErrorsAsEndOfStream) { + Assertions.checkState(!isCreateCalled); this.treatLoadErrorsAsEndOfStream = treatLoadErrorsAsEndOfStream; return this; } /** - * Builds a new {@link SingleSampleMediaSource} using the current parameters. - *

- * After this call, the builder should not be re-used. + * Returns a new {@link ExtractorMediaSource} using the current parameters. Media source events + * will not be delivered. * + * @param uri The {@link Uri}. + * @param format The {@link Format} of the media stream. + * @param durationUs The duration of the media stream in microseconds. + * @return The new {@link ExtractorMediaSource}. + */ + public SingleSampleMediaSource createMediaSource(Uri uri, Format format, long durationUs) { + return createMediaSource(uri, format, durationUs, null, null); + } + + /** + * Returns a new {@link SingleSampleMediaSource} using the current parameters. + * + * @param uri The {@link Uri}. + * @param format The {@link Format} of the media stream. + * @param durationUs The duration of the media stream in microseconds. + * @param eventHandler A handler for events. + * @param eventListener A listener of events., Format format, long durationUs * @return The newly built {@link SingleSampleMediaSource}. */ - public SingleSampleMediaSource build() { - Assertions.checkArgument((eventListener == null) == (eventHandler == null)); - Assertions.checkState(!isBuildCalled); - isBuildCalled = true; - - return new SingleSampleMediaSource(uri, dataSourceFactory, format, durationUs, - minLoadableRetryCount, eventHandler, eventListener, eventSourceId, + public SingleSampleMediaSource createMediaSource( + Uri uri, + Format format, + long durationUs, + @Nullable Handler eventHandler, + @Nullable MediaSourceEventListener eventListener) { + isCreateCalled = true; + return new SingleSampleMediaSource( + uri, + dataSourceFactory, + format, + durationUs, + minLoadableRetryCount, + eventHandler, + eventListener, treatLoadErrorsAsEndOfStream); } @@ -151,13 +149,12 @@ public final class SingleSampleMediaSource implements MediaSource { */ public static final int DEFAULT_MIN_LOADABLE_RETRY_COUNT = 3; - private final Uri uri; + private final DataSpec dataSpec; private final DataSource.Factory dataSourceFactory; private final Format format; + private final long durationUs; + private final MediaSourceEventListener.EventDispatcher eventDispatcher; private final int minLoadableRetryCount; - private final Handler eventHandler; - private final EventListener eventListener; - private final int eventSourceId; private final boolean treatLoadErrorsAsEndOfStream; private final Timeline timeline; @@ -167,11 +164,11 @@ public final class SingleSampleMediaSource implements MediaSource { * be obtained. * @param format The {@link Format} associated with the output track. * @param durationUs The duration of the media stream in microseconds. - * @deprecated Use {@link Builder} instead. + * @deprecated Use {@link Factory} instead. */ @Deprecated - public SingleSampleMediaSource(Uri uri, DataSource.Factory dataSourceFactory, Format format, - long durationUs) { + public SingleSampleMediaSource( + Uri uri, DataSource.Factory dataSourceFactory, Format format, long durationUs) { this(uri, dataSourceFactory, format, durationUs, DEFAULT_MIN_LOADABLE_RETRY_COUNT); } @@ -182,12 +179,16 @@ public final class SingleSampleMediaSource implements MediaSource { * @param format The {@link Format} associated with the output track. * @param durationUs The duration of the media stream in microseconds. * @param minLoadableRetryCount The minimum number of times to retry if a loading error occurs. - * @deprecated Use {@link Builder} instead. + * @deprecated Use {@link Factory} instead. */ @Deprecated - public SingleSampleMediaSource(Uri uri, DataSource.Factory dataSourceFactory, Format format, - long durationUs, int minLoadableRetryCount) { - this(uri, dataSourceFactory, format, durationUs, minLoadableRetryCount, null, null, 0, false); + public SingleSampleMediaSource( + Uri uri, + DataSource.Factory dataSourceFactory, + Format format, + long durationUs, + int minLoadableRetryCount) { + this(uri, dataSourceFactory, format, durationUs, minLoadableRetryCount, null, null, false); } /** @@ -203,20 +204,46 @@ public final class SingleSampleMediaSource implements MediaSource { * @param treatLoadErrorsAsEndOfStream If true, load errors will not be propagated by sample * streams, treating them as ended instead. If false, load errors will be propagated normally * by {@link SampleStream#maybeThrowError()}. - * @deprecated Use {@link Builder} instead. + * @deprecated Use {@link Factory} instead. */ @Deprecated - public SingleSampleMediaSource(Uri uri, DataSource.Factory dataSourceFactory, Format format, - long durationUs, int minLoadableRetryCount, Handler eventHandler, EventListener eventListener, - int eventSourceId, boolean treatLoadErrorsAsEndOfStream) { - this.uri = uri; + public SingleSampleMediaSource( + Uri uri, + DataSource.Factory dataSourceFactory, + Format format, + long durationUs, + int minLoadableRetryCount, + Handler eventHandler, + EventListener eventListener, + int eventSourceId, + boolean treatLoadErrorsAsEndOfStream) { + this( + uri, + dataSourceFactory, + format, + durationUs, + minLoadableRetryCount, + eventHandler, + eventListener == null ? null : new EventListenerWrapper(eventListener, eventSourceId), + treatLoadErrorsAsEndOfStream); + } + + private SingleSampleMediaSource( + Uri uri, + DataSource.Factory dataSourceFactory, + Format format, + long durationUs, + int minLoadableRetryCount, + Handler eventHandler, + MediaSourceEventListener eventListener, + boolean treatLoadErrorsAsEndOfStream) { this.dataSourceFactory = dataSourceFactory; this.format = format; + this.durationUs = durationUs; this.minLoadableRetryCount = minLoadableRetryCount; - this.eventHandler = eventHandler; - this.eventListener = eventListener; - this.eventSourceId = eventSourceId; this.treatLoadErrorsAsEndOfStream = treatLoadErrorsAsEndOfStream; + this.eventDispatcher = new EventDispatcher(eventHandler, eventListener); + dataSpec = new DataSpec(uri); timeline = new SinglePeriodTimeline(durationUs, true); } @@ -235,8 +262,14 @@ public final class SingleSampleMediaSource implements MediaSource { @Override public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) { Assertions.checkArgument(id.periodIndex == 0); - return new SingleSampleMediaPeriod(uri, dataSourceFactory, format, minLoadableRetryCount, - eventHandler, eventListener, eventSourceId, treatLoadErrorsAsEndOfStream); + return new SingleSampleMediaPeriod( + dataSpec, + dataSourceFactory, + format, + durationUs, + minLoadableRetryCount, + eventDispatcher, + treatLoadErrorsAsEndOfStream); } @Override @@ -249,4 +282,97 @@ public final class SingleSampleMediaSource implements MediaSource { // Do nothing. } + /** + * Wraps a deprecated {@link EventListener}, invoking its callback from the equivalent callback in + * {@link MediaSourceEventListener}. + */ + private static final class EventListenerWrapper implements MediaSourceEventListener { + + private final EventListener eventListener; + private final int eventSourceId; + + public EventListenerWrapper(EventListener eventListener, int eventSourceId) { + this.eventListener = Assertions.checkNotNull(eventListener); + this.eventSourceId = eventSourceId; + } + + @Override + public void onLoadStarted( + DataSpec dataSpec, + int dataType, + int trackType, + Format trackFormat, + int trackSelectionReason, + Object trackSelectionData, + long mediaStartTimeMs, + long mediaEndTimeMs, + long elapsedRealtimeMs) { + // Do nothing. + } + + @Override + public void onLoadCompleted( + DataSpec dataSpec, + int dataType, + int trackType, + Format trackFormat, + int trackSelectionReason, + Object trackSelectionData, + long mediaStartTimeMs, + long mediaEndTimeMs, + long elapsedRealtimeMs, + long loadDurationMs, + long bytesLoaded) { + // Do nothing. + } + + @Override + public void onLoadCanceled( + DataSpec dataSpec, + int dataType, + int trackType, + Format trackFormat, + int trackSelectionReason, + Object trackSelectionData, + long mediaStartTimeMs, + long mediaEndTimeMs, + long elapsedRealtimeMs, + long loadDurationMs, + long bytesLoaded) { + // Do nothing. + } + + @Override + public void onLoadError( + DataSpec dataSpec, + int dataType, + int trackType, + Format trackFormat, + int trackSelectionReason, + Object trackSelectionData, + long mediaStartTimeMs, + long mediaEndTimeMs, + long elapsedRealtimeMs, + long loadDurationMs, + long bytesLoaded, + IOException error, + boolean wasCanceled) { + eventListener.onLoadError(eventSourceId, error); + } + + @Override + public void onUpstreamDiscarded(int trackType, long mediaStartTimeMs, long mediaEndTimeMs) { + // Do nothing. + } + + @Override + public void onDownstreamFormatChanged( + int trackType, + Format trackFormat, + int trackSelectionReason, + Object trackSelectionData, + long mediaTimeMs) { + // Do nothing. + } + } } diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java index 3a55cb8a17..8ed202d961 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java @@ -131,7 +131,7 @@ public final class HlsMediaSource implements MediaSource, * * @return The new {@link HlsMediaSource}. */ - public MediaSource createMediaSource(Uri playlistUri) { + public HlsMediaSource createMediaSource(Uri playlistUri) { return createMediaSource(playlistUri, null, null); } @@ -144,7 +144,7 @@ public final class HlsMediaSource implements MediaSource, * @return The new {@link HlsMediaSource}. */ @Override - public MediaSource createMediaSource( + public HlsMediaSource createMediaSource( Uri playlistUri, @Nullable Handler eventHandler, @Nullable MediaSourceEventListener eventListener) {