mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-22 17:42:45 +02:00
First part of the voice broadcast recording feature
This commit is contained in:
+65
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
|
||||
struct VoiceBroadcastRecorderCoordinatorParameters {
|
||||
let session: MXSession
|
||||
let room: MXRoom
|
||||
let voiceBroadcastStartEvent: MXEvent
|
||||
}
|
||||
|
||||
final class VoiceBroadcastRecorderCoordinator: Coordinator, Presentable {
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let parameters: VoiceBroadcastRecorderCoordinatorParameters
|
||||
|
||||
private var voiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol
|
||||
private var voiceBroadcastRecorderViewModel: VoiceBroadcastRecorderViewModelProtocol
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(parameters: VoiceBroadcastRecorderCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
|
||||
voiceBroadcastRecorderService = VoiceBroadcastRecorderService(session: parameters.session, roomId: parameters.room.matrixItemId)
|
||||
|
||||
let viewModel = VoiceBroadcastRecorderViewModel(recorderService: voiceBroadcastRecorderService)
|
||||
voiceBroadcastRecorderViewModel = viewModel
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func start() { }
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
VectorHostingController(rootView: VoiceBroadcastRecorderView(viewModel: voiceBroadcastRecorderViewModel.context),
|
||||
forceZeroSafeAreaInsets: true)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import AVFoundation
|
||||
|
||||
class VoiceBroadcastRecorderProvider {
|
||||
|
||||
// MARK: - Constants
|
||||
static let shared = VoiceBroadcastRecorderProvider()
|
||||
|
||||
// MARK: - Properties
|
||||
// MARK: Public
|
||||
var session: MXSession?
|
||||
var coordinatorsForEventIdentifiers = [String: VoiceBroadcastRecorderCoordinator]()
|
||||
|
||||
// MARK: - Setup
|
||||
private init() { }
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
/// Create or retrieve the voiceBroadcast timeline coordinator for this event and return
|
||||
/// a view to be displayed in the timeline
|
||||
func buildVoiceBroadcastRecorderViewForEvent(_ event: MXEvent) -> UIView? {
|
||||
guard let session = session, let room = session.room(withRoomId: event.roomId) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if let coordinator = coordinatorsForEventIdentifiers[event.eventId] {
|
||||
return coordinator.toPresentable().view
|
||||
}
|
||||
|
||||
let parameters = VoiceBroadcastRecorderCoordinatorParameters(session: session, room: room, voiceBroadcastStartEvent: event)
|
||||
let coordinator = VoiceBroadcastRecorderCoordinator(parameters: parameters)
|
||||
|
||||
coordinatorsForEventIdentifiers[event.eventId] = coordinator
|
||||
|
||||
return coordinator.toPresentable().view
|
||||
}
|
||||
|
||||
/// Retrieve the voiceBroadcast timeline coordinator for the given event or nil if it hasn't been created yet
|
||||
func voiceBroadcastRecorderControllerForEventIdentifier(_ eventIdentifier: String) -> VoiceBroadcastRecorderCoordinator? {
|
||||
coordinatorsForEventIdentifiers[eventIdentifier]
|
||||
}
|
||||
}
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
|
||||
class VoiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let roomId: String
|
||||
private let session: MXSession
|
||||
private var voiceBroadcastService: VoiceBroadcastService? {
|
||||
session.voiceBroadcastService
|
||||
}
|
||||
|
||||
private let audioEngine = AVAudioEngine()
|
||||
|
||||
private var chunkFile: AVAudioFile! = nil
|
||||
private var chunkFrames: AVAudioFrameCount = 0
|
||||
private var chunkFileNumber: Int = 0
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, roomId: String) {
|
||||
self.session = session
|
||||
self.roomId = roomId
|
||||
}
|
||||
|
||||
// MARK: - VoiceBroadcastRecorderServiceProtocol
|
||||
|
||||
func startRecordingVoiceBroadcast() {
|
||||
let inputNode = audioEngine.inputNode
|
||||
|
||||
let inputBus = AVAudioNodeBus(0)
|
||||
let inputFormat = inputNode.inputFormat(forBus: inputBus)
|
||||
MXLog.debug("[VoiceBroadcastRecorderService] Start recording voice broadcast for bus name : \(String(describing: inputNode.name(forInputBus: inputBus)))")
|
||||
|
||||
inputNode.installTap(onBus: inputBus,
|
||||
bufferSize: 512,
|
||||
format: inputFormat) { (buffer, time) -> Void in
|
||||
DispatchQueue.main.async {
|
||||
self.writeBuffer(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Update state
|
||||
try? audioEngine.start()
|
||||
}
|
||||
|
||||
func stopRecordingVoiceBroadcast() {
|
||||
audioEngine.stop()
|
||||
audioEngine.reset() // FIXME: Really needed ?
|
||||
resetValues()
|
||||
|
||||
voiceBroadcastService?.stopVoiceBroadcast(success: { _ in
|
||||
// update recording state
|
||||
}, failure: { error in
|
||||
MXLog.error("[VoiceBroadcastRecorderService] Failed to stop voice broadcast", context: error)
|
||||
})
|
||||
}
|
||||
|
||||
func pauseRecordingVoiceBroadcast() {
|
||||
audioEngine.pause()
|
||||
|
||||
voiceBroadcastService?.pauseVoiceBroadcast(success: { _ in
|
||||
// update recording state
|
||||
}, failure: { error in
|
||||
MXLog.error("[VoiceBroadcastRecorderService] Failed to pause voice broadcast", context: error)
|
||||
})
|
||||
}
|
||||
|
||||
func resumeRecordingVoiceBroadcast() {
|
||||
try? audioEngine.start() // FIXME: Verifiy if start is ok for a restart/resume
|
||||
|
||||
voiceBroadcastService?.resumeVoiceBroadcast(success: { _ in
|
||||
// update recording state
|
||||
}, failure: { error in
|
||||
MXLog.error("[VoiceBroadcastRecorderService] Failed to resume voice broadcast", context: error)
|
||||
})
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
/// Reset chunk values.
|
||||
private func resetValues() {
|
||||
chunkFrames = 0
|
||||
chunkFileNumber = 0
|
||||
}
|
||||
|
||||
/// Write audio buffer to chunk file.
|
||||
private func writeBuffer(_ buffer: AVAudioPCMBuffer) {
|
||||
let sampleRate = buffer.format.sampleRate
|
||||
|
||||
if chunkFile == nil {
|
||||
createNewChunkFile(sampleRate: sampleRate)
|
||||
}
|
||||
try? chunkFile.write(from: buffer)
|
||||
|
||||
chunkFrames += buffer.frameLength
|
||||
|
||||
if chunkFrames > AVAudioFrameCount(Double(BuildSettings.voiceBroadcastChunkLength) * sampleRate) {
|
||||
sendChunkFile(at: chunkFile.url)
|
||||
// Reset chunkFile
|
||||
chunkFile = nil
|
||||
}
|
||||
}
|
||||
|
||||
/// Create new chunk file with sample rate.
|
||||
private func createNewChunkFile(sampleRate: Float64) {
|
||||
guard let directory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first else {
|
||||
// FIXME: Manage error
|
||||
return
|
||||
}
|
||||
let fileUrl = directory.appendingPathComponent("VoiceBroadcastChunk-\(roomId)-\(chunkFileNumber).m4a")
|
||||
MXLog.debug("[VoiceBroadcastRecorderService] Create chunk file to \(fileUrl)")
|
||||
|
||||
let settings: [String: Any] = [AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
|
||||
AVSampleRateKey: sampleRate,
|
||||
AVEncoderBitRateKey: 128000,
|
||||
AVNumberOfChannelsKey: 1,
|
||||
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue]
|
||||
|
||||
chunkFile = try! AVAudioFile(forWriting: fileUrl, settings: settings)
|
||||
|
||||
chunkFileNumber += 1
|
||||
chunkFrames = 0
|
||||
}
|
||||
|
||||
/// Send chunk file to the server.
|
||||
private func sendChunkFile(at url: URL) {
|
||||
guard let voiceBroadcastService = voiceBroadcastService else {
|
||||
// FIXME: Manage error
|
||||
return
|
||||
}
|
||||
|
||||
let dispatchGroup = DispatchGroup()
|
||||
var duration = 0.0
|
||||
|
||||
dispatchGroup.enter()
|
||||
VoiceMessageAudioConverter.mediaDurationAt(url) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
if let someDuration = try? result.get() {
|
||||
duration = someDuration
|
||||
} else {
|
||||
MXLog.error("[VoiceBroadcastRecorderService] Failed retrieving media duration")
|
||||
}
|
||||
case .failure(let error):
|
||||
MXLog.error("[VoiceBroadcastRecorderService] Failed getting audio duration", context: error)
|
||||
}
|
||||
|
||||
dispatchGroup.leave()
|
||||
}
|
||||
|
||||
dispatchGroup.notify(queue: .main) {
|
||||
voiceBroadcastService.sendChunkOfVoiceBroadcast(audioFileLocalURL: url,
|
||||
mimeType: "audio/mp4",
|
||||
duration: UInt(duration * 1000),
|
||||
samples: nil) { eventId in
|
||||
MXLog.debug("[VoiceBroadcastRecorderService] sendChunkOfVoiceBroadcast success.")
|
||||
if eventId != nil {
|
||||
self.deleteRecording(at: url)
|
||||
}
|
||||
} failure: { error in
|
||||
MXLog.error("[VoiceBroadcastRecorderService] sendChunkOfVoiceBroadcast error.", context: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete voice broadcast chunk at URL.
|
||||
private func deleteRecording(at url: URL?) {
|
||||
guard let url = url else {
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
try FileManager.default.removeItem(at: url)
|
||||
} catch {
|
||||
MXLog.error("[VoiceBroadcastRecorderService] deleteRecordingAtURL:", context: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
|
||||
protocol VoiceBroadcastRecorderServiceProtocol {
|
||||
/// Start voice broadcast recording.
|
||||
func startRecordingVoiceBroadcast()
|
||||
|
||||
/// Stop voice broadcast recording.
|
||||
func stopRecordingVoiceBroadcast()
|
||||
|
||||
/// Pause voice broadcast recording.
|
||||
func pauseRecordingVoiceBroadcast()
|
||||
|
||||
/// Resume voice broadcast recording after paused it.
|
||||
func resumeRecordingVoiceBroadcast()
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct VoiceBroadcastRecorderView: View {
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
@Environment(\.theme) private var theme: ThemeSwiftUI
|
||||
|
||||
// MARK: Public
|
||||
|
||||
@ObservedObject var viewModel: VoiceBroadcastRecorderViewModel.Context
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 16.0) {
|
||||
Text(VectorL10n.voiceBroadcastInTimelineTitle)
|
||||
.font(theme.fonts.bodySB)
|
||||
.foregroundColor(theme.colors.primaryContent)
|
||||
|
||||
HStack(alignment: .top, spacing: 16.0) {
|
||||
Button {
|
||||
// FIXME: Manage record in progress case
|
||||
viewModel.send(viewAction: .start)
|
||||
} label: {
|
||||
// FIXME: Manage record in progress case
|
||||
Image("voice_broadcast_record")
|
||||
.renderingMode(.original)
|
||||
}
|
||||
.accessibilityIdentifier("recordButton")
|
||||
|
||||
Button {
|
||||
// FIXME: Manage resume case
|
||||
viewModel.send(viewAction: .pause)
|
||||
} label: {
|
||||
Image("voice_broadcast_record_pause")
|
||||
.renderingMode(.original)
|
||||
}
|
||||
.accessibilityIdentifier("pauseButton")
|
||||
}
|
||||
|
||||
}
|
||||
.padding([.horizontal, .top], 2.0)
|
||||
.padding([.bottom])
|
||||
}
|
||||
|
||||
// private func updateRecordingStatus() {
|
||||
// switch viewModel.viewState.recordingState {
|
||||
// case .started:
|
||||
// viewModel.send(viewAction: .stop)
|
||||
// case .paused:
|
||||
// viewModel.send(viewAction: .resume)
|
||||
// case .stopped:
|
||||
// viewModel.send(viewAction: .start)
|
||||
// case .resumed:
|
||||
// viewModel.send(viewAction: .pause)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct VoiceBroadcastRecorderView_Previews: PreviewProvider {
|
||||
static let stateRenderer = MockVoiceBroadcastRecorderScreenState.stateRenderer
|
||||
static var previews: some View {
|
||||
stateRenderer.screenGroup()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum VoiceBroadcastRecorderViewAction {
|
||||
case start
|
||||
case stop
|
||||
case pause
|
||||
case resume
|
||||
}
|
||||
|
||||
enum VoiceBroadcastRecorderState {
|
||||
case started
|
||||
case stopped
|
||||
case paused
|
||||
case resumed
|
||||
}
|
||||
|
||||
struct VoiceBroadcastRecorderViewState: BindableState {
|
||||
var recordingState: VoiceBroadcastRecorderState
|
||||
var bindings: VoiceBroadcastRecorderViewStateBindings
|
||||
}
|
||||
|
||||
struct VoiceBroadcastRecorderViewStateBindings {
|
||||
// var alertInfo: AlertInfo<VoiceBroadcastRecorderAlertType>?
|
||||
}
|
||||
|
||||
enum VoiceBroadcastRecorderAlertType {
|
||||
// case failedClosingVoiceBroadcast
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
typealias MockVoiceBroadcastRecorderViewModelType = StateStoreViewModel<VoiceBroadcastRecorderViewState, VoiceBroadcastRecorderViewAction>
|
||||
class MockVoiceBroadcastRecorderViewModel: MockVoiceBroadcastRecorderViewModelType, VoiceBroadcastRecorderViewModelProtocol {
|
||||
|
||||
}
|
||||
|
||||
/// Using an enum for the screen allows you define the different state cases with
|
||||
/// the relevant associated data for each case.
|
||||
enum MockVoiceBroadcastRecorderScreenState: MockScreenState, CaseIterable {
|
||||
|
||||
var screenType: Any.Type {
|
||||
VoiceBroadcastRecorderView.self
|
||||
}
|
||||
|
||||
var screenView: ([Any], AnyView) {
|
||||
let viewModel = MockVoiceBroadcastRecorderViewModel(initialViewState: VoiceBroadcastRecorderViewState(recordingState: .stopped, bindings: VoiceBroadcastRecorderViewStateBindings()))
|
||||
|
||||
return (
|
||||
[false, viewModel],
|
||||
AnyView(VoiceBroadcastRecorderView(viewModel: viewModel.context))
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
typealias VoiceBroadcastRecorderViewModelType = StateStoreViewModel<VoiceBroadcastRecorderViewState, VoiceBroadcastRecorderViewAction>
|
||||
|
||||
class VoiceBroadcastRecorderViewModel: VoiceBroadcastRecorderViewModelType, VoiceBroadcastRecorderViewModelProtocol {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let voiceBroadcastRecorderService: VoiceBroadcastRecorderServiceProtocol
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(recorderService: VoiceBroadcastRecorderServiceProtocol) {
|
||||
self.voiceBroadcastRecorderService = recorderService
|
||||
super.init(initialViewState: VoiceBroadcastRecorderViewState(recordingState: .stopped, bindings: VoiceBroadcastRecorderViewStateBindings()))
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
override func process(viewAction: VoiceBroadcastRecorderViewAction) {
|
||||
switch viewAction {
|
||||
case .start:
|
||||
start()
|
||||
case .stop:
|
||||
stop()
|
||||
case .pause:
|
||||
pause()
|
||||
case .resume:
|
||||
resume()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
private func start() {
|
||||
self.state.recordingState = .started
|
||||
voiceBroadcastRecorderService.startRecordingVoiceBroadcast()
|
||||
}
|
||||
|
||||
private func stop() {
|
||||
self.state.recordingState = .stopped
|
||||
voiceBroadcastRecorderService.stopRecordingVoiceBroadcast()
|
||||
}
|
||||
|
||||
private func pause() {
|
||||
self.state.recordingState = .paused
|
||||
voiceBroadcastRecorderService.pauseRecordingVoiceBroadcast()
|
||||
}
|
||||
|
||||
private func resume() {
|
||||
self.state.recordingState = .resumed
|
||||
voiceBroadcastRecorderService.resumeRecordingVoiceBroadcast()
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol VoiceBroadcastRecorderViewModelProtocol {
|
||||
var context: VoiceBroadcastRecorderViewModelType.Context { get }
|
||||
}
|
||||
Reference in New Issue
Block a user