mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-01-17 02:58:07 +00:00
Add seeking support to the audio record player
This commit is contained in:
parent
0f8df65dff
commit
36fa752ccf
5 changed files with 69 additions and 14 deletions
|
|
@ -3051,6 +3051,21 @@ class VoiceRecordPlayerManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func seekVoiceRecordPlayer(percent: Double) {
|
||||||
|
guard !isPlayerClosed(),
|
||||||
|
let player = voiceRecordPlayer,
|
||||||
|
player.duration > 0 else { return }
|
||||||
|
|
||||||
|
let clamped = max(0, min(percent, 100))
|
||||||
|
|
||||||
|
let ratio = clamped / 100.0
|
||||||
|
|
||||||
|
let timeMs = Int(Double(player.duration) * ratio)
|
||||||
|
|
||||||
|
print("Seek voice record to \(clamped)% (\(timeMs) ms)")
|
||||||
|
try? player.seek(timeMs: timeMs)
|
||||||
|
}
|
||||||
|
|
||||||
func getSpeakerSoundCard(core: Core) -> String? {
|
func getSpeakerSoundCard(core: Core) -> String? {
|
||||||
var speakerCard: String? = nil
|
var speakerCard: String? = nil
|
||||||
var earpieceCard: String? = nil
|
var earpieceCard: String? = nil
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,14 @@ struct RecordingMediaPlayerFragment: View {
|
||||||
|
|
||||||
@State private var showShareSheet: Bool = false
|
@State private var showShareSheet: Bool = false
|
||||||
@State private var showPicker: Bool = false
|
@State private var showPicker: Bool = false
|
||||||
|
@State private var isSeeking: Bool = false
|
||||||
|
|
||||||
|
@State private var lastValue: Double = -1.0
|
||||||
@State private var value: Double = 40.0
|
@State private var value: Double = 40.0
|
||||||
|
|
||||||
@State private var timer: Timer?
|
@State private var timer: Timer?
|
||||||
|
|
||||||
|
|
||||||
init(recording: RecordingModel) {
|
init(recording: RecordingModel) {
|
||||||
_recordingMediaPlayerViewModel = StateObject(wrappedValue: RecordingMediaPlayerViewModel(recording: recording))
|
_recordingMediaPlayerViewModel = StateObject(wrappedValue: RecordingMediaPlayerViewModel(recording: recording))
|
||||||
}
|
}
|
||||||
|
|
@ -133,19 +138,41 @@ struct RecordingMediaPlayerFragment: View {
|
||||||
.frame(width: 50)
|
.frame(width: 50)
|
||||||
|
|
||||||
let radius = geometry.size.height * 0.5
|
let radius = geometry.size.height * 0.5
|
||||||
|
let barWidth = geometry.size.width - 120
|
||||||
ZStack(alignment: .leading) {
|
ZStack(alignment: .leading) {
|
||||||
Rectangle()
|
Rectangle()
|
||||||
.foregroundColor(Color.orangeMain100)
|
.foregroundColor(Color.orangeMain100)
|
||||||
.frame(width: (geometry.size.width - 120), height: 5)
|
.frame(width: barWidth, height: 5)
|
||||||
.clipShape(RoundedRectangle(cornerRadius: radius))
|
.clipShape(RoundedRectangle(cornerRadius: radius))
|
||||||
|
|
||||||
Rectangle()
|
Rectangle()
|
||||||
.foregroundColor(Color.orangeMain500)
|
.foregroundColor(Color.orangeMain500)
|
||||||
.frame(width: self.value * (geometry.size.width - 120) / 100, height: 5)
|
.frame(width: (self.value / 100) * barWidth, height: isSeeking ? 10 : 5)
|
||||||
.animation(self.value > 0 ? .linear(duration: 0.1) : nil, value: self.value)
|
|
||||||
.clipShape(RoundedRectangle(cornerRadius: radius))
|
.clipShape(RoundedRectangle(cornerRadius: radius))
|
||||||
}
|
}
|
||||||
.clipShape(RoundedRectangle(cornerRadius: radius))
|
.frame(width: barWidth, height: 20)
|
||||||
|
.contentShape(Rectangle())
|
||||||
|
.gesture(
|
||||||
|
DragGesture(minimumDistance: 0)
|
||||||
|
.onChanged { gesture in
|
||||||
|
isSeeking = true
|
||||||
|
|
||||||
|
let x = max(0, min(barWidth, gesture.location.x))
|
||||||
|
let percent = x / barWidth * 100
|
||||||
|
self.value = percent
|
||||||
|
}
|
||||||
|
.onEnded { gesture in
|
||||||
|
let x = max(0, min(barWidth, gesture.location.x))
|
||||||
|
let percent = x / barWidth * 100
|
||||||
|
self.value = percent
|
||||||
|
|
||||||
|
isSeeking = false
|
||||||
|
recordingMediaPlayerViewModel.seekTo(percent: percent)
|
||||||
|
|
||||||
|
lastValue = percent
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
Text(recordingMediaPlayerViewModel.recording.formattedDuration)
|
Text(recordingMediaPlayerViewModel.recording.formattedDuration)
|
||||||
.default_text_style_white_600(styleSize: 18)
|
.default_text_style_white_600(styleSize: 18)
|
||||||
|
|
@ -153,6 +180,7 @@ struct RecordingMediaPlayerFragment: View {
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
.frame(width: 70)
|
.frame(width: 70)
|
||||||
}
|
}
|
||||||
|
.padding(.bottom, 20)
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
.background(.black)
|
.background(.black)
|
||||||
|
|
@ -184,13 +212,17 @@ struct RecordingMediaPlayerFragment: View {
|
||||||
private func playProgress() {
|
private func playProgress() {
|
||||||
timer?.invalidate()
|
timer?.invalidate()
|
||||||
|
|
||||||
var lastValue = -1.0
|
lastValue = -1.0
|
||||||
|
|
||||||
value = recordingMediaPlayerViewModel.getPositionVoiceRecordPlayer()
|
value = recordingMediaPlayerViewModel.getPositionVoiceRecordPlayer()
|
||||||
|
|
||||||
timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { _ in
|
timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { _ in
|
||||||
let current = recordingMediaPlayerViewModel.getPositionVoiceRecordPlayer()
|
let current = recordingMediaPlayerViewModel.getPositionVoiceRecordPlayer()
|
||||||
|
|
||||||
|
if isSeeking {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if !recordingMediaPlayerViewModel.isPlaying {
|
if !recordingMediaPlayerViewModel.isPlaying {
|
||||||
self.value = current
|
self.value = current
|
||||||
lastValue = current
|
lastValue = current
|
||||||
|
|
|
||||||
|
|
@ -101,4 +101,12 @@ class RecordingMediaPlayerViewModel: ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func seekTo(percent: Double) {
|
||||||
|
coreContext.doOnCoreQueue { _ in
|
||||||
|
if self.vrpManager != nil {
|
||||||
|
self.vrpManager!.seekVoiceRecordPlayer(percent: percent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,7 @@
|
||||||
D719ABC92ABC6FD700B41C10 /* CoreContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = D719ABC82ABC6FD700B41C10 /* CoreContext.swift */; };
|
D719ABC92ABC6FD700B41C10 /* CoreContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = D719ABC82ABC6FD700B41C10 /* CoreContext.swift */; };
|
||||||
D719ABCC2ABC769C00B41C10 /* AssistantView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D719ABCB2ABC769C00B41C10 /* AssistantView.swift */; };
|
D719ABCC2ABC769C00B41C10 /* AssistantView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D719ABCB2ABC769C00B41C10 /* AssistantView.swift */; };
|
||||||
D719ABCF2ABC779A00B41C10 /* AccountLoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D719ABCE2ABC779A00B41C10 /* AccountLoginViewModel.swift */; };
|
D719ABCF2ABC779A00B41C10 /* AccountLoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D719ABCE2ABC779A00B41C10 /* AccountLoginViewModel.swift */; };
|
||||||
|
D719EF892EDF4AFA00509AAB /* GeneratedGit.swift in Sources */ = {isa = PBXBuildFile; fileRef = D719EF882EDF4AFA00509AAB /* GeneratedGit.swift */; };
|
||||||
D71A0E192B485ADF0002C6CD /* ViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71A0E182B485ADF0002C6CD /* ViewExtension.swift */; };
|
D71A0E192B485ADF0002C6CD /* ViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71A0E182B485ADF0002C6CD /* ViewExtension.swift */; };
|
||||||
D71FCA812AE14CFC00D2E43E /* ContactsListFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71FCA802AE14CFC00D2E43E /* ContactsListFragment.swift */; };
|
D71FCA812AE14CFC00D2E43E /* ContactsListFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71FCA802AE14CFC00D2E43E /* ContactsListFragment.swift */; };
|
||||||
D71FCA832AE14D6E00D2E43E /* ContactFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71FCA822AE14D6E00D2E43E /* ContactFragment.swift */; };
|
D71FCA832AE14D6E00D2E43E /* ContactFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D71FCA822AE14D6E00D2E43E /* ContactFragment.swift */; };
|
||||||
|
|
@ -180,7 +181,6 @@
|
||||||
D7D1698C2AE66FA500109A5C /* MagicSearchSingleton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D1698B2AE66FA500109A5C /* MagicSearchSingleton.swift */; };
|
D7D1698C2AE66FA500109A5C /* MagicSearchSingleton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D1698B2AE66FA500109A5C /* MagicSearchSingleton.swift */; };
|
||||||
D7D1F5262EDD91B30034EEB0 /* RecordingMediaPlayerFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D1F5252EDD91B10034EEB0 /* RecordingMediaPlayerFragment.swift */; };
|
D7D1F5262EDD91B30034EEB0 /* RecordingMediaPlayerFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D1F5252EDD91B10034EEB0 /* RecordingMediaPlayerFragment.swift */; };
|
||||||
D7D1F5282EDD939E0034EEB0 /* RecordingMediaPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D1F5272EDD939D0034EEB0 /* RecordingMediaPlayerViewModel.swift */; };
|
D7D1F5282EDD939E0034EEB0 /* RecordingMediaPlayerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D1F5272EDD939D0034EEB0 /* RecordingMediaPlayerViewModel.swift */; };
|
||||||
D7D1F5452EDDBBA70034EEB0 /* GeneratedGit.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D1F5442EDDBBA70034EEB0 /* GeneratedGit.swift */; };
|
|
||||||
D7D24D132AC1B4E800C6F35B /* NotoSans-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7D24D0D2AC1B4E800C6F35B /* NotoSans-Medium.ttf */; };
|
D7D24D132AC1B4E800C6F35B /* NotoSans-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7D24D0D2AC1B4E800C6F35B /* NotoSans-Medium.ttf */; };
|
||||||
D7D24D142AC1B4E800C6F35B /* NotoSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7D24D0E2AC1B4E800C6F35B /* NotoSans-Regular.ttf */; };
|
D7D24D142AC1B4E800C6F35B /* NotoSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7D24D0E2AC1B4E800C6F35B /* NotoSans-Regular.ttf */; };
|
||||||
D7D24D152AC1B4E800C6F35B /* NotoSans-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7D24D0F2AC1B4E800C6F35B /* NotoSans-Light.ttf */; };
|
D7D24D152AC1B4E800C6F35B /* NotoSans-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D7D24D0F2AC1B4E800C6F35B /* NotoSans-Light.ttf */; };
|
||||||
|
|
@ -324,6 +324,7 @@
|
||||||
D719ABC82ABC6FD700B41C10 /* CoreContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreContext.swift; sourceTree = "<group>"; };
|
D719ABC82ABC6FD700B41C10 /* CoreContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreContext.swift; sourceTree = "<group>"; };
|
||||||
D719ABCB2ABC769C00B41C10 /* AssistantView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssistantView.swift; sourceTree = "<group>"; };
|
D719ABCB2ABC769C00B41C10 /* AssistantView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssistantView.swift; sourceTree = "<group>"; };
|
||||||
D719ABCE2ABC779A00B41C10 /* AccountLoginViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountLoginViewModel.swift; sourceTree = "<group>"; };
|
D719ABCE2ABC779A00B41C10 /* AccountLoginViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountLoginViewModel.swift; sourceTree = "<group>"; };
|
||||||
|
D719EF882EDF4AFA00509AAB /* GeneratedGit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneratedGit.swift; sourceTree = "<group>"; };
|
||||||
D71A0E182B485ADF0002C6CD /* ViewExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewExtension.swift; sourceTree = "<group>"; };
|
D71A0E182B485ADF0002C6CD /* ViewExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewExtension.swift; sourceTree = "<group>"; };
|
||||||
D71C266F2E819A0D001A7F92 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = Localizable/sk.lproj/Localizable.strings; sourceTree = "<group>"; };
|
D71C266F2E819A0D001A7F92 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = Localizable/sk.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
D71FCA802AE14CFC00D2E43E /* ContactsListFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsListFragment.swift; sourceTree = "<group>"; };
|
D71FCA802AE14CFC00D2E43E /* ContactsListFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsListFragment.swift; sourceTree = "<group>"; };
|
||||||
|
|
@ -419,7 +420,6 @@
|
||||||
D7D1698B2AE66FA500109A5C /* MagicSearchSingleton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MagicSearchSingleton.swift; sourceTree = "<group>"; };
|
D7D1698B2AE66FA500109A5C /* MagicSearchSingleton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MagicSearchSingleton.swift; sourceTree = "<group>"; };
|
||||||
D7D1F5252EDD91B10034EEB0 /* RecordingMediaPlayerFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordingMediaPlayerFragment.swift; sourceTree = "<group>"; };
|
D7D1F5252EDD91B10034EEB0 /* RecordingMediaPlayerFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordingMediaPlayerFragment.swift; sourceTree = "<group>"; };
|
||||||
D7D1F5272EDD939D0034EEB0 /* RecordingMediaPlayerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordingMediaPlayerViewModel.swift; sourceTree = "<group>"; };
|
D7D1F5272EDD939D0034EEB0 /* RecordingMediaPlayerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordingMediaPlayerViewModel.swift; sourceTree = "<group>"; };
|
||||||
D7D1F5442EDDBBA70034EEB0 /* GeneratedGit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneratedGit.swift; sourceTree = "<group>"; };
|
|
||||||
D7D24D0D2AC1B4E800C6F35B /* NotoSans-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NotoSans-Medium.ttf"; sourceTree = "<group>"; };
|
D7D24D0D2AC1B4E800C6F35B /* NotoSans-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NotoSans-Medium.ttf"; sourceTree = "<group>"; };
|
||||||
D7D24D0E2AC1B4E800C6F35B /* NotoSans-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NotoSans-Regular.ttf"; sourceTree = "<group>"; };
|
D7D24D0E2AC1B4E800C6F35B /* NotoSans-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NotoSans-Regular.ttf"; sourceTree = "<group>"; };
|
||||||
D7D24D0F2AC1B4E800C6F35B /* NotoSans-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NotoSans-Light.ttf"; sourceTree = "<group>"; };
|
D7D24D0F2AC1B4E800C6F35B /* NotoSans-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NotoSans-Light.ttf"; sourceTree = "<group>"; };
|
||||||
|
|
@ -674,7 +674,7 @@
|
||||||
D719ABBD2ABC67BF00B41C10 /* Preview Content */,
|
D719ABBD2ABC67BF00B41C10 /* Preview Content */,
|
||||||
D7D24D0C2AC1B4C700C6F35B /* Fonts */,
|
D7D24D0C2AC1B4C700C6F35B /* Fonts */,
|
||||||
D7ADF6012AFE5C7C00212231 /* Ressources */,
|
D7ADF6012AFE5C7C00212231 /* Ressources */,
|
||||||
D7D1F5442EDDBBA70034EEB0 /* GeneratedGit.swift */,
|
D719EF882EDF4AFA00509AAB /* GeneratedGit.swift */,
|
||||||
);
|
);
|
||||||
path = Linphone;
|
path = Linphone;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
|
@ -1371,6 +1371,7 @@
|
||||||
D7C2DA1D2CA44DE400A2441B /* EventModel.swift in Sources */,
|
D7C2DA1D2CA44DE400A2441B /* EventModel.swift in Sources */,
|
||||||
D719ABC92ABC6FD700B41C10 /* CoreContext.swift in Sources */,
|
D719ABC92ABC6FD700B41C10 /* CoreContext.swift in Sources */,
|
||||||
D70A26F22B7F5D95006CC8FC /* ConversationFragment.swift in Sources */,
|
D70A26F22B7F5D95006CC8FC /* ConversationFragment.swift in Sources */,
|
||||||
|
D719EF892EDF4AFA00509AAB /* GeneratedGit.swift in Sources */,
|
||||||
D7D1F5282EDD939E0034EEB0 /* RecordingMediaPlayerViewModel.swift in Sources */,
|
D7D1F5282EDD939E0034EEB0 /* RecordingMediaPlayerViewModel.swift in Sources */,
|
||||||
D7DC096F2CFA1D7600A6D47C /* AccountProfileFragment.swift in Sources */,
|
D7DC096F2CFA1D7600A6D47C /* AccountProfileFragment.swift in Sources */,
|
||||||
D717A10E2CEB772300849D92 /* ShareSheetController.swift in Sources */,
|
D717A10E2CEB772300849D92 /* ShareSheetController.swift in Sources */,
|
||||||
|
|
@ -1419,7 +1420,6 @@
|
||||||
C628172E2C1C3A3600DBA646 /* AccountExtension.swift in Sources */,
|
C628172E2C1C3A3600DBA646 /* AccountExtension.swift in Sources */,
|
||||||
66C491FF2B24D4AC00CEA16D /* FileUtils.swift in Sources */,
|
66C491FF2B24D4AC00CEA16D /* FileUtils.swift in Sources */,
|
||||||
C62817322C1C400A00DBA646 /* StringExtension.swift in Sources */,
|
C62817322C1C400A00DBA646 /* StringExtension.swift in Sources */,
|
||||||
D7D1F5452EDDBBA70034EEB0 /* GeneratedGit.swift in Sources */,
|
|
||||||
D74C9D012ACB098C0021626A /* PermissionManager.swift in Sources */,
|
D74C9D012ACB098C0021626A /* PermissionManager.swift in Sources */,
|
||||||
D7B99E992B29B39000BE7BF2 /* CallViewModel.swift in Sources */,
|
D7B99E992B29B39000BE7BF2 /* CallViewModel.swift in Sources */,
|
||||||
D7702EF22AC7205000557C00 /* WelcomeView.swift in Sources */,
|
D7702EF22AC7205000557C00 /* WelcomeView.swift in Sources */,
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@
|
||||||
"location" : "https://gitlab.linphone.org/BC/public/linphone-sdk-swift-ios.git",
|
"location" : "https://gitlab.linphone.org/BC/public/linphone-sdk-swift-ios.git",
|
||||||
"state" : {
|
"state" : {
|
||||||
"branch" : "alpha",
|
"branch" : "alpha",
|
||||||
"revision" : "43ee1a062ef73808e27afe3c5341a27c1b82aae7"
|
"revision" : "4403eb00e8843352d9d4ca4e696c2824e53dd178"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue