mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-04-18 04:38:27 +00:00
Refactor audio session management and add incoming message notification sound
This commit is contained in:
parent
db9c9f1834
commit
9cc8923e3f
10 changed files with 185 additions and 24 deletions
|
|
@ -2,6 +2,6 @@ import Foundation
|
|||
|
||||
public enum AppGitInfo {
|
||||
public static let branch = "master"
|
||||
public static let commit = "ef09f6c41"
|
||||
public static let commit = "db9c9f183"
|
||||
public static let tag = "6.1.0-alpha"
|
||||
}
|
||||
|
|
|
|||
BIN
Linphone/Ressources/Sounds/incoming_chat.wav
Normal file
BIN
Linphone/Ressources/Sounds/incoming_chat.wav
Normal file
Binary file not shown.
|
|
@ -95,9 +95,9 @@ class CallViewModel: ObservableObject {
|
|||
|
||||
init() {
|
||||
do {
|
||||
try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .voiceChat, options: .allowBluetooth)
|
||||
} catch _ {
|
||||
|
||||
try configureAudio(.call)
|
||||
} catch {
|
||||
print("Audio session error: \(error)")
|
||||
}
|
||||
NotificationCenter.default.addObserver(forName: Notification.Name("CallViewModelReset"), object: nil, queue: nil) { notification in
|
||||
self.resetCallView()
|
||||
|
|
|
|||
|
|
@ -39,9 +39,9 @@ class MeetingWaitingRoomViewModel: ObservableObject {
|
|||
|
||||
init() {
|
||||
do {
|
||||
try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .voiceChat, options: .allowBluetooth)
|
||||
} catch _ {
|
||||
|
||||
try configureAudio(.call)
|
||||
} catch {
|
||||
print("Audio session error: \(error)")
|
||||
}
|
||||
if !telecomManager.callStarted {
|
||||
self.resetMeetingRoomView()
|
||||
|
|
@ -51,9 +51,9 @@ class MeetingWaitingRoomViewModel: ObservableObject {
|
|||
func resetMeetingRoomView() {
|
||||
if self.telecomManager.meetingWaitingRoomSelected != nil {
|
||||
do {
|
||||
try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .voiceChat, options: .allowBluetooth)
|
||||
} catch _ {
|
||||
|
||||
try configureAudio(.call)
|
||||
} catch {
|
||||
print("Audio session error: \(error)")
|
||||
}
|
||||
coreContext.doOnCoreQueue { core in
|
||||
|
||||
|
|
@ -212,9 +212,9 @@ class MeetingWaitingRoomViewModel: ObservableObject {
|
|||
|
||||
func enableAVAudioSession() {
|
||||
do {
|
||||
try AVAudioSession.sharedInstance().setActive(true)
|
||||
} catch _ {
|
||||
|
||||
try configureAudio(.call)
|
||||
} catch {
|
||||
print("Audio session error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1958,7 +1958,11 @@ struct VoiceRecorderPlayer: View {
|
|||
.padding(.horizontal, 4)
|
||||
.padding(.vertical, 5)
|
||||
.onAppear {
|
||||
self.audioRecorder.startRecording()
|
||||
conversationViewModel.isRecording = isRecording
|
||||
audioRecorder.startRecording()
|
||||
}
|
||||
.onChange(of: isRecording) { newValue in
|
||||
conversationViewModel.isRecording = newValue
|
||||
}
|
||||
.onDisappear {
|
||||
self.audioRecorder.stopVoiceRecorder()
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ class ConversationViewModel: ObservableObject {
|
|||
|
||||
var vrpManager: VoiceRecordPlayerManager?
|
||||
@Published var isPlaying = false
|
||||
@Published var progress: Double = 0.0
|
||||
@Published var isRecording = false
|
||||
|
||||
@Published var attachments: [Attachment] = []
|
||||
@Published var attachmentTransferInProgress: Attachment?
|
||||
|
|
@ -1511,6 +1511,10 @@ class ConversationViewModel: ObservableObject {
|
|||
|
||||
if !eventLogMessage.message.isOutgoing {
|
||||
self.displayedConversationUnreadMessagesCount = unreadMessagesCount
|
||||
|
||||
if !self.isPlaying && !self.isRecording {
|
||||
SoundPlayer.shared.playIncomingMessage()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2613,11 +2617,14 @@ class ConversationViewModel: ObservableObject {
|
|||
func startVoiceRecordPlayer(voiceRecordPath: URL) {
|
||||
coreContext.doOnCoreQueue { core in
|
||||
if self.vrpManager == nil || self.vrpManager!.voiceRecordPath != voiceRecordPath {
|
||||
self.vrpManager = VoiceRecordPlayerManager(core: core, voiceRecordPath: voiceRecordPath)
|
||||
self.vrpManager = VoiceRecordPlayerManager(core: core, voiceRecordPath: voiceRecordPath, isPlaying: self.isPlaying)
|
||||
}
|
||||
|
||||
if self.vrpManager != nil {
|
||||
self.vrpManager!.startVoiceRecordPlayer()
|
||||
DispatchQueue.main.async {
|
||||
self.isPlaying = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2642,6 +2649,9 @@ class ConversationViewModel: ObservableObject {
|
|||
coreContext.doOnCoreQueue { _ in
|
||||
if self.vrpManager != nil {
|
||||
self.vrpManager!.pauseVoiceRecordPlayer()
|
||||
DispatchQueue.main.async {
|
||||
self.isPlaying = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2650,6 +2660,9 @@ class ConversationViewModel: ObservableObject {
|
|||
coreContext.doOnCoreQueue { _ in
|
||||
if self.vrpManager != nil {
|
||||
self.vrpManager!.stopVoiceRecordPlayer()
|
||||
DispatchQueue.main.async {
|
||||
self.isPlaying = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3384,9 +3397,12 @@ class VoiceRecordPlayerManager {
|
|||
//private var voiceRecordPlayerPosition: Double = 0
|
||||
//private var voiceRecordingDuration: TimeInterval = 0
|
||||
|
||||
init(core: Core, voiceRecordPath: URL) {
|
||||
@State var isPlaying: Bool
|
||||
|
||||
init(core: Core, voiceRecordPath: URL, isPlaying: Bool) {
|
||||
self.core = core
|
||||
self.voiceRecordPath = voiceRecordPath
|
||||
self.isPlaying = isPlaying
|
||||
}
|
||||
|
||||
private func initVoiceRecordPlayer() {
|
||||
|
|
@ -3412,7 +3428,11 @@ class VoiceRecordPlayerManager {
|
|||
if voiceRecordAudioFocusRequest == nil {
|
||||
voiceRecordAudioFocusRequest = AVAudioSession.sharedInstance()
|
||||
if let request = voiceRecordAudioFocusRequest {
|
||||
try? request.setActive(true)
|
||||
do {
|
||||
try configureAudio(.voiceMessage)
|
||||
} catch {
|
||||
print("Audio session error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3428,6 +3448,7 @@ class VoiceRecordPlayerManager {
|
|||
}
|
||||
|
||||
do {
|
||||
self.isPlaying = true
|
||||
try voiceRecordPlayer!.start()
|
||||
print("Playing voice record")
|
||||
} catch {
|
||||
|
|
@ -3445,8 +3466,9 @@ class VoiceRecordPlayerManager {
|
|||
|
||||
func pauseVoiceRecordPlayer() {
|
||||
if !isPlayerClosed() {
|
||||
print("Pausing voice record")
|
||||
self.isPlaying = false
|
||||
try? voiceRecordPlayer?.pause()
|
||||
print("Pausing voice record")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3456,10 +3478,11 @@ class VoiceRecordPlayerManager {
|
|||
|
||||
func stopVoiceRecordPlayer() {
|
||||
if !isPlayerClosed() {
|
||||
print("Stopping voice record")
|
||||
self.isPlaying = false
|
||||
try? voiceRecordPlayer?.pause()
|
||||
try? voiceRecordPlayer?.seek(timeMs: 0)
|
||||
voiceRecordPlayer?.close()
|
||||
print("Stopping voice record")
|
||||
}
|
||||
|
||||
if let request = voiceRecordAudioFocusRequest {
|
||||
|
|
@ -3523,8 +3546,8 @@ class AudioRecorder: NSObject, ObservableObject {
|
|||
|
||||
if recordingSession != nil {
|
||||
do {
|
||||
try recordingSession!.setCategory(.playAndRecord, mode: .default)
|
||||
try recordingSession!.setActive(true)
|
||||
try configureAudio(.recording)
|
||||
|
||||
recordingSession!.requestRecordPermission { allowed in
|
||||
if allowed {
|
||||
self.initVoiceRecorder()
|
||||
|
|
@ -3533,7 +3556,7 @@ class AudioRecorder: NSObject, ObservableObject {
|
|||
}
|
||||
}
|
||||
} catch {
|
||||
print("Failed to setup recording session.")
|
||||
print("Audio session error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class RecordingMediaPlayerViewModel: ObservableObject {
|
|||
func startVoiceRecordPlayer(voiceRecordPath: URL) {
|
||||
coreContext.doOnCoreQueue { core in
|
||||
if self.vrpManager == nil || self.vrpManager!.voiceRecordPath != voiceRecordPath {
|
||||
self.vrpManager = VoiceRecordPlayerManager(core: core, voiceRecordPath: voiceRecordPath)
|
||||
self.vrpManager = VoiceRecordPlayerManager(core: core, voiceRecordPath: voiceRecordPath, isPlaying: self.isPlaying)
|
||||
}
|
||||
|
||||
if self.vrpManager != nil {
|
||||
|
|
|
|||
64
Linphone/Utils/AudioMode.swift
Normal file
64
Linphone/Utils/AudioMode.swift
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2025 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import AVFoundation
|
||||
|
||||
enum AudioMode {
|
||||
case notification
|
||||
case voiceMessage
|
||||
case recording
|
||||
case call
|
||||
}
|
||||
|
||||
func configureAudio(_ mode: AudioMode) throws {
|
||||
let session = AVAudioSession.sharedInstance()
|
||||
|
||||
switch mode {
|
||||
|
||||
case .notification:
|
||||
try session.setCategory(
|
||||
.ambient,
|
||||
mode: .default,
|
||||
options: [.mixWithOthers]
|
||||
)
|
||||
|
||||
case .voiceMessage:
|
||||
try session.setCategory(
|
||||
.playback,
|
||||
mode: .spokenAudio,
|
||||
options: [.allowBluetoothHFP, .mixWithOthers]
|
||||
)
|
||||
|
||||
case .recording:
|
||||
try session.setCategory(
|
||||
.playAndRecord,
|
||||
mode: .default,
|
||||
options: [.allowBluetoothHFP, .defaultToSpeaker, .mixWithOthers]
|
||||
)
|
||||
|
||||
case .call:
|
||||
try session.setCategory(
|
||||
.playAndRecord,
|
||||
mode: .voiceChat,
|
||||
options: [.allowBluetoothHFP]
|
||||
)
|
||||
}
|
||||
|
||||
try session.setActive(true)
|
||||
}
|
||||
50
Linphone/Utils/SoundPlayer.swift
Normal file
50
Linphone/Utils/SoundPlayer.swift
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2025 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import AVFoundation
|
||||
|
||||
final class SoundPlayer {
|
||||
static let shared = SoundPlayer()
|
||||
|
||||
private var player: AVAudioPlayer?
|
||||
|
||||
func playIncomingMessage() {
|
||||
guard let url = Bundle.main.url(
|
||||
forResource: "incoming_chat",
|
||||
withExtension: "wav"
|
||||
) else {
|
||||
print("Sound not found")
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
try configureAudio(.notification)
|
||||
} catch {
|
||||
print("Audio session error: \(error)")
|
||||
}
|
||||
|
||||
do {
|
||||
player = try AVAudioPlayer(contentsOf: url)
|
||||
player?.prepareToPlay()
|
||||
player?.play()
|
||||
} catch {
|
||||
print("Audio error:", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -173,6 +173,9 @@
|
|||
D7B5678E2B28888F00DE63EB /* CallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B5678D2B28888F00DE63EB /* CallView.swift */; };
|
||||
D7B99E992B29B39000BE7BF2 /* CallViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B99E982B29B39000BE7BF2 /* CallViewModel.swift */; };
|
||||
D7B99E9B2B29F7C300BE7BF2 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7B99E9A2B29F7C200BE7BF2 /* ActivityIndicator.swift */; };
|
||||
D7BC10D42F4EF18600F09BDA /* SoundPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7BC10D32F4EF18100F09BDA /* SoundPlayer.swift */; };
|
||||
D7BC10D72F4EF25400F09BDA /* incoming_chat.wav in Resources */ = {isa = PBXBuildFile; fileRef = D7BC10D62F4EF25400F09BDA /* incoming_chat.wav */; };
|
||||
D7BC10D92F4EF65900F09BDA /* AudioMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7BC10D82F4EF64E00F09BDA /* AudioMode.swift */; };
|
||||
D7C2DA1D2CA44DE400A2441B /* EventModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C2DA1C2CA44DE400A2441B /* EventModel.swift */; };
|
||||
D7C365082AEFAB7F00FE6142 /* ContactListBottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C365072AEFAB7F00FE6142 /* ContactListBottomSheet.swift */; };
|
||||
D7C3650A2AF001C300FE6142 /* EditContactFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C365092AF001C300FE6142 /* EditContactFragment.swift */; };
|
||||
|
|
@ -439,6 +442,9 @@
|
|||
D7B5678D2B28888F00DE63EB /* CallView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallView.swift; sourceTree = "<group>"; };
|
||||
D7B99E982B29B39000BE7BF2 /* CallViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallViewModel.swift; sourceTree = "<group>"; };
|
||||
D7B99E9A2B29F7C200BE7BF2 /* ActivityIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityIndicator.swift; sourceTree = "<group>"; };
|
||||
D7BC10D32F4EF18100F09BDA /* SoundPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoundPlayer.swift; sourceTree = "<group>"; };
|
||||
D7BC10D62F4EF25400F09BDA /* incoming_chat.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = incoming_chat.wav; sourceTree = "<group>"; };
|
||||
D7BC10D82F4EF64E00F09BDA /* AudioMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioMode.swift; sourceTree = "<group>"; };
|
||||
D7C2DA1C2CA44DE400A2441B /* EventModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventModel.swift; sourceTree = "<group>"; };
|
||||
D7C365072AEFAB7F00FE6142 /* ContactListBottomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactListBottomSheet.swift; sourceTree = "<group>"; };
|
||||
D7C365092AF001C300FE6142 /* EditContactFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditContactFragment.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -663,6 +669,8 @@
|
|||
D717071C2AC591EF0037746F /* Utils */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D7BC10D82F4EF64E00F09BDA /* AudioMode.swift */,
|
||||
D7BC10D32F4EF18100F09BDA /* SoundPlayer.swift */,
|
||||
C642277A2E8E4AC50094FEDC /* ThemeManager.swift */,
|
||||
D738ACED2E857BEF0039F7D1 /* KeyboardResponder.swift */,
|
||||
D7DF8BE82E2104E5003A3BC7 /* EmojiPickerView.swift */,
|
||||
|
|
@ -1038,6 +1046,7 @@
|
|||
D7ADF6012AFE5C7C00212231 /* Ressources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D7BC10D52F4EF21D00F09BDA /* Sounds */,
|
||||
D783C77A2B1089B200622CC2 /* assistant_linphone_default_values */,
|
||||
D783C77B2B1089B200622CC2 /* assistant_third_party_default_values */,
|
||||
D732A90A2B0376F500DB42BA /* linphonerc-default */,
|
||||
|
|
@ -1068,6 +1077,14 @@
|
|||
path = ViewModel;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D7BC10D52F4EF21D00F09BDA /* Sounds */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D7BC10D62F4EF25400F09BDA /* incoming_chat.wav */,
|
||||
);
|
||||
path = Sounds;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D7CEE0332B7A20A400FD79B7 /* Conversations */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -1362,6 +1379,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D7D24D142AC1B4E800C6F35B /* NotoSans-Regular.ttf in Resources */,
|
||||
D7BC10D72F4EF25400F09BDA /* incoming_chat.wav in Resources */,
|
||||
D7D24D182AC1B4E800C6F35B /* NotoSans-ExtraBold.ttf in Resources */,
|
||||
D7D24D152AC1B4E800C6F35B /* NotoSans-Light.ttf in Resources */,
|
||||
D7FC8E4A2EBA12F90080C09D /* Launch Screen.storyboard in Resources */,
|
||||
|
|
@ -1453,6 +1471,7 @@
|
|||
files = (
|
||||
D7C3650E2AF15BF200FE6142 /* PhotoPicker.swift in Sources */,
|
||||
D795F57E2EC5F9500022C17D /* RecordingsListFragment.swift in Sources */,
|
||||
D7BC10D92F4EF65900F09BDA /* AudioMode.swift in Sources */,
|
||||
D7ADF6002AFE356400212231 /* Avatar.swift in Sources */,
|
||||
D7CEE03B2B7A234200FD79B7 /* ConversationsFragment.swift in Sources */,
|
||||
D71707202AC5989C0037746F /* TextExtension.swift in Sources */,
|
||||
|
|
@ -1479,6 +1498,7 @@
|
|||
D719EF892EDF4AFA00509AAB /* GeneratedGit.swift in Sources */,
|
||||
D7D1F5282EDD939E0034EEB0 /* RecordingMediaPlayerViewModel.swift in Sources */,
|
||||
D7DC096F2CFA1D7600A6D47C /* AccountProfileFragment.swift in Sources */,
|
||||
D7BC10D42F4EF18600F09BDA /* SoundPlayer.swift in Sources */,
|
||||
D717A10E2CEB772300849D92 /* ShareSheetController.swift in Sources */,
|
||||
66C491FD2B24D36500CEA16D /* AudioRouteUtils.swift in Sources */,
|
||||
D738ACEE2E857BF10039F7D1 /* KeyboardResponder.swift in Sources */,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue