diff --git a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java
index 97b05e3f0a..6cf6309796 100644
--- a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java
+++ b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java
@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.ext.cast;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import com.google.android.exoplayer2.BasePlayer;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.PlaybackParameters;
@@ -31,7 +32,6 @@ import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.MimeTypes;
-import com.google.android.exoplayer2.util.Util;
import com.google.android.gms.cast.CastStatusCodes;
import com.google.android.gms.cast.MediaInfo;
import com.google.android.gms.cast.MediaQueueItem;
@@ -62,7 +62,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
*
*
Methods should be called on the application's main thread.
*/
-public final class CastPlayer implements Player {
+public final class CastPlayer extends BasePlayer {
/**
* Listener of changes in the cast session availability.
@@ -95,7 +95,6 @@ public final class CastPlayer implements Player {
private final CastContext castContext;
// TODO: Allow custom implementations of CastTimelineTracker.
private final CastTimelineTracker timelineTracker;
- private final Timeline.Window window;
private final Timeline.Period period;
private RemoteMediaClient remoteMediaClient;
@@ -128,7 +127,6 @@ public final class CastPlayer implements Player {
public CastPlayer(CastContext castContext) {
this.castContext = castContext;
timelineTracker = new CastTimelineTracker();
- window = new Timeline.Window();
period = new Timeline.Period();
statusListener = new StatusListener();
seekResultCallback = new SeekResultCallback();
@@ -341,21 +339,6 @@ public final class CastPlayer implements Player {
return playWhenReady;
}
- @Override
- public void seekToDefaultPosition() {
- seekTo(0);
- }
-
- @Override
- public void seekToDefaultPosition(int windowIndex) {
- seekTo(windowIndex, 0);
- }
-
- @Override
- public void seekTo(long positionMs) {
- seekTo(getCurrentWindowIndex(), positionMs);
- }
-
@Override
public void seekTo(int windowIndex, long positionMs) {
MediaStatus mediaStatus = getMediaStatus();
@@ -392,11 +375,6 @@ public final class CastPlayer implements Player {
return PlaybackParameters.DEFAULT;
}
- @Override
- public void stop() {
- stop(/* reset= */ false);
- }
-
@Override
public void stop(boolean reset) {
playbackState = STATE_IDLE;
@@ -486,32 +464,11 @@ public final class CastPlayer implements Player {
return pendingSeekWindowIndex != C.INDEX_UNSET ? pendingSeekWindowIndex : currentWindowIndex;
}
- @Override
- public int getNextWindowIndex() {
- return currentTimeline.isEmpty() ? C.INDEX_UNSET
- : currentTimeline.getNextWindowIndex(getCurrentWindowIndex(), repeatMode, false);
- }
-
- @Override
- public int getPreviousWindowIndex() {
- return currentTimeline.isEmpty() ? C.INDEX_UNSET
- : currentTimeline.getPreviousWindowIndex(getCurrentWindowIndex(), repeatMode, false);
- }
-
- @Override
- public @Nullable Object getCurrentTag() {
- int windowIndex = getCurrentWindowIndex();
- return windowIndex >= currentTimeline.getWindowCount()
- ? null
- : currentTimeline.getWindow(windowIndex, window, /* setTag= */ true).tag;
- }
-
// TODO: Fill the cast timeline information with ProgressListener's duration updates.
// See [Internal: b/65152553].
@Override
public long getDuration() {
- return currentTimeline.isEmpty() ? C.TIME_UNSET
- : currentTimeline.getWindow(getCurrentWindowIndex(), window).getDurationMs();
+ return getContentDuration();
}
@Override
@@ -528,15 +485,6 @@ public final class CastPlayer implements Player {
return getCurrentPosition();
}
- @Override
- public int getBufferedPercentage() {
- long position = getBufferedPosition();
- long duration = getDuration();
- return position == C.TIME_UNSET || duration == C.TIME_UNSET
- ? 0
- : duration == 0 ? 100 : Util.constrainValue((int) ((position * 100) / duration), 0, 100);
- }
-
@Override
public long getTotalBufferedDuration() {
long bufferedPosition = getBufferedPosition();
@@ -546,18 +494,6 @@ public final class CastPlayer implements Player {
: bufferedPosition - currentPosition;
}
- @Override
- public boolean isCurrentWindowDynamic() {
- return !currentTimeline.isEmpty()
- && currentTimeline.getWindow(getCurrentWindowIndex(), window).isDynamic;
- }
-
- @Override
- public boolean isCurrentWindowSeekable() {
- return !currentTimeline.isEmpty()
- && currentTimeline.getWindow(getCurrentWindowIndex(), window).isSeekable;
- }
-
@Override
public boolean isPlayingAd() {
return false;
@@ -573,11 +509,6 @@ public final class CastPlayer implements Player {
return C.INDEX_UNSET;
}
- @Override
- public long getContentDuration() {
- return getDuration();
- }
-
@Override
public boolean isLoading() {
return false;
diff --git a/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakePlayer.java b/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakePlayer.java
index 0c35c9b66d..b8024d6534 100644
--- a/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakePlayer.java
+++ b/extensions/ima/src/test/java/com/google/android/exoplayer2/ext/ima/FakePlayer.java
@@ -26,7 +26,6 @@ import java.util.ArrayList;
/* package */ final class FakePlayer extends StubExoPlayer {
private final ArrayList listeners;
- private final Timeline.Window window;
private final Timeline.Period period;
private final Timeline timeline;
@@ -41,7 +40,6 @@ import java.util.ArrayList;
public FakePlayer() {
listeners = new ArrayList<>();
- window = new Timeline.Window();
period = new Timeline.Period();
state = Player.STATE_IDLE;
playWhenReady = true;
@@ -151,16 +149,6 @@ import java.util.ArrayList;
return 0;
}
- @Override
- public int getNextWindowIndex() {
- return C.INDEX_UNSET;
- }
-
- @Override
- public int getPreviousWindowIndex() {
- return C.INDEX_UNSET;
- }
-
@Override
public long getDuration() {
if (timeline.isEmpty()) {
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/BasePlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/BasePlayer.java
new file mode 100644
index 0000000000..6ff0853d9b
--- /dev/null
+++ b/library/core/src/main/java/com/google/android/exoplayer2/BasePlayer.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.exoplayer2;
+
+import android.support.annotation.Nullable;
+import com.google.android.exoplayer2.util.Util;
+
+/** Abstract base {@link Player} which implements common implementation independent methods. */
+public abstract class BasePlayer implements Player {
+
+ protected final Timeline.Window window;
+
+ public BasePlayer() {
+ window = new Timeline.Window();
+ }
+
+ @Override
+ public final void seekToDefaultPosition() {
+ seekToDefaultPosition(getCurrentWindowIndex());
+ }
+
+ @Override
+ public final void seekToDefaultPosition(int windowIndex) {
+ seekTo(windowIndex, /* positionMs= */ C.TIME_UNSET);
+ }
+
+ @Override
+ public final void seekTo(long positionMs) {
+ seekTo(getCurrentWindowIndex(), positionMs);
+ }
+
+ @Override
+ public final boolean hasPrevious() {
+ return getPreviousWindowIndex() != C.INDEX_UNSET;
+ }
+
+ @Override
+ public final void previous() {
+ int previousWindowIndex = getPreviousWindowIndex();
+ if (previousWindowIndex != C.INDEX_UNSET) {
+ seekToDefaultPosition(previousWindowIndex);
+ }
+ }
+
+ @Override
+ public final boolean hasNext() {
+ return getNextWindowIndex() != C.INDEX_UNSET;
+ }
+
+ @Override
+ public final void next() {
+ int nextWindowIndex = getPreviousWindowIndex();
+ if (nextWindowIndex != C.INDEX_UNSET) {
+ seekToDefaultPosition(nextWindowIndex);
+ }
+ }
+
+ @Override
+ public final void stop() {
+ stop(/* reset= */ false);
+ }
+
+ @Override
+ public final int getNextWindowIndex() {
+ Timeline timeline = getCurrentTimeline();
+ return timeline.isEmpty()
+ ? C.INDEX_UNSET
+ : timeline.getNextWindowIndex(
+ getCurrentWindowIndex(), getRepeatMode(), getShuffleModeEnabled());
+ }
+
+ @Override
+ public final int getPreviousWindowIndex() {
+ Timeline timeline = getCurrentTimeline();
+ return timeline.isEmpty()
+ ? C.INDEX_UNSET
+ : timeline.getPreviousWindowIndex(
+ getCurrentWindowIndex(), getRepeatMode(), getShuffleModeEnabled());
+ }
+
+ @Override
+ @Nullable
+ public final Object getCurrentTag() {
+ int windowIndex = getCurrentWindowIndex();
+ Timeline timeline = getCurrentTimeline();
+ return windowIndex >= timeline.getWindowCount()
+ ? null
+ : timeline.getWindow(windowIndex, window, /* setTag= */ true).tag;
+ }
+
+ @Override
+ public final int getBufferedPercentage() {
+ long position = getBufferedPosition();
+ long duration = getDuration();
+ return position == C.TIME_UNSET || duration == C.TIME_UNSET
+ ? 0
+ : duration == 0 ? 100 : Util.constrainValue((int) ((position * 100) / duration), 0, 100);
+ }
+
+ @Override
+ public final boolean isCurrentWindowDynamic() {
+ Timeline timeline = getCurrentTimeline();
+ return !timeline.isEmpty() && timeline.getWindow(getCurrentWindowIndex(), window).isDynamic;
+ }
+
+ @Override
+ public final boolean isCurrentWindowSeekable() {
+ Timeline timeline = getCurrentTimeline();
+ return !timeline.isEmpty() && timeline.getWindow(getCurrentWindowIndex(), window).isSeekable;
+ }
+
+ @Override
+ public final long getContentDuration() {
+ Timeline timeline = getCurrentTimeline();
+ return timeline.isEmpty()
+ ? C.TIME_UNSET
+ : timeline.getWindow(getCurrentWindowIndex(), window).getDurationMs();
+ }
+}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java
index 04bc1c611d..ffdadb78f7 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java
@@ -40,10 +40,8 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
-/**
- * An {@link ExoPlayer} implementation. Instances can be obtained from {@link ExoPlayerFactory}.
- */
-/* package */ final class ExoPlayerImpl implements ExoPlayer {
+/** An {@link ExoPlayer} implementation. Instances can be obtained from {@link ExoPlayerFactory}. */
+/* package */ final class ExoPlayerImpl extends BasePlayer implements ExoPlayer {
private static final String TAG = "ExoPlayerImpl";
@@ -61,7 +59,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
private final ExoPlayerImplInternal internalPlayer;
private final Handler internalPlayerHandler;
private final CopyOnWriteArraySet listeners;
- private final Timeline.Window window;
private final Timeline.Period period;
private final ArrayDeque pendingPlaybackInfoUpdates;
@@ -118,7 +115,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
new RendererConfiguration[renderers.length],
new TrackSelection[renderers.length],
null);
- window = new Timeline.Window();
period = new Timeline.Period();
playbackParameters = PlaybackParameters.DEFAULT;
seekParameters = SeekParameters.DEFAULT;
@@ -293,21 +289,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
return playbackInfo.isLoading;
}
- @Override
- public void seekToDefaultPosition() {
- seekToDefaultPosition(getCurrentWindowIndex());
- }
-
- @Override
- public void seekToDefaultPosition(int windowIndex) {
- seekTo(windowIndex, C.TIME_UNSET);
- }
-
- @Override
- public void seekTo(long positionMs) {
- seekTo(getCurrentWindowIndex(), positionMs);
- }
-
@Override
public void seekTo(int windowIndex, long positionMs) {
Timeline timeline = playbackInfo.timeline;
@@ -377,19 +358,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
return seekParameters;
}
- @Override
- public @Nullable Object getCurrentTag() {
- int windowIndex = getCurrentWindowIndex();
- return windowIndex >= playbackInfo.timeline.getWindowCount()
- ? null
- : playbackInfo.timeline.getWindow(windowIndex, window, /* setTag= */ true).tag;
- }
-
- @Override
- public void stop() {
- stop(/* reset= */ false);
- }
-
@Override
public void stop(boolean reset) {
if (reset) {
@@ -494,20 +462,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
}
}
- @Override
- public int getNextWindowIndex() {
- Timeline timeline = playbackInfo.timeline;
- return timeline.isEmpty() ? C.INDEX_UNSET
- : timeline.getNextWindowIndex(getCurrentWindowIndex(), repeatMode, shuffleModeEnabled);
- }
-
- @Override
- public int getPreviousWindowIndex() {
- Timeline timeline = playbackInfo.timeline;
- return timeline.isEmpty() ? C.INDEX_UNSET
- : timeline.getPreviousWindowIndex(getCurrentWindowIndex(), repeatMode, shuffleModeEnabled);
- }
-
@Override
public long getDuration() {
if (isPlayingAd()) {
@@ -540,32 +494,11 @@ import java.util.concurrent.CopyOnWriteArraySet;
return getContentBufferedPosition();
}
- @Override
- public int getBufferedPercentage() {
- long position = getBufferedPosition();
- long duration = getDuration();
- return position == C.TIME_UNSET || duration == C.TIME_UNSET
- ? 0
- : (duration == 0 ? 100 : Util.constrainValue((int) ((position * 100) / duration), 0, 100));
- }
-
@Override
public long getTotalBufferedDuration() {
return Math.max(0, C.usToMs(playbackInfo.totalBufferedDurationUs));
}
- @Override
- public boolean isCurrentWindowDynamic() {
- Timeline timeline = playbackInfo.timeline;
- return !timeline.isEmpty() && timeline.getWindow(getCurrentWindowIndex(), window).isDynamic;
- }
-
- @Override
- public boolean isCurrentWindowSeekable() {
- Timeline timeline = playbackInfo.timeline;
- return !timeline.isEmpty() && timeline.getWindow(getCurrentWindowIndex(), window).isSeekable;
- }
-
@Override
public boolean isPlayingAd() {
return !shouldMaskPosition() && playbackInfo.periodId.isAd();
@@ -581,13 +514,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
return isPlayingAd() ? playbackInfo.periodId.adIndexInAdGroup : C.INDEX_UNSET;
}
- @Override
- public long getContentDuration() {
- return playbackInfo.timeline.isEmpty()
- ? C.TIME_UNSET
- : playbackInfo.timeline.getWindow(getCurrentWindowIndex(), window).getDurationMs();
- }
-
@Override
public long getContentPosition() {
if (isPlayingAd()) {
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java
index 39f1655ab5..8517556887 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java
@@ -64,7 +64,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
* be obtained from {@link ExoPlayerFactory}.
*/
@TargetApi(16)
-public class SimpleExoPlayer
+public class SimpleExoPlayer extends BasePlayer
implements ExoPlayer, Player.AudioComponent, Player.VideoComponent, Player.TextComponent {
/** @deprecated Use {@link com.google.android.exoplayer2.video.VideoListener}. */
@@ -927,27 +927,6 @@ public class SimpleExoPlayer
return player.isLoading();
}
- @Override
- public void seekToDefaultPosition() {
- verifyApplicationThread();
- analyticsCollector.notifySeekStarted();
- player.seekToDefaultPosition();
- }
-
- @Override
- public void seekToDefaultPosition(int windowIndex) {
- verifyApplicationThread();
- analyticsCollector.notifySeekStarted();
- player.seekToDefaultPosition(windowIndex);
- }
-
- @Override
- public void seekTo(long positionMs) {
- verifyApplicationThread();
- analyticsCollector.notifySeekStarted();
- player.seekTo(positionMs);
- }
-
@Override
public void seekTo(int windowIndex, long positionMs) {
verifyApplicationThread();
@@ -979,17 +958,6 @@ public class SimpleExoPlayer
return player.getSeekParameters();
}
- @Override
- public @Nullable Object getCurrentTag() {
- verifyApplicationThread();
- return player.getCurrentTag();
- }
-
- @Override
- public void stop() {
- stop(/* reset= */ false);
- }
-
@Override
public void stop(boolean reset) {
verifyApplicationThread();
@@ -1092,18 +1060,6 @@ public class SimpleExoPlayer
return player.getCurrentWindowIndex();
}
- @Override
- public int getNextWindowIndex() {
- verifyApplicationThread();
- return player.getNextWindowIndex();
- }
-
- @Override
- public int getPreviousWindowIndex() {
- verifyApplicationThread();
- return player.getPreviousWindowIndex();
- }
-
@Override
public long getDuration() {
verifyApplicationThread();
@@ -1122,30 +1078,12 @@ public class SimpleExoPlayer
return player.getBufferedPosition();
}
- @Override
- public int getBufferedPercentage() {
- verifyApplicationThread();
- return player.getBufferedPercentage();
- }
-
@Override
public long getTotalBufferedDuration() {
verifyApplicationThread();
return player.getTotalBufferedDuration();
}
- @Override
- public boolean isCurrentWindowDynamic() {
- verifyApplicationThread();
- return player.isCurrentWindowDynamic();
- }
-
- @Override
- public boolean isCurrentWindowSeekable() {
- verifyApplicationThread();
- return player.isCurrentWindowSeekable();
- }
-
@Override
public boolean isPlayingAd() {
verifyApplicationThread();
@@ -1164,12 +1102,6 @@ public class SimpleExoPlayer
return player.getCurrentAdIndexInAdGroup();
}
- @Override
- public long getContentDuration() {
- verifyApplicationThread();
- return player.getContentDuration();
- }
-
@Override
public long getContentPosition() {
verifyApplicationThread();
diff --git a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java
index f1349c1158..156b573df8 100644
--- a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java
+++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java
@@ -16,7 +16,7 @@
package com.google.android.exoplayer2.testutil;
import android.os.Looper;
-import android.support.annotation.Nullable;
+import com.google.android.exoplayer2.BasePlayer;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.PlaybackParameters;
@@ -32,7 +32,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
* An abstract {@link ExoPlayer} implementation that throws {@link UnsupportedOperationException}
* from every method.
*/
-public abstract class StubExoPlayer implements ExoPlayer {
+public abstract class StubExoPlayer extends BasePlayer implements ExoPlayer {
@Override
public AudioComponent getAudioComponent() {
@@ -129,21 +129,6 @@ public abstract class StubExoPlayer implements ExoPlayer {
throw new UnsupportedOperationException();
}
- @Override
- public void seekToDefaultPosition() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void seekToDefaultPosition(int windowIndex) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void seekTo(long positionMs) {
- throw new UnsupportedOperationException();
- }
-
@Override
public void seekTo(int windowIndex, long positionMs) {
throw new UnsupportedOperationException();
@@ -169,16 +154,6 @@ public abstract class StubExoPlayer implements ExoPlayer {
throw new UnsupportedOperationException();
}
- @Override
- public @Nullable Object getCurrentTag() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void stop() {
- throw new UnsupportedOperationException();
- }
-
@Override
public void stop(boolean resetStateAndPosition) {
throw new UnsupportedOperationException();
@@ -248,16 +223,6 @@ public abstract class StubExoPlayer implements ExoPlayer {
throw new UnsupportedOperationException();
}
- @Override
- public int getNextWindowIndex() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int getPreviousWindowIndex() {
- throw new UnsupportedOperationException();
- }
-
@Override
public long getDuration() {
throw new UnsupportedOperationException();
@@ -273,26 +238,11 @@ public abstract class StubExoPlayer implements ExoPlayer {
throw new UnsupportedOperationException();
}
- @Override
- public int getBufferedPercentage() {
- throw new UnsupportedOperationException();
- }
-
@Override
public long getTotalBufferedDuration() {
throw new UnsupportedOperationException();
}
- @Override
- public boolean isCurrentWindowDynamic() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean isCurrentWindowSeekable() {
- throw new UnsupportedOperationException();
- }
-
@Override
public boolean isPlayingAd() {
throw new UnsupportedOperationException();
@@ -308,11 +258,6 @@ public abstract class StubExoPlayer implements ExoPlayer {
throw new UnsupportedOperationException();
}
- @Override
- public long getContentDuration() {
- throw new UnsupportedOperationException();
- }
-
@Override
public long getContentPosition() {
throw new UnsupportedOperationException();