mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-01-17 02:58:07 +00:00
Refresh view when call is paused
This commit is contained in:
parent
8469e8583e
commit
d97f07942f
11 changed files with 677 additions and 560 deletions
21
Linphone/Assets.xcassets/phone-list.imageset/Contents.json
vendored
Normal file
21
Linphone/Assets.xcassets/phone-list.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "phone-list.svg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
6
Linphone/Assets.xcassets/phone-list.imageset/phone-list.svg
vendored
Normal file
6
Linphone/Assets.xcassets/phone-list.imageset/phone-list.svg
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20.1334 15.1013L15.8935 13.2014L15.8818 13.196C15.6617 13.1018 15.4216 13.0641 15.1832 13.0861C14.9448 13.1081 14.7157 13.1892 14.5165 13.322C14.493 13.3375 14.4705 13.3543 14.449 13.3724L12.2584 15.2399C10.8706 14.5658 9.43775 13.1438 8.76365 11.774L10.6339 9.55006C10.6519 9.52756 10.669 9.50506 10.6852 9.48076C10.8151 9.28213 10.894 9.05445 10.9147 8.81798C10.9355 8.5815 10.8974 8.34357 10.804 8.12535V8.11455L8.89865 3.86742C8.77512 3.58236 8.5627 3.34489 8.29311 3.19047C8.02353 3.03605 7.71123 2.97296 7.40284 3.01062C6.1833 3.1711 5.06388 3.77001 4.25366 4.69552C3.44343 5.62102 2.9978 6.80981 3.00001 8.03985C3.00001 15.1859 8.81405 20.9999 15.9601 20.9999C17.1901 21.0021 18.3789 20.5565 19.3044 19.7463C20.2299 18.9361 20.8289 17.8166 20.9893 16.5971C21.0271 16.2888 20.9641 15.9766 20.8098 15.707C20.6556 15.4375 20.4183 15.225 20.1334 15.1013ZM15.9601 19.5599C12.9058 19.5566 9.97757 18.3418 7.81786 16.1821C5.65814 14.0224 4.44335 11.0941 4.44002 8.03985C4.43663 7.16099 4.75327 6.31094 5.33079 5.64845C5.90831 4.98597 6.70723 4.55635 7.57834 4.43983C7.57799 4.44342 7.57799 4.44704 7.57834 4.45063L9.46835 8.68066L7.60804 10.9073C7.58916 10.929 7.57201 10.9522 7.55674 10.9766C7.4213 11.1844 7.34185 11.4237 7.32608 11.6713C7.31031 11.9188 7.35876 12.1663 7.46674 12.3896C8.28215 14.0573 9.96246 15.725 11.6482 16.5395C11.8731 16.6465 12.122 16.6933 12.3705 16.6753C12.6189 16.6573 12.8585 16.5752 13.0657 16.4369C13.0888 16.4213 13.111 16.4045 13.1323 16.3865L15.3202 14.5199L19.5502 16.4144C19.5502 16.4144 19.5574 16.4144 19.5601 16.4144C19.445 17.2868 19.016 18.0873 18.3534 18.6662C17.6908 19.2452 16.84 19.5629 15.9601 19.5599Z" fill="white"/>
|
||||
<path d="M12.7 8.90039H20.5" stroke="white" stroke-linecap="round"/>
|
||||
<path d="M12.7 6.19922H20.5" stroke="white" stroke-linecap="round"/>
|
||||
<path d="M12.7 3.5H20.5" stroke="white" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
21
Linphone/Assets.xcassets/phone-transfer.imageset/Contents.json
vendored
Normal file
21
Linphone/Assets.xcassets/phone-transfer.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "phone-transfer.svg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
4
Linphone/Assets.xcassets/phone-transfer.imageset/phone-transfer.svg
vendored
Normal file
4
Linphone/Assets.xcassets/phone-transfer.imageset/phone-transfer.svg
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20.9888 17.1636C20.8216 18.4339 20.1977 19.6 19.2337 20.444C18.2696 21.288 17.0313 21.7522 15.75 21.7499C8.30626 21.7499 2.25001 15.6936 2.25001 8.24986C2.24771 6.96857 2.7119 5.73025 3.55588 4.7662C4.39986 3.80214 5.56592 3.17827 6.83626 3.01111C7.15739 2.97181 7.48261 3.03739 7.76342 3.19807C8.04422 3.35875 8.26555 3.60591 8.39438 3.90267L10.3744 8.32392V8.33517C10.4729 8.56247 10.5136 8.81063 10.4928 9.05749C10.472 9.30435 10.3904 9.54222 10.2553 9.74986C10.2384 9.77517 10.2206 9.79861 10.2019 9.82205L8.25001 12.1358C8.9522 13.5627 10.4447 15.042 11.8903 15.7461L14.1722 13.8045C14.1946 13.7857 14.2181 13.7682 14.2425 13.752C14.4497 13.6131 14.6885 13.5283 14.9369 13.5054C15.1853 13.4825 15.4355 13.5221 15.6647 13.6208L15.6769 13.6264L20.0934 15.6055C20.3909 15.7337 20.6389 15.9548 20.8003 16.2356C20.9616 16.5165 21.0278 16.842 20.9888 17.1636ZM19.5 16.9761C19.5 16.9761 19.4934 16.9761 19.4897 16.9761L15.0834 15.0017L12.8006 16.9442C12.7785 16.963 12.7553 16.9805 12.7313 16.9967C12.5154 17.1407 12.2659 17.2263 12.0071 17.245C11.7483 17.2638 11.489 17.215 11.2547 17.1036C9.49876 16.2552 7.74845 14.518 6.89907 12.7808C6.7866 12.5482 6.73613 12.2904 6.75255 12.0325C6.76898 11.7747 6.85174 11.5254 6.99282 11.3089C7.00872 11.2835 7.02659 11.2594 7.04626 11.2367L9.00001 8.92017L7.03126 4.51392C7.03089 4.51018 7.03089 4.50641 7.03126 4.50267C6.12212 4.62126 5.28739 5.0672 4.68339 5.75697C4.0794 6.44673 3.74755 7.33302 3.75001 8.24986C3.75348 11.4314 5.01888 14.4816 7.26856 16.7313C9.51825 18.981 12.5685 20.2464 15.75 20.2499C16.6663 20.253 17.5523 19.9223 18.2425 19.3196C18.9327 18.7169 19.3797 17.8835 19.5 16.9752V16.9761Z" fill="white"/>
|
||||
<path d="M11.5076 5.75317C11.5075 5.6718 11.5235 5.59121 11.5546 5.51602C11.5858 5.44083 11.6314 5.37252 11.6889 5.31498C11.7465 5.25744 11.8148 5.2118 11.89 5.18068C11.9652 5.14956 12.0458 5.13357 12.1271 5.13361L15.8866 5.13416L14.7543 4.00176C14.6381 3.88563 14.5729 3.72813 14.5729 3.5639C14.5729 3.39968 14.6381 3.24218 14.7543 3.12605C14.8704 3.00993 15.0279 2.94469 15.1921 2.94469C15.3563 2.94469 15.5138 3.00993 15.63 3.12605L17.8192 5.31532C17.9354 5.43145 18.0006 5.58895 18.0006 5.75317C18.0006 5.9174 17.9354 6.0749 17.8192 6.19103L15.63 8.3803C15.5138 8.49642 15.3563 8.56166 15.1921 8.56166C15.0279 8.56166 14.8704 8.49642 14.7543 8.3803C14.6381 8.26417 14.5729 8.10667 14.5729 7.94244C14.5729 7.77822 14.6381 7.62071 14.7543 7.50459L15.8866 6.37219L12.1271 6.37274C12.0458 6.37278 11.9652 6.35679 11.89 6.32567C11.8148 6.29455 11.7465 6.24891 11.6889 6.19137C11.6314 6.13383 11.5858 6.06551 11.5546 5.99032C11.5235 5.91513 11.5075 5.83455 11.5076 5.75317Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
|
|
@ -105,10 +105,6 @@ final class CoreContext: ObservableObject {
|
|||
self.mCore.videoCaptureEnabled = true
|
||||
self.mCore.videoDisplayEnabled = true
|
||||
|
||||
let videoActivationPolicy = self.mCore.videoActivationPolicy!
|
||||
videoActivationPolicy.automaticallyAccept = true
|
||||
self.mCore.videoActivationPolicy! = videoActivationPolicy
|
||||
|
||||
try? self.mCore.start()
|
||||
|
||||
// Create a Core listener to listen for the callback we need
|
||||
|
|
|
|||
|
|
@ -107,6 +107,9 @@
|
|||
},
|
||||
"+" : {
|
||||
|
||||
},
|
||||
"|" : {
|
||||
|
||||
},
|
||||
"0" : {
|
||||
|
||||
|
|
@ -268,6 +271,9 @@
|
|||
},
|
||||
"Deny all" : {
|
||||
|
||||
},
|
||||
"Dialer" : {
|
||||
|
||||
},
|
||||
"Display Name" : {
|
||||
|
||||
|
|
@ -415,9 +421,6 @@
|
|||
},
|
||||
"Outgoing Call" : {
|
||||
|
||||
},
|
||||
"Participants" : {
|
||||
|
||||
},
|
||||
"password" : {
|
||||
"extractionState" : "manual",
|
||||
|
|
@ -438,6 +441,12 @@
|
|||
},
|
||||
"Pause" : {
|
||||
|
||||
},
|
||||
"Paused" : {
|
||||
|
||||
},
|
||||
"Paused by remote" : {
|
||||
|
||||
},
|
||||
"Personnalize your profil mode" : {
|
||||
|
||||
|
|
@ -474,9 +483,6 @@
|
|||
},
|
||||
"Scan QR code" : {
|
||||
|
||||
},
|
||||
"Screen share" : {
|
||||
|
||||
},
|
||||
"Search contact or history call" : {
|
||||
|
||||
|
|
@ -540,6 +546,9 @@
|
|||
},
|
||||
"to Linphone" : {
|
||||
|
||||
},
|
||||
"Transfer" : {
|
||||
|
||||
},
|
||||
"Transport" : {
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ ec_calibrator_cool_tones=1
|
|||
[video]
|
||||
auto_resize_preview_to_keep_ratio=1
|
||||
max_conference_size=vga
|
||||
automatically_accept=1
|
||||
automatically_initiate=0
|
||||
|
||||
[misc]
|
||||
enable_basic_to_client_group_chat_room_migration=0
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 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/>.
|
||||
*/
|
||||
* Copyright (c) 2010-2020 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/>.
|
||||
*/
|
||||
// swiftlint:disable line_length
|
||||
|
||||
import Foundation
|
||||
|
|
@ -56,19 +56,19 @@ class CallInfo {
|
|||
}
|
||||
|
||||
/*
|
||||
* A delegate to support callkit.
|
||||
*/
|
||||
* A delegate to support callkit.
|
||||
*/
|
||||
class ProviderDelegate: NSObject {
|
||||
let provider: CXProvider
|
||||
var uuids: [String: UUID] = [:]
|
||||
var callInfos: [UUID: CallInfo] = [:]
|
||||
|
||||
|
||||
override init() {
|
||||
provider = CXProvider(configuration: ProviderDelegate.providerConfiguration)
|
||||
super.init()
|
||||
provider.setDelegate(self, queue: nil)
|
||||
}
|
||||
|
||||
|
||||
static var providerConfiguration: CXProviderConfiguration {
|
||||
get {
|
||||
let providerConfiguration = CXProviderConfiguration()
|
||||
|
|
@ -97,18 +97,18 @@ class ProviderDelegate: NSObject {
|
|||
let callId = callInfo?.callId ?? ""
|
||||
|
||||
/*
|
||||
if (ConfigManager.instance().config?.hasEntry(section: "app", key: "max_calls") == 1) { // moved from misc to app section intentionally upon app start or remote configuration
|
||||
if let maxCalls = ConfigManager.instance().config?.getInt(section: "app",key: "max_calls",defaultValue: 10), Core.get().callsNb > maxCalls {
|
||||
Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: declining call, as max calls (\(maxCalls)) reached call-id: [\(String(describing: callId))] and UUID: [\(uuid.description)]")
|
||||
decline(uuid: uuid)
|
||||
|
||||
CoreContext.shared.doOnCoreQueue(synchronous: true) { core in
|
||||
try? call?.decline(reason: .Busy)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
*/
|
||||
if (ConfigManager.instance().config?.hasEntry(section: "app", key: "max_calls") == 1) { // moved from misc to app section intentionally upon app start or remote configuration
|
||||
if let maxCalls = ConfigManager.instance().config?.getInt(section: "app",key: "max_calls",defaultValue: 10), Core.get().callsNb > maxCalls {
|
||||
Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: declining call, as max calls (\(maxCalls)) reached call-id: [\(String(describing: callId))] and UUID: [\(uuid.description)]")
|
||||
decline(uuid: uuid)
|
||||
|
||||
CoreContext.shared.doOnCoreQueue(synchronous: true) { core in
|
||||
try? call?.decline(reason: .Busy)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
Log.info("CallKit: report new incoming call with call-id: [\(callId)] and UUID: [\(uuid.description)]")
|
||||
// TelecomManager.instance().setHeldOtherCalls(exceptCallid: callId ?? "") // ALREADY COMMENTED ON LINPHONE-IPHONE 5.2
|
||||
|
|
@ -140,7 +140,7 @@ class ProviderDelegate: NSObject {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func updateCall(uuid: UUID, handle: String, hasVideo: Bool = false, displayName: String) {
|
||||
let update = CXCallUpdate()
|
||||
update.remoteHandle = CXHandle(type: .generic, value: handle)
|
||||
|
|
@ -148,11 +148,11 @@ class ProviderDelegate: NSObject {
|
|||
update.hasVideo = hasVideo
|
||||
provider.reportCall(with: uuid, updated: update)
|
||||
}
|
||||
|
||||
|
||||
func reportOutgoingCallStartedConnecting(uuid: UUID) {
|
||||
provider.reportOutgoingCall(with: uuid, startedConnectingAt: nil)
|
||||
}
|
||||
|
||||
|
||||
func reportOutgoingCallConnected(uuid: UUID) {
|
||||
provider.reportOutgoingCall(with: uuid, connectedAt: nil)
|
||||
}
|
||||
|
|
@ -164,7 +164,7 @@ class ProviderDelegate: NSObject {
|
|||
func decline(uuid: UUID) {
|
||||
provider.reportCall(with: uuid, endedAt: .init(), reason: .unanswered)
|
||||
}
|
||||
|
||||
|
||||
func endCallNotExist(uuid: UUID, timeout: DispatchTime) {
|
||||
DispatchQueue.main.asyncAfter(deadline: timeout) {
|
||||
CoreContext.shared.doOnCoreQueue(synchronous: true) { core in
|
||||
|
|
@ -188,7 +188,7 @@ extension ProviderDelegate: CXProviderDelegate {
|
|||
|
||||
let uuid = action.callUUID
|
||||
let callId = callInfos[uuid]?.callId
|
||||
|
||||
|
||||
// remove call infos first, otherwise CXEndCallAction will be called more than onece
|
||||
if callId != nil {
|
||||
uuids.removeValue(forKey: callId!)
|
||||
|
|
@ -203,7 +203,7 @@ extension ProviderDelegate: CXProviderDelegate {
|
|||
action.fulfill()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
|
||||
let uuid = action.callUUID
|
||||
let callInfo = callInfos[uuid]
|
||||
|
|
@ -221,15 +221,17 @@ extension ProviderDelegate: CXProviderDelegate {
|
|||
|
||||
let call = core.getCallByCallid(callId: callId)
|
||||
|
||||
if UIApplication.shared.applicationState != .active {
|
||||
TelecomManager.shared.backgroundContextCall = call
|
||||
TelecomManager.shared.backgroundContextCameraIsEnabled = call?.params?.videoEnabled == true || call?.callLog?.wasConference() == true
|
||||
if #available(iOS 16.0, *) {
|
||||
if call?.cameraEnabled == true {
|
||||
call?.cameraEnabled = AVCaptureSession().isMultitaskingCameraAccessSupported
|
||||
DispatchQueue.main.async() {
|
||||
if UIApplication.shared.applicationState != .active {
|
||||
TelecomManager.shared.backgroundContextCall = call
|
||||
TelecomManager.shared.backgroundContextCameraIsEnabled = call?.params?.videoEnabled == true || call?.callLog?.wasConference() == true
|
||||
if #available(iOS 16.0, *) {
|
||||
if call?.cameraEnabled == true {
|
||||
call?.cameraEnabled = AVCaptureSession().isMultitaskingCameraAccessSupported
|
||||
}
|
||||
} else {
|
||||
call?.cameraEnabled = false // Disable camera while app is not on foreground
|
||||
}
|
||||
} else {
|
||||
call?.cameraEnabled = false // Disable camera while app is not on foreground
|
||||
}
|
||||
}
|
||||
TelecomManager.shared.callkitAudioSessionActivated = false
|
||||
|
|
@ -242,7 +244,7 @@ extension ProviderDelegate: CXProviderDelegate {
|
|||
action.fulfill()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func provider(_ provider: CXProvider, perform action: CXSetHeldCallAction) {
|
||||
let uuid = action.callUUID
|
||||
let callId = callInfos[uuid]?.callId ?? ""
|
||||
|
|
@ -275,29 +277,29 @@ extension ProviderDelegate: CXProviderDelegate {
|
|||
action.fulfill()
|
||||
} else {
|
||||
if call?.conference != nil && core.callsNb > 1 {/*
|
||||
try TelecomManager.shared.lc?.enterConference()
|
||||
action.fulfill()
|
||||
NotificationCenter.default.post(name: Notification.Name("LinphoneCallUpdate"), object: self)
|
||||
*/} else {
|
||||
try call!.resume()
|
||||
// We'll notify callkit that the action is fulfilled when receiving the 200Ok, which is the point
|
||||
// where we actually start the media streams.
|
||||
TelecomManager.shared.actionToFulFill = action
|
||||
// HORRIBLE HACK HERE - PLEASE APPLE FIX THIS !!
|
||||
// When resuming a SIP call after a native call has ended remotely, didActivate: audioSession
|
||||
// is never called.
|
||||
// It looks like in this case, it is implicit.
|
||||
// As a result we have to notify the Core that the AudioSession is active.
|
||||
// The SpeakerBox demo application written by Apple exhibits this behavior.
|
||||
// https://developer.apple.com/documentation/callkit/making_and_receiving_voip_calls_with_callkit
|
||||
// We can clearly see there that startAudio() is called immediately in the CXSetHeldCallAction
|
||||
// handler, while it is called from didActivate: audioSession otherwise.
|
||||
// Callkit's design is not consistent, or its documentation imcomplete, wich is somewhat disapointing.
|
||||
//
|
||||
Log.info("Assuming AudioSession is active when executing a CXSetHeldCallAction with isOnHold=false.")
|
||||
core.activateAudioSession(actived: true)
|
||||
TelecomManager.shared.callkitAudioSessionActivated = true
|
||||
}
|
||||
try TelecomManager.shared.lc?.enterConference()
|
||||
action.fulfill()
|
||||
NotificationCenter.default.post(name: Notification.Name("LinphoneCallUpdate"), object: self)
|
||||
*/} else {
|
||||
try call!.resume()
|
||||
// We'll notify callkit that the action is fulfilled when receiving the 200Ok, which is the point
|
||||
// where we actually start the media streams.
|
||||
TelecomManager.shared.actionToFulFill = action
|
||||
// HORRIBLE HACK HERE - PLEASE APPLE FIX THIS !!
|
||||
// When resuming a SIP call after a native call has ended remotely, didActivate: audioSession
|
||||
// is never called.
|
||||
// It looks like in this case, it is implicit.
|
||||
// As a result we have to notify the Core that the AudioSession is active.
|
||||
// The SpeakerBox demo application written by Apple exhibits this behavior.
|
||||
// https://developer.apple.com/documentation/callkit/making_and_receiving_voip_calls_with_callkit
|
||||
// We can clearly see there that startAudio() is called immediately in the CXSetHeldCallAction
|
||||
// handler, while it is called from didActivate: audioSession otherwise.
|
||||
// Callkit's design is not consistent, or its documentation imcomplete, wich is somewhat disapointing.
|
||||
//
|
||||
Log.info("Assuming AudioSession is active when executing a CXSetHeldCallAction with isOnHold=false.")
|
||||
core.activateAudioSession(actived: true)
|
||||
TelecomManager.shared.callkitAudioSessionActivated = true
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
|
|
@ -332,7 +334,7 @@ extension ProviderDelegate: CXProviderDelegate {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func provider(_ provider: CXProvider, perform action: CXSetGroupCallAction) {
|
||||
CoreContext.shared.doOnCoreQueue { core in
|
||||
Log.info("CallKit: Call grouped callUUid : \(action.callUUID) with callUUID: \(String(describing: action.callUUIDToGroupWith)).")
|
||||
|
|
@ -340,7 +342,7 @@ extension ProviderDelegate: CXProviderDelegate {
|
|||
action.fulfill()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
|
||||
let uuid = action.callUUID
|
||||
let callId = callInfos[uuid]?.callId
|
||||
|
|
@ -350,7 +352,7 @@ extension ProviderDelegate: CXProviderDelegate {
|
|||
action.fulfill()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func provider(_ provider: CXProvider, perform action: CXPlayDTMFCallAction) {
|
||||
let uuid = action.callUUID
|
||||
let callId = callInfos[uuid]?.callId ?? ""
|
||||
|
|
@ -368,18 +370,18 @@ extension ProviderDelegate: CXProviderDelegate {
|
|||
action.fulfill()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func provider(_ provider: CXProvider, timedOutPerforming action: CXAction) {
|
||||
let uuid = action.uuid
|
||||
let callId = callInfos[uuid]?.callId
|
||||
Log.error("CallKit: Call time out with call-id: \(String(describing: callId)) an UUID: \(uuid.description).")
|
||||
action.fulfill()
|
||||
}
|
||||
|
||||
|
||||
func providerDidReset(_ provider: CXProvider) {
|
||||
Log.info("CallKit: did reset.")
|
||||
}
|
||||
|
||||
|
||||
func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
|
||||
CoreContext.shared.doOnCoreQueue { core in
|
||||
Log.info("CallKit: audio session activated.")
|
||||
|
|
@ -387,7 +389,7 @@ extension ProviderDelegate: CXProviderDelegate {
|
|||
TelecomManager.shared.callkitAudioSessionActivated = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
|
||||
CoreContext.shared.doOnCoreQueue { core in
|
||||
Log.info("CallKit: audio session deactivated.")
|
||||
|
|
|
|||
|
|
@ -41,9 +41,11 @@ class TelecomManager: ObservableObject {
|
|||
let callController: CXCallController // to support callkit
|
||||
|
||||
@Published var callInProgress: Bool = false
|
||||
@Published var callStarted: Bool = false
|
||||
@Published var callStarted: Bool = false
|
||||
@Published var outgoingCallStarted: Bool = false
|
||||
@Published var remoteVideo: Bool = false
|
||||
@Published var isRemoteRecording: Bool = false
|
||||
@Published var isRecordingByRemote: Bool = false
|
||||
@Published var isPausedByRemote: Bool = false
|
||||
|
||||
var actionToFulFill: CXCallAction?
|
||||
var callkitAudioSessionActivated: Bool?
|
||||
|
|
@ -118,15 +120,15 @@ class TelecomManager: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
func doCallWithCore(addr: Address) {
|
||||
CoreContext.shared.doOnCoreQueue { core in
|
||||
func doCallWithCore(addr: Address) {
|
||||
CoreContext.shared.doOnCoreQueue { core in
|
||||
do {
|
||||
try self.startCallCallKit(core: core, addr: addr, isSas: false, isVideo: false, isConference: false)
|
||||
} catch {
|
||||
Log.error("[TelecomManager] unable to create address for a new outgoing call : \(addr) \(error) ")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func makeRecordFilePath() -> String{
|
||||
var filePath = "recording_"
|
||||
|
|
@ -140,25 +142,25 @@ class TelecomManager: ObservableObject {
|
|||
let writablePath = paths[0]
|
||||
return writablePath.appending("/\(filePath)")
|
||||
}
|
||||
|
||||
|
||||
func doCall(core: Core, addr: Address, isSas: Bool, isVideo: Bool, isConference: Bool = false) throws {
|
||||
// let displayName = FastAddressBook.displayName(for: addr.getCobject)
|
||||
|
||||
let lcallParams = try core.createCallParams(call: nil)
|
||||
/*
|
||||
if ConfigManager.instance().lpConfigBoolForKey(key: "edge_opt_preference") && AppManager.network() == .network_2g {
|
||||
Log.directLog(BCTBX_LOG_MESSAGE, text: "Enabling low bandwidth mode")
|
||||
lcallParams.lowBandwidthEnabled = true
|
||||
}
|
||||
|
||||
if (displayName != nil) {
|
||||
try addr.setDisplayname(newValue: displayName!)
|
||||
}
|
||||
|
||||
if(ConfigManager.instance().lpConfigBoolForKey(key: "override_domain_with_default_one")) {
|
||||
try addr.setDomain(newValue: ConfigManager.instance().lpConfigStringForKey(key: "domain", section: "assistant"))
|
||||
}
|
||||
*/
|
||||
if ConfigManager.instance().lpConfigBoolForKey(key: "edge_opt_preference") && AppManager.network() == .network_2g {
|
||||
Log.directLog(BCTBX_LOG_MESSAGE, text: "Enabling low bandwidth mode")
|
||||
lcallParams.lowBandwidthEnabled = true
|
||||
}
|
||||
|
||||
if (displayName != nil) {
|
||||
try addr.setDisplayname(newValue: displayName!)
|
||||
}
|
||||
|
||||
if(ConfigManager.instance().lpConfigBoolForKey(key: "override_domain_with_default_one")) {
|
||||
try addr.setDomain(newValue: ConfigManager.instance().lpConfigStringForKey(key: "domain", section: "assistant"))
|
||||
}
|
||||
*/
|
||||
|
||||
if nextCallIsTransfer {
|
||||
let call = core.currentCall
|
||||
|
|
@ -176,13 +178,13 @@ class TelecomManager: ObservableObject {
|
|||
lcallParams.mediaEncryption = .ZRTP
|
||||
}
|
||||
if isConference {
|
||||
/* if (ConferenceWaitingRoomViewModel.sharedModel.joinLayout.value! != .AudioOnly) {
|
||||
lcallParams.videoEnabled = true
|
||||
lcallParams.videoDirection = ConferenceWaitingRoomViewModel.sharedModel.isVideoEnabled.value == true ? .SendRecv : .RecvOnly
|
||||
lcallParams.conferenceVideoLayout = ConferenceWaitingRoomViewModel.sharedModel.joinLayout.value! == .Grid ? .Grid : .ActiveSpeaker
|
||||
} else {
|
||||
lcallParams.videoEnabled = false
|
||||
}*/
|
||||
/* if (ConferenceWaitingRoomViewModel.sharedModel.joinLayout.value! != .AudioOnly) {
|
||||
lcallParams.videoEnabled = true
|
||||
lcallParams.videoDirection = ConferenceWaitingRoomViewModel.sharedModel.isVideoEnabled.value == true ? .SendRecv : .RecvOnly
|
||||
lcallParams.conferenceVideoLayout = ConferenceWaitingRoomViewModel.sharedModel.joinLayout.value! == .Grid ? .Grid : .ActiveSpeaker
|
||||
} else {
|
||||
lcallParams.videoEnabled = false
|
||||
}*/
|
||||
} else {
|
||||
lcallParams.videoEnabled = isVideo
|
||||
}
|
||||
|
|
@ -202,7 +204,7 @@ class TelecomManager: ObservableObject {
|
|||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
|
||||
self.outgoingCallStarted = true
|
||||
self.callStarted = true
|
||||
if self.callInProgress == false {
|
||||
withAnimation {
|
||||
|
|
@ -220,12 +222,12 @@ class TelecomManager: ObservableObject {
|
|||
callParams.recordFile = makeRecordFilePath()
|
||||
callParams.videoEnabled = hasVideo
|
||||
/*if (ConfigManager.instance().lpConfigBoolForKey(key: "edge_opt_preference")) {
|
||||
let low_bandwidth = (AppManager.network() == .network_2g)
|
||||
if (low_bandwidth) {
|
||||
Log.directLog(BCTBX_LOG_MESSAGE, text: "Low bandwidth mode")
|
||||
}
|
||||
callParams.lowBandwidthEnabled = low_bandwidth
|
||||
}*/
|
||||
let low_bandwidth = (AppManager.network() == .network_2g)
|
||||
if (low_bandwidth) {
|
||||
Log.directLog(BCTBX_LOG_MESSAGE, text: "Low bandwidth mode")
|
||||
}
|
||||
callParams.lowBandwidthEnabled = low_bandwidth
|
||||
}*/
|
||||
|
||||
// We set the record file name here because we can't do it after the call is started.
|
||||
// let address = call.callLog?.fromAddress
|
||||
|
|
@ -234,10 +236,10 @@ class TelecomManager: ObservableObject {
|
|||
// callParams.recordFile = writablePath
|
||||
|
||||
/*
|
||||
if let chatView : ChatConversationView = PhoneMainView.instance().VIEW(ChatConversationView.compositeViewDescription()), chatView.isVoiceRecording {
|
||||
Log.directLog(BCTBX_LOG_MESSAGE, text: "Voice recording in progress, stopping it befoce accepting the call.")
|
||||
chatView.stopVoiceRecording()
|
||||
}*/
|
||||
if let chatView : ChatConversationView = PhoneMainView.instance().VIEW(ChatConversationView.compositeViewDescription()), chatView.isVoiceRecording {
|
||||
Log.directLog(BCTBX_LOG_MESSAGE, text: "Voice recording in progress, stopping it befoce accepting the call.")
|
||||
chatView.stopVoiceRecording()
|
||||
}*/
|
||||
|
||||
if call.callLog?.wasConference() == true {
|
||||
// Prevent incoming group call to start in audio only layout
|
||||
|
|
@ -249,9 +251,9 @@ class TelecomManager: ObservableObject {
|
|||
|
||||
try call.acceptWithParams(params: callParams)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.callStarted = true
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
self.callStarted = true
|
||||
}
|
||||
} catch {
|
||||
Log.error("accept call failed \(error)")
|
||||
}
|
||||
|
|
@ -334,17 +336,18 @@ class TelecomManager: ObservableObject {
|
|||
if cstate == .PushIncomingReceived {
|
||||
displayIncomingCall(call: call, handle: "Calling", hasVideo: false, callId: callId, displayName: "Calling")
|
||||
} else {
|
||||
remoteVideo = (core.videoActivationPolicy?.automaticallyAccept ?? false) && (call.remoteParams?.videoEnabled ?? false)
|
||||
|
||||
if remoteVideo {
|
||||
Log.info("[Call] Remote video is activated")
|
||||
}
|
||||
|
||||
isRemoteRecording = call.remoteParams?.isRecording ?? false
|
||||
|
||||
if isRemoteRecording && ToastViewModel.shared.toastMessage.isEmpty {
|
||||
DispatchQueue.main.async {
|
||||
self.remoteVideo = (core.videoActivationPolicy?.automaticallyAccept ?? false) && (call.remoteParams?.videoEnabled ?? false)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
if self.remoteVideo {
|
||||
Log.info("[Call] Remote video is activated")
|
||||
}
|
||||
|
||||
self.isRecordingByRemote = call.remoteParams?.isRecording ?? false
|
||||
|
||||
if self.isRecordingByRemote && ToastViewModel.shared.toastMessage.isEmpty {
|
||||
|
||||
var displayName = ""
|
||||
let friend = ContactsManager.shared.getFriendWithAddress(address: call.remoteAddress!)
|
||||
if friend != nil && friend!.address != nil && friend!.address!.displayName != nil {
|
||||
|
|
@ -358,22 +361,27 @@ class TelecomManager: ObservableObject {
|
|||
}
|
||||
|
||||
ToastViewModel.shared.toastMessage = "\(displayName) is recording"
|
||||
ToastViewModel.shared.displayToast = true
|
||||
ToastViewModel.shared.displayToast = true
|
||||
|
||||
Log.info("[Call] Call is recording by \(call.remoteAddress!.asStringUriOnly())")
|
||||
}
|
||||
|
||||
Log.info("[Call] Call is recording by \(call.remoteAddress!.asStringUriOnly())")
|
||||
}
|
||||
|
||||
if !isRemoteRecording && ToastViewModel.shared.toastMessage.contains("is recording") {
|
||||
|
||||
DispatchQueue.main.async {
|
||||
if !self.isRecordingByRemote && ToastViewModel.shared.toastMessage.contains("is recording") {
|
||||
|
||||
withAnimation {
|
||||
ToastViewModel.shared.toastMessage = ""
|
||||
ToastViewModel.shared.displayToast = false
|
||||
}
|
||||
|
||||
Log.info("[Call] Recording is stopped by \(call.remoteAddress!.asStringUriOnly())")
|
||||
}
|
||||
|
||||
Log.info("[Call] Call is recording Stop recording by \(call.remoteAddress!.asStringUriOnly())")
|
||||
switch call.state {
|
||||
case Call.State.PausedByRemote:
|
||||
self.isPausedByRemote = true
|
||||
default:
|
||||
self.isPausedByRemote = false
|
||||
}
|
||||
}
|
||||
|
||||
if call.userData == nil {
|
||||
|
|
@ -381,24 +389,28 @@ class TelecomManager: ObservableObject {
|
|||
TelecomManager.setAppData(sCall: call, appData: appData)
|
||||
}
|
||||
/*
|
||||
if let conference = call.conference, ConferenceViewModel.shared.conference.value == nil {
|
||||
Log.info("[Call] Found conference attached to call and no conference in dedicated view model, init & configure it")
|
||||
ConferenceViewModel.shared.initConference(conference)
|
||||
ConferenceViewModel.shared.configureConference(conference)
|
||||
}
|
||||
*/
|
||||
if let conference = call.conference, ConferenceViewModel.shared.conference.value == nil {
|
||||
Log.info("[Call] Found conference attached to call and no conference in dedicated view model, init & configure it")
|
||||
ConferenceViewModel.shared.initConference(conference)
|
||||
ConferenceViewModel.shared.configureConference(conference)
|
||||
}
|
||||
*/
|
||||
switch cstate {
|
||||
case .IncomingReceived:
|
||||
let addr = call.remoteAddress
|
||||
let displayName = incomingDisplayName(call: call)
|
||||
|
||||
#if targetEnvironment(simulator)
|
||||
#if targetEnvironment(simulator)
|
||||
DispatchQueue.main.async {
|
||||
withAnimation {
|
||||
TelecomManager.shared.callInProgress = true
|
||||
self.outgoingCallStarted = false
|
||||
self.callStarted = true
|
||||
if self.callInProgress == false {
|
||||
withAnimation {
|
||||
self.callInProgress = true
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if call.replacedCall != nil {
|
||||
endCallKitReplacedCall = false
|
||||
|
|
@ -415,17 +427,17 @@ class TelecomManager: ObservableObject {
|
|||
} else if TelecomManager.callKitEnabled(core: core) {
|
||||
/*
|
||||
let isConference = isConferenceCall(call: call)
|
||||
let isEarlyConference = isConference && CallsViewModel.shared.currentCallData.value??.isConferenceCall.value != true // Conference info not be received yet.
|
||||
if (isEarlyConference) {
|
||||
CallsViewModel.shared.currentCallData.readCurrentAndObserve { _ in
|
||||
let uuid = providerDelegate.uuids["\(callId)"]
|
||||
if (uuid != nil) {
|
||||
displayName = "\(VoipTexts.conference_incoming_title): \(CallsViewModel.shared.currentCallData.value??.remoteConferenceSubject.value ?? "") (\(CallsViewModel.shared.currentCallData.value??.conferenceParticipantsCountLabel.value ?? ""))"
|
||||
providerDelegate.updateCall(uuid: uuid!, handle: addr!.asStringUriOnly(), hasVideo: video, displayName: displayName)
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
let isEarlyConference = isConference && CallsViewModel.shared.currentCallData.value??.isConferenceCall.value != true // Conference info not be received yet.
|
||||
if (isEarlyConference) {
|
||||
CallsViewModel.shared.currentCallData.readCurrentAndObserve { _ in
|
||||
let uuid = providerDelegate.uuids["\(callId)"]
|
||||
if (uuid != nil) {
|
||||
displayName = "\(VoipTexts.conference_incoming_title): \(CallsViewModel.shared.currentCallData.value??.remoteConferenceSubject.value ?? "") (\(CallsViewModel.shared.currentCallData.value??.conferenceParticipantsCountLabel.value ?? ""))"
|
||||
providerDelegate.updateCall(uuid: uuid!, handle: addr!.asStringUriOnly(), hasVideo: video, displayName: displayName)
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
let uuid = providerDelegate.uuids["\(callId)"]
|
||||
if call.replacedCall == nil {
|
||||
TelecomManager.uuidReplacedCall = callId
|
||||
|
|
@ -438,18 +450,23 @@ class TelecomManager: ObservableObject {
|
|||
displayIncomingCall(call: call, handle: addr!.asStringUriOnly(), hasVideo: remoteVideo, callId: callId, displayName: displayName)
|
||||
}
|
||||
} /* else if UIApplication.shared.applicationState != .active {
|
||||
// not support callkit , use notif
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = NSLocalizedString("Incoming call", comment: "")
|
||||
content.body = displayName
|
||||
content.sound = UNNotificationSound.init(named: UNNotificationSoundName.init("notes_of_the_optimistic.caf"))
|
||||
content.categoryIdentifier = "call_cat"
|
||||
content.userInfo = ["CallId": callId]
|
||||
let req = UNNotificationRequest.init(identifier: "call_request", content: content, trigger: nil)
|
||||
UNUserNotificationCenter.current().add(req, withCompletionHandler: nil)
|
||||
} */
|
||||
// not support callkit , use notif
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = NSLocalizedString("Incoming call", comment: "")
|
||||
content.body = displayName
|
||||
content.sound = UNNotificationSound.init(named: UNNotificationSoundName.init("notes_of_the_optimistic.caf"))
|
||||
content.categoryIdentifier = "call_cat"
|
||||
content.userInfo = ["CallId": callId]
|
||||
let req = UNNotificationRequest.init(identifier: "call_request", content: content, trigger: nil)
|
||||
UNUserNotificationCenter.current().add(req, withCompletionHandler: nil)
|
||||
} */
|
||||
case .StreamsRunning:
|
||||
if TelecomManager.callKitEnabled(core: core) {
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.outgoingCallStarted = false
|
||||
}
|
||||
|
||||
let uuid = providerDelegate.uuids["\(callId)"]
|
||||
if uuid != nil {
|
||||
let callInfo = providerDelegate.callInfos[uuid!]
|
||||
|
|
@ -463,10 +480,10 @@ class TelecomManager: ObservableObject {
|
|||
}
|
||||
|
||||
/*
|
||||
if speakerBeforePause {
|
||||
speakerBeforePause = false
|
||||
AudioRouteUtils.routeAudioToSpeaker(core: core)
|
||||
}
|
||||
if speakerBeforePause {
|
||||
speakerBeforePause = false
|
||||
AudioRouteUtils.routeAudioToSpeaker(core: core)
|
||||
}
|
||||
*/
|
||||
|
||||
actionToFulFill?.fulfill()
|
||||
|
|
@ -491,12 +508,12 @@ class TelecomManager: ObservableObject {
|
|||
providerDelegate.reportOutgoingCallStartedConnecting(uuid: uuid!)
|
||||
} else {
|
||||
if false { /* isConferenceCall(call: call) {
|
||||
let uuid = UUID()
|
||||
let callInfo = CallInfo.newOutgoingCallInfo(addr: call.remoteAddress!, isSas: call.params?.mediaEncryption == .ZRTP, displayName: VoipTexts.conference_default_title, isVideo: call.params?.videoEnabled == true, isConference:true)
|
||||
providerDelegate.callInfos.updateValue(callInfo, forKey: uuid)
|
||||
providerDelegate.uuids.updateValue(uuid, forKey: "")
|
||||
providerDelegate.reportOutgoingCallStartedConnecting(uuid: uuid)
|
||||
Core.get().activateAudioSession(actived: true) */
|
||||
let uuid = UUID()
|
||||
let callInfo = CallInfo.newOutgoingCallInfo(addr: call.remoteAddress!, isSas: call.params?.mediaEncryption == .ZRTP, displayName: VoipTexts.conference_default_title, isVideo: call.params?.videoEnabled == true, isConference:true)
|
||||
providerDelegate.callInfos.updateValue(callInfo, forKey: uuid)
|
||||
providerDelegate.uuids.updateValue(uuid, forKey: "")
|
||||
providerDelegate.reportOutgoingCallStartedConnecting(uuid: uuid)
|
||||
Core.get().activateAudioSession(actived: true) */
|
||||
} else {
|
||||
referedToCall = callId
|
||||
}
|
||||
|
|
@ -505,19 +522,6 @@ class TelecomManager: ObservableObject {
|
|||
case .End,
|
||||
.Error:
|
||||
|
||||
DispatchQueue.main.async {
|
||||
withAnimation {
|
||||
self.callInProgress = false
|
||||
self.callStarted = false
|
||||
}
|
||||
}
|
||||
var displayName = "Unknown"
|
||||
if call.dir == .Incoming {
|
||||
displayName = incomingDisplayName(call: call)
|
||||
} else { // if let addr = call.remoteAddress, let contactName = FastAddressBook.displayName(for: addr.getCobject) {
|
||||
displayName = "TODOContactName"
|
||||
}
|
||||
|
||||
UIDevice.current.isProximityMonitoringEnabled = false
|
||||
if core.callsNb == 0 {
|
||||
core.outputAudioDevice = core.defaultOutputAudioDevice
|
||||
|
|
@ -527,18 +531,34 @@ class TelecomManager: ObservableObject {
|
|||
// bluetoothEnabled = false
|
||||
}
|
||||
|
||||
if UIApplication.shared.applicationState != .active && (callLog == nil || callLog?.status == .Missed || callLog?.status == .Aborted || callLog?.status == .EarlyAborted) {
|
||||
// Configure the notification's payload.
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = NSString.localizedUserNotificationString(forKey: NSLocalizedString("Missed call", comment: ""), arguments: nil)
|
||||
content.body = NSString.localizedUserNotificationString(forKey: displayName, arguments: nil)
|
||||
DispatchQueue.main.async {
|
||||
withAnimation {
|
||||
self.outgoingCallStarted = false
|
||||
self.callInProgress = false
|
||||
self.callStarted = false
|
||||
}
|
||||
|
||||
// Deliver the notification.
|
||||
let request = UNNotificationRequest(identifier: "call_request", content: content, trigger: nil) // Schedule the notification.
|
||||
let center = UNUserNotificationCenter.current()
|
||||
center.add(request) { (error: Error?) in
|
||||
if error != nil {
|
||||
Log.info("Error while adding notification request : \(error!.localizedDescription)")
|
||||
var displayName = "Unknown"
|
||||
if call.dir == .Incoming {
|
||||
displayName = self.incomingDisplayName(call: call)
|
||||
} else { // if let addr = call.remoteAddress, let contactName = FastAddressBook.displayName(for: addr.getCobject) {
|
||||
displayName = "TODOContactName"
|
||||
}
|
||||
|
||||
|
||||
if UIApplication.shared.applicationState != .active && (callLog == nil || callLog?.status == .Missed || callLog?.status == .Aborted || callLog?.status == .EarlyAborted) {
|
||||
// Configure the notification's payload.
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = NSString.localizedUserNotificationString(forKey: NSLocalizedString("Missed call", comment: ""), arguments: nil)
|
||||
content.body = NSString.localizedUserNotificationString(forKey: displayName, arguments: nil)
|
||||
|
||||
// Deliver the notification.
|
||||
let request = UNNotificationRequest(identifier: "call_request", content: content, trigger: nil) // Schedule the notification.
|
||||
let center = UNUserNotificationCenter.current()
|
||||
center.add(request) { (error: Error?) in
|
||||
if error != nil {
|
||||
Log.info("Error while adding notification request : \(error!.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -583,22 +603,6 @@ class TelecomManager: ObservableObject {
|
|||
default:
|
||||
break
|
||||
}
|
||||
|
||||
// AudioRouteUtils.isBluetoothAvailable(core: core)
|
||||
// AudioRouteUtils.isHeadsetAudioRouteAvailable(core: core)
|
||||
// AudioRouteUtils.isBluetoothAudioRouteAvailable(core: core)
|
||||
|
||||
/*
|
||||
let readyForRoutechange = callkitAudioSessionActivated == nil || (callkitAudioSessionActivated == true)
|
||||
if readyForRoutechange && (cstate == .IncomingReceived || cstate == .OutgoingInit || cstate == .Connected || cstate == .StreamsRunning) {
|
||||
if (call.currentParams?.videoEnabled ?? false) && AudioRouteUtils.isReceiverEnabled(core: core) && call.conference == nil {
|
||||
AudioRouteUtils.routeAudioToSpeaker(core: core, call: call)
|
||||
} else if AudioRouteUtils.isBluetoothAvailable(core: core) {
|
||||
// Use bluetooth device by default if one is available
|
||||
AudioRouteUtils.routeAudioToBluetooth(core: core, call: call)
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
// post Notification kLinphoneCallUpdate
|
||||
NotificationCenter.default.post(name: Notification.Name("LinphoneCallUpdate"), object: self, userInfo: [
|
||||
|
|
|
|||
|
|
@ -24,10 +24,10 @@ import AVFAudio
|
|||
import linphonesw
|
||||
|
||||
struct CallView: View {
|
||||
|
||||
@ObservedObject private var coreContext = CoreContext.shared
|
||||
@ObservedObject private var telecomManager = TelecomManager.shared
|
||||
@ObservedObject private var contactsManager = ContactsManager.shared
|
||||
|
||||
@ObservedObject private var coreContext = CoreContext.shared
|
||||
@ObservedObject private var telecomManager = TelecomManager.shared
|
||||
@ObservedObject private var contactsManager = ContactsManager.shared
|
||||
|
||||
@ObservedObject var callViewModel: CallViewModel
|
||||
|
||||
|
|
@ -35,8 +35,8 @@ struct CallView: View {
|
|||
@State private var orientation = UIDevice.current.orientation
|
||||
|
||||
let pub = NotificationCenter.default.publisher(for: AVAudioSession.routeChangeNotification)
|
||||
|
||||
@State var startDate = Date.now
|
||||
|
||||
@State var startDate = Date.now
|
||||
@State var audioRouteSheet: Bool = false
|
||||
@State var hideButtonsSheet: Bool = false
|
||||
@State var options: Int = 1
|
||||
|
|
@ -45,10 +45,10 @@ struct CallView: View {
|
|||
|
||||
@State var angleDegree = 0.0
|
||||
@State var fullscreenVideo = false
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geo in
|
||||
if #available(iOS 16.4, *) {
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geo in
|
||||
if #available(iOS 16.4, *) {
|
||||
innerView(geometry: geo)
|
||||
.sheet(isPresented:
|
||||
.constant(
|
||||
|
|
@ -60,54 +60,55 @@ struct CallView: View {
|
|||
)
|
||||
) {
|
||||
GeometryReader { _ in
|
||||
VStack(spacing: 0) {
|
||||
HStack(spacing: 12) {
|
||||
Button {
|
||||
VStack(spacing: 0) {
|
||||
HStack(spacing: 12) {
|
||||
Button {
|
||||
callViewModel.terminateCall()
|
||||
} label: {
|
||||
Image("phone-disconnect")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 32, height: 32)
|
||||
|
||||
}
|
||||
.frame(width: 90, height: 60)
|
||||
.background(Color.redDanger500)
|
||||
.cornerRadius(40)
|
||||
|
||||
Spacer()
|
||||
|
||||
Button {
|
||||
} label: {
|
||||
Image("phone-disconnect")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 32, height: 32)
|
||||
|
||||
}
|
||||
.frame(width: 90, height: 60)
|
||||
.background(Color.redDanger500)
|
||||
.cornerRadius(40)
|
||||
|
||||
Spacer()
|
||||
|
||||
Button {
|
||||
callViewModel.toggleVideo()
|
||||
} label: {
|
||||
} label: {
|
||||
Image(callViewModel.cameraDisplayed ? "video-camera" : "video-camera-slash")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 32, height: 32)
|
||||
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
.background(Color.gray500)
|
||||
.cornerRadius(40)
|
||||
|
||||
Button {
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle((callViewModel.isPaused || telecomManager.isPausedByRemote) ? Color.gray500 : .white)
|
||||
.frame(width: 32, height: 32)
|
||||
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
.background((callViewModel.isPaused || telecomManager.isPausedByRemote) ? Color.gray600 : Color.gray500)
|
||||
.cornerRadius(40)
|
||||
.disabled(callViewModel.isPaused || telecomManager.isPausedByRemote)
|
||||
|
||||
Button {
|
||||
callViewModel.toggleMuteMicrophone()
|
||||
} label: {
|
||||
} label: {
|
||||
Image(callViewModel.micMutted ? "microphone-slash" : "microphone")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(callViewModel.micMutted ? .black : .white)
|
||||
.frame(width: 32, height: 32)
|
||||
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
.frame(width: 32, height: 32)
|
||||
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
.background(callViewModel.micMutted ? .white : Color.gray500)
|
||||
.cornerRadius(40)
|
||||
|
||||
Button {
|
||||
if AVAudioSession.sharedInstance().availableInputs != nil
|
||||
.cornerRadius(40)
|
||||
|
||||
Button {
|
||||
if AVAudioSession.sharedInstance().availableInputs != nil
|
||||
&& !AVAudioSession.sharedInstance().availableInputs!.filter({ $0.portType.rawValue.contains("Bluetooth") }).isEmpty {
|
||||
|
||||
hideButtonsSheet = true
|
||||
|
|
@ -123,200 +124,206 @@ struct CallView: View {
|
|||
}
|
||||
}
|
||||
|
||||
} label: {
|
||||
} label: {
|
||||
Image(imageAudioRoute)
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 32, height: 32)
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 32, height: 32)
|
||||
.onAppear(perform: getAudioRouteImage)
|
||||
.onReceive(pub) { (output) in
|
||||
self.getAudioRouteImage()
|
||||
}
|
||||
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
.background(Color.gray500)
|
||||
.cornerRadius(40)
|
||||
}
|
||||
.frame(height: geo.size.height * 0.15)
|
||||
.padding(.horizontal, 20)
|
||||
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
.background(Color.gray500)
|
||||
.cornerRadius(40)
|
||||
}
|
||||
.frame(height: geo.size.height * 0.15)
|
||||
.padding(.horizontal, 20)
|
||||
.padding(.top, -6)
|
||||
|
||||
HStack(spacing: 0) {
|
||||
VStack {
|
||||
Button {
|
||||
} label: {
|
||||
Image("screencast")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 32, height: 32)
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
.background(Color.gray500)
|
||||
.cornerRadius(40)
|
||||
|
||||
Text("Screen share")
|
||||
.foregroundStyle(.white)
|
||||
.default_text_style(styleSize: 15)
|
||||
}
|
||||
.frame(width: geo.size.width * 0.25, height: geo.size.width * 0.25)
|
||||
|
||||
VStack {
|
||||
Button {
|
||||
} label: {
|
||||
Image("users")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 32, height: 32)
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
.background(Color.gray500)
|
||||
.cornerRadius(40)
|
||||
|
||||
Text("Participants")
|
||||
.foregroundStyle(.white)
|
||||
.default_text_style(styleSize: 15)
|
||||
}
|
||||
.frame(width: geo.size.width * 0.25, height: geo.size.width * 0.25)
|
||||
|
||||
VStack {
|
||||
Button {
|
||||
} label: {
|
||||
Image("chat-teardrop-text")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 32, height: 32)
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
.background(Color.gray500)
|
||||
.cornerRadius(40)
|
||||
|
||||
Text("Messages")
|
||||
.foregroundStyle(.white)
|
||||
.default_text_style(styleSize: 15)
|
||||
}
|
||||
.frame(width: geo.size.width * 0.25, height: geo.size.width * 0.25)
|
||||
|
||||
VStack {
|
||||
Button {
|
||||
} label: {
|
||||
Image("notebook")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 32, height: 32)
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
.background(Color.gray500)
|
||||
.cornerRadius(40)
|
||||
|
||||
Text("Disposition")
|
||||
.foregroundStyle(.white)
|
||||
.default_text_style(styleSize: 15)
|
||||
}
|
||||
.frame(width: geo.size.width * 0.25, height: geo.size.width * 0.25)
|
||||
}
|
||||
.frame(height: geo.size.height * 0.15)
|
||||
|
||||
HStack(spacing: 0) {
|
||||
VStack {
|
||||
Button {
|
||||
} label: {
|
||||
Image("phone-call")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 32, height: 32)
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
.background(Color.gray500)
|
||||
.cornerRadius(40)
|
||||
|
||||
Text("Call list")
|
||||
.foregroundStyle(.white)
|
||||
.default_text_style(styleSize: 15)
|
||||
}
|
||||
.frame(width: geo.size.width * 0.25, height: geo.size.width * 0.25)
|
||||
|
||||
VStack {
|
||||
Button {
|
||||
|
||||
HStack(spacing: 0) {
|
||||
VStack {
|
||||
Button {
|
||||
} label: {
|
||||
Image("phone-transfer")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 32, height: 32)
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
.background(Color.gray500)
|
||||
.cornerRadius(40)
|
||||
|
||||
Text("Transfer")
|
||||
.foregroundStyle(.white)
|
||||
.default_text_style(styleSize: 15)
|
||||
}
|
||||
.frame(width: geo.size.width * 0.25, height: geo.size.width * 0.25)
|
||||
|
||||
VStack {
|
||||
Button {
|
||||
} label: {
|
||||
Image("phone-plus")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 32, height: 32)
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
.background(Color.gray500)
|
||||
.cornerRadius(40)
|
||||
|
||||
Text("New call")
|
||||
.foregroundStyle(.white)
|
||||
.default_text_style(styleSize: 15)
|
||||
}
|
||||
.frame(width: geo.size.width * 0.25, height: geo.size.width * 0.25)
|
||||
|
||||
VStack {
|
||||
Button {
|
||||
} label: {
|
||||
Image("phone-list")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 32, height: 32)
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
.background(Color.gray500)
|
||||
.cornerRadius(40)
|
||||
|
||||
Text("Call list")
|
||||
.foregroundStyle(.white)
|
||||
.default_text_style(styleSize: 15)
|
||||
}
|
||||
.frame(width: geo.size.width * 0.25, height: geo.size.width * 0.25)
|
||||
|
||||
VStack {
|
||||
Button {
|
||||
} label: {
|
||||
Image("dialer")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 32, height: 32)
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
.background(Color.gray500)
|
||||
.cornerRadius(40)
|
||||
|
||||
Text("Dialer")
|
||||
.foregroundStyle(.white)
|
||||
.default_text_style(styleSize: 15)
|
||||
}
|
||||
.frame(width: geo.size.width * 0.25, height: geo.size.width * 0.25)
|
||||
}
|
||||
.frame(height: geo.size.height * 0.15)
|
||||
|
||||
HStack(spacing: 0) {
|
||||
VStack {
|
||||
Button {
|
||||
} label: {
|
||||
Image("chat-teardrop-text")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
//.foregroundStyle((callViewModel.isPaused || telecomManager.isPausedByRemote) ? Color.gray500 : .white)
|
||||
.foregroundStyle(Color.gray500)
|
||||
.frame(width: 32, height: 32)
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
//.background((callViewModel.isPaused || telecomManager.isPausedByRemote) ? Color.gray600 : Color.gray500)
|
||||
.background(Color.gray600)
|
||||
.cornerRadius(40)
|
||||
//.disabled(callViewModel.isPaused || telecomManager.isPausedByRemote)
|
||||
.disabled(true)
|
||||
|
||||
Text("Messages")
|
||||
.foregroundStyle(.white)
|
||||
.default_text_style(styleSize: 15)
|
||||
}
|
||||
.frame(width: geo.size.width * 0.25, height: geo.size.width * 0.25)
|
||||
|
||||
VStack {
|
||||
Button {
|
||||
callViewModel.togglePause()
|
||||
} label: {
|
||||
Image("pause")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 32, height: 32)
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
.background(Color.gray500)
|
||||
.cornerRadius(40)
|
||||
|
||||
Text("Pause")
|
||||
.foregroundStyle(.white)
|
||||
.default_text_style(styleSize: 15)
|
||||
}
|
||||
.frame(width: geo.size.width * 0.25, height: geo.size.width * 0.25)
|
||||
|
||||
VStack {
|
||||
Button {
|
||||
} label: {
|
||||
Image(callViewModel.isPaused ? "play" : "pause")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(telecomManager.isPausedByRemote ? Color.gray500 : .white)
|
||||
.frame(width: 32, height: 32)
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
.background(telecomManager.isPausedByRemote ? Color.gray600 : (callViewModel.isPaused ? Color.greenSuccess500 : Color.gray500))
|
||||
.cornerRadius(40)
|
||||
.disabled(telecomManager.isPausedByRemote)
|
||||
|
||||
Text("Pause")
|
||||
.foregroundStyle(.white)
|
||||
.default_text_style(styleSize: 15)
|
||||
}
|
||||
.frame(width: geo.size.width * 0.25, height: geo.size.width * 0.25)
|
||||
|
||||
VStack {
|
||||
Button {
|
||||
callViewModel.toggleRecording()
|
||||
} label: {
|
||||
Image("record-fill")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 32, height: 32)
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
.background(callViewModel.isRecording ? Color.redDanger500 : Color.gray500)
|
||||
.cornerRadius(40)
|
||||
|
||||
Text("Record")
|
||||
.foregroundStyle(.white)
|
||||
.default_text_style(styleSize: 15)
|
||||
}
|
||||
.frame(width: geo.size.width * 0.25, height: geo.size.width * 0.25)
|
||||
|
||||
VStack {
|
||||
Button {
|
||||
} label: {
|
||||
Image("video-camera")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 32, height: 32)
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
.background(Color.gray500)
|
||||
.cornerRadius(40)
|
||||
|
||||
Text("Disposition")
|
||||
.foregroundStyle(.white)
|
||||
.default_text_style(styleSize: 15)
|
||||
}
|
||||
.frame(width: geo.size.width * 0.25, height: geo.size.width * 0.25)
|
||||
.hidden()
|
||||
}
|
||||
.frame(height: geo.size.height * 0.15)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.frame(maxHeight: .infinity, alignment: .top)
|
||||
} label: {
|
||||
Image("record-fill")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle((callViewModel.isPaused || telecomManager.isPausedByRemote) ? Color.gray500 : .white)
|
||||
.frame(width: 32, height: 32)
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
.background((callViewModel.isPaused || telecomManager.isPausedByRemote) ? Color.gray600 : (callViewModel.isRecording ? Color.redDanger500 : Color.gray500))
|
||||
.cornerRadius(40)
|
||||
.disabled(callViewModel.isPaused || telecomManager.isPausedByRemote)
|
||||
|
||||
Text("Record")
|
||||
.foregroundStyle(.white)
|
||||
.default_text_style(styleSize: 15)
|
||||
}
|
||||
.frame(width: geo.size.width * 0.25, height: geo.size.width * 0.25)
|
||||
|
||||
VStack {
|
||||
Button {
|
||||
} label: {
|
||||
Image("video-camera")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 32, height: 32)
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
.background(Color.gray500)
|
||||
.cornerRadius(40)
|
||||
|
||||
Text("Disposition")
|
||||
.foregroundStyle(.white)
|
||||
.default_text_style(styleSize: 15)
|
||||
}
|
||||
.frame(width: geo.size.width * 0.25, height: geo.size.width * 0.25)
|
||||
.hidden()
|
||||
}
|
||||
.frame(height: geo.size.height * 0.15)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.frame(maxHeight: .infinity, alignment: .top)
|
||||
.presentationBackground(.black)
|
||||
.presentationDetents([.fraction(0.1), .medium])
|
||||
.interactiveDismissDisabled()
|
||||
.presentationBackgroundInteraction(.enabled)
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $audioRouteSheet, onDismiss: {
|
||||
.presentationDetents([.fraction(0.1), .fraction(0.45)])
|
||||
.interactiveDismissDisabled()
|
||||
.presentationBackgroundInteraction(.enabled)
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $audioRouteSheet, onDismiss: {
|
||||
audioRouteSheet = false
|
||||
hideButtonsSheet = false
|
||||
}) {
|
||||
}) {
|
||||
VStack(spacing: 0) {
|
||||
Button(action: {
|
||||
options = 1
|
||||
|
|
@ -346,9 +353,9 @@ struct CallView: View {
|
|||
|
||||
Image(!callViewModel.isHeadPhoneAvailable() ? "ear" : "headset")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.frame(width: 25, height: 25, alignment: .leading)
|
||||
.frame(width: 25, height: 25, alignment: .leading)
|
||||
}
|
||||
})
|
||||
.frame(maxHeight: .infinity)
|
||||
|
|
@ -416,16 +423,16 @@ struct CallView: View {
|
|||
}
|
||||
.padding(.horizontal, 20)
|
||||
.presentationBackground(Color.gray600)
|
||||
.presentationDetents([.fraction(0.3)])
|
||||
.presentationDetents([.fraction(0.3)])
|
||||
.frame(maxHeight: .infinity)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func innerView(geometry: GeometryProxy) -> some View {
|
||||
VStack {
|
||||
VStack {
|
||||
if !fullscreenVideo {
|
||||
Rectangle()
|
||||
.foregroundColor(Color.orangeMain500)
|
||||
|
|
@ -451,6 +458,35 @@ struct CallView: View {
|
|||
.foregroundStyle(.white)
|
||||
}
|
||||
|
||||
if !telecomManager.outgoingCallStarted && telecomManager.callInProgress {
|
||||
Text("|")
|
||||
.foregroundStyle(.white)
|
||||
|
||||
ZStack {
|
||||
Text(callViewModel.timeElapsed.convertDurationToString())
|
||||
.onAppear {
|
||||
callViewModel.timeElapsed = 0
|
||||
startDate = Date.now
|
||||
}
|
||||
.onReceive(callViewModel.timer) { firedDate in
|
||||
callViewModel.timeElapsed = Int(firedDate.timeIntervalSince(startDate))
|
||||
|
||||
}
|
||||
.foregroundStyle(.white)
|
||||
.if(callViewModel.isPaused || telecomManager.isPausedByRemote) { view in
|
||||
view.hidden()
|
||||
}
|
||||
|
||||
if callViewModel.isPaused {
|
||||
Text("Paused")
|
||||
.foregroundStyle(.white)
|
||||
} else if telecomManager.isPausedByRemote {
|
||||
Text("Paused by remote")
|
||||
.foregroundStyle(.white)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
if callViewModel.cameraDisplayed {
|
||||
|
|
@ -469,65 +505,65 @@ struct CallView: View {
|
|||
.frame(height: 40)
|
||||
.zIndex(1)
|
||||
}
|
||||
|
||||
ZStack {
|
||||
VStack {
|
||||
Spacer()
|
||||
|
||||
if callViewModel.remoteAddress != nil {
|
||||
let addressFriend = contactsManager.getFriendWithAddress(address: callViewModel.remoteAddress!)
|
||||
|
||||
let contactAvatarModel = addressFriend != nil
|
||||
? ContactsManager.shared.avatarListModel.first(where: {
|
||||
($0.friend!.consolidatedPresence == .Online || $0.friend!.consolidatedPresence == .Busy)
|
||||
&& $0.friend!.name == addressFriend!.name
|
||||
&& $0.friend!.address!.asStringUriOnly() == addressFriend!.address!.asStringUriOnly()
|
||||
})
|
||||
: ContactAvatarModel(friend: nil, withPresence: false)
|
||||
|
||||
if addressFriend != nil && addressFriend!.photo != nil && !addressFriend!.photo!.isEmpty {
|
||||
if contactAvatarModel != nil {
|
||||
Avatar(contactAvatarModel: contactAvatarModel!, avatarSize: 100, hidePresence: true)
|
||||
}
|
||||
} else {
|
||||
if callViewModel.remoteAddress!.displayName != nil {
|
||||
Image(uiImage: contactsManager.textToImage(
|
||||
firstName: callViewModel.remoteAddress!.displayName!,
|
||||
lastName: callViewModel.remoteAddress!.displayName!.components(separatedBy: " ").count > 1
|
||||
? callViewModel.remoteAddress!.displayName!.components(separatedBy: " ")[1]
|
||||
: ""))
|
||||
.resizable()
|
||||
.frame(width: 100, height: 100)
|
||||
.clipShape(Circle())
|
||||
|
||||
} else {
|
||||
Image(uiImage: contactsManager.textToImage(
|
||||
firstName: callViewModel.remoteAddress!.username ?? "Username Error",
|
||||
lastName: callViewModel.remoteAddress!.username!.components(separatedBy: " ").count > 1
|
||||
? callViewModel.remoteAddress!.username!.components(separatedBy: " ")[1]
|
||||
: ""))
|
||||
.resizable()
|
||||
.frame(width: 100, height: 100)
|
||||
.clipShape(Circle())
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
Image("profil-picture-default")
|
||||
.resizable()
|
||||
.frame(width: 100, height: 100)
|
||||
.clipShape(Circle())
|
||||
}
|
||||
|
||||
Text(callViewModel.displayName)
|
||||
.padding(.top)
|
||||
.foregroundStyle(.white)
|
||||
|
||||
Text(callViewModel.remoteAddressString)
|
||||
.foregroundStyle(.white)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
|
||||
ZStack {
|
||||
VStack {
|
||||
Spacer()
|
||||
|
||||
if callViewModel.remoteAddress != nil {
|
||||
let addressFriend = contactsManager.getFriendWithAddress(address: callViewModel.remoteAddress!)
|
||||
|
||||
let contactAvatarModel = addressFriend != nil
|
||||
? ContactsManager.shared.avatarListModel.first(where: {
|
||||
($0.friend!.consolidatedPresence == .Online || $0.friend!.consolidatedPresence == .Busy)
|
||||
&& $0.friend!.name == addressFriend!.name
|
||||
&& $0.friend!.address!.asStringUriOnly() == addressFriend!.address!.asStringUriOnly()
|
||||
})
|
||||
: ContactAvatarModel(friend: nil, withPresence: false)
|
||||
|
||||
if addressFriend != nil && addressFriend!.photo != nil && !addressFriend!.photo!.isEmpty {
|
||||
if contactAvatarModel != nil {
|
||||
Avatar(contactAvatarModel: contactAvatarModel!, avatarSize: 100, hidePresence: true)
|
||||
}
|
||||
} else {
|
||||
if callViewModel.remoteAddress!.displayName != nil {
|
||||
Image(uiImage: contactsManager.textToImage(
|
||||
firstName: callViewModel.remoteAddress!.displayName!,
|
||||
lastName: callViewModel.remoteAddress!.displayName!.components(separatedBy: " ").count > 1
|
||||
? callViewModel.remoteAddress!.displayName!.components(separatedBy: " ")[1]
|
||||
: ""))
|
||||
.resizable()
|
||||
.frame(width: 100, height: 100)
|
||||
.clipShape(Circle())
|
||||
|
||||
} else {
|
||||
Image(uiImage: contactsManager.textToImage(
|
||||
firstName: callViewModel.remoteAddress!.username ?? "Username Error",
|
||||
lastName: callViewModel.remoteAddress!.username!.components(separatedBy: " ").count > 1
|
||||
? callViewModel.remoteAddress!.username!.components(separatedBy: " ")[1]
|
||||
: ""))
|
||||
.resizable()
|
||||
.frame(width: 100, height: 100)
|
||||
.clipShape(Circle())
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
Image("profil-picture-default")
|
||||
.resizable()
|
||||
.frame(width: 100, height: 100)
|
||||
.clipShape(Circle())
|
||||
}
|
||||
|
||||
Text(callViewModel.displayName)
|
||||
.padding(.top)
|
||||
.foregroundStyle(.white)
|
||||
|
||||
Text(callViewModel.remoteAddressString)
|
||||
.foregroundStyle(.white)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
|
||||
LinphoneVideoViewHolder { view in
|
||||
coreContext.doOnCoreQueue { core in
|
||||
|
|
@ -535,7 +571,7 @@ struct CallView: View {
|
|||
}
|
||||
}
|
||||
.frame(
|
||||
width:
|
||||
width:
|
||||
angleDegree == 0
|
||||
? 120 * ((geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom) / 160)
|
||||
: 120 * ((geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing) / 120),
|
||||
|
|
@ -577,8 +613,8 @@ struct CallView: View {
|
|||
VStack {
|
||||
Image("record-fill")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(Color.redDanger500)
|
||||
.resizable()
|
||||
.foregroundStyle(Color.redDanger500)
|
||||
.frame(width: 32, height: 32)
|
||||
.padding(10)
|
||||
.if(fullscreenVideo) { view in
|
||||
|
|
@ -594,31 +630,39 @@ struct CallView: View {
|
|||
)
|
||||
}
|
||||
|
||||
if !telecomManager.callStarted && !fullscreenVideo {
|
||||
VStack {
|
||||
ActivityIndicator()
|
||||
.frame(width: 20, height: 20)
|
||||
.padding(.top, 100)
|
||||
|
||||
if telecomManager.outgoingCallStarted {
|
||||
VStack {
|
||||
ActivityIndicator()
|
||||
.frame(width: 20, height: 20)
|
||||
.padding(.top, 100)
|
||||
|
||||
Text(callViewModel.counterToMinutes())
|
||||
.onAppear {
|
||||
callViewModel.timeElapsed = 0
|
||||
startDate = Date.now
|
||||
}
|
||||
.onReceive(callViewModel.timer) { firedDate in
|
||||
callViewModel.timeElapsed = Int(firedDate.timeIntervalSince(startDate))
|
||||
|
||||
}
|
||||
.padding(.top)
|
||||
.foregroundStyle(.white)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.background(.clear)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.padding(.top)
|
||||
.foregroundStyle(.white)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.frame(
|
||||
maxWidth: fullscreenVideo ? geometry.size.width : geometry.size.width - 8,
|
||||
maxHeight: fullscreenVideo ? geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom : geometry.size.height - 140
|
||||
)
|
||||
.background(.clear)
|
||||
}
|
||||
}
|
||||
.frame(
|
||||
maxWidth: fullscreenVideo ? geometry.size.width : geometry.size.width - 8,
|
||||
maxHeight: fullscreenVideo ? geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom : geometry.size.height - 140
|
||||
)
|
||||
.background(Color.gray600)
|
||||
.cornerRadius(20)
|
||||
.background(Color.gray600)
|
||||
.cornerRadius(20)
|
||||
.padding(.horizontal, fullscreenVideo ? 0 : 4)
|
||||
.onRotate { newOrientation in
|
||||
orientation = newOrientation
|
||||
|
|
@ -647,7 +691,7 @@ struct CallView: View {
|
|||
|
||||
callViewModel.orientationUpdate(orientation: orientation)
|
||||
}
|
||||
|
||||
|
||||
if !fullscreenVideo {
|
||||
if telecomManager.callStarted {
|
||||
if telecomManager.callStarted && idiom != .pad && !(orientation == .landscapeLeft || orientation == .landscapeRight
|
||||
|
|
@ -684,13 +728,14 @@ struct CallView: View {
|
|||
Image("video-camera")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle((callViewModel.isPaused || telecomManager.isPausedByRemote) ? Color.gray500 : .white)
|
||||
.frame(width: 32, height: 32)
|
||||
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
.background(Color.gray500)
|
||||
.background((callViewModel.isPaused || telecomManager.isPausedByRemote) ? Color.gray600 : Color.gray500)
|
||||
.cornerRadius(40)
|
||||
.disabled(callViewModel.isPaused || telecomManager.isPausedByRemote)
|
||||
|
||||
Button {
|
||||
callViewModel.toggleMuteMicrophone()
|
||||
|
|
@ -764,13 +809,13 @@ struct CallView: View {
|
|||
.padding(.top, 20)
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.background(Color.gray900)
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.background(Color.gray900)
|
||||
.if(fullscreenVideo) { view in
|
||||
view.ignoresSafeArea(.all)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getAudioRouteImage() {
|
||||
imageAudioRoute = AVAudioSession.sharedInstance().currentRoute.outputs.filter({ $0.portType.rawValue == "Speaker" }).isEmpty
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@ class CallViewModel: ObservableObject {
|
|||
@Published var cameraDisplayed: Bool = false
|
||||
@Published var isRecording: Bool = false
|
||||
@Published var isRemoteRecording: Bool = false
|
||||
@State var timeElapsed: Int = 0
|
||||
@Published var isPaused: Bool = false
|
||||
@Published var timeElapsed: Int = 0
|
||||
|
||||
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
|
||||
|
||||
|
|
@ -76,6 +77,8 @@ class CallViewModel: ObservableObject {
|
|||
self.micMutted = self.currentCall!.microphoneMuted
|
||||
self.cameraDisplayed = self.currentCall!.cameraEnabled == true
|
||||
self.isRecording = self.currentCall!.params!.isRecording
|
||||
self.isPaused = self.isCallPaused()
|
||||
self.timeElapsed = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -83,8 +86,9 @@ class CallViewModel: ObservableObject {
|
|||
|
||||
func terminateCall() {
|
||||
withAnimation {
|
||||
telecomManager.callInProgress = false
|
||||
telecomManager.outgoingCallStarted = false
|
||||
telecomManager.callStarted = false
|
||||
telecomManager.callInProgress = false
|
||||
}
|
||||
|
||||
coreContext.doOnCoreQueue { _ in
|
||||
|
|
@ -98,6 +102,7 @@ class CallViewModel: ObservableObject {
|
|||
|
||||
func acceptCall() {
|
||||
withAnimation {
|
||||
telecomManager.outgoingCallStarted = false
|
||||
telecomManager.callInProgress = true
|
||||
telecomManager.callStarted = true
|
||||
}
|
||||
|
|
@ -184,9 +189,11 @@ class CallViewModel: ObservableObject {
|
|||
if self.isCallPaused() {
|
||||
Log.info("[CallViewModel] Resuming call \(self.currentCall!.remoteAddress!.asStringUriOnly())")
|
||||
try self.currentCall!.resume()
|
||||
self.isPaused = false
|
||||
} else {
|
||||
Log.info("[CallViewModel] Pausing call \(self.currentCall!.remoteAddress!.asStringUriOnly())")
|
||||
try self.currentCall!.pause()
|
||||
self.isPaused = true
|
||||
}
|
||||
} catch _ {
|
||||
|
||||
|
|
@ -195,7 +202,7 @@ class CallViewModel: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
private func isCallPaused() -> Bool {
|
||||
func isCallPaused() -> Bool {
|
||||
var result = false
|
||||
if self.currentCall != nil {
|
||||
switch self.currentCall!.state {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue