diff --git a/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java index 3c25624d00..84fb46dc5f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/BindableConversationItem.java @@ -57,8 +57,8 @@ public interface BindableConversationItem extends Unbindable { void onRegisterVoiceNoteCallbacks(@NonNull Observer onPlaybackStartObserver); void onUnregisterVoiceNoteCallbacks(@NonNull Observer onPlaybackStartObserver); void onVoiceNotePause(@NonNull Uri uri); - void onVoiceNotePlay(@NonNull Uri uri, long messageId, long position); - void onVoiceNoteSeekTo(@NonNull Uri uri, long position); + void onVoiceNotePlay(@NonNull Uri uri, long messageId, double position); + void onVoiceNoteSeekTo(@NonNull Uri uri, double position); /** @return true if handled, false if you want to let the normal url handling continue */ boolean onUrlClicked(@NonNull String url); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/AudioView.java b/app/src/main/java/org/thoughtcrime/securesms/components/AudioView.java index 02d021cbe5..d5f48e0357 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/AudioView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/AudioView.java @@ -192,7 +192,7 @@ public final class AudioView extends FrameLayout { private void onPlaybackState(@NonNull VoiceNotePlaybackState voiceNotePlaybackState) { onStart(voiceNotePlaybackState.getUri(), voiceNotePlaybackState.isAutoReset()); onProgress(voiceNotePlaybackState.getUri(), - (double) voiceNotePlaybackState.getPlayheadPositionMillis() / durationMillis, + (double) voiceNotePlaybackState.getPlayheadPositionMillis() / voiceNotePlaybackState.getTrackDuration(), voiceNotePlaybackState.getPlayheadPositionMillis()); } @@ -258,7 +258,7 @@ public final class AudioView extends FrameLayout { } 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; } @@ -358,7 +358,7 @@ public final class AudioView extends FrameLayout { if (callbacks != null) { if (lottieDirection == REVERSE) { - callbacks.onPlay(audioSlide.getUri(), getPosition()); + callbacks.onPlay(audioSlide.getUri(), getProgress()); } else { callbacks.onPause(audioSlide.getUri()); } @@ -371,10 +371,6 @@ public final class AudioView extends FrameLayout { updateProgress(0, 0); } - private long getPosition() { - return (long) (getProgress() * durationMillis); - } - private class DownloadClickedListener implements View.OnClickListener { private final @NonNull AudioSlide slide; @@ -394,10 +390,6 @@ public final class AudioView extends FrameLayout { @Override 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 @@ -418,7 +410,7 @@ public final class AudioView extends FrameLayout { if (callbacks != null) { 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 { - void onPlay(@NonNull Uri audioUri, long position); + void onPlay(@NonNull Uri audioUri, double progress); void onPause(@NonNull Uri audioUri); - void onSeekTo(@NonNull Uri audioUri, long position); + void onSeekTo(@NonNull Uri audioUri, double progress); void onStopAndReset(@NonNull Uri audioUri); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNoteMediaController.java b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNoteMediaController.java index 11efcea972..0540cd22d0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNoteMediaController.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNoteMediaController.java @@ -35,7 +35,7 @@ import java.util.Objects; public class VoiceNoteMediaController implements DefaultLifecycleObserver { 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"; 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) { - startPlayback(audioSlideUri, messageId, position, false); + public void startConsecutivePlayback(@NonNull Uri audioSlideUri, long messageId, double progress) { + startPlayback(audioSlideUri, messageId, progress, false); } - public void startSinglePlayback(@NonNull Uri audioSlideUri, long messageId, long position) { - startPlayback(audioSlideUri, messageId, position, true); + public void startSinglePlayback(@NonNull Uri audioSlideUri, long messageId, double progress) { + startPlayback(audioSlideUri, messageId, progress, true); } /** @@ -111,17 +111,19 @@ public class VoiceNoteMediaController implements DefaultLifecycleObserver { * * @param audioSlideUri The Uri of the desired 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. */ - 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)) { - getMediaController().getTransportControls().seekTo(position); + long duration = getMediaController().getMetadata().getLong(MediaMetadataCompat.METADATA_KEY_DURATION); + + getMediaController().getTransportControls().seekTo((long) (duration * progress)); getMediaController().getTransportControls().play(); } else { Bundle extras = new Bundle(); extras.putLong(EXTRA_MESSAGE_ID, messageId); - extras.putLong(EXTRA_PLAYHEAD, position); + extras.putDouble(EXTRA_PROGRESS, progress); extras.putBoolean(EXTRA_PLAY_SINGLE, singlePlayback); 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. * * @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)) { + long duration = getMediaController().getMetadata().getLong(MediaMetadataCompat.METADATA_KEY_DURATION); + getMediaController().getTransportControls().pause(); - getMediaController().getTransportControls().seekTo(position); + getMediaController().getTransportControls().seekTo((long) (duration * progress)); getMediaController().getTransportControls().play(); } } @@ -226,6 +230,7 @@ public class VoiceNoteMediaController implements DefaultLifecycleObserver { voiceNotePlaybackState.postValue(new VoiceNotePlaybackState(mediaUri, mediaController.getPlaybackState().getPosition(), + mediaMetadataCompat.getLong(MediaMetadataCompat.METADATA_KEY_DURATION), autoReset)); sendEmptyMessageDelayed(0, 50); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackPreparer.java b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackPreparer.java index c2188f1981..dc698136fb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackPreparer.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackPreparer.java @@ -89,7 +89,7 @@ final class VoiceNotePlaybackPreparer implements MediaSessionConnector.PlaybackP @Override public void onPrepareFromUri(final Uri uri, Bundle extras) { 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); canLoadMore = false; @@ -116,7 +116,7 @@ final class VoiceNotePlaybackPreparer implements MediaSessionConnector.PlaybackP @Override public void onTimelineChanged(Timeline timeline, @Nullable Object manifest, int reason) { if (timeline.getWindowCount() >= window) { - player.seekTo(window, position); + player.seekTo(window, (long) (player.getDuration() * progress)); player.removeListener(this); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackState.java b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackState.java index 83ce65cbc3..fd4658a1c2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackState.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNotePlaybackState.java @@ -9,15 +9,17 @@ import androidx.annotation.NonNull; */ 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 long playheadPositionMillis; + private final long trackDuration; 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.playheadPositionMillis = playheadPositionMillis; + this.trackDuration = trackDuration; this.autoReset = autoReset; } @@ -35,6 +37,13 @@ public class VoiceNotePlaybackState { return playheadPositionMillis; } + /** + * @return The track duration in ms + */ + public long getTrackDuration() { + return trackDuration; + } + /** * @return true if we should reset the currently playing clip. */ diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java index 018d1f7960..4939f3a2f3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -1377,13 +1377,13 @@ public class ConversationFragment extends LoggingFragment { } @Override - public void onVoiceNotePlay(@NonNull Uri uri, long messageId, long position) { - voiceNoteMediaController.startConsecutivePlayback(uri, messageId, position); + public void onVoiceNotePlay(@NonNull Uri uri, long messageId, double progress) { + voiceNoteMediaController.startConsecutivePlayback(uri, messageId, progress); } @Override - public void onVoiceNoteSeekTo(@NonNull Uri uri, long position) { - voiceNoteMediaController.seekToPosition(uri, position); + public void onVoiceNoteSeekTo(@NonNull Uri uri, double progress) { + voiceNoteMediaController.seekToPosition(uri, progress); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java index 3fceaef4f3..6197a60301 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationItem.java @@ -1548,10 +1548,10 @@ public class ConversationItem extends LinearLayout implements BindableConversati private final class AudioViewCallbacks implements AudioView.Callbacks { @Override - public void onPlay(@NonNull Uri audioUri, long position) { + public void onPlay(@NonNull Uri audioUri, double progress) { if (eventListener == null) return; - eventListener.onVoiceNotePlay(audioUri, messageRecord.getId(), position); + eventListener.onVoiceNotePlay(audioUri, messageRecord.getId(), progress); } @Override @@ -1562,10 +1562,10 @@ public class ConversationItem extends LinearLayout implements BindableConversati } @Override - public void onSeekTo(@NonNull Uri audioUri, long position) { + public void onSeekTo(@NonNull Uri audioUri, double progress) { if (eventListener == null) return; - eventListener.onVoiceNoteSeekTo(audioUri, position); + eventListener.onVoiceNoteSeekTo(audioUri, progress); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaGalleryAllAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaGalleryAllAdapter.java index 43d6370760..ba23ede94b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaGalleryAllAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaGalleryAllAdapter.java @@ -502,8 +502,8 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter { } @Override - public void onPlay(@NonNull Uri audioUri, long position) { - audioItemListener.onPlay(audioUri, position, messageId); + public void onPlay(@NonNull Uri audioUri, double progress) { + audioItemListener.onPlay(audioUri, progress, messageId); } @Override @@ -512,8 +512,8 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter { } @Override - public void onSeekTo(@NonNull Uri audioUri, long position) { - audioItemListener.onSeekTo(audioUri, position); + public void onSeekTo(@NonNull Uri audioUri, double progress) { + audioItemListener.onSeekTo(audioUri, progress); } @Override @@ -528,9 +528,9 @@ final class MediaGalleryAllAdapter extends StickyHeaderGridAdapter { } 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 onSeekTo(@NonNull Uri audioUri, long position); + void onSeekTo(@NonNull Uri audioUri, double progress); void onStopAndReset(@NonNull Uri audioUri); void registerPlaybackStateObserver(@NonNull Observer observer); void unregisterPlaybackStateObserver(@NonNull Observer observer); diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaOverviewPageFragment.java b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaOverviewPageFragment.java index 51b6cf7f1b..29183e85a5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaOverviewPageFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mediaoverview/MediaOverviewPageFragment.java @@ -311,8 +311,8 @@ public final class MediaOverviewPageFragment extends Fragment } @Override - public void onPlay(@NonNull Uri audioUri, long position, long messageId) { - voiceNoteMediaController.startSinglePlayback(audioUri, messageId, position); + public void onPlay(@NonNull Uri audioUri, double progress, long messageId) { + voiceNoteMediaController.startSinglePlayback(audioUri, messageId, progress); } @Override @@ -321,8 +321,8 @@ public final class MediaOverviewPageFragment extends Fragment } @Override - public void onSeekTo(@NonNull Uri audioUri, long position) { - voiceNoteMediaController.seekToPosition(audioUri, position); + public void onSeekTo(@NonNull Uri audioUri, double progress) { + voiceNoteMediaController.seekToPosition(audioUri, progress); } @Override