Fix seeking voice notes that do not have waveforms.

This commit is contained in:
Alex Hart 2020-10-16 15:37:26 -03:00 committed by GitHub
parent 082d9e852c
commit bd3b14a27f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 56 additions and 50 deletions

View File

@ -57,8 +57,8 @@ public interface BindableConversationItem extends Unbindable {
void onRegisterVoiceNoteCallbacks(@NonNull Observer<VoiceNotePlaybackState> onPlaybackStartObserver); void onRegisterVoiceNoteCallbacks(@NonNull Observer<VoiceNotePlaybackState> onPlaybackStartObserver);
void onUnregisterVoiceNoteCallbacks(@NonNull Observer<VoiceNotePlaybackState> onPlaybackStartObserver); void onUnregisterVoiceNoteCallbacks(@NonNull Observer<VoiceNotePlaybackState> onPlaybackStartObserver);
void onVoiceNotePause(@NonNull Uri uri); void onVoiceNotePause(@NonNull Uri uri);
void onVoiceNotePlay(@NonNull Uri uri, long messageId, long position); void onVoiceNotePlay(@NonNull Uri uri, long messageId, double position);
void onVoiceNoteSeekTo(@NonNull Uri uri, long position); void onVoiceNoteSeekTo(@NonNull Uri uri, double position);
/** @return true if handled, false if you want to let the normal url handling continue */ /** @return true if handled, false if you want to let the normal url handling continue */
boolean onUrlClicked(@NonNull String url); boolean onUrlClicked(@NonNull String url);

View File

@ -192,7 +192,7 @@ public final class AudioView extends FrameLayout {
private void onPlaybackState(@NonNull VoiceNotePlaybackState voiceNotePlaybackState) { private void onPlaybackState(@NonNull VoiceNotePlaybackState voiceNotePlaybackState) {
onStart(voiceNotePlaybackState.getUri(), voiceNotePlaybackState.isAutoReset()); onStart(voiceNotePlaybackState.getUri(), voiceNotePlaybackState.isAutoReset());
onProgress(voiceNotePlaybackState.getUri(), onProgress(voiceNotePlaybackState.getUri(),
(double) voiceNotePlaybackState.getPlayheadPositionMillis() / durationMillis, (double) voiceNotePlaybackState.getPlayheadPositionMillis() / voiceNotePlaybackState.getTrackDuration(),
voiceNotePlaybackState.getPlayheadPositionMillis()); voiceNotePlaybackState.getPlayheadPositionMillis());
} }
@ -258,7 +258,7 @@ public final class AudioView extends FrameLayout {
} }
private void onProgress(@NonNull Uri uri, double progress, long millis) { private void onProgress(@NonNull Uri uri, double progress, long millis) {
if (!Objects.equals(uri, audioSlide.getUri())) { if (audioSlide == null || !Objects.equals(uri, audioSlide.getUri())) {
return; return;
} }
@ -358,7 +358,7 @@ public final class AudioView extends FrameLayout {
if (callbacks != null) { if (callbacks != null) {
if (lottieDirection == REVERSE) { if (lottieDirection == REVERSE) {
callbacks.onPlay(audioSlide.getUri(), getPosition()); callbacks.onPlay(audioSlide.getUri(), getProgress());
} else { } else {
callbacks.onPause(audioSlide.getUri()); callbacks.onPause(audioSlide.getUri());
} }
@ -371,10 +371,6 @@ public final class AudioView extends FrameLayout {
updateProgress(0, 0); updateProgress(0, 0);
} }
private long getPosition() {
return (long) (getProgress() * durationMillis);
}
private class DownloadClickedListener implements View.OnClickListener { private class DownloadClickedListener implements View.OnClickListener {
private final @NonNull AudioSlide slide; private final @NonNull AudioSlide slide;
@ -394,10 +390,6 @@ public final class AudioView extends FrameLayout {
@Override @Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser && durationMillis > 0) {
float progressFloat = progress / (float) seekBar.getMax();
updateProgress(progressFloat, (long) (durationMillis * progressFloat));
}
} }
@Override @Override
@ -418,7 +410,7 @@ public final class AudioView extends FrameLayout {
if (callbacks != null) { if (callbacks != null) {
if (wasPlaying) { if (wasPlaying) {
callbacks.onSeekTo(audioSlide.getUri(), getPosition()); callbacks.onSeekTo(audioSlide.getUri(), getProgress());
} }
} }
} }
@ -439,9 +431,9 @@ public final class AudioView extends FrameLayout {
} }
public interface Callbacks { public interface Callbacks {
void onPlay(@NonNull Uri audioUri, long position); void onPlay(@NonNull Uri audioUri, double progress);
void onPause(@NonNull Uri audioUri); void onPause(@NonNull Uri audioUri);
void onSeekTo(@NonNull Uri audioUri, long position); void onSeekTo(@NonNull Uri audioUri, double progress);
void onStopAndReset(@NonNull Uri audioUri); void onStopAndReset(@NonNull Uri audioUri);
} }
} }

View File

@ -35,7 +35,7 @@ import java.util.Objects;
public class VoiceNoteMediaController implements DefaultLifecycleObserver { public class VoiceNoteMediaController implements DefaultLifecycleObserver {
public static final String EXTRA_MESSAGE_ID = "voice.note.message_id"; public static final String EXTRA_MESSAGE_ID = "voice.note.message_id";
public static final String EXTRA_PLAYHEAD = "voice.note.playhead"; public static final String EXTRA_PROGRESS = "voice.note.playhead";
public static final String EXTRA_PLAY_SINGLE = "voice.note.play.single"; public static final String EXTRA_PLAY_SINGLE = "voice.note.play.single";
private static final String TAG = Log.tag(VoiceNoteMediaController.class); private static final String TAG = Log.tag(VoiceNoteMediaController.class);
@ -97,12 +97,12 @@ public class VoiceNoteMediaController implements DefaultLifecycleObserver {
} }
public void startConsecutivePlayback(@NonNull Uri audioSlideUri, long messageId, long position) { public void startConsecutivePlayback(@NonNull Uri audioSlideUri, long messageId, double progress) {
startPlayback(audioSlideUri, messageId, position, false); startPlayback(audioSlideUri, messageId, progress, false);
} }
public void startSinglePlayback(@NonNull Uri audioSlideUri, long messageId, long position) { public void startSinglePlayback(@NonNull Uri audioSlideUri, long messageId, double progress) {
startPlayback(audioSlideUri, messageId, position, true); startPlayback(audioSlideUri, messageId, progress, true);
} }
/** /**
@ -111,17 +111,19 @@ public class VoiceNoteMediaController implements DefaultLifecycleObserver {
* *
* @param audioSlideUri The Uri of the desired audio slide * @param audioSlideUri The Uri of the desired audio slide
* @param messageId The Message id of the given audio slide * @param messageId The Message id of the given audio slide
* @param position The desired position in milliseconds at which to start playback. * @param progress The desired progress % to seek to.
* @param singlePlayback The player will only play back the specified Uri, and not build a playlist. * @param singlePlayback The player will only play back the specified Uri, and not build a playlist.
*/ */
private void startPlayback(@NonNull Uri audioSlideUri, long messageId, long position, boolean singlePlayback) { private void startPlayback(@NonNull Uri audioSlideUri, long messageId, double progress, boolean singlePlayback) {
if (isCurrentTrack(audioSlideUri)) { if (isCurrentTrack(audioSlideUri)) {
getMediaController().getTransportControls().seekTo(position); long duration = getMediaController().getMetadata().getLong(MediaMetadataCompat.METADATA_KEY_DURATION);
getMediaController().getTransportControls().seekTo((long) (duration * progress));
getMediaController().getTransportControls().play(); getMediaController().getTransportControls().play();
} else { } else {
Bundle extras = new Bundle(); Bundle extras = new Bundle();
extras.putLong(EXTRA_MESSAGE_ID, messageId); extras.putLong(EXTRA_MESSAGE_ID, messageId);
extras.putLong(EXTRA_PLAYHEAD, position); extras.putDouble(EXTRA_PROGRESS, progress);
extras.putBoolean(EXTRA_PLAY_SINGLE, singlePlayback); extras.putBoolean(EXTRA_PLAY_SINGLE, singlePlayback);
getMediaController().getTransportControls().playFromUri(audioSlideUri, extras); getMediaController().getTransportControls().playFromUri(audioSlideUri, extras);
@ -144,12 +146,14 @@ public class VoiceNoteMediaController implements DefaultLifecycleObserver {
* is ignored if the given audio slide is not currently playing. * is ignored if the given audio slide is not currently playing.
* *
* @param audioSlideUri The Uri of the audio slide to seek. * @param audioSlideUri The Uri of the audio slide to seek.
* @param position The position in milliseconds to seek to. * @param progress The progress percentage to seek to.
*/ */
public void seekToPosition(@NonNull Uri audioSlideUri, long position) { public void seekToPosition(@NonNull Uri audioSlideUri, double progress) {
if (isCurrentTrack(audioSlideUri)) { if (isCurrentTrack(audioSlideUri)) {
long duration = getMediaController().getMetadata().getLong(MediaMetadataCompat.METADATA_KEY_DURATION);
getMediaController().getTransportControls().pause(); getMediaController().getTransportControls().pause();
getMediaController().getTransportControls().seekTo(position); getMediaController().getTransportControls().seekTo((long) (duration * progress));
getMediaController().getTransportControls().play(); getMediaController().getTransportControls().play();
} }
} }
@ -226,6 +230,7 @@ public class VoiceNoteMediaController implements DefaultLifecycleObserver {
voiceNotePlaybackState.postValue(new VoiceNotePlaybackState(mediaUri, voiceNotePlaybackState.postValue(new VoiceNotePlaybackState(mediaUri,
mediaController.getPlaybackState().getPosition(), mediaController.getPlaybackState().getPosition(),
mediaMetadataCompat.getLong(MediaMetadataCompat.METADATA_KEY_DURATION),
autoReset)); autoReset));
sendEmptyMessageDelayed(0, 50); sendEmptyMessageDelayed(0, 50);

View File

@ -89,7 +89,7 @@ final class VoiceNotePlaybackPreparer implements MediaSessionConnector.PlaybackP
@Override @Override
public void onPrepareFromUri(final Uri uri, Bundle extras) { public void onPrepareFromUri(final Uri uri, Bundle extras) {
long messageId = extras.getLong(VoiceNoteMediaController.EXTRA_MESSAGE_ID); long messageId = extras.getLong(VoiceNoteMediaController.EXTRA_MESSAGE_ID);
long position = extras.getLong(VoiceNoteMediaController.EXTRA_PLAYHEAD, 0); double progress = extras.getDouble(VoiceNoteMediaController.EXTRA_PROGRESS, 0);
boolean singlePlayback = extras.getBoolean(VoiceNoteMediaController.EXTRA_PLAY_SINGLE, false); boolean singlePlayback = extras.getBoolean(VoiceNoteMediaController.EXTRA_PLAY_SINGLE, false);
canLoadMore = false; canLoadMore = false;
@ -116,7 +116,7 @@ final class VoiceNotePlaybackPreparer implements MediaSessionConnector.PlaybackP
@Override @Override
public void onTimelineChanged(Timeline timeline, @Nullable Object manifest, int reason) { public void onTimelineChanged(Timeline timeline, @Nullable Object manifest, int reason) {
if (timeline.getWindowCount() >= window) { if (timeline.getWindowCount() >= window) {
player.seekTo(window, position); player.seekTo(window, (long) (player.getDuration() * progress));
player.removeListener(this); player.removeListener(this);
} }
} }

View File

@ -9,15 +9,17 @@ import androidx.annotation.NonNull;
*/ */
public class VoiceNotePlaybackState { public class VoiceNotePlaybackState {
public static final VoiceNotePlaybackState NONE = new VoiceNotePlaybackState(Uri.EMPTY, 0, false); public static final VoiceNotePlaybackState NONE = new VoiceNotePlaybackState(Uri.EMPTY, 0, 0, false);
private final Uri uri; private final Uri uri;
private final long playheadPositionMillis; private final long playheadPositionMillis;
private final long trackDuration;
private final boolean autoReset; private final boolean autoReset;
public VoiceNotePlaybackState(@NonNull Uri uri, long playheadPositionMillis, boolean autoReset) { public VoiceNotePlaybackState(@NonNull Uri uri, long playheadPositionMillis, long trackDuration, boolean autoReset) {
this.uri = uri; this.uri = uri;
this.playheadPositionMillis = playheadPositionMillis; this.playheadPositionMillis = playheadPositionMillis;
this.trackDuration = trackDuration;
this.autoReset = autoReset; this.autoReset = autoReset;
} }
@ -35,6 +37,13 @@ public class VoiceNotePlaybackState {
return playheadPositionMillis; return playheadPositionMillis;
} }
/**
* @return The track duration in ms
*/
public long getTrackDuration() {
return trackDuration;
}
/** /**
* @return true if we should reset the currently playing clip. * @return true if we should reset the currently playing clip.
*/ */

View File

@ -1377,13 +1377,13 @@ public class ConversationFragment extends LoggingFragment {
} }
@Override @Override
public void onVoiceNotePlay(@NonNull Uri uri, long messageId, long position) { public void onVoiceNotePlay(@NonNull Uri uri, long messageId, double progress) {
voiceNoteMediaController.startConsecutivePlayback(uri, messageId, position); voiceNoteMediaController.startConsecutivePlayback(uri, messageId, progress);
} }
@Override @Override
public void onVoiceNoteSeekTo(@NonNull Uri uri, long position) { public void onVoiceNoteSeekTo(@NonNull Uri uri, double progress) {
voiceNoteMediaController.seekToPosition(uri, position); voiceNoteMediaController.seekToPosition(uri, progress);
} }
@Override @Override

View File

@ -1548,10 +1548,10 @@ public class ConversationItem extends LinearLayout implements BindableConversati
private final class AudioViewCallbacks implements AudioView.Callbacks { private final class AudioViewCallbacks implements AudioView.Callbacks {
@Override @Override
public void onPlay(@NonNull Uri audioUri, long position) { public void onPlay(@NonNull Uri audioUri, double progress) {
if (eventListener == null) return; if (eventListener == null) return;
eventListener.onVoiceNotePlay(audioUri, messageRecord.getId(), position); eventListener.onVoiceNotePlay(audioUri, messageRecord.getId(), progress);
} }
@Override @Override
@ -1562,10 +1562,10 @@ public class ConversationItem extends LinearLayout implements BindableConversati
} }
@Override @Override
public void onSeekTo(@NonNull Uri audioUri, long position) { public void onSeekTo(@NonNull Uri audioUri, double progress) {
if (eventListener == null) return; if (eventListener == null) return;
eventListener.onVoiceNoteSeekTo(audioUri, position); eventListener.onVoiceNoteSeekTo(audioUri, progress);
} }
@Override @Override

View File

@ -502,8 +502,8 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
} }
@Override @Override
public void onPlay(@NonNull Uri audioUri, long position) { public void onPlay(@NonNull Uri audioUri, double progress) {
audioItemListener.onPlay(audioUri, position, messageId); audioItemListener.onPlay(audioUri, progress, messageId);
} }
@Override @Override
@ -512,8 +512,8 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
} }
@Override @Override
public void onSeekTo(@NonNull Uri audioUri, long position) { public void onSeekTo(@NonNull Uri audioUri, double progress) {
audioItemListener.onSeekTo(audioUri, position); audioItemListener.onSeekTo(audioUri, progress);
} }
@Override @Override
@ -528,9 +528,9 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter {
} }
interface AudioItemListener { interface AudioItemListener {
void onPlay(@NonNull Uri audioUri, long position, long messageId); void onPlay(@NonNull Uri audioUri, double progress, long messageId);
void onPause(@NonNull Uri audioUri); void onPause(@NonNull Uri audioUri);
void onSeekTo(@NonNull Uri audioUri, long position); void onSeekTo(@NonNull Uri audioUri, double progress);
void onStopAndReset(@NonNull Uri audioUri); void onStopAndReset(@NonNull Uri audioUri);
void registerPlaybackStateObserver(@NonNull Observer<VoiceNotePlaybackState> observer); void registerPlaybackStateObserver(@NonNull Observer<VoiceNotePlaybackState> observer);
void unregisterPlaybackStateObserver(@NonNull Observer<VoiceNotePlaybackState> observer); void unregisterPlaybackStateObserver(@NonNull Observer<VoiceNotePlaybackState> observer);

View File

@ -311,8 +311,8 @@ public final class MediaOverviewPageFragment extends Fragment
} }
@Override @Override
public void onPlay(@NonNull Uri audioUri, long position, long messageId) { public void onPlay(@NonNull Uri audioUri, double progress, long messageId) {
voiceNoteMediaController.startSinglePlayback(audioUri, messageId, position); voiceNoteMediaController.startSinglePlayback(audioUri, messageId, progress);
} }
@Override @Override
@ -321,8 +321,8 @@ public final class MediaOverviewPageFragment extends Fragment
} }
@Override @Override
public void onSeekTo(@NonNull Uri audioUri, long position) { public void onSeekTo(@NonNull Uri audioUri, double progress) {
voiceNoteMediaController.seekToPosition(audioUri, position); voiceNoteMediaController.seekToPosition(audioUri, progress);
} }
@Override @Override