mirror of https://github.com/NekoX-Dev/NekoX.git
302 lines
9.8 KiB
Java
302 lines
9.8 KiB
Java
/*
|
|
* 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;
|
|
|
|
import android.os.Handler;
|
|
import androidx.annotation.Nullable;
|
|
import com.google.android.exoplayer2.util.Assertions;
|
|
|
|
/**
|
|
* Defines a player message which can be sent with a {@link Sender} and received by a {@link
|
|
* Target}.
|
|
*/
|
|
public final class PlayerMessage {
|
|
|
|
/** A target for messages. */
|
|
public interface Target {
|
|
|
|
/**
|
|
* Handles a message delivered to the target.
|
|
*
|
|
* @param messageType The message type.
|
|
* @param payload The message payload.
|
|
* @throws ExoPlaybackException If an error occurred whilst handling the message. Should only be
|
|
* thrown by targets that handle messages on the playback thread.
|
|
*/
|
|
void handleMessage(int messageType, @Nullable Object payload) throws ExoPlaybackException;
|
|
}
|
|
|
|
/** A sender for messages. */
|
|
public interface Sender {
|
|
|
|
/**
|
|
* Sends a message.
|
|
*
|
|
* @param message The message to be sent.
|
|
*/
|
|
void sendMessage(PlayerMessage message);
|
|
}
|
|
|
|
private final Target target;
|
|
private final Sender sender;
|
|
private final Timeline timeline;
|
|
|
|
private int type;
|
|
@Nullable private Object payload;
|
|
private Handler handler;
|
|
private int windowIndex;
|
|
private long positionMs;
|
|
private boolean deleteAfterDelivery;
|
|
private boolean isSent;
|
|
private boolean isDelivered;
|
|
private boolean isProcessed;
|
|
private boolean isCanceled;
|
|
|
|
/**
|
|
* Creates a new message.
|
|
*
|
|
* @param sender The {@link Sender} used to send the message.
|
|
* @param target The {@link Target} the message is sent to.
|
|
* @param timeline The timeline used when setting the position with {@link #setPosition(long)}. If
|
|
* set to {@link Timeline#EMPTY}, any position can be specified.
|
|
* @param defaultWindowIndex The default window index in the {@code timeline} when no other window
|
|
* index is specified.
|
|
* @param defaultHandler The default handler to send the message on when no other handler is
|
|
* specified.
|
|
*/
|
|
public PlayerMessage(
|
|
Sender sender,
|
|
Target target,
|
|
Timeline timeline,
|
|
int defaultWindowIndex,
|
|
Handler defaultHandler) {
|
|
this.sender = sender;
|
|
this.target = target;
|
|
this.timeline = timeline;
|
|
this.handler = defaultHandler;
|
|
this.windowIndex = defaultWindowIndex;
|
|
this.positionMs = C.TIME_UNSET;
|
|
this.deleteAfterDelivery = true;
|
|
}
|
|
|
|
/** Returns the timeline used for setting the position with {@link #setPosition(long)}. */
|
|
public Timeline getTimeline() {
|
|
return timeline;
|
|
}
|
|
|
|
/** Returns the target the message is sent to. */
|
|
public Target getTarget() {
|
|
return target;
|
|
}
|
|
|
|
/**
|
|
* Sets the message type forwarded to {@link Target#handleMessage(int, Object)}.
|
|
*
|
|
* @param messageType The message type.
|
|
* @return This message.
|
|
* @throws IllegalStateException If {@link #send()} has already been called.
|
|
*/
|
|
public PlayerMessage setType(int messageType) {
|
|
Assertions.checkState(!isSent);
|
|
this.type = messageType;
|
|
return this;
|
|
}
|
|
|
|
/** Returns the message type forwarded to {@link Target#handleMessage(int, Object)}. */
|
|
public int getType() {
|
|
return type;
|
|
}
|
|
|
|
/**
|
|
* Sets the message payload forwarded to {@link Target#handleMessage(int, Object)}.
|
|
*
|
|
* @param payload The message payload.
|
|
* @return This message.
|
|
* @throws IllegalStateException If {@link #send()} has already been called.
|
|
*/
|
|
public PlayerMessage setPayload(@Nullable Object payload) {
|
|
Assertions.checkState(!isSent);
|
|
this.payload = payload;
|
|
return this;
|
|
}
|
|
|
|
/** Returns the message payload forwarded to {@link Target#handleMessage(int, Object)}. */
|
|
@Nullable
|
|
public Object getPayload() {
|
|
return payload;
|
|
}
|
|
|
|
/**
|
|
* Sets the handler the message is delivered on.
|
|
*
|
|
* @param handler A {@link Handler}.
|
|
* @return This message.
|
|
* @throws IllegalStateException If {@link #send()} has already been called.
|
|
*/
|
|
public PlayerMessage setHandler(Handler handler) {
|
|
Assertions.checkState(!isSent);
|
|
this.handler = handler;
|
|
return this;
|
|
}
|
|
|
|
/** Returns the handler the message is delivered on. */
|
|
public Handler getHandler() {
|
|
return handler;
|
|
}
|
|
|
|
/**
|
|
* Returns position in window at {@link #getWindowIndex()} at which the message will be delivered,
|
|
* in milliseconds. If {@link C#TIME_UNSET}, the message will be delivered immediately.
|
|
*/
|
|
public long getPositionMs() {
|
|
return positionMs;
|
|
}
|
|
|
|
/**
|
|
* Sets a position in the current window at which the message will be delivered.
|
|
*
|
|
* @param positionMs The position in the current window at which the message will be sent, in
|
|
* milliseconds.
|
|
* @return This message.
|
|
* @throws IllegalStateException If {@link #send()} has already been called.
|
|
*/
|
|
public PlayerMessage setPosition(long positionMs) {
|
|
Assertions.checkState(!isSent);
|
|
this.positionMs = positionMs;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets a position in a window at which the message will be delivered.
|
|
*
|
|
* @param windowIndex The index of the window at which the message will be sent.
|
|
* @param positionMs The position in the window with index {@code windowIndex} at which the
|
|
* message will be sent, in milliseconds.
|
|
* @return This message.
|
|
* @throws IllegalSeekPositionException If the timeline returned by {@link #getTimeline()} is not
|
|
* empty and the provided window index is not within the bounds of the timeline.
|
|
* @throws IllegalStateException If {@link #send()} has already been called.
|
|
*/
|
|
public PlayerMessage setPosition(int windowIndex, long positionMs) {
|
|
Assertions.checkState(!isSent);
|
|
Assertions.checkArgument(positionMs != C.TIME_UNSET);
|
|
if (windowIndex < 0 || (!timeline.isEmpty() && windowIndex >= timeline.getWindowCount())) {
|
|
throw new IllegalSeekPositionException(timeline, windowIndex, positionMs);
|
|
}
|
|
this.windowIndex = windowIndex;
|
|
this.positionMs = positionMs;
|
|
return this;
|
|
}
|
|
|
|
/** Returns window index at which the message will be delivered. */
|
|
public int getWindowIndex() {
|
|
return windowIndex;
|
|
}
|
|
|
|
/**
|
|
* Sets whether the message will be deleted after delivery. If false, the message will be resent
|
|
* if playback reaches the specified position again. Only allowed to be false if a position is set
|
|
* with {@link #setPosition(long)}.
|
|
*
|
|
* @param deleteAfterDelivery Whether the message is deleted after delivery.
|
|
* @return This message.
|
|
* @throws IllegalStateException If {@link #send()} has already been called.
|
|
*/
|
|
public PlayerMessage setDeleteAfterDelivery(boolean deleteAfterDelivery) {
|
|
Assertions.checkState(!isSent);
|
|
this.deleteAfterDelivery = deleteAfterDelivery;
|
|
return this;
|
|
}
|
|
|
|
/** Returns whether the message will be deleted after delivery. */
|
|
public boolean getDeleteAfterDelivery() {
|
|
return deleteAfterDelivery;
|
|
}
|
|
|
|
/**
|
|
* Sends the message. If the target throws an {@link ExoPlaybackException} then it is propagated
|
|
* out of the player as an error using {@link
|
|
* Player.EventListener#onPlayerError(ExoPlaybackException)}.
|
|
*
|
|
* @return This message.
|
|
* @throws IllegalStateException If this message has already been sent.
|
|
*/
|
|
public PlayerMessage send() {
|
|
Assertions.checkState(!isSent);
|
|
if (positionMs == C.TIME_UNSET) {
|
|
Assertions.checkArgument(deleteAfterDelivery);
|
|
}
|
|
isSent = true;
|
|
sender.sendMessage(this);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Cancels the message delivery.
|
|
*
|
|
* @return This message.
|
|
* @throws IllegalStateException If this method is called before {@link #send()}.
|
|
*/
|
|
public synchronized PlayerMessage cancel() {
|
|
Assertions.checkState(isSent);
|
|
isCanceled = true;
|
|
markAsProcessed(/* isDelivered= */ false);
|
|
return this;
|
|
}
|
|
|
|
/** Returns whether the message delivery has been canceled. */
|
|
public synchronized boolean isCanceled() {
|
|
return isCanceled;
|
|
}
|
|
|
|
/**
|
|
* Blocks until after the message has been delivered or the player is no longer able to deliver
|
|
* the message.
|
|
*
|
|
* <p>Note that this method can't be called if the current thread is the same thread used by the
|
|
* message handler set with {@link #setHandler(Handler)} as it would cause a deadlock.
|
|
*
|
|
* @return Whether the message was delivered successfully.
|
|
* @throws IllegalStateException If this method is called before {@link #send()}.
|
|
* @throws IllegalStateException If this method is called on the same thread used by the message
|
|
* handler set with {@link #setHandler(Handler)}.
|
|
* @throws InterruptedException If the current thread is interrupted while waiting for the message
|
|
* to be delivered.
|
|
*/
|
|
public synchronized boolean blockUntilDelivered() throws InterruptedException {
|
|
Assertions.checkState(isSent);
|
|
Assertions.checkState(handler.getLooper().getThread() != Thread.currentThread());
|
|
while (!isProcessed) {
|
|
wait();
|
|
}
|
|
return isDelivered;
|
|
}
|
|
|
|
/**
|
|
* Marks the message as processed. Should only be called by a {@link Sender} and may be called
|
|
* multiple times.
|
|
*
|
|
* @param isDelivered Whether the message has been delivered to its target. The message is
|
|
* considered as being delivered when this method has been called with {@code isDelivered} set
|
|
* to true at least once.
|
|
*/
|
|
public synchronized void markAsProcessed(boolean isDelivered) {
|
|
this.isDelivered |= isDelivered;
|
|
isProcessed = true;
|
|
notifyAll();
|
|
}
|
|
}
|