Simplify MediaSessionConnector
- Make MediaSessionConnector use a ControlDispatcher and add setControlDispatcher, setFastForwardIncrementMs and setRewindIncrementMs methods. This brings it in line with our other UI components, including PlayerControlView and PlayerNotificationManager. - Collapsed DefaultPlaybackController into MediaSessionConnector, since I'm not sure there's a legitimate alternative implementation (note ControlDispatcher does provide some equivalent functionality e.g. to modify calls being made on the player). - Pass the Player and ControlDispatcher to command receivers and custom actions, so they don't need their own references. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=220822082
This commit is contained in:
parent
8ec757ad2b
commit
063f5704cd
@ -1,158 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2017 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.ext.mediasession;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.ResultReceiver;
|
|
||||||
import android.support.v4.media.session.PlaybackStateCompat;
|
|
||||||
import com.google.android.exoplayer2.C;
|
|
||||||
import com.google.android.exoplayer2.Player;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A default implementation of {@link MediaSessionConnector.PlaybackController}.
|
|
||||||
* <p>
|
|
||||||
* Methods can be safely overridden by subclasses to intercept calls for given actions.
|
|
||||||
*/
|
|
||||||
public class DefaultPlaybackController implements MediaSessionConnector.PlaybackController {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The default fast forward increment, in milliseconds.
|
|
||||||
*/
|
|
||||||
public static final int DEFAULT_FAST_FORWARD_MS = 15000;
|
|
||||||
/**
|
|
||||||
* The default rewind increment, in milliseconds.
|
|
||||||
*/
|
|
||||||
public static final int DEFAULT_REWIND_MS = 5000;
|
|
||||||
|
|
||||||
private static final long BASE_ACTIONS = PlaybackStateCompat.ACTION_PLAY_PAUSE
|
|
||||||
| PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE
|
|
||||||
| PlaybackStateCompat.ACTION_STOP | PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE
|
|
||||||
| PlaybackStateCompat.ACTION_SET_REPEAT_MODE;
|
|
||||||
|
|
||||||
protected final long rewindIncrementMs;
|
|
||||||
protected final long fastForwardIncrementMs;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance.
|
|
||||||
*
|
|
||||||
* <p>Equivalent to {@code DefaultPlaybackController(DefaultPlaybackController.DEFAULT_REWIND_MS,
|
|
||||||
* DefaultPlaybackController.DEFAULT_FAST_FORWARD_MS)}.
|
|
||||||
*/
|
|
||||||
public DefaultPlaybackController() {
|
|
||||||
this(DEFAULT_REWIND_MS, DEFAULT_FAST_FORWARD_MS);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance with the given fast forward and rewind increments.
|
|
||||||
*
|
|
||||||
* @param rewindIncrementMs The rewind increment in milliseconds. A zero or negative value will
|
|
||||||
* cause the rewind action to be disabled.
|
|
||||||
* @param fastForwardIncrementMs The fast forward increment in milliseconds. A zero or negative
|
|
||||||
*/
|
|
||||||
public DefaultPlaybackController(long rewindIncrementMs, long fastForwardIncrementMs) {
|
|
||||||
this.rewindIncrementMs = rewindIncrementMs;
|
|
||||||
this.fastForwardIncrementMs = fastForwardIncrementMs;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getSupportedPlaybackActions(Player player) {
|
|
||||||
if (player == null || player.getCurrentTimeline().isEmpty()) {
|
|
||||||
return 0;
|
|
||||||
} else if (!player.isCurrentWindowSeekable()) {
|
|
||||||
return BASE_ACTIONS;
|
|
||||||
}
|
|
||||||
long actions = BASE_ACTIONS | PlaybackStateCompat.ACTION_SEEK_TO;
|
|
||||||
if (fastForwardIncrementMs > 0) {
|
|
||||||
actions |= PlaybackStateCompat.ACTION_FAST_FORWARD;
|
|
||||||
}
|
|
||||||
if (rewindIncrementMs > 0) {
|
|
||||||
actions |= PlaybackStateCompat.ACTION_REWIND;
|
|
||||||
}
|
|
||||||
return actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPlay(Player player) {
|
|
||||||
player.setPlayWhenReady(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause(Player player) {
|
|
||||||
player.setPlayWhenReady(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSeekTo(Player player, long positionMs) {
|
|
||||||
long duration = player.getDuration();
|
|
||||||
if (duration != C.TIME_UNSET) {
|
|
||||||
positionMs = Math.min(positionMs, duration);
|
|
||||||
}
|
|
||||||
player.seekTo(Math.max(positionMs, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFastForward(Player player) {
|
|
||||||
if (fastForwardIncrementMs <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onSeekTo(player, player.getCurrentPosition() + fastForwardIncrementMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRewind(Player player) {
|
|
||||||
if (rewindIncrementMs <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onSeekTo(player, player.getCurrentPosition() - rewindIncrementMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStop(Player player) {
|
|
||||||
player.stop(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSetShuffleMode(Player player, int shuffleMode) {
|
|
||||||
player.setShuffleModeEnabled(shuffleMode == PlaybackStateCompat.SHUFFLE_MODE_ALL
|
|
||||||
|| shuffleMode == PlaybackStateCompat.SHUFFLE_MODE_GROUP);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSetRepeatMode(Player player, int mediaSessionRepeatMode) {
|
|
||||||
int repeatMode;
|
|
||||||
switch (mediaSessionRepeatMode) {
|
|
||||||
case PlaybackStateCompat.REPEAT_MODE_ALL:
|
|
||||||
case PlaybackStateCompat.REPEAT_MODE_GROUP:
|
|
||||||
repeatMode = Player.REPEAT_MODE_ALL;
|
|
||||||
break;
|
|
||||||
case PlaybackStateCompat.REPEAT_MODE_ONE:
|
|
||||||
repeatMode = Player.REPEAT_MODE_ONE;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
repeatMode = Player.REPEAT_MODE_OFF;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
player.setRepeatMode(repeatMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
// CommandReceiver implementation.
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCommand(Player player, String command, Bundle extras, ResultReceiver cb) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -22,6 +22,7 @@ import android.os.Handler;
|
|||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.ResultReceiver;
|
import android.os.ResultReceiver;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
import android.support.annotation.LongDef;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.media.MediaDescriptionCompat;
|
import android.support.v4.media.MediaDescriptionCompat;
|
||||||
@ -32,6 +33,8 @@ import android.support.v4.media.session.MediaSessionCompat;
|
|||||||
import android.support.v4.media.session.PlaybackStateCompat;
|
import android.support.v4.media.session.PlaybackStateCompat;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
|
import com.google.android.exoplayer2.ControlDispatcher;
|
||||||
|
import com.google.android.exoplayer2.DefaultControlDispatcher;
|
||||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||||
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
|
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
|
||||||
import com.google.android.exoplayer2.PlaybackParameters;
|
import com.google.android.exoplayer2.PlaybackParameters;
|
||||||
@ -39,7 +42,10 @@ import com.google.android.exoplayer2.Player;
|
|||||||
import com.google.android.exoplayer2.Timeline;
|
import com.google.android.exoplayer2.Timeline;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.ErrorMessageProvider;
|
import com.google.android.exoplayer2.util.ErrorMessageProvider;
|
||||||
|
import com.google.android.exoplayer2.util.RepeatModeUtil;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -76,7 +82,52 @@ public final class MediaSessionConnector {
|
|||||||
ExoPlayerLibraryInfo.registerModule("goog.exo.mediasession");
|
ExoPlayerLibraryInfo.registerModule("goog.exo.mediasession");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Playback actions supported by the connector. */
|
||||||
|
@LongDef(
|
||||||
|
flag = true,
|
||||||
|
value = {
|
||||||
|
PlaybackStateCompat.ACTION_PLAY_PAUSE,
|
||||||
|
PlaybackStateCompat.ACTION_PLAY,
|
||||||
|
PlaybackStateCompat.ACTION_PAUSE,
|
||||||
|
PlaybackStateCompat.ACTION_SEEK_TO,
|
||||||
|
PlaybackStateCompat.ACTION_FAST_FORWARD,
|
||||||
|
PlaybackStateCompat.ACTION_REWIND,
|
||||||
|
PlaybackStateCompat.ACTION_STOP,
|
||||||
|
PlaybackStateCompat.ACTION_SET_REPEAT_MODE,
|
||||||
|
PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE
|
||||||
|
})
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface PlaybackActions {}
|
||||||
|
|
||||||
|
@PlaybackActions
|
||||||
|
public static final long ALL_PLAYBACK_ACTIONS =
|
||||||
|
PlaybackStateCompat.ACTION_PLAY_PAUSE
|
||||||
|
| PlaybackStateCompat.ACTION_PLAY
|
||||||
|
| PlaybackStateCompat.ACTION_PAUSE
|
||||||
|
| PlaybackStateCompat.ACTION_SEEK_TO
|
||||||
|
| PlaybackStateCompat.ACTION_FAST_FORWARD
|
||||||
|
| PlaybackStateCompat.ACTION_REWIND
|
||||||
|
| PlaybackStateCompat.ACTION_STOP
|
||||||
|
| PlaybackStateCompat.ACTION_SET_REPEAT_MODE
|
||||||
|
| PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE;
|
||||||
|
|
||||||
|
/** The default playback actions. */
|
||||||
|
@PlaybackActions public static final long DEFAULT_PLAYBACK_ACTIONS = ALL_PLAYBACK_ACTIONS;
|
||||||
|
|
||||||
|
/** The default fast forward increment, in milliseconds. */
|
||||||
|
public static final int DEFAULT_FAST_FORWARD_MS = 15000;
|
||||||
|
/** The default rewind increment, in milliseconds. */
|
||||||
|
public static final int DEFAULT_REWIND_MS = 5000;
|
||||||
|
|
||||||
public static final String EXTRAS_PITCH = "EXO_PITCH";
|
public static final String EXTRAS_PITCH = "EXO_PITCH";
|
||||||
|
|
||||||
|
private static final long BASE_PLAYBACK_ACTIONS =
|
||||||
|
PlaybackStateCompat.ACTION_PLAY_PAUSE
|
||||||
|
| PlaybackStateCompat.ACTION_PLAY
|
||||||
|
| PlaybackStateCompat.ACTION_PAUSE
|
||||||
|
| PlaybackStateCompat.ACTION_STOP
|
||||||
|
| PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE
|
||||||
|
| PlaybackStateCompat.ACTION_SET_REPEAT_MODE;
|
||||||
private static final int BASE_MEDIA_SESSION_FLAGS =
|
private static final int BASE_MEDIA_SESSION_FLAGS =
|
||||||
MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
|
MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
|
||||||
| MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS;
|
| MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS;
|
||||||
@ -86,11 +137,24 @@ public final class MediaSessionConnector {
|
|||||||
/** Receiver of media commands sent by a media controller. */
|
/** Receiver of media commands sent by a media controller. */
|
||||||
public interface CommandReceiver {
|
public interface CommandReceiver {
|
||||||
/**
|
/**
|
||||||
* See {@link MediaSessionCompat.Callback#onCommand(String, Bundle, ResultReceiver)}.
|
* See {@link MediaSessionCompat.Callback#onCommand(String, Bundle, ResultReceiver)}. The
|
||||||
|
* receiver may handle the command, but is not required to do so. Changes to the player should
|
||||||
|
* be made via the {@link ControlDispatcher}.
|
||||||
*
|
*
|
||||||
* @return Whether the command was handled by the receiver.
|
* @param player The player connected to the media session.
|
||||||
|
* @param controlDispatcher A {@link ControlDispatcher} that should be used for dispatching
|
||||||
|
* changes to the player.
|
||||||
|
* @param command The command name.
|
||||||
|
* @param extras Optional parameters for the command, may be null.
|
||||||
|
* @param cb A result receiver to which a result may be sent by the command, may be null.
|
||||||
|
* @return Whether the receiver handled the command.
|
||||||
*/
|
*/
|
||||||
boolean onCommand(Player player, String command, Bundle extras, ResultReceiver cb);
|
boolean onCommand(
|
||||||
|
Player player,
|
||||||
|
ControlDispatcher controlDispatcher,
|
||||||
|
String command,
|
||||||
|
Bundle extras,
|
||||||
|
ResultReceiver cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Interface to which playback preparation actions are delegated. */
|
/** Interface to which playback preparation actions are delegated. */
|
||||||
@ -128,51 +192,6 @@ public final class MediaSessionConnector {
|
|||||||
void onPrepareFromUri(Uri uri, Bundle extras);
|
void onPrepareFromUri(Uri uri, Bundle extras);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Interface to which playback actions are delegated. */
|
|
||||||
public interface PlaybackController extends CommandReceiver {
|
|
||||||
|
|
||||||
long ACTIONS =
|
|
||||||
PlaybackStateCompat.ACTION_PLAY_PAUSE
|
|
||||||
| PlaybackStateCompat.ACTION_PLAY
|
|
||||||
| PlaybackStateCompat.ACTION_PAUSE
|
|
||||||
| PlaybackStateCompat.ACTION_SEEK_TO
|
|
||||||
| PlaybackStateCompat.ACTION_FAST_FORWARD
|
|
||||||
| PlaybackStateCompat.ACTION_REWIND
|
|
||||||
| PlaybackStateCompat.ACTION_STOP
|
|
||||||
| PlaybackStateCompat.ACTION_SET_REPEAT_MODE
|
|
||||||
| PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the actions which are supported by the controller. The supported actions must be a
|
|
||||||
* bitmask combined out of {@link PlaybackStateCompat#ACTION_PLAY_PAUSE}, {@link
|
|
||||||
* PlaybackStateCompat#ACTION_PLAY}, {@link PlaybackStateCompat#ACTION_PAUSE}, {@link
|
|
||||||
* PlaybackStateCompat#ACTION_SEEK_TO}, {@link PlaybackStateCompat#ACTION_FAST_FORWARD}, {@link
|
|
||||||
* PlaybackStateCompat#ACTION_REWIND}, {@link PlaybackStateCompat#ACTION_STOP}, {@link
|
|
||||||
* PlaybackStateCompat#ACTION_SET_REPEAT_MODE} and {@link
|
|
||||||
* PlaybackStateCompat#ACTION_SET_SHUFFLE_MODE}.
|
|
||||||
*
|
|
||||||
* @param player The player.
|
|
||||||
* @return The bitmask of the supported media actions.
|
|
||||||
*/
|
|
||||||
long getSupportedPlaybackActions(@Nullable Player player);
|
|
||||||
/** See {@link MediaSessionCompat.Callback#onPlay()}. */
|
|
||||||
void onPlay(Player player);
|
|
||||||
/** See {@link MediaSessionCompat.Callback#onPause()}. */
|
|
||||||
void onPause(Player player);
|
|
||||||
/** See {@link MediaSessionCompat.Callback#onSeekTo(long)}. */
|
|
||||||
void onSeekTo(Player player, long positionMs);
|
|
||||||
/** See {@link MediaSessionCompat.Callback#onFastForward()}. */
|
|
||||||
void onFastForward(Player player);
|
|
||||||
/** See {@link MediaSessionCompat.Callback#onRewind()}. */
|
|
||||||
void onRewind(Player player);
|
|
||||||
/** See {@link MediaSessionCompat.Callback#onStop()}. */
|
|
||||||
void onStop(Player player);
|
|
||||||
/** See {@link MediaSessionCompat.Callback#onSetShuffleMode(int)}. */
|
|
||||||
void onSetShuffleMode(Player player, int shuffleMode);
|
|
||||||
/** See {@link MediaSessionCompat.Callback#onSetRepeatMode(int)}. */
|
|
||||||
void onSetRepeatMode(Player player, int repeatMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles queue navigation actions, and updates the media session queue by calling {@code
|
* Handles queue navigation actions, and updates the media session queue by calling {@code
|
||||||
* MediaSessionCompat.setQueue()}.
|
* MediaSessionCompat.setQueue()}.
|
||||||
@ -190,20 +209,20 @@ public final class MediaSessionConnector {
|
|||||||
* PlaybackStateCompat#ACTION_SKIP_TO_NEXT}, {@link
|
* PlaybackStateCompat#ACTION_SKIP_TO_NEXT}, {@link
|
||||||
* PlaybackStateCompat#ACTION_SKIP_TO_PREVIOUS}.
|
* PlaybackStateCompat#ACTION_SKIP_TO_PREVIOUS}.
|
||||||
*
|
*
|
||||||
* @param player The {@link Player}.
|
* @param player The player connected to the media session.
|
||||||
* @return The bitmask of the supported media actions.
|
* @return The bitmask of the supported media actions.
|
||||||
*/
|
*/
|
||||||
long getSupportedQueueNavigatorActions(@Nullable Player player);
|
long getSupportedQueueNavigatorActions(@Nullable Player player);
|
||||||
/**
|
/**
|
||||||
* Called when the timeline of the player has changed.
|
* Called when the timeline of the player has changed.
|
||||||
*
|
*
|
||||||
* @param player The player of which the timeline has changed.
|
* @param player The player connected to the media session.
|
||||||
*/
|
*/
|
||||||
void onTimelineChanged(Player player);
|
void onTimelineChanged(Player player);
|
||||||
/**
|
/**
|
||||||
* Called when the current window index changed.
|
* Called when the current window index changed.
|
||||||
*
|
*
|
||||||
* @param player The player of which the current window index of the timeline has changed.
|
* @param player The player connected to the media session.
|
||||||
*/
|
*/
|
||||||
void onCurrentWindowIndexChanged(Player player);
|
void onCurrentWindowIndexChanged(Player player);
|
||||||
/**
|
/**
|
||||||
@ -218,12 +237,30 @@ public final class MediaSessionConnector {
|
|||||||
* @return The id of the active queue item.
|
* @return The id of the active queue item.
|
||||||
*/
|
*/
|
||||||
long getActiveQueueItemId(@Nullable Player player);
|
long getActiveQueueItemId(@Nullable Player player);
|
||||||
/** See {@link MediaSessionCompat.Callback#onSkipToPrevious()}. */
|
/**
|
||||||
void onSkipToPrevious(Player player);
|
* See {@link MediaSessionCompat.Callback#onSkipToPrevious()}.
|
||||||
/** See {@link MediaSessionCompat.Callback#onSkipToQueueItem(long)}. */
|
*
|
||||||
void onSkipToQueueItem(Player player, long id);
|
* @param player The player connected to the media session.
|
||||||
/** See {@link MediaSessionCompat.Callback#onSkipToNext()}. */
|
* @param controlDispatcher A {@link ControlDispatcher} that should be used for dispatching
|
||||||
void onSkipToNext(Player player);
|
* changes to the player.
|
||||||
|
*/
|
||||||
|
void onSkipToPrevious(Player player, ControlDispatcher controlDispatcher);
|
||||||
|
/**
|
||||||
|
* See {@link MediaSessionCompat.Callback#onSkipToQueueItem(long)}.
|
||||||
|
*
|
||||||
|
* @param player The player connected to the media session.
|
||||||
|
* @param controlDispatcher A {@link ControlDispatcher} that should be used for dispatching
|
||||||
|
* changes to the player.
|
||||||
|
*/
|
||||||
|
void onSkipToQueueItem(Player player, ControlDispatcher controlDispatcher, long id);
|
||||||
|
/**
|
||||||
|
* See {@link MediaSessionCompat.Callback#onSkipToNext()}.
|
||||||
|
*
|
||||||
|
* @param player The player connected to the media session.
|
||||||
|
* @param controlDispatcher A {@link ControlDispatcher} that should be used for dispatching
|
||||||
|
* changes to the player.
|
||||||
|
*/
|
||||||
|
void onSkipToNext(Player player, ControlDispatcher controlDispatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handles media session queue edits. */
|
/** Handles media session queue edits. */
|
||||||
@ -263,19 +300,24 @@ public final class MediaSessionConnector {
|
|||||||
/**
|
/**
|
||||||
* Called when a custom action provided by this provider is sent to the media session.
|
* Called when a custom action provided by this provider is sent to the media session.
|
||||||
*
|
*
|
||||||
|
* @param player The player connected to the media session.
|
||||||
|
* @param controlDispatcher A {@link ControlDispatcher} that should be used for dispatching
|
||||||
|
* changes to the player.
|
||||||
* @param action The name of the action which was sent by a media controller.
|
* @param action The name of the action which was sent by a media controller.
|
||||||
* @param extras Optional extras sent by a media controller.
|
* @param extras Optional extras sent by a media controller.
|
||||||
*/
|
*/
|
||||||
void onCustomAction(String action, Bundle extras);
|
void onCustomAction(
|
||||||
|
Player player, ControlDispatcher controlDispatcher, String action, Bundle extras);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@link PlaybackStateCompat.CustomAction} which will be published to the media
|
* Returns a {@link PlaybackStateCompat.CustomAction} which will be published to the media
|
||||||
* session by the connector or {@code null} if this action should not be published at the given
|
* session by the connector or {@code null} if this action should not be published at the given
|
||||||
* player state.
|
* player state.
|
||||||
*
|
*
|
||||||
|
* @param player The player connected to the media session.
|
||||||
* @return The custom action to be included in the session playback state or {@code null}.
|
* @return The custom action to be included in the session playback state or {@code null}.
|
||||||
*/
|
*/
|
||||||
PlaybackStateCompat.CustomAction getCustomAction();
|
PlaybackStateCompat.CustomAction getCustomAction(Player player);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Provides a {@link MediaMetadataCompat} for a given player state. */
|
/** Provides a {@link MediaMetadataCompat} for a given player state. */
|
||||||
@ -283,7 +325,7 @@ public final class MediaSessionConnector {
|
|||||||
/**
|
/**
|
||||||
* Gets the {@link MediaMetadataCompat} to be published to the session.
|
* Gets the {@link MediaMetadataCompat} to be published to the session.
|
||||||
*
|
*
|
||||||
* @param player The player for which to provide metadata.
|
* @param player The player connected to the media session.
|
||||||
* @return The {@link MediaMetadataCompat} to be published to the session.
|
* @return The {@link MediaMetadataCompat} to be published to the session.
|
||||||
*/
|
*/
|
||||||
MediaMetadataCompat getMetadata(Player player);
|
MediaMetadataCompat getMetadata(Player player);
|
||||||
@ -292,47 +334,37 @@ public final class MediaSessionConnector {
|
|||||||
/** The wrapped {@link MediaSessionCompat}. */
|
/** The wrapped {@link MediaSessionCompat}. */
|
||||||
public final MediaSessionCompat mediaSession;
|
public final MediaSessionCompat mediaSession;
|
||||||
|
|
||||||
private @Nullable final MediaMetadataProvider mediaMetadataProvider;
|
@Nullable private final MediaMetadataProvider mediaMetadataProvider;
|
||||||
private final ExoPlayerEventListener exoPlayerEventListener;
|
private final ExoPlayerEventListener exoPlayerEventListener;
|
||||||
private final MediaSessionCallback mediaSessionCallback;
|
private final MediaSessionCallback mediaSessionCallback;
|
||||||
private final PlaybackController playbackController;
|
|
||||||
private final ArrayList<CommandReceiver> commandReceivers;
|
private final ArrayList<CommandReceiver> commandReceivers;
|
||||||
|
|
||||||
private Player player;
|
private Player player;
|
||||||
|
private ControlDispatcher controlDispatcher;
|
||||||
private CustomActionProvider[] customActionProviders;
|
private CustomActionProvider[] customActionProviders;
|
||||||
private Map<String, CustomActionProvider> customActionMap;
|
private Map<String, CustomActionProvider> customActionMap;
|
||||||
private @Nullable ErrorMessageProvider<? super ExoPlaybackException> errorMessageProvider;
|
@Nullable private ErrorMessageProvider<? super ExoPlaybackException> errorMessageProvider;
|
||||||
private @Nullable Pair<Integer, CharSequence> customError;
|
@Nullable private Pair<Integer, CharSequence> customError;
|
||||||
private PlaybackPreparer playbackPreparer;
|
private PlaybackPreparer playbackPreparer;
|
||||||
private QueueNavigator queueNavigator;
|
private QueueNavigator queueNavigator;
|
||||||
private QueueEditor queueEditor;
|
private QueueEditor queueEditor;
|
||||||
private RatingCallback ratingCallback;
|
private RatingCallback ratingCallback;
|
||||||
|
|
||||||
/**
|
private long enabledPlaybackActions;
|
||||||
* Creates an instance.
|
private int rewindMs;
|
||||||
*
|
private int fastForwardMs;
|
||||||
* <p>Equivalent to {@code MediaSessionConnector(mediaSession, new DefaultPlaybackController())}.
|
|
||||||
*
|
|
||||||
* @param mediaSession The {@link MediaSessionCompat} to connect to.
|
|
||||||
*/
|
|
||||||
public MediaSessionConnector(MediaSessionCompat mediaSession) {
|
|
||||||
this(mediaSession, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance.
|
* Creates an instance.
|
||||||
*
|
*
|
||||||
* <p>Equivalent to {@code MediaSessionConnector(mediaSession, playbackController, new
|
* <p>Equivalent to {@code MediaSessionConnector(mediaSession, new
|
||||||
* DefaultMediaMetadataProvider(mediaSession.getController(), null))}.
|
* DefaultMediaMetadataProvider(mediaSession.getController(), null))}.
|
||||||
*
|
*
|
||||||
* @param mediaSession The {@link MediaSessionCompat} to connect to.
|
* @param mediaSession The {@link MediaSessionCompat} to connect to.
|
||||||
* @param playbackController A {@link PlaybackController} for handling playback actions.
|
|
||||||
*/
|
*/
|
||||||
public MediaSessionConnector(
|
public MediaSessionConnector(MediaSessionCompat mediaSession) {
|
||||||
MediaSessionCompat mediaSession, PlaybackController playbackController) {
|
|
||||||
this(
|
this(
|
||||||
mediaSession,
|
mediaSession,
|
||||||
playbackController,
|
|
||||||
new DefaultMediaMetadataProvider(mediaSession.getController(), null));
|
new DefaultMediaMetadataProvider(mediaSession.getController(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,52 +372,24 @@ public final class MediaSessionConnector {
|
|||||||
* Creates an instance.
|
* Creates an instance.
|
||||||
*
|
*
|
||||||
* @param mediaSession The {@link MediaSessionCompat} to connect to.
|
* @param mediaSession The {@link MediaSessionCompat} to connect to.
|
||||||
* @param playbackController A {@link PlaybackController} for handling playback actions, or {@code
|
|
||||||
* null} if the connector should handle playback actions directly.
|
|
||||||
* @param doMaintainMetadata Whether the connector should maintain the metadata of the session.
|
|
||||||
* @param metadataExtrasPrefix A string to prefix extra keys which are propagated from the active
|
|
||||||
* queue item to the session metadata.
|
|
||||||
* @deprecated Use {@link MediaSessionConnector#MediaSessionConnector(MediaSessionCompat,
|
|
||||||
* PlaybackController, MediaMetadataProvider)}.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public MediaSessionConnector(
|
|
||||||
MediaSessionCompat mediaSession,
|
|
||||||
@Nullable PlaybackController playbackController,
|
|
||||||
boolean doMaintainMetadata,
|
|
||||||
@Nullable String metadataExtrasPrefix) {
|
|
||||||
this(
|
|
||||||
mediaSession,
|
|
||||||
playbackController,
|
|
||||||
doMaintainMetadata
|
|
||||||
? new DefaultMediaMetadataProvider(mediaSession.getController(), metadataExtrasPrefix)
|
|
||||||
: null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an instance.
|
|
||||||
*
|
|
||||||
* @param mediaSession The {@link MediaSessionCompat} to connect to.
|
|
||||||
* @param playbackController A {@link PlaybackController} for handling playback actions, or {@code
|
|
||||||
* null} if the connector should handle playback actions directly.
|
|
||||||
* @param mediaMetadataProvider A {@link MediaMetadataProvider} for providing a custom metadata
|
* @param mediaMetadataProvider A {@link MediaMetadataProvider} for providing a custom metadata
|
||||||
* object to be published to the media session, or {@code null} if metadata shouldn't be
|
* object to be published to the media session, or {@code null} if metadata shouldn't be
|
||||||
* published.
|
* published.
|
||||||
*/
|
*/
|
||||||
public MediaSessionConnector(
|
public MediaSessionConnector(
|
||||||
MediaSessionCompat mediaSession,
|
MediaSessionCompat mediaSession,
|
||||||
@Nullable PlaybackController playbackController,
|
|
||||||
@Nullable MediaMetadataProvider mediaMetadataProvider) {
|
@Nullable MediaMetadataProvider mediaMetadataProvider) {
|
||||||
this.mediaSession = mediaSession;
|
this.mediaSession = mediaSession;
|
||||||
this.playbackController =
|
|
||||||
playbackController != null ? playbackController : new DefaultPlaybackController();
|
|
||||||
this.mediaMetadataProvider = mediaMetadataProvider;
|
this.mediaMetadataProvider = mediaMetadataProvider;
|
||||||
mediaSession.setFlags(BASE_MEDIA_SESSION_FLAGS);
|
mediaSession.setFlags(BASE_MEDIA_SESSION_FLAGS);
|
||||||
mediaSessionCallback = new MediaSessionCallback();
|
mediaSessionCallback = new MediaSessionCallback();
|
||||||
exoPlayerEventListener = new ExoPlayerEventListener();
|
exoPlayerEventListener = new ExoPlayerEventListener();
|
||||||
|
controlDispatcher = new DefaultControlDispatcher();
|
||||||
customActionMap = Collections.emptyMap();
|
customActionMap = Collections.emptyMap();
|
||||||
commandReceivers = new ArrayList<>();
|
commandReceivers = new ArrayList<>();
|
||||||
registerCommandReceiver(playbackController);
|
enabledPlaybackActions = DEFAULT_PLAYBACK_ACTIONS;
|
||||||
|
rewindMs = DEFAULT_REWIND_MS;
|
||||||
|
fastForwardMs = DEFAULT_FAST_FORWARD_MS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -410,8 +414,8 @@ public final class MediaSessionConnector {
|
|||||||
this.player.removeListener(exoPlayerEventListener);
|
this.player.removeListener(exoPlayerEventListener);
|
||||||
mediaSession.setCallback(null);
|
mediaSession.setCallback(null);
|
||||||
}
|
}
|
||||||
unregisterCommandReceiver(this.playbackPreparer);
|
|
||||||
|
|
||||||
|
unregisterCommandReceiver(this.playbackPreparer);
|
||||||
this.player = player;
|
this.player = player;
|
||||||
this.playbackPreparer = playbackPreparer;
|
this.playbackPreparer = playbackPreparer;
|
||||||
registerCommandReceiver(playbackPreparer);
|
registerCommandReceiver(playbackPreparer);
|
||||||
@ -429,6 +433,58 @@ public final class MediaSessionConnector {
|
|||||||
invalidateMediaSessionMetadata();
|
invalidateMediaSessionMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link ControlDispatcher}.
|
||||||
|
*
|
||||||
|
* @param controlDispatcher The {@link ControlDispatcher}, or null to use {@link
|
||||||
|
* DefaultControlDispatcher}.
|
||||||
|
*/
|
||||||
|
public void setControlDispatcher(@Nullable ControlDispatcher controlDispatcher) {
|
||||||
|
if (this.controlDispatcher != controlDispatcher) {
|
||||||
|
this.controlDispatcher =
|
||||||
|
controlDispatcher == null ? new DefaultControlDispatcher() : controlDispatcher;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the enabled playback actions.
|
||||||
|
*
|
||||||
|
* @param enabledPlaybackActions The enabled playback actions.
|
||||||
|
*/
|
||||||
|
public void setEnabledPlaybackActions(@PlaybackActions long enabledPlaybackActions) {
|
||||||
|
enabledPlaybackActions &= ALL_PLAYBACK_ACTIONS;
|
||||||
|
if (this.enabledPlaybackActions != enabledPlaybackActions) {
|
||||||
|
this.enabledPlaybackActions = enabledPlaybackActions;
|
||||||
|
invalidateMediaSessionPlaybackState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the rewind increment in milliseconds.
|
||||||
|
*
|
||||||
|
* @param rewindMs The rewind increment in milliseconds. A non-positive value will cause the
|
||||||
|
* rewind button to be disabled.
|
||||||
|
*/
|
||||||
|
public void setRewindIncrementMs(int rewindMs) {
|
||||||
|
if (this.rewindMs != rewindMs) {
|
||||||
|
this.rewindMs = rewindMs;
|
||||||
|
invalidateMediaSessionPlaybackState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the fast forward increment in milliseconds.
|
||||||
|
*
|
||||||
|
* @param fastForwardMs The fast forward increment in milliseconds. A non-positive value will
|
||||||
|
* cause the fast forward button to be disabled.
|
||||||
|
*/
|
||||||
|
public void setFastForwardIncrementMs(int fastForwardMs) {
|
||||||
|
if (this.fastForwardMs != fastForwardMs) {
|
||||||
|
this.fastForwardMs = fastForwardMs;
|
||||||
|
invalidateMediaSessionPlaybackState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the optional {@link ErrorMessageProvider}.
|
* Sets the optional {@link ErrorMessageProvider}.
|
||||||
*
|
*
|
||||||
@ -537,7 +593,7 @@ public final class MediaSessionConnector {
|
|||||||
|
|
||||||
Map<String, CustomActionProvider> currentActions = new HashMap<>();
|
Map<String, CustomActionProvider> currentActions = new HashMap<>();
|
||||||
for (CustomActionProvider customActionProvider : customActionProviders) {
|
for (CustomActionProvider customActionProvider : customActionProviders) {
|
||||||
PlaybackStateCompat.CustomAction customAction = customActionProvider.getCustomAction();
|
PlaybackStateCompat.CustomAction customAction = customActionProvider.getCustomAction(player);
|
||||||
if (customAction != null) {
|
if (customAction != null) {
|
||||||
currentActions.put(customAction.getAction(), customActionProvider);
|
currentActions.put(customAction.getAction(), customActionProvider);
|
||||||
builder.addCustomAction(customAction);
|
builder.addCustomAction(customAction);
|
||||||
@ -602,8 +658,20 @@ public final class MediaSessionConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private long buildPlaybackActions() {
|
private long buildPlaybackActions() {
|
||||||
long actions =
|
long actions = 0;
|
||||||
(PlaybackController.ACTIONS & playbackController.getSupportedPlaybackActions(player));
|
if (player != null && !player.getCurrentTimeline().isEmpty()) {
|
||||||
|
long playbackActions = BASE_PLAYBACK_ACTIONS;
|
||||||
|
if (player.isCurrentWindowSeekable()) {
|
||||||
|
playbackActions |= PlaybackStateCompat.ACTION_SEEK_TO;
|
||||||
|
if (fastForwardMs > 0) {
|
||||||
|
playbackActions |= PlaybackStateCompat.ACTION_FAST_FORWARD;
|
||||||
|
}
|
||||||
|
if (rewindMs > 0) {
|
||||||
|
playbackActions |= PlaybackStateCompat.ACTION_REWIND;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
actions |= (playbackActions & enabledPlaybackActions);
|
||||||
|
}
|
||||||
if (playbackPreparer != null) {
|
if (playbackPreparer != null) {
|
||||||
actions |= (PlaybackPreparer.ACTIONS & playbackPreparer.getSupportedPrepareActions());
|
actions |= (PlaybackPreparer.ACTIONS & playbackPreparer.getSupportedPrepareActions());
|
||||||
}
|
}
|
||||||
@ -630,18 +698,15 @@ public final class MediaSessionConnector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean canDispatchPlaybackAction(long action) {
|
||||||
|
return (enabledPlaybackActions & action) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean canDispatchToPlaybackPreparer(long action) {
|
private boolean canDispatchToPlaybackPreparer(long action) {
|
||||||
return playbackPreparer != null
|
return playbackPreparer != null
|
||||||
&& (playbackPreparer.getSupportedPrepareActions() & PlaybackPreparer.ACTIONS & action) != 0;
|
&& (playbackPreparer.getSupportedPrepareActions() & PlaybackPreparer.ACTIONS & action) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean canDispatchToPlaybackController(long action) {
|
|
||||||
return (playbackController.getSupportedPlaybackActions(player)
|
|
||||||
& PlaybackController.ACTIONS
|
|
||||||
& action)
|
|
||||||
!= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean canDispatchToQueueNavigator(long action) {
|
private boolean canDispatchToQueueNavigator(long action) {
|
||||||
return queueNavigator != null
|
return queueNavigator != null
|
||||||
&& (queueNavigator.getSupportedQueueNavigatorActions(player)
|
&& (queueNavigator.getSupportedQueueNavigatorActions(player)
|
||||||
@ -650,6 +715,31 @@ public final class MediaSessionConnector {
|
|||||||
!= 0;
|
!= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void rewind() {
|
||||||
|
if (rewindMs > 0) {
|
||||||
|
seekTo(player.getCurrentPosition() - rewindMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fastForward() {
|
||||||
|
if (fastForwardMs > 0) {
|
||||||
|
seekTo(player.getCurrentPosition() + fastForwardMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void seekTo(long positionMs) {
|
||||||
|
seekTo(player.getCurrentWindowIndex(), positionMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void seekTo(int windowIndex, long positionMs) {
|
||||||
|
long durationMs = player.getDuration();
|
||||||
|
if (durationMs != C.TIME_UNSET) {
|
||||||
|
positionMs = Math.min(positionMs, durationMs);
|
||||||
|
}
|
||||||
|
positionMs = Math.max(positionMs, 0);
|
||||||
|
controlDispatcher.dispatchSeekTo(player, windowIndex, positionMs);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a default {@link MediaMetadataCompat} with properties and extras propagated from the
|
* Provides a default {@link MediaMetadataCompat} with properties and extras propagated from the
|
||||||
* active queue item to the session metadata.
|
* active queue item to the session metadata.
|
||||||
@ -825,85 +915,101 @@ public final class MediaSessionConnector {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPlay() {
|
public void onPlay() {
|
||||||
if (canDispatchToPlaybackController(PlaybackStateCompat.ACTION_PLAY)) {
|
if (canDispatchPlaybackAction(PlaybackStateCompat.ACTION_PLAY)) {
|
||||||
playbackController.onPlay(player);
|
controlDispatcher.dispatchSetPlayWhenReady(player, /* playWhenReady= */ true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPause() {
|
public void onPause() {
|
||||||
if (canDispatchToPlaybackController(PlaybackStateCompat.ACTION_PAUSE)) {
|
if (canDispatchPlaybackAction(PlaybackStateCompat.ACTION_PAUSE)) {
|
||||||
playbackController.onPause(player);
|
controlDispatcher.dispatchSetPlayWhenReady(player, /* playWhenReady= */ false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSeekTo(long positionMs) {
|
public void onSeekTo(long positionMs) {
|
||||||
if (canDispatchToPlaybackController(PlaybackStateCompat.ACTION_SEEK_TO)) {
|
if (canDispatchPlaybackAction(PlaybackStateCompat.ACTION_SEEK_TO)) {
|
||||||
playbackController.onSeekTo(player, positionMs);
|
seekTo(positionMs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFastForward() {
|
public void onFastForward() {
|
||||||
if (canDispatchToPlaybackController(PlaybackStateCompat.ACTION_FAST_FORWARD)) {
|
if (canDispatchPlaybackAction(PlaybackStateCompat.ACTION_FAST_FORWARD)) {
|
||||||
playbackController.onFastForward(player);
|
fastForward();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRewind() {
|
public void onRewind() {
|
||||||
if (canDispatchToPlaybackController(PlaybackStateCompat.ACTION_REWIND)) {
|
if (canDispatchPlaybackAction(PlaybackStateCompat.ACTION_REWIND)) {
|
||||||
playbackController.onRewind(player);
|
rewind();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStop() {
|
public void onStop() {
|
||||||
if (canDispatchToPlaybackController(PlaybackStateCompat.ACTION_STOP)) {
|
if (canDispatchPlaybackAction(PlaybackStateCompat.ACTION_STOP)) {
|
||||||
playbackController.onStop(player);
|
controlDispatcher.dispatchStop(player, /* reset= */ true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSetShuffleMode(int shuffleMode) {
|
public void onSetShuffleMode(int shuffleMode) {
|
||||||
if (canDispatchToPlaybackController(PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE)) {
|
if (canDispatchPlaybackAction(PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE)) {
|
||||||
playbackController.onSetShuffleMode(player, shuffleMode);
|
boolean shuffleModeEnabled =
|
||||||
|
shuffleMode == PlaybackStateCompat.SHUFFLE_MODE_ALL
|
||||||
|
|| shuffleMode == PlaybackStateCompat.SHUFFLE_MODE_GROUP;
|
||||||
|
controlDispatcher.dispatchSetShuffleModeEnabled(player, shuffleModeEnabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSetRepeatMode(int repeatMode) {
|
public void onSetRepeatMode(int mediaSessionRepeatMode) {
|
||||||
if (canDispatchToPlaybackController(PlaybackStateCompat.ACTION_SET_REPEAT_MODE)) {
|
if (canDispatchPlaybackAction(PlaybackStateCompat.ACTION_SET_REPEAT_MODE)) {
|
||||||
playbackController.onSetRepeatMode(player, repeatMode);
|
@RepeatModeUtil.RepeatToggleModes int repeatMode;
|
||||||
|
switch (mediaSessionRepeatMode) {
|
||||||
|
case PlaybackStateCompat.REPEAT_MODE_ALL:
|
||||||
|
case PlaybackStateCompat.REPEAT_MODE_GROUP:
|
||||||
|
repeatMode = Player.REPEAT_MODE_ALL;
|
||||||
|
break;
|
||||||
|
case PlaybackStateCompat.REPEAT_MODE_ONE:
|
||||||
|
repeatMode = Player.REPEAT_MODE_ONE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
repeatMode = Player.REPEAT_MODE_OFF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
controlDispatcher.dispatchSetRepeatMode(player, repeatMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSkipToNext() {
|
public void onSkipToNext() {
|
||||||
if (canDispatchToQueueNavigator(PlaybackStateCompat.ACTION_SKIP_TO_NEXT)) {
|
if (canDispatchToQueueNavigator(PlaybackStateCompat.ACTION_SKIP_TO_NEXT)) {
|
||||||
queueNavigator.onSkipToNext(player);
|
queueNavigator.onSkipToNext(player, controlDispatcher);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSkipToPrevious() {
|
public void onSkipToPrevious() {
|
||||||
if (canDispatchToQueueNavigator(PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)) {
|
if (canDispatchToQueueNavigator(PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)) {
|
||||||
queueNavigator.onSkipToPrevious(player);
|
queueNavigator.onSkipToPrevious(player, controlDispatcher);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSkipToQueueItem(long id) {
|
public void onSkipToQueueItem(long id) {
|
||||||
if (canDispatchToQueueNavigator(PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM)) {
|
if (canDispatchToQueueNavigator(PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM)) {
|
||||||
queueNavigator.onSkipToQueueItem(player, id);
|
queueNavigator.onSkipToQueueItem(player, controlDispatcher, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCustomAction(@NonNull String action, @Nullable Bundle extras) {
|
public void onCustomAction(@NonNull String action, @Nullable Bundle extras) {
|
||||||
if (customActionMap.containsKey(action)) {
|
if (customActionMap.containsKey(action)) {
|
||||||
customActionMap.get(action).onCustomAction(action, extras);
|
customActionMap.get(action).onCustomAction(player, controlDispatcher, action, extras);
|
||||||
invalidateMediaSessionPlaybackState();
|
invalidateMediaSessionPlaybackState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -911,7 +1017,7 @@ public final class MediaSessionConnector {
|
|||||||
@Override
|
@Override
|
||||||
public void onCommand(String command, Bundle extras, ResultReceiver cb) {
|
public void onCommand(String command, Bundle extras, ResultReceiver cb) {
|
||||||
for (int i = 0; i < commandReceivers.size(); i++) {
|
for (int i = 0; i < commandReceivers.size(); i++) {
|
||||||
if (commandReceivers.get(i).onCommand(player, command, extras, cb)) {
|
if (commandReceivers.get(i).onCommand(player, controlDispatcher, command, extras, cb)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ package com.google.android.exoplayer2.ext.mediasession;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.media.session.PlaybackStateCompat;
|
import android.support.v4.media.session.PlaybackStateCompat;
|
||||||
|
import com.google.android.exoplayer2.ControlDispatcher;
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
import com.google.android.exoplayer2.util.RepeatModeUtil;
|
import com.google.android.exoplayer2.util.RepeatModeUtil;
|
||||||
|
|
||||||
@ -33,7 +34,6 @@ public final class RepeatModeActionProvider implements MediaSessionConnector.Cus
|
|||||||
|
|
||||||
private static final String ACTION_REPEAT_MODE = "ACTION_EXO_REPEAT_MODE";
|
private static final String ACTION_REPEAT_MODE = "ACTION_EXO_REPEAT_MODE";
|
||||||
|
|
||||||
private final Player player;
|
|
||||||
@RepeatModeUtil.RepeatToggleModes
|
@RepeatModeUtil.RepeatToggleModes
|
||||||
private final int repeatToggleModes;
|
private final int repeatToggleModes;
|
||||||
private final CharSequence repeatAllDescription;
|
private final CharSequence repeatAllDescription;
|
||||||
@ -42,27 +42,23 @@ public final class RepeatModeActionProvider implements MediaSessionConnector.Cus
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
* <p>
|
*
|
||||||
* Equivalent to {@code RepeatModeActionProvider(context, player,
|
* <p>Equivalent to {@code RepeatModeActionProvider(context, DEFAULT_REPEAT_TOGGLE_MODES)}.
|
||||||
* MediaSessionConnector.DEFAULT_REPEAT_TOGGLE_MODES)}.
|
|
||||||
*
|
*
|
||||||
* @param context The context.
|
* @param context The context.
|
||||||
* @param player The player on which to toggle the repeat mode.
|
|
||||||
*/
|
*/
|
||||||
public RepeatModeActionProvider(Context context, Player player) {
|
public RepeatModeActionProvider(Context context) {
|
||||||
this(context, player, DEFAULT_REPEAT_TOGGLE_MODES);
|
this(context, DEFAULT_REPEAT_TOGGLE_MODES);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance enabling the given repeat toggle modes.
|
* Creates a new instance enabling the given repeat toggle modes.
|
||||||
*
|
*
|
||||||
* @param context The context.
|
* @param context The context.
|
||||||
* @param player The player on which to toggle the repeat mode.
|
|
||||||
* @param repeatToggleModes The toggle modes to enable.
|
* @param repeatToggleModes The toggle modes to enable.
|
||||||
*/
|
*/
|
||||||
public RepeatModeActionProvider(Context context, Player player,
|
public RepeatModeActionProvider(
|
||||||
@RepeatModeUtil.RepeatToggleModes int repeatToggleModes) {
|
Context context, @RepeatModeUtil.RepeatToggleModes int repeatToggleModes) {
|
||||||
this.player = player;
|
|
||||||
this.repeatToggleModes = repeatToggleModes;
|
this.repeatToggleModes = repeatToggleModes;
|
||||||
repeatAllDescription = context.getString(R.string.exo_media_action_repeat_all_description);
|
repeatAllDescription = context.getString(R.string.exo_media_action_repeat_all_description);
|
||||||
repeatOneDescription = context.getString(R.string.exo_media_action_repeat_one_description);
|
repeatOneDescription = context.getString(R.string.exo_media_action_repeat_one_description);
|
||||||
@ -70,16 +66,17 @@ public final class RepeatModeActionProvider implements MediaSessionConnector.Cus
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCustomAction(String action, Bundle extras) {
|
public void onCustomAction(
|
||||||
|
Player player, ControlDispatcher controlDispatcher, String action, Bundle extras) {
|
||||||
int mode = player.getRepeatMode();
|
int mode = player.getRepeatMode();
|
||||||
int proposedMode = RepeatModeUtil.getNextRepeatMode(mode, repeatToggleModes);
|
int proposedMode = RepeatModeUtil.getNextRepeatMode(mode, repeatToggleModes);
|
||||||
if (mode != proposedMode) {
|
if (mode != proposedMode) {
|
||||||
player.setRepeatMode(proposedMode);
|
controlDispatcher.dispatchSetRepeatMode(player, proposedMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PlaybackStateCompat.CustomAction getCustomAction() {
|
public PlaybackStateCompat.CustomAction getCustomAction(Player player) {
|
||||||
CharSequence actionLabel;
|
CharSequence actionLabel;
|
||||||
int iconResourceId;
|
int iconResourceId;
|
||||||
switch (player.getRepeatMode()) {
|
switch (player.getRepeatMode()) {
|
||||||
|
@ -23,6 +23,7 @@ import android.support.v4.media.MediaDescriptionCompat;
|
|||||||
import android.support.v4.media.session.MediaControllerCompat;
|
import android.support.v4.media.session.MediaControllerCompat;
|
||||||
import android.support.v4.media.session.MediaSessionCompat;
|
import android.support.v4.media.session.MediaSessionCompat;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
|
import com.google.android.exoplayer2.ControlDispatcher;
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
|
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
|
||||||
import com.google.android.exoplayer2.source.MediaSource;
|
import com.google.android.exoplayer2.source.MediaSource;
|
||||||
@ -194,7 +195,12 @@ public final class TimelineQueueEditor
|
|||||||
// CommandReceiver implementation.
|
// CommandReceiver implementation.
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCommand(Player player, String command, Bundle extras, ResultReceiver cb) {
|
public boolean onCommand(
|
||||||
|
Player player,
|
||||||
|
ControlDispatcher controlDispatcher,
|
||||||
|
String command,
|
||||||
|
Bundle extras,
|
||||||
|
ResultReceiver cb) {
|
||||||
if (!COMMAND_MOVE_QUEUE_ITEM.equals(command)) {
|
if (!COMMAND_MOVE_QUEUE_ITEM.equals(command)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import android.support.v4.media.MediaDescriptionCompat;
|
|||||||
import android.support.v4.media.session.MediaSessionCompat;
|
import android.support.v4.media.session.MediaSessionCompat;
|
||||||
import android.support.v4.media.session.PlaybackStateCompat;
|
import android.support.v4.media.session.PlaybackStateCompat;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
|
import com.google.android.exoplayer2.ControlDispatcher;
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
import com.google.android.exoplayer2.Timeline;
|
import com.google.android.exoplayer2.Timeline;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
@ -124,7 +125,7 @@ public abstract class TimelineQueueNavigator implements MediaSessionConnector.Qu
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSkipToPrevious(Player player) {
|
public void onSkipToPrevious(Player player, ControlDispatcher controlDispatcher) {
|
||||||
Timeline timeline = player.getCurrentTimeline();
|
Timeline timeline = player.getCurrentTimeline();
|
||||||
if (timeline.isEmpty() || player.isPlayingAd()) {
|
if (timeline.isEmpty() || player.isPlayingAd()) {
|
||||||
return;
|
return;
|
||||||
@ -135,26 +136,26 @@ public abstract class TimelineQueueNavigator implements MediaSessionConnector.Qu
|
|||||||
if (previousWindowIndex != C.INDEX_UNSET
|
if (previousWindowIndex != C.INDEX_UNSET
|
||||||
&& (player.getCurrentPosition() <= MAX_POSITION_FOR_SEEK_TO_PREVIOUS
|
&& (player.getCurrentPosition() <= MAX_POSITION_FOR_SEEK_TO_PREVIOUS
|
||||||
|| (window.isDynamic && !window.isSeekable))) {
|
|| (window.isDynamic && !window.isSeekable))) {
|
||||||
player.seekTo(previousWindowIndex, C.TIME_UNSET);
|
controlDispatcher.dispatchSeekTo(player, previousWindowIndex, C.TIME_UNSET);
|
||||||
} else {
|
} else {
|
||||||
player.seekTo(0);
|
controlDispatcher.dispatchSeekTo(player, windowIndex, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSkipToQueueItem(Player player, long id) {
|
public void onSkipToQueueItem(Player player, ControlDispatcher controlDispatcher, long id) {
|
||||||
Timeline timeline = player.getCurrentTimeline();
|
Timeline timeline = player.getCurrentTimeline();
|
||||||
if (timeline.isEmpty() || player.isPlayingAd()) {
|
if (timeline.isEmpty() || player.isPlayingAd()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int windowIndex = (int) id;
|
int windowIndex = (int) id;
|
||||||
if (0 <= windowIndex && windowIndex < timeline.getWindowCount()) {
|
if (0 <= windowIndex && windowIndex < timeline.getWindowCount()) {
|
||||||
player.seekTo(windowIndex, C.TIME_UNSET);
|
controlDispatcher.dispatchSeekTo(player, windowIndex, C.TIME_UNSET);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSkipToNext(Player player) {
|
public void onSkipToNext(Player player, ControlDispatcher controlDispatcher) {
|
||||||
Timeline timeline = player.getCurrentTimeline();
|
Timeline timeline = player.getCurrentTimeline();
|
||||||
if (timeline.isEmpty() || player.isPlayingAd()) {
|
if (timeline.isEmpty() || player.isPlayingAd()) {
|
||||||
return;
|
return;
|
||||||
@ -162,16 +163,21 @@ public abstract class TimelineQueueNavigator implements MediaSessionConnector.Qu
|
|||||||
int windowIndex = player.getCurrentWindowIndex();
|
int windowIndex = player.getCurrentWindowIndex();
|
||||||
int nextWindowIndex = player.getNextWindowIndex();
|
int nextWindowIndex = player.getNextWindowIndex();
|
||||||
if (nextWindowIndex != C.INDEX_UNSET) {
|
if (nextWindowIndex != C.INDEX_UNSET) {
|
||||||
player.seekTo(nextWindowIndex, C.TIME_UNSET);
|
controlDispatcher.dispatchSeekTo(player, nextWindowIndex, C.TIME_UNSET);
|
||||||
} else if (timeline.getWindow(windowIndex, window).isDynamic) {
|
} else if (timeline.getWindow(windowIndex, window).isDynamic) {
|
||||||
player.seekTo(windowIndex, C.TIME_UNSET);
|
controlDispatcher.dispatchSeekTo(player, windowIndex, C.TIME_UNSET);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommandReceiver implementation.
|
// CommandReceiver implementation.
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCommand(Player player, String command, Bundle extras, ResultReceiver cb) {
|
public boolean onCommand(
|
||||||
|
Player player,
|
||||||
|
ControlDispatcher controlDispatcher,
|
||||||
|
String command,
|
||||||
|
Bundle extras,
|
||||||
|
ResultReceiver cb) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,4 +203,3 @@ public abstract class TimelineQueueNavigator implements MediaSessionConnector.Qu
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user