1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.google.android.exoplayer2; 17 18 /** Default {@link ControlDispatcher}. */ 19 public class DefaultControlDispatcher implements ControlDispatcher { 20 21 /** The default fast forward increment, in milliseconds. */ 22 public static final int DEFAULT_FAST_FORWARD_MS = 15000; 23 /** The default rewind increment, in milliseconds. */ 24 public static final int DEFAULT_REWIND_MS = 5000; 25 26 private static final int MAX_POSITION_FOR_SEEK_TO_PREVIOUS = 3000; 27 28 private final Timeline.Window window; 29 30 private long rewindIncrementMs; 31 private long fastForwardIncrementMs; 32 33 /** Creates an instance. */ DefaultControlDispatcher()34 public DefaultControlDispatcher() { 35 this(DEFAULT_FAST_FORWARD_MS, DEFAULT_REWIND_MS); 36 } 37 38 /** 39 * Creates an instance with the given increments. 40 * 41 * @param fastForwardIncrementMs The fast forward increment in milliseconds. A non-positive value 42 * disables the fast forward operation. 43 * @param rewindIncrementMs The rewind increment in milliseconds. A non-positive value disables 44 * the rewind operation. 45 */ DefaultControlDispatcher(long fastForwardIncrementMs, long rewindIncrementMs)46 public DefaultControlDispatcher(long fastForwardIncrementMs, long rewindIncrementMs) { 47 this.fastForwardIncrementMs = fastForwardIncrementMs; 48 this.rewindIncrementMs = rewindIncrementMs; 49 window = new Timeline.Window(); 50 } 51 52 @Override dispatchSetPlayWhenReady(Player player, boolean playWhenReady)53 public boolean dispatchSetPlayWhenReady(Player player, boolean playWhenReady) { 54 player.setPlayWhenReady(playWhenReady); 55 return true; 56 } 57 58 @Override dispatchSeekTo(Player player, int windowIndex, long positionMs)59 public boolean dispatchSeekTo(Player player, int windowIndex, long positionMs) { 60 player.seekTo(windowIndex, positionMs); 61 return true; 62 } 63 64 @Override dispatchPrevious(Player player)65 public boolean dispatchPrevious(Player player) { 66 Timeline timeline = player.getCurrentTimeline(); 67 if (timeline.isEmpty() || player.isPlayingAd()) { 68 return true; 69 } 70 int windowIndex = player.getCurrentWindowIndex(); 71 timeline.getWindow(windowIndex, window); 72 int previousWindowIndex = player.getPreviousWindowIndex(); 73 if (previousWindowIndex != C.INDEX_UNSET 74 && (player.getCurrentPosition() <= MAX_POSITION_FOR_SEEK_TO_PREVIOUS 75 || (window.isDynamic && !window.isSeekable))) { 76 player.seekTo(previousWindowIndex, C.TIME_UNSET); 77 } else { 78 player.seekTo(windowIndex, /* positionMs= */ 0); 79 } 80 return true; 81 } 82 83 @Override dispatchNext(Player player)84 public boolean dispatchNext(Player player) { 85 Timeline timeline = player.getCurrentTimeline(); 86 if (timeline.isEmpty() || player.isPlayingAd()) { 87 return true; 88 } 89 int windowIndex = player.getCurrentWindowIndex(); 90 int nextWindowIndex = player.getNextWindowIndex(); 91 if (nextWindowIndex != C.INDEX_UNSET) { 92 player.seekTo(nextWindowIndex, C.TIME_UNSET); 93 } else if (timeline.getWindow(windowIndex, window).isLive) { 94 player.seekTo(windowIndex, C.TIME_UNSET); 95 } 96 return true; 97 } 98 99 @Override dispatchRewind(Player player)100 public boolean dispatchRewind(Player player) { 101 if (isRewindEnabled() && player.isCurrentWindowSeekable()) { 102 seekToOffset(player, -rewindIncrementMs); 103 } 104 return true; 105 } 106 107 @Override dispatchFastForward(Player player)108 public boolean dispatchFastForward(Player player) { 109 if (isFastForwardEnabled() && player.isCurrentWindowSeekable()) { 110 seekToOffset(player, fastForwardIncrementMs); 111 } 112 return true; 113 } 114 115 @Override dispatchSetRepeatMode(Player player, @Player.RepeatMode int repeatMode)116 public boolean dispatchSetRepeatMode(Player player, @Player.RepeatMode int repeatMode) { 117 player.setRepeatMode(repeatMode); 118 return true; 119 } 120 121 @Override dispatchSetShuffleModeEnabled(Player player, boolean shuffleModeEnabled)122 public boolean dispatchSetShuffleModeEnabled(Player player, boolean shuffleModeEnabled) { 123 player.setShuffleModeEnabled(shuffleModeEnabled); 124 return true; 125 } 126 127 @Override dispatchStop(Player player, boolean reset)128 public boolean dispatchStop(Player player, boolean reset) { 129 player.stop(reset); 130 return true; 131 } 132 133 @Override isRewindEnabled()134 public boolean isRewindEnabled() { 135 return rewindIncrementMs > 0; 136 } 137 138 @Override isFastForwardEnabled()139 public boolean isFastForwardEnabled() { 140 return fastForwardIncrementMs > 0; 141 } 142 143 /** Returns the rewind increment in milliseconds. */ getRewindIncrementMs()144 public long getRewindIncrementMs() { 145 return rewindIncrementMs; 146 } 147 148 /** Returns the fast forward increment in milliseconds. */ getFastForwardIncrementMs()149 public long getFastForwardIncrementMs() { 150 return fastForwardIncrementMs; 151 } 152 153 /** 154 * @deprecated Create a new instance instead and pass the new instance to the UI component. This 155 * makes sure the UI gets updated and is in sync with the new values. 156 */ 157 @Deprecated setRewindIncrementMs(long rewindMs)158 public void setRewindIncrementMs(long rewindMs) { 159 this.rewindIncrementMs = rewindMs; 160 } 161 162 /** 163 * @deprecated Create a new instance instead and pass the new instance to the UI component. This 164 * makes sure the UI gets updated and is in sync with the new values. 165 */ 166 @Deprecated setFastForwardIncrementMs(long fastForwardMs)167 public void setFastForwardIncrementMs(long fastForwardMs) { 168 this.fastForwardIncrementMs = fastForwardMs; 169 } 170 171 // Internal methods. 172 seekToOffset(Player player, long offsetMs)173 private static void seekToOffset(Player player, long offsetMs) { 174 long positionMs = player.getCurrentPosition() + offsetMs; 175 long durationMs = player.getDuration(); 176 if (durationMs != C.TIME_UNSET) { 177 positionMs = Math.min(positionMs, durationMs); 178 } 179 positionMs = Math.max(positionMs, 0); 180 player.seekTo(player.getCurrentWindowIndex(), positionMs); 181 } 182 } 183