Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VoiceBroadcast: Add backward and forward buttons for playback #7146

Merged
merged 3 commits into from
Dec 14, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "voice_broadcast_backward_30s.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "voice_broadcast_forward_30s.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,7 @@
"images" : [
{
"filename" : "voice_broadcast_spinner.svg",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
"idiom" : "universal"
}
],
"info" : {
Expand Down
2 changes: 2 additions & 0 deletions Riot/Generated/Images.swift
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,8 @@ internal class Asset: NSObject {
internal static let tabHome = ImageAsset(name: "tab_home")
internal static let tabPeople = ImageAsset(name: "tab_people")
internal static let tabRooms = ImageAsset(name: "tab_rooms")
internal static let voiceBroadcastBackward30s = ImageAsset(name: "voice_broadcast_backward_30s")
internal static let voiceBroadcastForward30s = ImageAsset(name: "voice_broadcast_forward_30s")
internal static let voiceBroadcastLive = ImageAsset(name: "voice_broadcast_live")
internal static let voiceBroadcastPause = ImageAsset(name: "voice_broadcast_pause")
internal static let voiceBroadcastPlay = ImageAsset(name: "voice_broadcast_play")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic
return (!isPlaybackInitialized || isPlayingLastChunk) && (state.broadcastState == .started || state.broadcastState == .resumed)
}

private static let defaultBackwardForwardValue: Float = 30000.0 // 30sec in ms

// MARK: Public

// MARK: - Setup
Expand All @@ -71,7 +73,7 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic
let viewState = VoiceBroadcastPlaybackViewState(details: details,
broadcastState: voiceBroadcastAggregator.voiceBroadcastState,
playbackState: .stopped,
playingState: VoiceBroadcastPlayingState(duration: Float(voiceBroadcastAggregator.voiceBroadcast.duration), isLive: false),
playingState: VoiceBroadcastPlayingState(duration: Float(voiceBroadcastAggregator.voiceBroadcast.duration), isLive: false, canMoveForward: false, canMoveBackward: false),
bindings: VoiceBroadcastPlaybackViewStateBindings(progress: 0))
super.init(initialViewState: viewState)

Expand Down Expand Up @@ -101,6 +103,10 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic
pause()
case .sliderChange(let didChange):
didSliderChanged(didChange)
case .backward:
backward()
case .forward:
forward()
}
}

Expand Down Expand Up @@ -164,6 +170,52 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic
audioPlayer?.stop()
}

/// Backward (30sec) a voice broadcast
private func backward() {
let newProgressValue = context.progress - VoiceBroadcastPlaybackViewModel.defaultBackwardForwardValue
seekTo(newProgressValue > 0 ? newProgressValue : 0)

state.bindings.progress = newProgressValue
updateBackwardForwardButtons()
yostyle marked this conversation as resolved.
Show resolved Hide resolved
}

/// Forward (30sec) a voice broadcast
private func forward() {
let newProgressValue = context.progress + VoiceBroadcastPlaybackViewModel.defaultBackwardForwardValue
seekTo(newProgressValue < state.playingState.duration ? newProgressValue : state.playingState.duration)

state.bindings.progress = newProgressValue
updateBackwardForwardButtons()
yostyle marked this conversation as resolved.
Show resolved Hide resolved
}
yostyle marked this conversation as resolved.
Show resolved Hide resolved

private func seekTo(_ seekTime: Float) {
// Flush the chunks queue and the current audio player playlist
voiceBroadcastChunkQueue = []
reloadVoiceBroadcastChunkQueue = isProcessingVoiceBroadcastChunk
audioPlayer?.removeAllPlayerItems()

let chunks = reorderVoiceBroadcastChunks(chunks: Array(voiceBroadcastAggregator.voiceBroadcast.chunks))

// Reinject the chunks we need and play them
let remainingTime = state.playingState.duration - seekTime
var chunksDuration: UInt = 0
for chunk in chunks.reversed() {
chunksDuration += chunk.duration
voiceBroadcastChunkQueue.append(chunk)
if Float(chunksDuration) >= remainingTime {
break
}
}

MXLog.debug("[VoiceBroadcastPlaybackViewModel] seekTo: restart to time: \(seekTime) milliseconds")
let time = seekTime - state.playingState.duration + Float(chunksDuration)
seekToChunkTime = TimeInterval(time / 1000)
// Check the condition to resume the playback when data will be ready (after the chunk process).
if state.playbackState != .stopped, isActuallyPaused == false {
state.playbackState = .buffering
}
processPendingVoiceBroadcastChunks()
}

// MARK: - Voice broadcast chunks playback

Expand Down Expand Up @@ -295,32 +347,8 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic
audioPlayer?.pause()
displayLink.isPaused = true
} else {
// Flush the chunks queue and the current audio player playlist
voiceBroadcastChunkQueue = []
reloadVoiceBroadcastChunkQueue = isProcessingVoiceBroadcastChunk
audioPlayer?.removeAllPlayerItems()

let chunks = reorderVoiceBroadcastChunks(chunks: Array(voiceBroadcastAggregator.voiceBroadcast.chunks))

// Reinject the chunks we need and play them
let remainingTime = state.playingState.duration - state.bindings.progress
var chunksDuration: UInt = 0
for chunk in chunks.reversed() {
chunksDuration += chunk.duration
voiceBroadcastChunkQueue.append(chunk)
if Float(chunksDuration) >= remainingTime {
break
}
}

MXLog.debug("[VoiceBroadcastPlaybackViewModel] didSliderChanged: restart to time: \(state.bindings.progress) milliseconds")
let time = state.bindings.progress - state.playingState.duration + Float(chunksDuration)
seekToChunkTime = TimeInterval(time / 1000)
// Check the condition to resume the playback when data will be ready (after the chunk process).
if state.playbackState != .stopped, isActuallyPaused == false {
state.playbackState = .buffering
}
processPendingVoiceBroadcastChunks()
seekTo(state.bindings.progress)
updateBackwardForwardButtons()
yostyle marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand All @@ -343,6 +371,13 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic
}.reduce(0) { $0 + $1.duration}) + (audioPlayer?.currentTime.rounded() ?? 0) * 1000

state.bindings.progress = Float(progress)

updateBackwardForwardButtons()
yostyle marked this conversation as resolved.
Show resolved Hide resolved
}

private func updateBackwardForwardButtons() {
state.playingState.canMoveBackward = state.bindings.progress > 0
state.playingState.canMoveForward = state.bindings.progress < state.playingState.duration
}

private func handleVoiceBroadcastChunksProcessing() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,19 @@ struct VoiceBroadcastPlaybackView: View {
if viewModel.viewState.playbackState == .error {
VoiceBroadcastPlaybackErrorView()
} else {
ZStack {
HStack (spacing: 17.0) {
if viewModel.viewState.playingState.canMoveBackward {
Button {
viewModel.send(viewAction: .backward)
} label: {
Image(uiImage: Asset.Images.voiceBroadcastBackward30s.image)
.renderingMode(.original)
}
.accessibilityIdentifier("backwardButton")
} else {
Spacer().frame(width: 25.0)
}

if viewModel.viewState.playbackState == .playing || viewModel.viewState.playbackState == .buffering {
Button { viewModel.send(viewAction: .pause) } label: {
Image(uiImage: Asset.Images.voiceBroadcastPause.image)
Expand All @@ -125,6 +137,18 @@ struct VoiceBroadcastPlaybackView: View {
.disabled(viewModel.viewState.playbackState == .buffering)
.accessibilityIdentifier("playButton")
}

if viewModel.viewState.playingState.canMoveForward {
Button {
viewModel.send(viewAction: .forward)
} label: {
Image(uiImage: Asset.Images.voiceBroadcastForward30s.image)
.renderingMode(.original)
}
.accessibilityIdentifier("forwardButton")
} else {
Spacer().frame(width: 25.0)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ enum VoiceBroadcastPlaybackViewAction {
case play
case pause
case sliderChange(didChange: Bool)
case backward
case forward
}

enum VoiceBroadcastPlaybackState {
Expand All @@ -40,6 +42,8 @@ struct VoiceBroadcastPlayingState {
var duration: Float
var durationLabel: String?
var isLive: Bool
var canMoveForward: Bool
var canMoveBackward: Bool
}

struct VoiceBroadcastPlaybackViewState: BindableState {
Expand Down
Loading