diff --git a/.github/workflows/build-and-publish-javadoc.yml b/.github/workflows/build-and-publish-javadoc.yml index 49f220a0b3..89aa12b39b 100644 --- a/.github/workflows/build-and-publish-javadoc.yml +++ b/.github/workflows/build-and-publish-javadoc.yml @@ -12,7 +12,7 @@ jobs: # WebFX requires at least JDK 13 due to javac bugs in prior versions (otherwise JDK 11+ should be enough in theory) jdk-version: '19' repo-dir: . - target-javadoc-dir: ./target/site/apidocs + target-javadoc-dir: ./target/reports/apidocs web-push-repository-name: 'webfx-netlify' web-push-repository-owner: 'webfx-project' web-push-branch: 'javadoc' @@ -36,7 +36,7 @@ jobs: # Build JavaDoc - name: Build JavaDoc - run: mvn -B package javadoc:aggregate -Ddoclint=none -Dmaven.compiler.release=8 # Using Java 8 for now due to JavaDoc errors with modules + run: mvn -B javadoc:aggregate -Ddoclint=none -Dmaven.compiler.release=8 # Using Java 8 for now due to JavaDoc errors with modules - name: Publish JavaDoc to ${{ env.web-push-branch }} branch uses: cpina/github-action-push-to-another-repository@master diff --git a/webfx-kit/webfx-kit-javafxbase-emul/src/main/java/javafx/beans/binding/Bindings.java b/webfx-kit/webfx-kit-javafxbase-emul/src/main/java/javafx/beans/binding/Bindings.java index e564c91351..5c8981bd45 100644 --- a/webfx-kit/webfx-kit-javafxbase-emul/src/main/java/javafx/beans/binding/Bindings.java +++ b/webfx-kit/webfx-kit-javafxbase-emul/src/main/java/javafx/beans/binding/Bindings.java @@ -481,4 +481,43 @@ public static NumberBinding divide(final ObservableNumberValue op1, int op2) { return Bindings.divide(op1, IntegerConstant.valueOf(op2), op1); } + /** + * Creates a new {@link javafx.beans.binding.BooleanBinding} that holds {@code true} + * if a given {@link javafx.collections.ObservableList} is empty. + * + * @param op + * the {@code ObservableList} + * @param type of the {@code List} elements + * @return the new {@code BooleanBinding} + * @throws NullPointerException + * if the {@code ObservableList} is {@code null} + * @since JavaFX 2.1 + */ + public static BooleanBinding isEmpty(final ObservableList op) { + if (op == null) { + throw new NullPointerException("List cannot be null."); + } + + return new BooleanBinding() { + { + super.bind(op); + } + + @Override + public void dispose() { + super.unbind(op); + } + + @Override + protected boolean computeValue() { + return op.isEmpty(); + } + + @Override + public ObservableList getDependencies() { + return FXCollections.singletonObservableList(op); + } + }; + } + } diff --git a/webfx-kit/webfx-kit-javafxgraphics-emul/src/main/java/javafx/scene/text/Text.java b/webfx-kit/webfx-kit-javafxgraphics-emul/src/main/java/javafx/scene/text/Text.java index ddbc9aa172..d3821a3098 100644 --- a/webfx-kit/webfx-kit-javafxgraphics-emul/src/main/java/javafx/scene/text/Text.java +++ b/webfx-kit/webfx-kit-javafxgraphics-emul/src/main/java/javafx/scene/text/Text.java @@ -136,6 +136,32 @@ public final Property boundsTypeProperty() { return boundsType; } + /** + * Defines if each line of text should have a line through it. + * + * @return if each line of text should have a line through it + * @defaultValue false + */ + public final BooleanProperty strikethroughProperty() { + if (strikethroughProperty == null) + strikethroughProperty = new SimpleBooleanProperty(); + return strikethroughProperty; + } + + private BooleanProperty strikethroughProperty; + + public final void setStrikethrough(boolean value) { + strikethroughProperty().set(value); + } + + public final boolean isStrikethrough() { + if (strikethroughProperty == null) { + return false; + } + return strikethroughProperty.get(); + } + + @Override public double getBaselineOffset() { Font font = getFont(); diff --git a/webfx-kit/webfx-kit-javafxgraphics-gwt-j2cl/pom.xml b/webfx-kit/webfx-kit-javafxgraphics-gwt-j2cl/pom.xml index 775ec9e664..8853cd3c17 100644 --- a/webfx-kit/webfx-kit-javafxgraphics-gwt-j2cl/pom.xml +++ b/webfx-kit/webfx-kit-javafxgraphics-gwt-j2cl/pom.xml @@ -71,6 +71,12 @@ 0.1.0-SNAPSHOT + + dev.webfx + webfx-platform-uischeduler + 0.1.0-SNAPSHOT + + dev.webfx webfx-platform-useragent diff --git a/webfx-kit/webfx-kit-javafxgraphics-peers-base/src/main/java/dev/webfx/kit/mapper/peers/javafxgraphics/base/TextPeerBase.java b/webfx-kit/webfx-kit-javafxgraphics-peers-base/src/main/java/dev/webfx/kit/mapper/peers/javafxgraphics/base/TextPeerBase.java index 4e63fc06d5..62e09320fb 100644 --- a/webfx-kit/webfx-kit-javafxgraphics-peers-base/src/main/java/dev/webfx/kit/mapper/peers/javafxgraphics/base/TextPeerBase.java +++ b/webfx-kit/webfx-kit-javafxgraphics-peers-base/src/main/java/dev/webfx/kit/mapper/peers/javafxgraphics/base/TextPeerBase.java @@ -8,22 +8,23 @@ * @author Bruno Salmon */ public class TextPeerBase - , NM extends TextPeerMixin> + , NM extends TextPeerMixin> - extends ShapePeerBase { + extends ShapePeerBase { @Override public void bind(N t, SceneRequester sceneRequester) { super.bind(t, sceneRequester); requestUpdateOnPropertiesChange(sceneRequester - , t.textOriginProperty() - , t.xProperty() - , t.yProperty() - , t.wrappingWidthProperty() - , t.lineSpacingProperty() - , t.textAlignmentProperty() - , t.fontProperty() - , t.textProperty() + , t.textOriginProperty() + , t.xProperty() + , t.yProperty() + , t.wrappingWidthProperty() + , t.lineSpacingProperty() + , t.strikethroughProperty() + , t.textAlignmentProperty() + , t.fontProperty() + , t.textProperty() ); } @@ -31,14 +32,15 @@ public void bind(N t, SceneRequester sceneRequester) { public boolean updateProperty(ObservableValue changedProperty) { Text ts = node; return super.updateProperty(changedProperty) - || updateProperty(ts.textProperty(), changedProperty, mixin::updateText) - || updateProperty(ts.xProperty(), changedProperty, p -> mixin.updateX(p.doubleValue())) - || updateProperty(ts.yProperty(), changedProperty, p -> mixin.updateY(p.doubleValue())) - || updateProperty(ts.wrappingWidthProperty(), changedProperty, p -> mixin.updateWrappingWidth(p.doubleValue())) - || updateProperty(ts.lineSpacingProperty(), changedProperty, mixin::updateLineSpacing) - || updateProperty(ts.textAlignmentProperty(), changedProperty, mixin::updateTextAlignment) - || updateProperty(ts.textOriginProperty(), changedProperty, mixin::updateTextOrigin) - || updateProperty(ts.fontProperty(), changedProperty, mixin::updateFont) - ; + || updateProperty(ts.textProperty(), changedProperty, mixin::updateText) + || updateProperty(ts.xProperty(), changedProperty, p -> mixin.updateX(p.doubleValue())) + || updateProperty(ts.yProperty(), changedProperty, p -> mixin.updateY(p.doubleValue())) + || updateProperty(ts.wrappingWidthProperty(), changedProperty, p -> mixin.updateWrappingWidth(p.doubleValue())) + || updateProperty(ts.lineSpacingProperty(), changedProperty, mixin::updateLineSpacing) + || updateProperty(ts.strikethroughProperty(), changedProperty, mixin::updateStrikethrough) + || updateProperty(ts.textAlignmentProperty(), changedProperty, mixin::updateTextAlignment) + || updateProperty(ts.textOriginProperty(), changedProperty, mixin::updateTextOrigin) + || updateProperty(ts.fontProperty(), changedProperty, mixin::updateFont) + ; } } diff --git a/webfx-kit/webfx-kit-javafxgraphics-peers-base/src/main/java/dev/webfx/kit/mapper/peers/javafxgraphics/base/TextPeerMixin.java b/webfx-kit/webfx-kit-javafxgraphics-peers-base/src/main/java/dev/webfx/kit/mapper/peers/javafxgraphics/base/TextPeerMixin.java index c1e31e2c88..eca6c9d72f 100644 --- a/webfx-kit/webfx-kit-javafxgraphics-peers-base/src/main/java/dev/webfx/kit/mapper/peers/javafxgraphics/base/TextPeerMixin.java +++ b/webfx-kit/webfx-kit-javafxgraphics-peers-base/src/main/java/dev/webfx/kit/mapper/peers/javafxgraphics/base/TextPeerMixin.java @@ -22,8 +22,11 @@ public interface TextPeerMixin void updateY(Double y); void updateWrappingWidth(Double wrappingWidth); + void updateLineSpacing(Number lineSpacing); + void updateStrikethrough(Boolean strikethrough); + void updateTextAlignment(TextAlignment textAlignment); void updateFont(Font font); diff --git a/webfx-kit/webfx-kit-javafxgraphics-peers-gwt-j2cl/src/main/java/dev/webfx/kit/mapper/peers/javafxgraphics/gwtj2cl/html/HtmlSvgTextPeer.java b/webfx-kit/webfx-kit-javafxgraphics-peers-gwt-j2cl/src/main/java/dev/webfx/kit/mapper/peers/javafxgraphics/gwtj2cl/html/HtmlSvgTextPeer.java index adb17e33f5..61604343bc 100644 --- a/webfx-kit/webfx-kit-javafxgraphics-peers-gwt-j2cl/src/main/java/dev/webfx/kit/mapper/peers/javafxgraphics/gwtj2cl/html/HtmlSvgTextPeer.java +++ b/webfx-kit/webfx-kit-javafxgraphics-peers-gwt-j2cl/src/main/java/dev/webfx/kit/mapper/peers/javafxgraphics/gwtj2cl/html/HtmlSvgTextPeer.java @@ -172,6 +172,12 @@ public void updateLineSpacing(Number lineSpacing) { updateViewBox(); } + @Override + public void updateStrikethrough(Boolean strikethrough) { + svgTextPeer.updateStrikethrough(strikethrough); + updateViewBox(); + } + @Override public void updateTextAlignment(TextAlignment textAlignment) { svgTextPeer.updateTextAlignment(textAlignment); diff --git a/webfx-kit/webfx-kit-javafxgraphics-peers-gwt-j2cl/src/main/java/dev/webfx/kit/mapper/peers/javafxgraphics/gwtj2cl/html/HtmlTextPeer.java b/webfx-kit/webfx-kit-javafxgraphics-peers-gwt-j2cl/src/main/java/dev/webfx/kit/mapper/peers/javafxgraphics/gwtj2cl/html/HtmlTextPeer.java index 98daa2f3aa..9af17480bb 100644 --- a/webfx-kit/webfx-kit-javafxgraphics-peers-gwt-j2cl/src/main/java/dev/webfx/kit/mapper/peers/javafxgraphics/gwtj2cl/html/HtmlTextPeer.java +++ b/webfx-kit/webfx-kit-javafxgraphics-peers-gwt-j2cl/src/main/java/dev/webfx/kit/mapper/peers/javafxgraphics/gwtj2cl/html/HtmlTextPeer.java @@ -172,6 +172,11 @@ private void updateWrappingWithAndLineSpacing(double wrappingWidth, double lineS updateYInAnimationFrame(true); } + @Override + public void updateStrikethrough(Boolean strikethrough) { + setElementStyleAttribute("text-decoration", Boolean.TRUE.equals(strikethrough) ? "line-through" : null); + } + @Override public void updateTextAlignment(TextAlignment textAlignment) { setElementStyleAttribute("text-align", toCssTextAlignment(textAlignment)); diff --git a/webfx-kit/webfx-kit-javafxgraphics-peers-gwt-j2cl/src/main/java/dev/webfx/kit/mapper/peers/javafxgraphics/gwtj2cl/svg/SvgTextPeer.java b/webfx-kit/webfx-kit-javafxgraphics-peers-gwt-j2cl/src/main/java/dev/webfx/kit/mapper/peers/javafxgraphics/gwtj2cl/svg/SvgTextPeer.java index b0962bfae6..6237aa06b5 100644 --- a/webfx-kit/webfx-kit-javafxgraphics-peers-gwt-j2cl/src/main/java/dev/webfx/kit/mapper/peers/javafxgraphics/gwtj2cl/svg/SvgTextPeer.java +++ b/webfx-kit/webfx-kit-javafxgraphics-peers-gwt-j2cl/src/main/java/dev/webfx/kit/mapper/peers/javafxgraphics/gwtj2cl/svg/SvgTextPeer.java @@ -78,6 +78,11 @@ public void updateLineSpacing(Number lineSpacing) { // TODO: implement SVG line spacing } + @Override + public void updateStrikethrough(Boolean strikethrough) { + // TODO: implement SVG strikethrough + } + @Override public void updateTextAlignment(TextAlignment textAlignment) { setElementAttribute("text-anchor", textAlignmentToSvgTextAnchor(textAlignment)); diff --git a/webfx-kit/webfx-kit-javafxmedia-emul/src/main/java/dev/webfx/kit/mapper/peers/javafxmedia/MediaPlayerPeer.java b/webfx-kit/webfx-kit-javafxmedia-emul/src/main/java/dev/webfx/kit/mapper/peers/javafxmedia/MediaPlayerPeer.java index 0644eaa874..818f438c91 100644 --- a/webfx-kit/webfx-kit-javafxmedia-emul/src/main/java/dev/webfx/kit/mapper/peers/javafxmedia/MediaPlayerPeer.java +++ b/webfx-kit/webfx-kit-javafxmedia-emul/src/main/java/dev/webfx/kit/mapper/peers/javafxmedia/MediaPlayerPeer.java @@ -33,10 +33,6 @@ public interface MediaPlayerPeer { void setAudioSpectrumListener(AudioSpectrumListener listener); - void setOnEndOfMedia(Runnable onEndOfMedia); - - void setOnPlaying(Runnable onPlaying); - void seek(Duration duration); } diff --git a/webfx-kit/webfx-kit-javafxmedia-emul/src/main/java/javafx/scene/media/MediaPlayer.java b/webfx-kit/webfx-kit-javafxmedia-emul/src/main/java/javafx/scene/media/MediaPlayer.java index db0b1c33ae..853062eb6f 100644 --- a/webfx-kit/webfx-kit-javafxmedia-emul/src/main/java/javafx/scene/media/MediaPlayer.java +++ b/webfx-kit/webfx-kit-javafxmedia-emul/src/main/java/javafx/scene/media/MediaPlayer.java @@ -211,14 +211,6 @@ public final void setAudioSpectrumListener(AudioSpectrumListener listener) { peer.setAudioSpectrumListener(listener); } - public final void setOnEndOfMedia(Runnable onEndOfMedia) { - peer.setOnEndOfMedia(onEndOfMedia); - } - - public final void setOnPlaying(Runnable onPlaying) { - peer.setOnPlaying(onPlaying); - } - public ObjectProperty currentTimeProperty() { return peer.mediaPlayerCurrentTimeProperty(); } @@ -229,14 +221,262 @@ public void seek(Duration duration) { public ReadOnlyObjectProperty statusProperty() { if (statusProperty == null) - statusProperty = new SimpleObjectProperty<>(Status.UNKNOWN); + statusProperty = new SimpleObjectProperty<>(Status.UNKNOWN) { + @Override + protected void invalidated() { + Runnable statusRunnable = getStatusRunnable(); + if (statusRunnable != null) + statusRunnable.run(); + } + }; return statusProperty; } + private Runnable getStatusRunnable() { + switch (getStatus()) { + case READY: return getOnReady(); + case PAUSED: return getOnPaused(); + case PLAYING: return getOnPlaying(); + case STOPPED: return getOnStopped(); + case STALLED: return getOnStalled(); + case HALTED: return getOnHalted(); + } + // Note: onRepeat & endOfMedia are not called by this class, this responsibility is delegated to the peer + return null; + } + public Status getStatus() { return statusProperty == null ? Status.UNKNOWN : statusProperty.get(); } + private ObjectProperty onEndOfMedia; + + /** + * Sets the end of media event handler. + * @param value the event handler or null. + */ + public final void setOnEndOfMedia(Runnable value) { + onEndOfMediaProperty().set(value); + } + + /** + * Retrieves the end of media event handler. + * @return the event handler or null. + */ + public final Runnable getOnEndOfMedia() { + return onEndOfMedia == null ? null : onEndOfMedia.get(); + } + + public ObjectProperty onEndOfMediaProperty() { + if (onEndOfMedia == null) { + onEndOfMedia = new SimpleObjectProperty(this, "onEndOfMedia"); + } + return onEndOfMedia; + } + + /** + * Event handler invoked when the status changes to + * READY. + */ + private ObjectProperty onReady; // Player is ready and media has prerolled + + /** + * Sets the {@link Status#READY} event handler. + * @param value the event handler or null. + */ + public final void setOnReady(Runnable value) { + onReadyProperty().set(value); + } + + /** + * Retrieves the {@link Status#READY} event handler. + * @return the event handler or null. + */ + public final Runnable getOnReady() { + return onReady == null ? null : onReady.get(); + } + + public ObjectProperty onReadyProperty() { + if (onReady == null) { + onReady = new SimpleObjectProperty(this, "onReady"); + } + return onReady; + } + + /** + * Event handler invoked when the status changes to + * PLAYING. + */ + private ObjectProperty onPlaying; // Media has reached its end. + + /** + * Sets the {@link Status#PLAYING} event handler. + * @param value the event handler or null. + */ + public final void setOnPlaying(Runnable value) { + onPlayingProperty().set(value); + } + + /** + * Retrieves the {@link Status#PLAYING} event handler. + * @return the event handler or null. + */ + public final Runnable getOnPlaying() { + return onPlaying == null ? null : onPlaying.get(); + } + + public ObjectProperty onPlayingProperty() { + if (onPlaying == null) { + onPlaying = new SimpleObjectProperty(this, "onPlaying"); + } + return onPlaying; + } + + /** + * Event handler invoked when the status changes to PAUSED. + */ + private ObjectProperty onPaused; // Media has reached its end. + + /** + * Sets the {@link Status#PAUSED} event handler. + * @param value the event handler or null. + */ + public final void setOnPaused(Runnable value) { + onPausedProperty().set(value); + } + + /** + * Retrieves the {@link Status#PAUSED} event handler. + * @return the event handler or null. + */ + public final Runnable getOnPaused() { + return onPaused == null ? null : onPaused.get(); + } + + public ObjectProperty onPausedProperty() { + if (onPaused == null) { + onPaused = new SimpleObjectProperty(this, "onPaused"); + } + return onPaused; + } + + /** + * Event handler invoked when the status changes to + * STOPPED. + */ + private ObjectProperty onStopped; // Media has reached its end. + + /** + * Sets the {@link Status#STOPPED} event handler. + * @param value the event handler or null. + */ + public final void setOnStopped(Runnable value) { + onStoppedProperty().set(value); + } + + /** + * Retrieves the {@link Status#STOPPED} event handler. + * @return the event handler or null. + */ + public final Runnable getOnStopped() { + return onStopped == null ? null : onStopped.get(); + } + + public ObjectProperty onStoppedProperty() { + if (onStopped == null) { + onStopped = new SimpleObjectProperty(this, "onStopped"); + } + return onStopped; + } + + /** + * Event handler invoked when the status changes to HALTED. + */ + private ObjectProperty onHalted; // Media caught an irrecoverable error. + + /** + * Sets the {@link Status#HALTED} event handler. + * @param value the event handler or null. + */ + public final void setOnHalted(Runnable value) { + onHaltedProperty().set(value); + } + + /** + * Retrieves the {@link Status#HALTED} event handler. + * @return the event handler or null. + */ + public final Runnable getOnHalted() { + return onHalted == null ? null : onHalted.get(); + } + + public ObjectProperty onHaltedProperty() { + if (onHalted == null) { + onHalted = new SimpleObjectProperty(this, "onHalted"); + } + return onHalted; + } + /** + * Event handler invoked when the player currentTime reaches + * stopTime and will be repeating. This callback is made + * prior to seeking back to startTime. + * + * @see cycleCount + */ + private ObjectProperty onRepeat; + + /** + * Sets the repeat event handler. + * @param value the event handler or null. + */ + public final void setOnRepeat(Runnable value) { + onRepeatProperty().set(value); + } + + /** + * Retrieves the repeat event handler. + * @return the event handler or null. + */ + public final Runnable getOnRepeat() { + return onRepeat == null ? null : onRepeat.get(); + } + + public ObjectProperty onRepeatProperty() { + if (onRepeat == null) { + onRepeat = new SimpleObjectProperty(this, "onRepeat"); + } + return onRepeat; + } + + /** + * Event handler invoked when the status changes to + * STALLED. + */ + private ObjectProperty onStalled; + + /** + * Sets the {@link Status#STALLED} event handler. + * @param value the event handler or null. + */ + public final void setOnStalled(Runnable value) { + onStalledProperty().set(value); + } + + /** + * Retrieves the {@link Status#STALLED} event handler. + * @return the event handler or null. + */ + public final Runnable getOnStalled() { + return onStalled == null ? null : onStalled.get(); + } + + public ObjectProperty onStalledProperty() { + if (onStalled == null) { + onStalled = new SimpleObjectProperty(this, "onStalled"); + } + return onStalled; + } + // For internal WebFX usage only public void setStatus(Status status) { diff --git a/webfx-kit/webfx-kit-javafxmedia-peers-gwt-j2cl/src/main/java/dev/webfx/kit/mapper/peers/javafxmedia/spi/gwtj2cl/GwtJ2clMediaPlayerPeer.java b/webfx-kit/webfx-kit-javafxmedia-peers-gwt-j2cl/src/main/java/dev/webfx/kit/mapper/peers/javafxmedia/spi/gwtj2cl/GwtJ2clMediaPlayerPeer.java index 79989dd238..d7ca7b3721 100644 --- a/webfx-kit/webfx-kit-javafxmedia-peers-gwt-j2cl/src/main/java/dev/webfx/kit/mapper/peers/javafxmedia/spi/gwtj2cl/GwtJ2clMediaPlayerPeer.java +++ b/webfx-kit/webfx-kit-javafxmedia-peers-gwt-j2cl/src/main/java/dev/webfx/kit/mapper/peers/javafxmedia/spi/gwtj2cl/GwtJ2clMediaPlayerPeer.java @@ -66,7 +66,6 @@ public final class GwtJ2clMediaPlayerPeer implements MediaPlayerPeer { private float[] magnitudes, phases; private int cycleCount = 1; private int playedCycleCount = 0; - private Runnable onEndOfMedia, onPlaying; public GwtJ2clMediaPlayerPeer(MediaPlayer mediaPlayer, boolean audioClip) { this.mediaPlayer = mediaPlayer; @@ -145,8 +144,9 @@ private double readAndSetMediaDuration(boolean setMediaPlayerStatusToReady) { media.setDuration(duration); } // if requested, we set the player status to READY, checking however its status was UNKNOWN (initial state) - if (setMediaPlayerStatusToReady && mediaPlayer.getStatus() == MediaPlayer.Status.UNKNOWN) + if (setMediaPlayerStatusToReady && mediaPlayer.getStatus() == MediaPlayer.Status.UNKNOWN) { mediaPlayer.setStatus(MediaPlayer.Status.READY); + } return durationSeconds; } @@ -513,21 +513,9 @@ public void setAudioSpectrumListener(AudioSpectrumListener listener) { this.listener = listener; } - @Override - public void setOnEndOfMedia(Runnable onEndOfMedia) { - this.onEndOfMedia = onEndOfMedia; - } - - @Override - public void setOnPlaying(Runnable onPlaying) { - this.onPlaying = onPlaying; - } - private void doOnPlaying() { if (!audioClip) mediaPlayer.setStatus(MediaPlayer.Status.PLAYING); - if (onPlaying != null) - onPlaying.run(); } private void doOnEnded() { @@ -538,16 +526,22 @@ private void doOnEnded() { return; } playedCycleCount++; - if (playedCycleCount < cycleCount) + if (playedCycleCount < cycleCount) { playOnceCycle(); - else { + if (!audioClip) { + Runnable onRepeat = mediaPlayer.getOnRepeat(); + if (onRepeat != null) + onRepeat.run(); + } + } else { if (!audioClip) { stopMediaPlayerCurrentTimePropertyPeriodicSyncer(); mediaPlayer.setStatus(MediaPlayer.Status.STOPPED); + Runnable onEndOfMedia = mediaPlayer.getOnEndOfMedia(); + if (onEndOfMedia != null) + onEndOfMedia.run(); } bufferSourceStartOffset = 0; - if (onEndOfMedia != null) - onEndOfMedia.run(); } }