diff --git a/Classes/AudioHelper.m b/Classes/AudioHelper.m deleted file mode 100644 index 7298e65b3..000000000 --- a/Classes/AudioHelper.m +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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 . - */ - -#import "AudioHelper.h" - -@implementation AudioHelper - -+ (NSArray *)bluetoothRoutes { - return @[AVAudioSessionPortBluetoothHFP, AVAudioSessionPortCarAudio, AVAudioSessionPortBluetoothA2DP, AVAudioSessionPortBluetoothLE ]; -} - -+ (AVAudioSessionPortDescription *)bluetoothAudioDevice { - return [AudioHelper audioDeviceFromTypes:[AudioHelper bluetoothRoutes]]; -} - -+ (AVAudioSessionPortDescription *)builtinAudioDevice { - NSArray *builtinRoutes = @[ AVAudioSessionPortBuiltInMic ]; - return [AudioHelper audioDeviceFromTypes:builtinRoutes]; -} - -+ (AVAudioSessionPortDescription *)speakerAudioDevice { - NSArray *builtinRoutes = @[ AVAudioSessionPortBuiltInSpeaker ]; - return [AudioHelper audioDeviceFromTypes:builtinRoutes]; -} - -+ (AVAudioSessionPortDescription *)audioDeviceFromTypes:(NSArray *)types { - NSArray *routes = [[AVAudioSession sharedInstance] availableInputs]; - for (AVAudioSessionPortDescription *route in routes) { - if ([types containsObject:route.portType]) { - return route; - } - } - return nil; -} - -@end diff --git a/Classes/Base.lproj/CallIncomingView.xib b/Classes/Base.lproj/CallIncomingView.xib deleted file mode 100644 index ae5bd9336..000000000 --- a/Classes/Base.lproj/CallIncomingView.xib +++ /dev/null @@ -1,317 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Classes/Base.lproj/CallOutgoingView.xib b/Classes/Base.lproj/CallOutgoingView.xib deleted file mode 100644 index 890553e00..000000000 --- a/Classes/Base.lproj/CallOutgoingView.xib +++ /dev/null @@ -1,623 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Classes/Base.lproj/CallView.xib b/Classes/Base.lproj/CallView.xib deleted file mode 100644 index 57a17a2c7..000000000 --- a/Classes/Base.lproj/CallView.xib +++ /dev/null @@ -1,1857 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGvEBELDBkaHxQkKSoxNDdBSUpOUVUkbnVsbNYNDg8QERITFBUW -FxhWTlNTaXplXk5TUmVzaXppbmdNb2RlViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzV05TQ29sb3KA -AhAAgBASIMAAAIADgAtYezMzLCAzM33SGw8cHlpOUy5vYmplY3RzoR2ABIAK0hsPICOiISKABYAGgAnT -DyUmJygUXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGlyZWN0aW9ugAiA -B08RGbpNTQAqAAARDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAABAwMDFQYGBiYGBgYiAgICDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAIGBgYhCQkJNgkJCTEJCQkzCQkJMwMDAxQAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDAxUKCgo2BQUFIAEBAQgCAgIMCAgIKwkJCTEBAQEHAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAEAwMDFAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAYGBiMICAgwAAAABQAAAAAAAAAAAgIC -EQoKCjYCAgISAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAACAgIOCgoKNQcHBy4DAwMSAAAAAQAAAAAAAAAAAAAAAAYGBiEICAgxAQEB -BwAAAAAAAAAAAwMDFAoKCjYCAgIQAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMPCQkJMgkJCTUJCQk0BAQEGQAAAAAAAAAAAAAA -AAUFBRsKCgo3BQUFHwAAAAAAAAAEBwcHLAkJCTMCAgIMAAAAAAAAAAABAQEHCAgIKwkJCS0GBgYiAgIC -EQAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUFBRoJCQkyCQkJNAkJCTMKCgo1BAQE -FwAAAAAAAAAABAQEGQkJCTMICAgxAwMDFwAAAAAAAAADBQUFIgkJCTQHBwcuAgICDQAAAAAAAAAFBQUF -IQcHBykJCQkyCgoKNgcHBygBAQEKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBgYGHgoKCjYHBwcnBwcH -KAoKCjYHBwcnAAAAAgAAAAADAwMUCgoKNgcHBycBAQEJAAAAAAAAAAAAAAAAAAAAAQICAhIJCQkwCAgI -LwEBAQcAAAAAAAAAAAAAAAEAAAAGBQUFGwkJCTMICAgvAgICCgAAAAAAAAAAAAAAAAAAAAADAwMWCgoK -NgYGBiEAAAACAwMDEwgICC4CAgIKAAAAAAAAAAIHBwcsBwcHLAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAACAgIQCgoKNAUFBR0AAAAAAAAAAAAAAAAAAAAAAAAAAAMDAw8JCQkyCAgIKgAAAAIAAAAAAAAA -AAAAAAQHBwcuBwcHLAAAAAQAAAAAAAAAAAAAAAIAAAAAAAAAAAICAgkJCQk0BQUFHAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAABCQkJLQcHBygAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQaCgoK -NQUFBSEFBQUfAwMDEgMDAxIKCgo2BAQEFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQgJCQkzCAgI -LwUFBSIFBQUaAwMDFQMDAxQDAwMUBAQEFgUFBRwHBwclCQkJNQcHBycAAAAAAAAAAAAAAAAAAAAAAAAA -AQUFBR8ICAgwCQkJMwkJCTQKCgo3BQUFHwYGBiEJCQk0AQEBCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAEEBAQYBwcHKQgICDAJCQkzCQkJNQkJCTUJCQk1CQkJNAkJCTMICAgvBgYGJAICAg4AAAAAAAAA -AAAAAAAAAAAAAAAAAQUFBR8JCQk0CQkJNAkJCTQHBwcqAAAABAYGBiUICAgvAAAABgAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUCAgILAwMDDwICAhEDAwMPAwMDDgEBAQkAAAADAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwMTCQkJLQkJCTIDAwMOAAAAAAEBAQcCAgIMAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAEAAAAAAAAAAAAAAAAAAAAAAQEBBgICAgwAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBBwUFBSAHBwcsCAgIKwUFBRwAAAADAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICAgoGBgYjBwcHLAcHByoEBAQYAAAAAQAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCAgIKgoKCjUICAgoCAgIKwkJCTYGBgYjAAAA -AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBCQgICC8JCQkzBwcHKAcHBywKCgo2BQUF -HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQXCgoKNgQEBBcAAAAAAAAA -AgUFBR8JCQk1AwMDDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQUFHwkJCTQCAgIRAAAA -AAAAAAMGBgYmCQkJMgEBAQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBQUgCQkJ -MgAAAAQAAAAAAAAAAAICAgwJCQk1BAQEFwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwcH -KQgICCsAAAABAAAAAAAAAAADAwMUCQkJNQICAhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAEBAQYCQkJNQICAhAAAAAAAAAAAAQEBBgJCQk1AgICEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAABQUFIQkJCTEBAQEKAAAAAAAAAAEFBQUgCQkJMwEBAQoAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAMGBgYeCgoKNwYGBiYAAAAAAAAAAggICC4JCQk1BAQEGAAAAAEAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAFBgYGJQoKCjgFBQUfAAAAAAEBAQgJCQkyCAgIMgICAhEAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwYGBiMJCQk2BwcHJwICAg0AAAAAAAAAAAMDAxIICAgrCQkJ -NgUFBR0AAAABAAAAAAAAAAAAAAAAAAAAAAAAAAYHBwcqCQkJNQYGBiMBAQEKAAAAAAAAAAEDAwMXCAgI -LgkJCTQEBAQWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQUFGgoKCjYFBQUdAAAAAQAAAAAAAAAAAAAA -AAAAAAAAAAAEBgYGJAoKCjUCAgIRAAAAAAAAAAAAAAAAAAAAAAYGBiEKCgo1BAQEFwAAAAAAAAAAAAAA -AAAAAAAAAAAAAQEBCAgICCoJCQkzAgICCwAAAAAAAAAAAAAAAAAAAAAAAAAECQkJLQcHBykAAAABAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQgICC8HBwcnAAAAAAAAAAAAAAAAAQEBCAkJCTIFBQUiAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQoJCQk0BQUFHgAAAAAAAAAAAAAAAAAAAAABAQEICQkJ -MQYGBiMAAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAABAQEBBggICCoHBwctAAAAAgAAAAAAAAAAAgIC -DQoKCjUFBQUcAAAAAwAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgEBAQoICAgvBgYGJgAAAAAAAAAAAAAA -AAAAAAAAAAAEBwcHLAkJCTUICAgxCAgIKQYGBiQGBgYjBgYGIwYGBiQICAgqCQkJMgkJCTUHBwcmAAAA -AAAAAAAAAAAAAQEBCQgICDAJCQk1CAgILwcHBygGBgYjBgYGIwYGBiMGBgYlBwcHLAkJCTMJCQk2BgYG -HgAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBCQMDAxYFBQUiCAgIKggICDAICAgxCAgIMAgICC8HBwcpBQUF -IAMDAxQBAQEGAAAAAAAAAAAAAAAAAAAAAAICAgwEBAQYBgYGIwgICCsICAgwCAgIMAgICDAICAguBwcH -KAUFBR4DAwMSAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAADAAAA -AgAAAAIAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAA -AwAAAAIAAAACAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAEBAQkDAwMPAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIFBQUfBwcHLQkJCTIJCQk1AwMDDwAAAAAAAAAAAAAA -AAAAAAAEBAQXBQUFHwAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMICAguCQkJNAkJCTQHBwctAQEB -CAAAAAIAAAAFAgICDQYGBiQKCgo2BwcHJwAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGBgYlCQkJ -NQkJCTQJCQk0CQkJLQgICCoICAguCQkJNQkJCTMEBAQfAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAFBQUcCgoKNgUFBR8FBQUaBwcHJgcHBykHBwcmBQUFGwICAgoAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAABAQELBgYGIQAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAQAAAwAAAAEA -IQAAAQEAAwAAAAEAIQAAAQIAAwAAAAQAABHSAQMAAwAAAAEAAQAAAQYAAwAAAAEAAgAAAQoAAwAAAAEA -AQAAAREABAAAAAEAAAAIARIAAwAAAAEAAQAAARUAAwAAAAEABAAAARYAAwAAAAEAIQAAARcABAAAAAEA -ABEEARwAAwAAAAEAAQAAASgAAwAAAAEAAgAAAVIAAwAAAAEAAQAAAVMAAwAAAAQAABHah3MABwAAB9gA -ABHiAAAAAAAIAAgACAAIAAEAAQABAAEAAAfYYXBwbAIgAABtbnRyUkdCIFhZWiAH2QACABkACwAaAAth -Y3NwQVBQTAAAAABhcHBsAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtkZXNjAAABCAAAAG9kc2NtAAABeAAABZxj -cHJ0AAAHFAAAADh3dHB0AAAHTAAAABRyWFlaAAAHYAAAABRnWFlaAAAHdAAAABRiWFlaAAAHiAAAABRy -VFJDAAAHnAAAAA5jaGFkAAAHrAAAACxiVFJDAAAHnAAAAA5nVFJDAAAHnAAAAA5kZXNjAAAAAAAAABRH -ZW5lcmljIFJHQiBQcm9maWxlAAAAAAAAAAAAAAAUR2VuZXJpYyBSR0IgUHJvZmlsZQAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNrU0sA -AAAoAAABhGRhREsAAAAuAAABrGNhRVMAAAAkAAAB2nZpVk4AAAAkAAAB/nB0QlIAAAAmAAACInVrVUEA -AAAqAAACSGZyRlUAAAAoAAACcmh1SFUAAAAoAAACmnpoVFcAAAAWAAACwm5iTk8AAAAmAAAC2GNzQ1oA -AAAiAAAC/mhlSUwAAAAeAAADIGl0SVQAAAAoAAADPnJvUk8AAAAkAAADZmRlREUAAAAsAAADimtvS1IA -AAAWAAADtnN2U0UAAAAmAAAC2HpoQ04AAAAWAAADzGphSlAAAAAaAAAD4mVsR1IAAAAiAAAD/HB0UE8A -AAAmAAAEHm5sTkwAAAAoAAAERGVzRVMAAAAmAAAEHnRoVEgAAAAkAAAEbHRyVFIAAAAiAAAEkGZpRkkA -AAAoAAAEsmhySFIAAAAoAAAE2nBsUEwAAAAsAAAFAnJ1UlUAAAAiAAAFLmFyRUcAAAAmAAAFUGVuVVMA -AAAmAAAFdgBWAWEAZQBvAGIAZQBjAG4A/QAgAFIARwBCACAAcAByAG8AZgBpAGwARwBlAG4AZQByAGUA -bAAgAFIARwBCAC0AYgBlAHMAawByAGkAdgBlAGwAcwBlAFAAZQByAGYAaQBsACAAUgBHAEIAIABnAGUA -bgDoAHIAaQBjAEMepQB1ACAAaADsAG4AaAAgAFIARwBCACAAQwBoAHUAbgBnAFAAZQByAGYAaQBsACAA -UgBHAEIAIABHAGUAbgDpAHIAaQBjAG8EFwQwBDMEMAQ7BEwEPQQ4BDkAIAQ/BEAEPgREBDAEOQQ7ACAA -UgBHAEIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAFIAVgBCAMEAbAB0AGEAbADhAG4A -bwBzACAAUgBHAEIAIABwAHIAbwBmAGkAbJAadSgAIABSAEcAQgAggnJfaWPPj/AARwBlAG4AZQByAGkA -cwBrACAAUgBHAEIALQBwAHIAbwBmAGkAbABPAGIAZQBjAG4A/QAgAFIARwBCACAAcAByAG8AZgBpAGwF -5AXoBdUF5AXZBdwAIABSAEcAQgAgBdsF3AXcBdkAUAByAG8AZgBpAGwAbwAgAFIARwBCACAAZwBlAG4A -ZQByAGkAYwBvAFAAcgBvAGYAaQBsACAAUgBHAEIAIABnAGUAbgBlAHIAaQBjAEEAbABsAGcAZQBtAGUA -aQBuAGUAcwAgAFIARwBCAC0AUAByAG8AZgBpAGzHfLwYACAAUgBHAEIAINUEuFzTDMd8Zm6QGgAgAFIA -RwBCACBjz4/wZYdO9k4AgiwAIABSAEcAQgAgMNcw7TDVMKEwpDDrA5MDtQO9A7kDugPMACADwAPBA78D -xgOvA7sAIABSAEcAQgBQAGUAcgBmAGkAbAAgAFIARwBCACAAZwBlAG4A6QByAGkAYwBvAEEAbABnAGUA -bQBlAGUAbgAgAFIARwBCAC0AcAByAG8AZgBpAGUAbA5CDhsOIw5EDh8OJQ5MACAAUgBHAEIAIA4XDjEO -SA4nDkQOGwBHAGUAbgBlAGwAIABSAEcAQgAgAFAAcgBvAGYAaQBsAGkAWQBsAGUAaQBuAGUAbgAgAFIA -RwBCAC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0AawBpACAAUgBHAEIAIABwAHIAbwBmAGkA -bABVAG4AaQB3AGUAcgBzAGEAbABuAHkAIABwAHIAbwBmAGkAbAAgAFIARwBCBB4EMQRJBDgEOQAgBD8E -QAQ+BEQEOAQ7BEwAIABSAEcAQgZFBkQGQQAgBioGOQYxBkoGQQAgAFIARwBCACAGJwZEBjkGJwZFAEcA -ZQBuAGUAcgBpAGMAIABSAEcAQgAgAFAAcgBvAGYAaQBsAGV0ZXh0AAAAAENvcHlyaWdodCAyMDA3IEFw -cGxlIEluYy4sIGFsbCByaWdodHMgcmVzZXJ2ZWQuAFhZWiAAAAAAAADzUgABAAAAARbPWFlaIAAAAAAA -AHRNAAA97gAAA9BYWVogAAAAAAAAWnUAAKxzAAAXNFhZWiAAAAAAAAAoGgAAFZ8AALg2Y3VydgAAAAAA -AAABAc0AAHNmMzIAAAAAAAEMQgAABd7///MmAAAHkgAA/ZH///ui///9owAAA9wAAMBs0issLS5aJGNs -YXNzbmFtZVgkY2xhc3Nlc18QEE5TQml0bWFwSW1hZ2VSZXCjLS8wWk5TSW1hZ2VSZXBYTlNPYmplY3TS -KywyM1dOU0FycmF5ojIw0issNTZeTlNNdXRhYmxlQXJyYXmjNTIw1Tg5OjsPPD0+P0BXTlNXaGl0ZVxO -U0NvbXBvbmVudHNcTlNDb2xvclNwYWNlXxASTlNDdXN0b21Db2xvclNwYWNlRDAgMABDMCAwEAOADIAP -1EJDRA9FRkdIVE5TSURVTlNJQ0NXTlNNb2RlbBAJgA0QAIAOTxERnAAAEZxhcHBsAgAAAG1udHJHUkFZ -WFlaIAfcAAgAFwAPAC4AD2Fjc3BBUFBMAAAAAG5vbmUAAAAAAAAAAAAAAAAAAAAAAAD21gABAAAAANMt -YXBwbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABWRlc2MAAADA -AAAAeWRzY20AAAE8AAAIGmNwcnQAAAlYAAAAI3d0cHQAAAl8AAAAFGtUUkMAAAmQAAAIDGRlc2MAAAAA -AAAAH0dlbmVyaWMgR3JheSBHYW1tYSAyLjIgUHJvZmlsZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABtbHVj -AAAAAAAAAB8AAAAMc2tTSwAAAC4AAAGEZGFESwAAADoAAAGyY2FFUwAAADgAAAHsdmlWTgAAAEAAAAIk -cHRCUgAAAEoAAAJkdWtVQQAAACwAAAKuZnJGVQAAAD4AAALaaHVIVQAAADQAAAMYemhUVwAAABoAAANM -a29LUgAAACIAAANmbmJOTwAAADoAAAOIY3NDWgAAACgAAAPCaGVJTAAAACQAAAPqcm9STwAAACoAAAQO -ZGVERQAAAE4AAAQ4aXRJVAAAAE4AAASGc3ZTRQAAADgAAATUemhDTgAAABoAAAUMamFKUAAAACYAAAUm -ZWxHUgAAACoAAAVMcHRQTwAAAFIAAAV2bmxOTAAAAEAAAAXIZXNFUwAAAEwAAAYIdGhUSAAAADIAAAZU -dHJUUgAAACQAAAaGZmlGSQAAAEYAAAaqaHJIUgAAAD4AAAbwcGxQTAAAAEoAAAcuYXJFRwAAACwAAAd4 -cnVSVQAAADoAAAekZW5VUwAAADwAAAfeAFYBYQBlAG8AYgBlAGMAbgDhACAAcwBpAHYA4QAgAGcAYQBt -AGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAgADIALAAyACAAZwBhAG0AbQBhAC0AcABy -AG8AZgBpAGwARwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBvAHMAIABnAGUAbgDoAHIAaQBjAGEAIAAy -AC4AMgBDHqUAdQAgAGgA7ABuAGgAIABNAOAAdQAgAHgA4QBtACAAQwBoAHUAbgBnACAARwBhAG0AbQBh -ACAAMgAuADIAUABlAHIAZgBpAGwAIABHAGUAbgDpAHIAaQBjAG8AIABkAGEAIABHAGEAbQBhACAAZABl -ACAAQwBpAG4AegBhAHMAIAAyACwAMgQXBDAEMwQwBDsETAQ9BDAAIABHAHIAYQB5AC0EMwQwBDwEMAAg -ADIALgAyAFAAcgBvAGYAaQBsACAAZwDpAG4A6QByAGkAcQB1AGUAIABnAHIAaQBzACAAZwBhAG0AbQBh -ACAAMgAsADIAwQBsAHQAYQBsAOEAbgBvAHMAIABzAHoA/AByAGsAZQAgAGcAYQBtAG0AYQAgADIALgAy -kBp1KHBwlo5RSV6mADIALgAygnJfaWPPj/DHfLwYACDWjMDJACCsELnIACAAMgAuADIAINUEuFzTDMd8 -AEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAZwBhAG0AbQBhACAAMgAsADIALQBwAHIAbwBmAGkAbABP -AGIAZQBjAG4A4QAgAWEAZQBkAOEAIABnAGEAbQBhACAAMgAuADIF0gXQBd4F1AAgBdAF5AXVBegAIAXb -BdwF3AXZACAAMgAuADIARwBhAG0AYQAgAGcAcgBpACAAZwBlAG4AZQByAGkAYwEDACAAMgAsADIAQQBs -AGwAZwBlAG0AZQBpAG4AZQBzACAARwByAGEAdQBzAHQAdQBmAGUAbgAtAFAAcgBvAGYAaQBsACAARwBh -AG0AbQBhACAAMgAsADIAUAByAG8AZgBpAGwAbwAgAGcAcgBpAGcAaQBvACAAZwBlAG4AZQByAGkAYwBv -ACAAZABlAGwAbABhACAAZwBhAG0AbQBhACAAMgAsADIARwBlAG4AZQByAGkAcwBrACAAZwByAOUAIAAy -ACwAMgAgAGcAYQBtAG0AYQBwAHIAbwBmAGkAbGZukBpwcF6mfPtlcAAyAC4AMmPPj/Blh072TgCCLDCw -MOwwpDCsMPMw3gAgADIALgAyACAw1zDtMNUwoTCkMOsDkwO1A70DuQO6A8wAIAOTA7oDwQO5ACADkwOs -A7wDvAOxACAAMgAuADIAUABlAHIAZgBpAGwAIABnAGUAbgDpAHIAaQBjAG8AIABkAGUAIABjAGkAbgB6 -AGUAbgB0AG8AcwAgAGQAYQAgAEcAYQBtAG0AYQAgADIALAAyAEEAbABnAGUAbQBlAGUAbgAgAGcAcgBp -AGoAcwAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBpAGUAbABQAGUAcgBmAGkAbAAgAGcAZQBu -AOkAcgBpAGMAbwAgAGQAZQAgAGcAYQBtAG0AYQAgAGQAZQAgAGcAcgBpAHMAZQBzACAAMgAsADIOIw4x -DgcOKg41DkEOAQ4hDiEOMg5ADgEOIw4iDkwOFw4xDkgOJw5EDhsAIAAyAC4AMgBHAGUAbgBlAGwAIABH -AHIAaQAgAEcAYQBtAGEAIAAyACwAMgBZAGwAZQBpAG4AZQBuACAAaABhAHIAbQBhAGEAbgAgAGcAYQBt -AG0AYQAgADIALAAyACAALQBwAHIAbwBmAGkAaQBsAGkARwBlAG4AZQByAGkBDQBrAGkAIABHAHIAYQB5 -ACAARwBhAG0AbQBhACAAMgAuADIAIABwAHIAbwBmAGkAbABVAG4AaQB3AGUAcgBzAGEAbABuAHkAIABw -AHIAbwBmAGkAbAAgAHMAegBhAHIAbwFbAGMAaQAgAGcAYQBtAG0AYQAgADIALAAyBjoGJwZFBicAIAAy -AC4AMgAgBkQGSAZGACAGMQZFBicGLwZKACAGOQYnBkUEHgQxBEkEMARPACAEQQQ1BEAEMARPACAEMwQw -BDwEPAQwACAAMgAsADIALQQ/BEAEPgREBDgEOwRMAEcAZQBuAGUAcgBpAGMAIABHAHIAYQB5ACAARwBh -AG0AbQBhACAAMgAuADIAIABQAHIAbwBmAGkAbABlAAB0ZXh0AAAAAENvcHlyaWdodCBBcHBsZSBJbmMu -LCAyMDEyAABYWVogAAAAAAAA81EAAQAAAAEWzGN1cnYAAAAAAAAEAAAAAAUACgAPABQAGQAeACMAKAAt -ADIANwA7AEAARQBKAE8AVABZAF4AYwBoAG0AcgB3AHwAgQCGAIsAkACVAJoAnwCkAKkArgCyALcAvADB -AMYAywDQANUA2wDgAOUA6wDwAPYA+wEBAQcBDQETARkBHwElASsBMgE4AT4BRQFMAVIBWQFgAWcBbgF1 -AXwBgwGLAZIBmgGhAakBsQG5AcEByQHRAdkB4QHpAfIB+gIDAgwCFAIdAiYCLwI4AkECSwJUAl0CZwJx -AnoChAKOApgCogKsArYCwQLLAtUC4ALrAvUDAAMLAxYDIQMtAzgDQwNPA1oDZgNyA34DigOWA6IDrgO6 -A8cD0wPgA+wD+QQGBBMEIAQtBDsESARVBGMEcQR+BIwEmgSoBLYExATTBOEE8AT+BQ0FHAUrBToFSQVY -BWcFdwWGBZYFpgW1BcUF1QXlBfYGBgYWBicGNwZIBlkGagZ7BowGnQavBsAG0QbjBvUHBwcZBysHPQdP -B2EHdAeGB5kHrAe/B9IH5Qf4CAsIHwgyCEYIWghuCIIIlgiqCL4I0gjnCPsJEAklCToJTwlkCXkJjwmk -CboJzwnlCfsKEQonCj0KVApqCoEKmAquCsUK3ArzCwsLIgs5C1ELaQuAC5gLsAvIC+EL+QwSDCoMQwxc -DHUMjgynDMAM2QzzDQ0NJg1ADVoNdA2ODakNww3eDfgOEw4uDkkOZA5/DpsOtg7SDu4PCQ8lD0EPXg96 -D5YPsw/PD+wQCRAmEEMQYRB+EJsQuRDXEPURExExEU8RbRGMEaoRyRHoEgcSJhJFEmQShBKjEsMS4xMD -EyMTQxNjE4MTpBPFE+UUBhQnFEkUahSLFK0UzhTwFRIVNBVWFXgVmxW9FeAWAxYmFkkWbBaPFrIW1hb6 -Fx0XQRdlF4kXrhfSF/cYGxhAGGUYihivGNUY+hkgGUUZaxmRGbcZ3RoEGioaURp3Gp4axRrsGxQbOxtj -G4obshvaHAIcKhxSHHscoxzMHPUdHh1HHXAdmR3DHeweFh5AHmoelB6+HukfEx8+H2kflB+/H+ogFSBB -IGwgmCDEIPAhHCFIIXUhoSHOIfsiJyJVIoIiryLdIwojOCNmI5QjwiPwJB8kTSR8JKsk2iUJJTglaCWX -Jccl9yYnJlcmhya3JugnGCdJJ3onqyfcKA0oPyhxKKIo1CkGKTgpaymdKdAqAio1KmgqmyrPKwIrNitp -K50r0SwFLDksbiyiLNctDC1BLXYtqy3hLhYuTC6CLrcu7i8kL1ovkS/HL/4wNTBsMKQw2zESMUoxgjG6 -MfIyKjJjMpsy1DMNM0YzfzO4M/E0KzRlNJ402DUTNU01hzXCNf02NzZyNq426TckN2A3nDfXOBQ4UDiM -OMg5BTlCOX85vDn5OjY6dDqyOu87LTtrO6o76DwnPGU8pDzjPSI9YT2hPeA+ID5gPqA+4D8hP2E/oj/i -QCNAZECmQOdBKUFqQaxB7kIwQnJCtUL3QzpDfUPARANER0SKRM5FEkVVRZpF3kYiRmdGq0bwRzVHe0fA -SAVIS0iRSNdJHUljSalJ8Eo3Sn1KxEsMS1NLmkviTCpMcky6TQJNSk2TTdxOJU5uTrdPAE9JT5NP3VAn -UHFQu1EGUVBRm1HmUjFSfFLHUxNTX1OqU/ZUQlSPVNtVKFV1VcJWD1ZcVqlW91dEV5JX4FgvWH1Yy1ka -WWlZuFoHWlZaplr1W0VblVvlXDVchlzWXSddeF3JXhpebF69Xw9fYV+zYAVgV2CqYPxhT2GiYfViSWKc -YvBjQ2OXY+tkQGSUZOllPWWSZedmPWaSZuhnPWeTZ+loP2iWaOxpQ2maafFqSGqfavdrT2una/9sV2yv -bQhtYG25bhJua27Ebx5veG/RcCtwhnDgcTpxlXHwcktypnMBc11zuHQUdHB0zHUodYV14XY+dpt2+HdW -d7N4EXhueMx5KnmJeed6RnqlewR7Y3vCfCF8gXzhfUF9oX4BfmJ+wn8jf4R/5YBHgKiBCoFrgc2CMIKS -gvSDV4O6hB2EgITjhUeFq4YOhnKG14c7h5+IBIhpiM6JM4mZif6KZIrKizCLlov8jGOMyo0xjZiN/45m -js6PNo+ekAaQbpDWkT+RqJIRknqS45NNk7aUIJSKlPSVX5XJljSWn5cKl3WX4JhMmLiZJJmQmfyaaJrV -m0Kbr5wcnImc951kndKeQJ6unx2fi5/6oGmg2KFHobaiJqKWowajdqPmpFakx6U4pammGqaLpv2nbqfg -qFKoxKk3qamqHKqPqwKrdavprFys0K1ErbiuLa6hrxavi7AAsHWw6rFgsdayS7LCszizrrQltJy1E7WK -tgG2ebbwt2i34LhZuNG5SrnCuju6tbsuu6e8IbybvRW9j74KvoS+/796v/XAcMDswWfB48JfwtvDWMPU -xFHEzsVLxcjGRsbDx0HHv8g9yLzJOsm5yjjKt8s2y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB -00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp22vvbgNwF3IrdEN2W3hzeot8p36/gNuC94UThzOJT -4tvjY+Pr5HPk/OWE5g3mlucf56noMui86Ubp0Opb6uXrcOv77IbtEe2c7ijutO9A78zwWPDl8XLx//KM -8xnzp/Q09ML1UPXe9m32+/eK+Bn4qPk4+cf6V/rn+3f8B/yY/Sn9uv5L/tz/bf//0issS0xcTlNDb2xv -clNwYWNlok0wXE5TQ29sb3JTcGFjZdIrLE9QV05TQ29sb3KiTzDSKyxSU1dOU0ltYWdlolIwAAgAEQAa -ACQAKQAyADcASQBMAFEAUwBnAG0AegCBAJAAlwCkAKsAswC1ALcAuQC+AMAAwgDLANAA2wDdAN8A4QDm -AOkA6wDtAO8A9gENASkBKwEtGusa8Br7GwQbFxsbGyYbLxs0GzwbPxtEG1MbVxtiG2obdxuEG5kbnhui -G6QbphuoG7Ebthu8G8QbxhvIG8obzC1sLXEtfi2BLY4tky2bLZ4toy2rAAAAAAAAAgEAAAAAAAAAVAAA -AAAAAAAAAAAAAAAALa4 - - - - - -YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T -S2V5ZWRBcmNoaXZlctEICVRyb290gAGvEBELDBkaHxQkKSoxNDdBSUpOUVUkbnVsbNYNDg8QERITFBUW -FxhWTlNTaXplXk5TUmVzaXppbmdNb2RlViRjbGFzc1xOU0ltYWdlRmxhZ3NWTlNSZXBzV05TQ29sb3KA -AhAAgBASIMAAAIADgAtYezQwLCAzNn3SGw8cHlpOUy5vYmplY3RzoR2ABIAK0hsPICOiISKABYAGgAnT -DyUmJygUXxAUTlNUSUZGUmVwcmVzZW50YXRpb25fEBlOU0ludGVybmFsTGF5b3V0RGlyZWN0aW9ugAiA -B08RHzZNTQAqAAAWiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAABhgAABkYAAAZBQAABgAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAOwABPJsABJ7IAAXMyAAFzJ0ABKA/AAFBAQAAAgAAAAAAAAAAAAAAAAAAAAACAgICAQEB -AQAAAAAKCgoKBAQEBAAAAAAAAAAACQkJCQkJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAkJCQkHBwcHAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASQACS9gABtz7AAf/+wAH -//sAB//7AAf/2wAG31AAAlIAAAAAAAAAAAAAAAAAAAAAW1tbW6KioqKZmZmZ29vb22BgYGIICAgIh4eH -icnJycnJycnKenp6fAcHBwcAAAAAEhISEpOTk5XS0tLTy8vLzHh4eHoHBwcHAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAFgAAF7wABcD7AAf/+wAH//sAB//7AAf/+wAH//sAB//AAAXEGgAA -GwAAAAAAAAAAAAAAAHBwcHD/////gYGBgygoKCoNDQ0NiIiIiq6urq8XFxcXFxcXGbm5ublycnJyAAAA -AJiYmJnBwcHBKCgoKC0tLS/T09PUV1dXVwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AEQAAUbrAAbv+wAH//sAB//7AAf/+wAH//sAB//7AAf/7AAG8EoAAkwAAAAAAAAAAAAAAAB0dHR00NDQ -0AAAAAAAAAAABwcHB9TU1NWRkZGTY2NjY2NjY2Ojo6OjyMjIyBYWFhbd3d3dOzs7PQAAAAAAAAAAFxcX -GRUVFRUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSAAJU8gAG9vsAB//7AAf/+wAH -//sAB//7AAf/+wAH//QABvhcAAJeAAAAAAAAAAAAAAAAdnZ2dre3t7cAAAAAAAAAAA4ODg7g4ODhoaGh -oYyMjIyMjIyMhoaGhlJSUlQcHBwc4eHh4ioqKioAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAOAABOeQABuj7AAf/+wAH//sAB//7AAf/+wAH//sAB//mAAbqPgAB -PwAAAAAAAAAAAAAAAHZ2dna4uLi4AAAAAAAAAAAEBAQEwsLCw2JiYmIAAAAAAAAAADMzMzNeXl5eCQkJ -Cc3Nzc5paWlpAAAAAAAAAABxcXFzWVlZWwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAsAAAyiAASl+wAH//sAB//7AAf/+wAH//sAB//7AAf/pgAEqQ8AABAAAAAAAAAAAAAAAAB4eHh4vLy8 -vAAAAAAAAAAAAAAAAEhISEjX19fYmZmZmouLi43a2trbYmJiZAAAAABRUVFT39/f35ubm5yenp6f3Nzc -3Tk5OTkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKgABK7QABbf1AAb5+wAH -//sAB//2AAb6tgAFuSwAAS0AAAAAAAAAAAAAAAAAAAAAKioqKkFBQUMAAAAAAAAAAAAAAAAAAAAAJiYm -KG5ubnBxcXFzMjIyNAAAAAAAAAAAAAAAADAwMDB0dHR0bGxsbiMjIyMAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAAaYwACZZYABJmVAASYZQACZxwAAB0AAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAFAAAGBAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAQAQAAAwAAAAEAKAAAAQEAAwAAAAEAJAAAAQIAAwAAAAQAABdOAQMAAwAAAAEA -AQAAAQYAAwAAAAEAAgAAAQoAAwAAAAEAAQAAAREABAAAAAEAAAAIARIAAwAAAAEAAQAAARUAAwAAAAEA -BAAAARYAAwAAAAEAJAAAARcABAAAAAEAABaAARwAAwAAAAEAAQAAASgAAwAAAAEAAgAAAVIAAwAAAAEA -AQAAAVMAAwAAAAQAABdWh3MABwAAB9gAABdeAAAAAAAIAAgACAAIAAEAAQABAAEAAAfYYXBwbAIgAABt -bnRyUkdCIFhZWiAH2QACABkACwAaAAthY3NwQVBQTAAAAABhcHBsAAAAAAAAAAAAAAAAAAAAAAAA9tYA -AQAAAADTLWFwcGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtk -ZXNjAAABCAAAAG9kc2NtAAABeAAABZxjcHJ0AAAHFAAAADh3dHB0AAAHTAAAABRyWFlaAAAHYAAAABRn -WFlaAAAHdAAAABRiWFlaAAAHiAAAABRyVFJDAAAHnAAAAA5jaGFkAAAHrAAAACxiVFJDAAAHnAAAAA5n -VFJDAAAHnAAAAA5kZXNjAAAAAAAAABRHZW5lcmljIFJHQiBQcm9maWxlAAAAAAAAAAAAAAAUR2VuZXJp -YyBSR0IgUHJvZmlsZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAbWx1YwAAAAAAAAAfAAAADHNrU0sAAAAoAAABhGRhREsAAAAuAAABrGNhRVMAAAAkAAAB2nZpVk4A -AAAkAAAB/nB0QlIAAAAmAAACInVrVUEAAAAqAAACSGZyRlUAAAAoAAACcmh1SFUAAAAoAAACmnpoVFcA -AAAWAAACwm5iTk8AAAAmAAAC2GNzQ1oAAAAiAAAC/mhlSUwAAAAeAAADIGl0SVQAAAAoAAADPnJvUk8A -AAAkAAADZmRlREUAAAAsAAADimtvS1IAAAAWAAADtnN2U0UAAAAmAAAC2HpoQ04AAAAWAAADzGphSlAA -AAAaAAAD4mVsR1IAAAAiAAAD/HB0UE8AAAAmAAAEHm5sTkwAAAAoAAAERGVzRVMAAAAmAAAEHnRoVEgA -AAAkAAAEbHRyVFIAAAAiAAAEkGZpRkkAAAAoAAAEsmhySFIAAAAoAAAE2nBsUEwAAAAsAAAFAnJ1UlUA -AAAiAAAFLmFyRUcAAAAmAAAFUGVuVVMAAAAmAAAFdgBWAWEAZQBvAGIAZQBjAG4A/QAgAFIARwBCACAA -cAByAG8AZgBpAGwARwBlAG4AZQByAGUAbAAgAFIARwBCAC0AYgBlAHMAawByAGkAdgBlAGwAcwBlAFAA -ZQByAGYAaQBsACAAUgBHAEIAIABnAGUAbgDoAHIAaQBjAEMepQB1ACAAaADsAG4AaAAgAFIARwBCACAA -QwBoAHUAbgBnAFAAZQByAGYAaQBsACAAUgBHAEIAIABHAGUAbgDpAHIAaQBjAG8EFwQwBDMEMAQ7BEwE -PQQ4BDkAIAQ/BEAEPgREBDAEOQQ7ACAAUgBHAEIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUA -ZQAgAFIAVgBCAMEAbAB0AGEAbADhAG4AbwBzACAAUgBHAEIAIABwAHIAbwBmAGkAbJAadSgAIABSAEcA -QgAggnJfaWPPj/AARwBlAG4AZQByAGkAcwBrACAAUgBHAEIALQBwAHIAbwBmAGkAbABPAGIAZQBjAG4A -/QAgAFIARwBCACAAcAByAG8AZgBpAGwF5AXoBdUF5AXZBdwAIABSAEcAQgAgBdsF3AXcBdkAUAByAG8A -ZgBpAGwAbwAgAFIARwBCACAAZwBlAG4AZQByAGkAYwBvAFAAcgBvAGYAaQBsACAAUgBHAEIAIABnAGUA -bgBlAHIAaQBjAEEAbABsAGcAZQBtAGUAaQBuAGUAcwAgAFIARwBCAC0AUAByAG8AZgBpAGzHfLwYACAA -UgBHAEIAINUEuFzTDMd8Zm6QGgAgAFIARwBCACBjz4/wZYdO9k4AgiwAIABSAEcAQgAgMNcw7TDVMKEw -pDDrA5MDtQO9A7kDugPMACADwAPBA78DxgOvA7sAIABSAEcAQgBQAGUAcgBmAGkAbAAgAFIARwBCACAA -ZwBlAG4A6QByAGkAYwBvAEEAbABnAGUAbQBlAGUAbgAgAFIARwBCAC0AcAByAG8AZgBpAGUAbA5CDhsO -Iw5EDh8OJQ5MACAAUgBHAEIAIA4XDjEOSA4nDkQOGwBHAGUAbgBlAGwAIABSAEcAQgAgAFAAcgBvAGYA -aQBsAGkAWQBsAGUAaQBuAGUAbgAgAFIARwBCAC0AcAByAG8AZgBpAGkAbABpAEcAZQBuAGUAcgBpAQ0A -awBpACAAUgBHAEIAIABwAHIAbwBmAGkAbABVAG4AaQB3AGUAcgBzAGEAbABuAHkAIABwAHIAbwBmAGkA -bAAgAFIARwBCBB4EMQRJBDgEOQAgBD8EQAQ+BEQEOAQ7BEwAIABSAEcAQgZFBkQGQQAgBioGOQYxBkoG -QQAgAFIARwBCACAGJwZEBjkGJwZFAEcAZQBuAGUAcgBpAGMAIABSAEcAQgAgAFAAcgBvAGYAaQBsAGV0 -ZXh0AAAAAENvcHlyaWdodCAyMDA3IEFwcGxlIEluYy4sIGFsbCByaWdodHMgcmVzZXJ2ZWQuAFhZWiAA -AAAAAADzUgABAAAAARbPWFlaIAAAAAAAAHRNAAA97gAAA9BYWVogAAAAAAAAWnUAAKxzAAAXNFhZWiAA -AAAAAAAoGgAAFZ8AALg2Y3VydgAAAAAAAAABAc0AAHNmMzIAAAAAAAEMQgAABd7///MmAAAHkgAA/ZH/ -//ui///9owAAA9wAAMBs0issLS5aJGNsYXNzbmFtZVgkY2xhc3Nlc18QEE5TQml0bWFwSW1hZ2VSZXCj -LS8wWk5TSW1hZ2VSZXBYTlNPYmplY3TSKywyM1dOU0FycmF5ojIw0issNTZeTlNNdXRhYmxlQXJyYXmj -NTIw1Tg5OjsPPD0+P0BXTlNXaGl0ZVxOU0NvbXBvbmVudHNcTlNDb2xvclNwYWNlXxASTlNDdXN0b21D -b2xvclNwYWNlRDAgMABDMCAwEAOADIAP1EJDRA9FRkdIVE5TSURVTlNJQ0NXTlNNb2RlbBAJgA0QAIAO -TxERnAAAEZxhcHBsAgAAAG1udHJHUkFZWFlaIAfcAAgAFwAPAC4AD2Fjc3BBUFBMAAAAAG5vbmUAAAAA -AAAAAAAAAAAAAAAAAAD21gABAAAAANMtYXBwbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAABWRlc2MAAADAAAAAeWRzY20AAAE8AAAIGmNwcnQAAAlYAAAAI3d0cHQAAAl8 -AAAAFGtUUkMAAAmQAAAIDGRlc2MAAAAAAAAAH0dlbmVyaWMgR3JheSBHYW1tYSAyLjIgUHJvZmlsZQAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAABtbHVjAAAAAAAAAB8AAAAMc2tTSwAAAC4AAAGEZGFESwAAADoAAAGy -Y2FFUwAAADgAAAHsdmlWTgAAAEAAAAIkcHRCUgAAAEoAAAJkdWtVQQAAACwAAAKuZnJGVQAAAD4AAALa -aHVIVQAAADQAAAMYemhUVwAAABoAAANMa29LUgAAACIAAANmbmJOTwAAADoAAAOIY3NDWgAAACgAAAPC -aGVJTAAAACQAAAPqcm9STwAAACoAAAQOZGVERQAAAE4AAAQ4aXRJVAAAAE4AAASGc3ZTRQAAADgAAATU -emhDTgAAABoAAAUMamFKUAAAACYAAAUmZWxHUgAAACoAAAVMcHRQTwAAAFIAAAV2bmxOTAAAAEAAAAXI -ZXNFUwAAAEwAAAYIdGhUSAAAADIAAAZUdHJUUgAAACQAAAaGZmlGSQAAAEYAAAaqaHJIUgAAAD4AAAbw -cGxQTAAAAEoAAAcuYXJFRwAAACwAAAd4cnVSVQAAADoAAAekZW5VUwAAADwAAAfeAFYBYQBlAG8AYgBl -AGMAbgDhACAAcwBpAHYA4QAgAGcAYQBtAGEAIAAyACwAMgBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QAg -ADIALAAyACAAZwBhAG0AbQBhAC0AcAByAG8AZgBpAGwARwBhAG0AbQBhACAAZABlACAAZwByAGkAcwBv -AHMAIABnAGUAbgDoAHIAaQBjAGEAIAAyAC4AMgBDHqUAdQAgAGgA7ABuAGgAIABNAOAAdQAgAHgA4QBt -ACAAQwBoAHUAbgBnACAARwBhAG0AbQBhACAAMgAuADIAUABlAHIAZgBpAGwAIABHAGUAbgDpAHIAaQBj -AG8AIABkAGEAIABHAGEAbQBhACAAZABlACAAQwBpAG4AegBhAHMAIAAyACwAMgQXBDAEMwQwBDsETAQ9 -BDAAIABHAHIAYQB5AC0EMwQwBDwEMAAgADIALgAyAFAAcgBvAGYAaQBsACAAZwDpAG4A6QByAGkAcQB1 -AGUAIABnAHIAaQBzACAAZwBhAG0AbQBhACAAMgAsADIAwQBsAHQAYQBsAOEAbgBvAHMAIABzAHoA/ABy -AGsAZQAgAGcAYQBtAG0AYQAgADIALgAykBp1KHBwlo5RSV6mADIALgAygnJfaWPPj/DHfLwYACDWjMDJ -ACCsELnIACAAMgAuADIAINUEuFzTDMd8AEcAZQBuAGUAcgBpAHMAawAgAGcAcgDlACAAZwBhAG0AbQBh -ACAAMgAsADIALQBwAHIAbwBmAGkAbABPAGIAZQBjAG4A4QAgAWEAZQBkAOEAIABnAGEAbQBhACAAMgAu -ADIF0gXQBd4F1AAgBdAF5AXVBegAIAXbBdwF3AXZACAAMgAuADIARwBhAG0AYQAgAGcAcgBpACAAZwBl -AG4AZQByAGkAYwEDACAAMgAsADIAQQBsAGwAZwBlAG0AZQBpAG4AZQBzACAARwByAGEAdQBzAHQAdQBm -AGUAbgAtAFAAcgBvAGYAaQBsACAARwBhAG0AbQBhACAAMgAsADIAUAByAG8AZgBpAGwAbwAgAGcAcgBp -AGcAaQBvACAAZwBlAG4AZQByAGkAYwBvACAAZABlAGwAbABhACAAZwBhAG0AbQBhACAAMgAsADIARwBl -AG4AZQByAGkAcwBrACAAZwByAOUAIAAyACwAMgAgAGcAYQBtAG0AYQBwAHIAbwBmAGkAbGZukBpwcF6m -fPtlcAAyAC4AMmPPj/Blh072TgCCLDCwMOwwpDCsMPMw3gAgADIALgAyACAw1zDtMNUwoTCkMOsDkwO1 -A70DuQO6A8wAIAOTA7oDwQO5ACADkwOsA7wDvAOxACAAMgAuADIAUABlAHIAZgBpAGwAIABnAGUAbgDp -AHIAaQBjAG8AIABkAGUAIABjAGkAbgB6AGUAbgB0AG8AcwAgAGQAYQAgAEcAYQBtAG0AYQAgADIALAAy -AEEAbABnAGUAbQBlAGUAbgAgAGcAcgBpAGoAcwAgAGcAYQBtAG0AYQAgADIALAAyAC0AcAByAG8AZgBp -AGUAbABQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGcAYQBtAG0AYQAgAGQAZQAg -AGcAcgBpAHMAZQBzACAAMgAsADIOIw4xDgcOKg41DkEOAQ4hDiEOMg5ADgEOIw4iDkwOFw4xDkgOJw5E -DhsAIAAyAC4AMgBHAGUAbgBlAGwAIABHAHIAaQAgAEcAYQBtAGEAIAAyACwAMgBZAGwAZQBpAG4AZQBu -ACAAaABhAHIAbQBhAGEAbgAgAGcAYQBtAG0AYQAgADIALAAyACAALQBwAHIAbwBmAGkAaQBsAGkARwBl -AG4AZQByAGkBDQBrAGkAIABHAHIAYQB5ACAARwBhAG0AbQBhACAAMgAuADIAIABwAHIAbwBmAGkAbABV -AG4AaQB3AGUAcgBzAGEAbABuAHkAIABwAHIAbwBmAGkAbAAgAHMAegBhAHIAbwFbAGMAaQAgAGcAYQBt -AG0AYQAgADIALAAyBjoGJwZFBicAIAAyAC4AMgAgBkQGSAZGACAGMQZFBicGLwZKACAGOQYnBkUEHgQx -BEkEMARPACAEQQQ1BEAEMARPACAEMwQwBDwEPAQwACAAMgAsADIALQQ/BEAEPgREBDgEOwRMAEcAZQBu -AGUAcgBpAGMAIABHAHIAYQB5ACAARwBhAG0AbQBhACAAMgAuADIAIABQAHIAbwBmAGkAbABlAAB0ZXh0 -AAAAAENvcHlyaWdodCBBcHBsZSBJbmMuLCAyMDEyAABYWVogAAAAAAAA81EAAQAAAAEWzGN1cnYAAAAA -AAAEAAAAAAUACgAPABQAGQAeACMAKAAtADIANwA7AEAARQBKAE8AVABZAF4AYwBoAG0AcgB3AHwAgQCG -AIsAkACVAJoAnwCkAKkArgCyALcAvADBAMYAywDQANUA2wDgAOUA6wDwAPYA+wEBAQcBDQETARkBHwEl -ASsBMgE4AT4BRQFMAVIBWQFgAWcBbgF1AXwBgwGLAZIBmgGhAakBsQG5AcEByQHRAdkB4QHpAfIB+gID -AgwCFAIdAiYCLwI4AkECSwJUAl0CZwJxAnoChAKOApgCogKsArYCwQLLAtUC4ALrAvUDAAMLAxYDIQMt -AzgDQwNPA1oDZgNyA34DigOWA6IDrgO6A8cD0wPgA+wD+QQGBBMEIAQtBDsESARVBGMEcQR+BIwEmgSo -BLYExATTBOEE8AT+BQ0FHAUrBToFSQVYBWcFdwWGBZYFpgW1BcUF1QXlBfYGBgYWBicGNwZIBlkGagZ7 -BowGnQavBsAG0QbjBvUHBwcZBysHPQdPB2EHdAeGB5kHrAe/B9IH5Qf4CAsIHwgyCEYIWghuCIIIlgiq -CL4I0gjnCPsJEAklCToJTwlkCXkJjwmkCboJzwnlCfsKEQonCj0KVApqCoEKmAquCsUK3ArzCwsLIgs5 -C1ELaQuAC5gLsAvIC+EL+QwSDCoMQwxcDHUMjgynDMAM2QzzDQ0NJg1ADVoNdA2ODakNww3eDfgOEw4u -DkkOZA5/DpsOtg7SDu4PCQ8lD0EPXg96D5YPsw/PD+wQCRAmEEMQYRB+EJsQuRDXEPURExExEU8RbRGM -EaoRyRHoEgcSJhJFEmQShBKjEsMS4xMDEyMTQxNjE4MTpBPFE+UUBhQnFEkUahSLFK0UzhTwFRIVNBVW -FXgVmxW9FeAWAxYmFkkWbBaPFrIW1hb6Fx0XQRdlF4kXrhfSF/cYGxhAGGUYihivGNUY+hkgGUUZaxmR -GbcZ3RoEGioaURp3Gp4axRrsGxQbOxtjG4obshvaHAIcKhxSHHscoxzMHPUdHh1HHXAdmR3DHeweFh5A -HmoelB6+HukfEx8+H2kflB+/H+ogFSBBIGwgmCDEIPAhHCFIIXUhoSHOIfsiJyJVIoIiryLdIwojOCNm -I5QjwiPwJB8kTSR8JKsk2iUJJTglaCWXJccl9yYnJlcmhya3JugnGCdJJ3onqyfcKA0oPyhxKKIo1CkG -KTgpaymdKdAqAio1KmgqmyrPKwIrNitpK50r0SwFLDksbiyiLNctDC1BLXYtqy3hLhYuTC6CLrcu7i8k -L1ovkS/HL/4wNTBsMKQw2zESMUoxgjG6MfIyKjJjMpsy1DMNM0YzfzO4M/E0KzRlNJ402DUTNU01hzXC -Nf02NzZyNq426TckN2A3nDfXOBQ4UDiMOMg5BTlCOX85vDn5OjY6dDqyOu87LTtrO6o76DwnPGU8pDzj -PSI9YT2hPeA+ID5gPqA+4D8hP2E/oj/iQCNAZECmQOdBKUFqQaxB7kIwQnJCtUL3QzpDfUPARANER0SK -RM5FEkVVRZpF3kYiRmdGq0bwRzVHe0fASAVIS0iRSNdJHUljSalJ8Eo3Sn1KxEsMS1NLmkviTCpMcky6 -TQJNSk2TTdxOJU5uTrdPAE9JT5NP3VAnUHFQu1EGUVBRm1HmUjFSfFLHUxNTX1OqU/ZUQlSPVNtVKFV1 -VcJWD1ZcVqlW91dEV5JX4FgvWH1Yy1kaWWlZuFoHWlZaplr1W0VblVvlXDVchlzWXSddeF3JXhpebF69 -Xw9fYV+zYAVgV2CqYPxhT2GiYfViSWKcYvBjQ2OXY+tkQGSUZOllPWWSZedmPWaSZuhnPWeTZ+loP2iW -aOxpQ2maafFqSGqfavdrT2una/9sV2yvbQhtYG25bhJua27Ebx5veG/RcCtwhnDgcTpxlXHwcktypnMB -c11zuHQUdHB0zHUodYV14XY+dpt2+HdWd7N4EXhueMx5KnmJeed6RnqlewR7Y3vCfCF8gXzhfUF9oX4B -fmJ+wn8jf4R/5YBHgKiBCoFrgc2CMIKSgvSDV4O6hB2EgITjhUeFq4YOhnKG14c7h5+IBIhpiM6JM4mZ -if6KZIrKizCLlov8jGOMyo0xjZiN/45mjs6PNo+ekAaQbpDWkT+RqJIRknqS45NNk7aUIJSKlPSVX5XJ -ljSWn5cKl3WX4JhMmLiZJJmQmfyaaJrVm0Kbr5wcnImc951kndKeQJ6unx2fi5/6oGmg2KFHobaiJqKW -owajdqPmpFakx6U4pammGqaLpv2nbqfgqFKoxKk3qamqHKqPqwKrdavprFys0K1ErbiuLa6hrxavi7AA -sHWw6rFgsdayS7LCszizrrQltJy1E7WKtgG2ebbwt2i34LhZuNG5SrnCuju6tbsuu6e8IbybvRW9j74K -voS+/796v/XAcMDswWfB48JfwtvDWMPUxFHEzsVLxcjGRsbDx0HHv8g9yLzJOsm5yjjKt8s2y7bMNcy1 -zTXNtc42zrbPN8+40DnQutE80b7SP9LB00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp22vvbgNwF -3IrdEN2W3hzeot8p36/gNuC94UThzOJT4tvjY+Pr5HPk/OWE5g3mlucf56noMui86Ubp0Opb6uXrcOv7 -7IbtEe2c7ijutO9A78zwWPDl8XLx//KM8xnzp/Q09ML1UPXe9m32+/eK+Bn4qPk4+cf6V/rn+3f8B/yY -/Sn9uv5L/tz/bf//0issS0xcTlNDb2xvclNwYWNlok0wXE5TQ29sb3JTcGFjZdIrLE9QV05TQ29sb3Ki -TzDSKyxSU1dOU0ltYWdlolIwAAgAEQAaACQAKQAyADcASQBMAFEAUwBnAG0AegCBAJAAlwCkAKsAswC1 -ALcAuQC+AMAAwgDLANAA2wDdAN8A4QDmAOkA6wDtAO8A9gENASkBKwEtIGcgbCB3IIAgkyCXIKIgqyCw -ILgguyDAIM8g0yDeIOYg8yEAIRUhGiEeISAhIiEkIS0hMiE4IUAhQiFEIUYhSDLoMu0y+jL9MwozDzMX -MxozHzMnAAAAAAAAAgEAAAAAAAAAVAAAAAAAAAAAAAAAAAAAMyo - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Classes/Base.lproj/CallView~ipad.xib b/Classes/Base.lproj/CallView~ipad.xib deleted file mode 100644 index 684d42dc6..000000000 --- a/Classes/Base.lproj/CallView~ipad.xib +++ /dev/null @@ -1,1397 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Classes/CallConferenceTableView.m b/Classes/CallConferenceTableView.m deleted file mode 100644 index 1abf56af9..000000000 --- a/Classes/CallConferenceTableView.m +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 . - */ - -#import "CallConferenceTableView.h" -#import "UICallConferenceCell.h" -#import "LinphoneManager.h" -#import "Utils.h" -#import "linphoneapp-Swift.h" - -@implementation CallConferenceTableView - -#pragma mark - UI change - -- (void)update { - [self.tableView reloadData]; -} - -#pragma mark - UITableViewDataSource Functions - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - NSString *kCellId = NSStringFromClass(UICallConferenceCell.class); - UICallConferenceCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellId]; - if (cell == nil) { - cell = [[UICallConferenceCell alloc] initWithIdentifier:kCellId]; - } - LinphoneConference *c = [CallManager.instance getConference]; - if (c != nil) { - [cell setParticipant:bctbx_list_nth_data(linphone_conference_get_participant_list(c),(int)indexPath.row)]; - } - cell.contentView.userInteractionEnabled = NO; - return cell; -} - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return [CallManager.instance getConference] ? linphone_conference_get_participant_count([CallManager.instance getConference]) : 0; -} - -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { - return 1; -} - -- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { - return 1e-5; -} -- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { - return 1e-5; -} - -@end diff --git a/Classes/CallIncomingView.h b/Classes/CallIncomingView.h deleted file mode 100644 index 8031a7b2e..000000000 --- a/Classes/CallIncomingView.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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 . - */ - -#import - -#import "UICompositeView.h" -#import "TPMultiLayoutViewController.h" -#import "UIRoundedImageView.h" -#include "LinphoneManager.h" - -@protocol IncomingCallViewDelegate - -- (void)incomingCallAccepted:(LinphoneCall *)call evenWithVideo:(BOOL)video; -- (void)incomingCallDeclined:(LinphoneCall *)call; -- (void)incomingCallAborted:(LinphoneCall *)call; - -@end - -@interface CallIncomingView : TPMultiLayoutViewController { -} - -@property(nonatomic) Boolean earlyMedia; - -@property(weak, nonatomic) IBOutlet UILabel *nameLabel; -@property(nonatomic, strong) IBOutlet UILabel *addressLabel; -@property(nonatomic, strong) IBOutlet UIRoundedImageView *avatarImage; -@property(nonatomic, assign) LinphoneCall *call; -@property(nonatomic, strong) id delegate; -@property(weak, nonatomic) IBOutlet UIView *tabVideoBar; -@property(weak, nonatomic) IBOutlet UIView *tabBar; -@property (weak, nonatomic) IBOutlet UIView *earlyMediaView; - - -- (IBAction)onAcceptClick:(id)event; -- (IBAction)onDeclineClick:(id)event; -- (IBAction)onAcceptAudioOnlyClick:(id)sender; - -@end diff --git a/Classes/CallIncomingView.m b/Classes/CallIncomingView.m deleted file mode 100644 index ccf22052b..000000000 --- a/Classes/CallIncomingView.m +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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 . - */ - -#import "CallIncomingView.h" -#import "LinphoneManager.h" -#import "FastAddressBook.h" -#import "PhoneMainView.h" -#import "Utils.h" - -@implementation CallIncomingView - -#pragma mark - ViewController Functions - -- (void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - - [NSNotificationCenter.defaultCenter addObserver:self - selector:@selector(callUpdateEvent:) - name:kLinphoneCallUpdate - object:nil]; -} - -- (void)viewWillDisappear:(BOOL)animated { - [super viewWillDisappear:animated]; - [NSNotificationCenter.defaultCenter removeObserver:self name:kLinphoneCallUpdate object:nil]; - _call = NULL; -} - -#pragma mark - UICompositeViewDelegate Functions - -static UICompositeViewDescription *compositeDescription = nil; - -+ (UICompositeViewDescription *)compositeViewDescription { - if (compositeDescription == nil) { - compositeDescription = [[UICompositeViewDescription alloc] init:self.class - statusBar:StatusBarView.class - tabBar:nil - sideMenu:CallSideMenuView.class - fullscreen:false - isLeftFragment:YES - fragmentWith:nil]; - compositeDescription.darkBackground = true; - } - return compositeDescription; -} - -- (UICompositeViewDescription *)compositeViewDescription { - return self.class.compositeViewDescription; -} - -- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { - [super didRotateFromInterfaceOrientation:fromInterfaceOrientation]; - if (_earlyMedia && [LinphoneManager.instance lpConfigBoolForKey:@"pref_accept_early_media"] && linphone_core_get_calls_nb(LC) < 2) { - _earlyMediaView.hidden = NO; - linphone_core_set_native_video_window_id(LC, (__bridge void *)(_earlyMediaView)); - } - if (_call) { - [self update]; - } -} - -#pragma mark - Event Functions - -- (void)callUpdateEvent:(NSNotification *)notif { - LinphoneCall *acall = [[notif.userInfo objectForKey:@"call"] pointerValue]; - LinphoneCallState astate = [[notif.userInfo objectForKey:@"state"] intValue]; - [self callUpdate:acall state:astate]; -} - -- (void)callUpdate:(LinphoneCall *)acall state:(LinphoneCallState)astate { - if (_call == acall && (astate == LinphoneCallEnd || astate == LinphoneCallError)) { - [_delegate incomingCallAborted:_call]; - } else if ([LinphoneManager.instance lpConfigBoolForKey:@"auto_answer"]) { - LinphoneCallState state = linphone_call_get_state(_call); - if (state == LinphoneCallIncomingReceived) { - LOGI(@"Auto answering call"); - [self onAcceptClick:nil]; - } - } -} - -#pragma mark - - -- (void)update { - const LinphoneAddress *addr = linphone_call_get_remote_address(_call); - [ContactDisplay setDisplayNameLabel:_nameLabel forAddress:addr withAddressLabel:_addressLabel]; - char *uri = linphone_address_as_string_uri_only(addr); - ms_free(uri); - [_avatarImage setImage:[FastAddressBook imageForAddress:addr] bordered:YES withRoundedRadius:YES]; - - _tabBar.hidden = linphone_call_params_video_enabled(linphone_call_get_remote_params(_call)); - _tabVideoBar.hidden = !_tabBar.hidden; -} - -#pragma mark - Property Functions -static void hideSpinner(LinphoneCall *call, void *user_data) { - CallIncomingView *thiz = (__bridge CallIncomingView *)user_data; - thiz.earlyMedia = TRUE; - thiz.earlyMediaView.hidden = NO; - linphone_core_set_native_video_window_id(LC, (__bridge void *)(thiz.earlyMediaView)); -} - -- (void)setCall:(LinphoneCall *)call { - _call = call; - _earlyMedia = FALSE; - if ([LinphoneManager.instance lpConfigBoolForKey:@"pref_accept_early_media"] && linphone_core_get_calls_nb(LC) < 2) { - linphone_call_accept_early_media(_call); - // linphone_call_params_get_used_video_codec return 0 if no video stream enabled - if (linphone_call_params_get_used_video_codec(linphone_call_get_current_params(_call))) { - linphone_call_set_next_video_frame_decoded_callback(call, hideSpinner, (__bridge void *)(self)); - } - } else { - _earlyMediaView.hidden = YES; - } - - [self update]; - [self callUpdate:_call state:linphone_call_get_state(call)]; -} - -#pragma mark - Action Functions - -- (IBAction)onAcceptClick:(id)event { - [_delegate incomingCallAccepted:_call evenWithVideo:YES]; -} - -- (IBAction)onDeclineClick:(id)event { - [_delegate incomingCallDeclined:_call]; -} - -- (IBAction)onAcceptAudioOnlyClick:(id)sender { - [_delegate incomingCallAccepted:_call evenWithVideo:NO]; -} - -@end diff --git a/Classes/CallManager.swift b/Classes/CallManager.swift index b267ae43f..6d3a7930f 100644 --- a/Classes/CallManager.swift +++ b/Classes/CallManager.swift @@ -37,15 +37,13 @@ import AVFoundation static var theCallManager: CallManager? let providerDelegate: ProviderDelegate! // to support callkit let callController: CXCallController! // to support callkit - var lc: Core? - @objc var speakerBeforePause : Bool = false + var core: Core? @objc var nextCallIsTransfer: Bool = false var referedFromCall: String? var referedToCall: String? var endCallkit: Bool = false var globalState : GlobalState = .Off var actionsToPerformOnceWhenCoreIsOn : [(()->Void)] = [] - var conference: Conference? @@ -65,8 +63,8 @@ import AVFoundation } @objc func setCore(core: OpaquePointer) { - lc = Core.getSwiftObject(cObject: core) - lc?.addDelegate(delegate: self) + self.core = Core.getSwiftObject(cObject: core) + self.core?.addDelegate(delegate: self) } @objc static func getAppData(call: OpaquePointer) -> CallAppData? { @@ -106,7 +104,7 @@ import AVFoundation if (callId == nil) { return nil } - let calls = lc?.calls + let calls = core?.calls if let callTmp = calls?.first(where: { $0.callLog?.callId == callId }) { return callTmp } @@ -114,8 +112,8 @@ import AVFoundation } @objc func stopLinphoneCore() { - if (lc?.callsNb == 0) { - lc?.stopAsync() + if (core?.callsNb == 0) { + core?.stopAsync() } } @@ -139,60 +137,6 @@ import AVFoundation return false } - @objc func changeRouteToSpeaker() { - for device in lc!.audioDevices { - if (device.type == AudioDeviceType.Speaker) { - lc!.outputAudioDevice = device - break - } - } - UIDevice.current.isProximityMonitoringEnabled = false - } - - @objc func changeRouteToBluetooth() { - for device in lc!.audioDevices { - if (device.type == AudioDeviceType.Bluetooth || device.type == AudioDeviceType.BluetoothA2DP) { - lc!.outputAudioDevice = device - break - } - } - UIDevice.current.isProximityMonitoringEnabled = (lc!.callsNb > 0) - } - - @objc func changeRouteToDefault() { - lc!.outputAudioDevice = lc!.defaultOutputAudioDevice - } - - @objc func isBluetoothAvailable() -> Bool { - for device in lc!.audioDevices { - if (device.type == AudioDeviceType.Bluetooth || device.type == AudioDeviceType.BluetoothA2DP) { - return true; - } - } - return false; - } - - @objc func isSpeakerEnabled() -> Bool { - if let outputDevice = lc!.outputAudioDevice { - return outputDevice.type == AudioDeviceType.Speaker - } - return false - } - - @objc func isBluetoothEnabled() -> Bool { - if let outputDevice = lc!.outputAudioDevice { - return (outputDevice.type == AudioDeviceType.Bluetooth || outputDevice.type == AudioDeviceType.BluetoothA2DP) - } - return false - } - - @objc func isReceiverEnabled() -> Bool { - if let outputDevice = lc!.outputAudioDevice { - return outputDevice.type == AudioDeviceType.Microphone - } - return false - } - func requestTransaction(_ transaction: CXTransaction, action: String) { callController.request(transaction) { error in @@ -238,7 +182,7 @@ import AVFoundation func acceptCall(call: Call, hasVideo:Bool) { do { - let callParams = try lc!.createCallParams(call: call) + let callParams = try core!.createCallParams(call: call) callParams.videoEnabled = hasVideo if (ConfigManager.instance().lpConfigBoolForKey(key: "edge_opt_preference")) { let low_bandwidth = (AppManager.network() == .network_2g) @@ -269,7 +213,7 @@ import AVFoundation } let sAddr = Address.getSwiftObject(cObject: addr!) - if (CallManager.callKitEnabled() && !CallManager.instance().nextCallIsTransfer && !isInConference()) { + if (CallManager.callKitEnabled() && !CallManager.instance().nextCallIsTransfer && core?.conference?.isIn != true) { let uuid = UUID() let name = FastAddressBook.displayName(for: addr) ?? "unknow" let handle = CXHandle(type: .generic, value: sAddr.asStringUriOnly()) @@ -290,7 +234,7 @@ import AVFoundation func doCall(addr: Address, isSas: Bool) throws { let displayName = FastAddressBook.displayName(for: addr.getCobject) - let lcallParams = try CallManager.instance().lc!.createCallParams(call: nil) + let lcallParams = try CallManager.instance().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 @@ -305,7 +249,7 @@ import AVFoundation } if (CallManager.instance().nextCallIsTransfer) { - let call = CallManager.instance().lc!.currentCall + let call = CallManager.instance().core!.currentCall try call?.transferTo(referTo: addr) CallManager.instance().nextCallIsTransfer = false } else { @@ -316,7 +260,7 @@ import AVFoundation if (isSas) { lcallParams.mediaEncryption = .ZRTP } - let call = CallManager.instance().lc!.inviteAddressWithParams(addr: addr, params: lcallParams) + let call = CallManager.instance().core!.inviteAddressWithParams(addr: addr, params: lcallParams) if (call != nil) { // The LinphoneCallAppData object should be set on call creation with callback // - (void)onCall:StateChanged:withMessage:. If not, we are in big trouble and expect it to crash @@ -333,32 +277,6 @@ import AVFoundation } } - @objc func groupCall() { - if (CallManager.callKitEnabled()) { - let calls = lc?.calls - if (calls == nil || calls!.isEmpty) { - return - } - let firstCall = calls!.first?.callLog?.callId ?? "" - let lastCall = (calls!.count > 1) ? calls!.last?.callLog?.callId ?? "" : "" - - let currentUuid = CallManager.instance().providerDelegate.uuids["\(firstCall)"] - if (currentUuid == nil) { - Log.directLog(BCTBX_LOG_ERROR, text: "Can not find correspondant call to group.") - return - } - - let newUuid = CallManager.instance().providerDelegate.uuids["\(lastCall)"] - let groupAction = CXSetGroupCallAction(call: currentUuid!, callUUIDToGroupWith: newUuid) - let transcation = CXTransaction(action: groupAction) - requestTransaction(transcation, action: "groupCall") - - setResumeCalls() - } else { - try? lc?.addAllToConference() - } - } - @objc func removeAllCallInfos() { providerDelegate.callInfos.removeAll() providerDelegate.uuids.removeAll() @@ -418,7 +336,7 @@ import AVFoundation } @objc func setHeldOtherCalls(exceptCallid: String) { - for call in CallManager.instance().lc!.calls { + for call in CallManager.instance().core!.calls { if (call.callLog?.callId != exceptCallid && call.state != .Paused && call.state != .Pausing && call.state != .PausedByRemote) { setHeld(call: call, hold: true) } @@ -426,7 +344,7 @@ import AVFoundation } func setResumeCalls() { - for call in CallManager.instance().lc!.calls { + for call in CallManager.instance().core!.calls { if (call.state == .Paused || call.state == .Pausing || call.state == .PausedByRemote) { setHeld(call: call, hold: false) } @@ -443,7 +361,7 @@ import AVFoundation @objc func acceptVideo(call: OpaquePointer, confirm: Bool) { let sCall = Call.getSwiftObject(cObject: call) - let params = try? lc?.createCallParams(call: sCall) + let params = try? core?.createCallParams(call: sCall) params?.videoEnabled = confirm try? sCall.acceptUpdate(params: params) } @@ -464,7 +382,7 @@ import AVFoundation for call in CallManager.instance().providerDelegate.uuids { let callId = CallManager.instance().providerDelegate.callInfos[call.value]?.callId if (callId != nil) { - let call = CallManager.instance().lc?.getCallByCallid(callId: callId!) + let call = CallManager.instance().core?.getCallByCallid(callId: callId!) if (call != nil && call?.state != .PushIncomingReceived) { // sometimes (for example) due to network, registration failed, in this case, keep the call continue @@ -478,12 +396,6 @@ import AVFoundation CallManager.instance().endCallkit = false } } - - func onConferenceStateChanged(core: Core, conference: Conference, state: Conference.State) { - if (state == .Terminated) { - CallManager.instance().conference = nil - } - } func onCallStateChanged(core: Core, call: Call, state cstate: Call.State, message: String) { let callLog = call.callLog @@ -535,11 +447,6 @@ import AVFoundation } } } - - if (CallManager.instance().speakerBeforePause) { - CallManager.instance().speakerBeforePause = false - CallManager.instance().changeRouteToSpeaker() - } break case .OutgoingInit, .OutgoingProgress, @@ -568,14 +475,6 @@ import AVFoundation displayName = contactName } - UIDevice.current.isProximityMonitoringEnabled = false - if (CallManager.instance().lc!.callsNb == 0) { - CallManager.instance().changeRouteToDefault() - // disable this because I don't find anygood reason for it: _bluetoothAvailable = FALSE; - // furthermore it introduces a bug when calling multiple times since route may not be - // reconfigured between cause leading to bluetooth being disabled while it should not - //CallManager.instance().bluetoothEnabled = false - } if UIApplication.shared.applicationState != .active && (callLog == nil || callLog?.status == .Missed || callLog?.status == .Aborted || callLog?.status == .EarlyAborted) { // Configure the notification's payload. @@ -634,12 +533,6 @@ import AVFoundation break } - if (cstate == .IncomingReceived || cstate == .OutgoingInit || cstate == .Connected || cstate == .StreamsRunning) { - let check = call.currentParams?.videoEnabled - if ((call.currentParams?.videoEnabled ?? false) && CallManager.instance().isReceiverEnabled()) { - CallManager.instance().changeRouteToSpeaker() - } - } } // post Notification kLinphoneCallUpdate NotificationCenter.default.post(name: Notification.Name("LinphoneCallUpdate"), object: self, userInfo: [ @@ -652,13 +545,13 @@ import AVFoundation // Audio messages @objc func activateAudioSession() { - lc?.activateAudioSession(actived: true) + core?.activateAudioSession(actived: true) } @objc func getSpeakerSoundCard() -> String? { var speakerCard: String? = nil var earpieceCard: String? = nil - lc?.audioDevices.forEach { device in + core?.audioDevices.forEach { device in if (device.hasCapability(capability: .CapabilityPlay)) { if (device.type == .Speaker) { speakerCard = device.id @@ -670,116 +563,46 @@ import AVFoundation return speakerCard != nil ? speakerCard : earpieceCard } - - - // Conference - - @objc func hostConference() -> Bool { - return conference != nil - } + // Local Conference - func addAllToConference() { - if (conference == nil) { - guard let cp = try?lc?.createConferenceParams() else { - Log.directLog(BCTBX_LOG_ERROR, text: "Unable to create conference parameters") + @objc func startLocalConference() { + if (CallManager.callKitEnabled()) { + let calls = core?.calls + if (calls == nil || calls!.isEmpty) { return } - if let currentCall = lc?.currentCall, let currentParams = currentCall.currentParams { - cp.videoEnabled = currentParams.videoEnabled - } - conference = try?lc?.createConferenceWithParams(params: cp) - } - lc?.calls.forEach { call in - if (call.conference == nil || call.conference?.participantCount == 1) { - try?conference?.addParticipant(call: call) - } - } - } - - @objc func getConference() -> OpaquePointer? { - guard let core = lc else { - return nil - } - return (core.conference != nil) ? core.conference?.getCobject : (core.currentCall?.conference != nil) ? core.currentCall!.conference!.getCobject : nil - } - - func getConference() -> Conference? { - guard let core = lc else { - return nil - } - return (core.conference != nil) ? core.conference : (core.currentCall?.conference != nil) ? core.currentCall!.conference : nil - } - - @objc func isInConference() -> Bool { - return isInConferenceAsHost()||isInConferenceAsGuest() - } - - @objc func isInConferenceAsGuest() -> Bool { - guard let core = lc else { - return false - } - return !isInConferenceAsHost() && core.currentCall != nil && core.currentCall?.conference != nil && (core.currentCall?.conference!.participantCount)! > 1 - } - - @objc func isInConferenceAsHost() -> Bool { - guard let core = lc else { - return false - } - return core.conference?.isIn == true - } - - @objc func hasConferenceAsGuest() -> Bool { - guard let core = lc else { - return false - } - if (core.callsNb<=1) { - return false - } - var found = false - core.calls.forEach { - let c = $0.conference - if (c != nil && c!.participantCount > 1 && hostConference()) { - found = true + let firstCall = calls!.first?.callLog?.callId ?? "" + let lastCall = (calls!.count > 1) ? calls!.last?.callLog?.callId ?? "" : "" + + let currentUuid = CallManager.instance().providerDelegate.uuids["\(firstCall)"] + if (currentUuid == nil) { + Log.directLog(BCTBX_LOG_ERROR, text: "Can not find correspondant call to group.") return } + + let newUuid = CallManager.instance().providerDelegate.uuids["\(lastCall)"] + let groupAction = CXSetGroupCallAction(call: currentUuid!, callUUIDToGroupWith: newUuid) + let transcation = CXTransaction(action: groupAction) + requestTransaction(transcation, action: "groupCall") + + setResumeCalls() + } else { + addAllToLocalConference() } - return found } - @objc func getCallFor(participant : OpaquePointer) -> OpaquePointer? { - let p = Participant.getSwiftObject(cObject: participant) - guard let core = lc else { - return nil - } - var call:Call? = nil - core.calls.forEach { (callIt) in - let c = callIt.conference - c?.participantList.forEach { (p2) in - if (p2.address?.asStringUriOnly() == p.address?.asStringUriOnly()) { - call = callIt - return - } + func addAllToLocalConference() { + do { + if let core = core, let params = try? core.createConferenceParams() { + params.videoEnabled = false // We disable video for local conferencing (cf Android) + let conference = core.conference != nil ? core.conference : try core.createConferenceWithParams(params: params) + try conference?.addParticipants(calls: core.calls) } + } catch { + Log.directLog(BCTBX_LOG_ERROR, text: "accept call failed \(error)") } - return call?.getCobject } - @objc func inVideoConf() -> Bool { - guard let core = lc else { - return false - } - let result = isInConference() && (getConference()?.currentParams?.isVideoEnabled == true || core.currentCall?.currentParams?.videoEnabled == true) - NSLog("cdes \(result) \(core.currentCall?.currentParams?.videoEnabled)") - return result - } - - - @objc func inAudioConf() -> Bool { - guard let core = lc else { - return false - } - return core.conference?.isIn == true && core.conference != nil && core.currentCall?.conference?.currentParams?.isVideoEnabled == false - } } diff --git a/Classes/CallOutgoingView.h b/Classes/CallOutgoingView.h deleted file mode 100644 index 1d4430efe..000000000 --- a/Classes/CallOutgoingView.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 . - */ - -#import - -#import "UICompositeView.h" -#import "TPMultiLayoutViewController.h" -#include "linphone/linphonecore.h" -#import "UIRoundedImageView.h" -#import "UIDigitButton.h" - - -@interface CallOutgoingView : TPMultiLayoutViewController { -} -@property(weak, nonatomic) IBOutlet UIRoundedImageView *avatarImage; -@property(weak, nonatomic) IBOutlet UILabel *nameLabel; -@property(weak, nonatomic) IBOutlet UISpeakerButton *speakerButton; -@property(weak, nonatomic) IBOutlet UILabel *addressLabel; -@property(weak, nonatomic) IBOutlet UIToggleButton *routesButton; -@property(weak, nonatomic) IBOutlet UIView *routesView; -@property(weak, nonatomic) IBOutlet UIBluetoothButton *routesBluetoothButton; -@property(weak, nonatomic) IBOutlet UIButton *routesEarpieceButton; -@property(weak, nonatomic) IBOutlet UISpeakerButton *routesSpeakerButton; -@property(weak, nonatomic) IBOutlet UIMutedMicroButton *microButton; -@property (weak, nonatomic) IBOutlet UIButton *declineButton; - -@property (weak, nonatomic) IBOutlet UIToggleButton *numpadButton; -@property (strong, nonatomic) IBOutlet UIView *numpadView; -@property(nonatomic, strong) IBOutlet UIDigitButton *oneButton; -@property(nonatomic, strong) IBOutlet UIDigitButton *twoButton; -@property(nonatomic, strong) IBOutlet UIDigitButton *threeButton; -@property(nonatomic, strong) IBOutlet UIDigitButton *fourButton; -@property(nonatomic, strong) IBOutlet UIDigitButton *fiveButton; -@property(nonatomic, strong) IBOutlet UIDigitButton *sixButton; -@property(nonatomic, strong) IBOutlet UIDigitButton *sevenButton; -@property(nonatomic, strong) IBOutlet UIDigitButton *eightButton; -@property(nonatomic, strong) IBOutlet UIDigitButton *nineButton; -@property(nonatomic, strong) IBOutlet UIDigitButton *starButton; -@property(nonatomic, strong) IBOutlet UIDigitButton *zeroButton; -@property(nonatomic, strong) IBOutlet UIDigitButton *hashButton; -@property (weak, nonatomic) IBOutlet UIButton *declineButton_earlyMedia; - -- (IBAction)onDeclineClick:(id)sender; - -@end diff --git a/Classes/CallOutgoingView.m b/Classes/CallOutgoingView.m deleted file mode 100644 index c5057377d..000000000 --- a/Classes/CallOutgoingView.m +++ /dev/null @@ -1,267 +0,0 @@ -/* - * 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 . - */ - -#import "CallOutgoingView.h" -#import "PhoneMainView.h" - -@implementation CallOutgoingView - -#pragma mark - UICompositeViewDelegate Functions - -static UICompositeViewDescription *compositeDescription = nil; - -+ (UICompositeViewDescription *)compositeViewDescription { - if (compositeDescription == nil) { - compositeDescription = [[UICompositeViewDescription alloc] init:self.class - statusBar:StatusBarView.class - tabBar:nil - sideMenu:CallSideMenuView.class - fullscreen:false - isLeftFragment:NO - fragmentWith:nil]; - - compositeDescription.darkBackground = true; - } - return compositeDescription; -} - -- (UICompositeViewDescription *)compositeViewDescription { - return self.class.compositeViewDescription; -} - -- (void)viewDidLoad { - _routesEarpieceButton.enabled = !IPAD; - - [_zeroButton setDigit:'0']; - [_zeroButton setDtmf:true]; - [_oneButton setDigit:'1']; - [_oneButton setDtmf:true]; - [_twoButton setDigit:'2']; - [_twoButton setDtmf:true]; - [_threeButton setDigit:'3']; - [_threeButton setDtmf:true]; - [_fourButton setDigit:'4']; - [_fourButton setDtmf:true]; - [_fiveButton setDigit:'5']; - [_fiveButton setDtmf:true]; - [_sixButton setDigit:'6']; - [_sixButton setDtmf:true]; - [_sevenButton setDigit:'7']; - [_sevenButton setDtmf:true]; - [_eightButton setDigit:'8']; - [_eightButton setDtmf:true]; - [_nineButton setDigit:'9']; - [_nineButton setDtmf:true]; - [_starButton setDigit:'*']; - [_starButton setDtmf:true]; - [_hashButton setDigit:'#']; - [_hashButton setDtmf:true]; - -} - -- (void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - - [NSNotificationCenter.defaultCenter addObserver:self - selector:@selector(bluetoothAvailabilityUpdateEvent:) - name:kLinphoneBluetoothAvailabilityUpdate - object:nil]; - - [NSNotificationCenter.defaultCenter addObserver:self - selector:@selector(callUpdateEvent:) - name:kLinphoneCallUpdate - object:nil]; - - LinphoneCall *call = linphone_core_get_current_call(LC); - if (!call) { - return; - } - - const LinphoneAddress *addr = linphone_call_get_remote_address(call); - [ContactDisplay setDisplayNameLabel:_nameLabel forAddress:addr withAddressLabel:_addressLabel]; - char *uri = linphone_address_as_string_uri_only(addr); - ms_free(uri); - [_avatarImage setImage:[FastAddressBook imageForAddress:addr] bordered:NO withRoundedRadius:YES]; - - [self hideSpeaker:LinphoneManager.instance.bluetoothAvailable]; - - [_speakerButton update]; - [_microButton update]; - [_routesButton update]; - [self hidePad:TRUE animated:FALSE]; - [self callUpdate:call state:linphone_call_get_state(call) animated:true]; - -} - -- (void)viewDidAppear:(BOOL)animated { - [super viewDidAppear:animated]; - // if there is no call (for whatever reason), we must wait viewDidAppear method - // before popping current view, because UICompositeView cannot handle view change - // directly in viewWillAppear (this would lead to crash in deallocated memory - easily - // reproductible on iPad mini). - if (!linphone_core_get_current_call(LC)) { - [PhoneMainView.instance popCurrentView]; - } -} - -- (void)viewWillDisappear:(BOOL)animated { - [super viewWillDisappear:animated]; - [NSNotificationCenter.defaultCenter removeObserver:self]; -} - -- (IBAction)onRoutesBluetoothClick:(id)sender { - [self hideRoutes:TRUE animated:TRUE]; - [CallManager.instance changeRouteToBluetooth]; -} - -- (IBAction)onRoutesEarpieceClick:(id)sender { - [self hideRoutes:TRUE animated:TRUE]; - [CallManager.instance changeRouteToDefault]; -} - -- (IBAction)onRoutesSpeakerClick:(id)sender { - [self hideRoutes:TRUE animated:TRUE]; - [CallManager.instance changeRouteToSpeaker]; -} - -- (IBAction)onRoutesClick:(id)sender { - if ([_routesView isHidden]) { - [self hideRoutes:FALSE animated:ANIMATED]; - } else { - [self hideRoutes:TRUE animated:ANIMATED]; - } -} - -- (IBAction)onNumpadClick:(id)sender { - if ([_numpadView isHidden]) { - [self hidePad:FALSE animated:ANIMATED]; - } else { - [self hidePad:TRUE animated:ANIMATED]; - } -} - -- (IBAction)onDeclineClick:(id)sender { - LinphoneCall *call = linphone_core_get_current_call(LC); - if (call) { - [CallManager.instance terminateCallWithCall:call]; - } -} - -- (void)hidePad:(BOOL)hidden animated:(BOOL)animated { - if (hidden) { - [_numpadButton setOff]; - } else { - [_numpadButton setOn]; - } - if (hidden != _numpadView.hidden) { - if (animated) { - [self hideAnimation:hidden forView:_numpadView completion:nil]; - } else { - [_numpadView setHidden:hidden]; - } - } -} - -- (void)hideRoutes:(BOOL)hidden animated:(BOOL)animated { - if (hidden) { - [_routesButton setOff]; - } else { - [_routesButton setOn]; - } - - _routesBluetoothButton.selected = [CallManager.instance isBluetoothEnabled]; - _routesSpeakerButton.selected = [CallManager.instance isSpeakerEnabled]; - _routesEarpieceButton.selected = !_routesBluetoothButton.selected && !_routesSpeakerButton.selected; - - if (hidden != _routesView.hidden) { - [_routesView setHidden:hidden]; - } -} - -- (void)hideSpeaker:(BOOL)hidden { - _speakerButton.hidden = hidden; - _routesButton.hidden = !hidden; -} - -#pragma mark - Event Functions - -- (void)bluetoothAvailabilityUpdateEvent:(NSNotification *)notif { - bool available = [[notif.userInfo objectForKey:@"available"] intValue]; - dispatch_async(dispatch_get_main_queue(), ^{ - [self hideSpeaker:available]; - }); -} - -- (void)callUpdateEvent:(NSNotification *)notif { - LinphoneCall *call = [[notif.userInfo objectForKey:@"call"] pointerValue]; - LinphoneCallState state = [[notif.userInfo objectForKey:@"state"] intValue]; - [self callUpdate:call state:state animated:TRUE]; -} - -- (void)callUpdate:(LinphoneCall *)call state:(LinphoneCallState)state animated:(BOOL)animated { - _declineButton_earlyMedia.hidden = linphone_call_get_state(call) != LinphoneCallStateOutgoingEarlyMedia; - _declineButton.hidden = !_declineButton_earlyMedia.hidden; - _numpadButton.hidden = _declineButton_earlyMedia.hidden; -} - -#pragma mark - Animation - -- (void)hideAnimation:(BOOL)hidden forView:(UIView *)target completion:(void (^)(BOOL finished))completion { - if (hidden) { - int original_y = target.frame.origin.y; - CGRect newFrame = target.frame; - newFrame.origin.y = self.view.frame.size.height; - [UIView animateWithDuration:0.5 - delay:0.0 - options:UIViewAnimationOptionCurveEaseIn - animations:^{ - target.frame = newFrame; - } - completion:^(BOOL finished) { - CGRect originFrame = target.frame; - originFrame.origin.y = original_y; - target.hidden = YES; - target.frame = originFrame; - if (completion) - completion(finished); - }]; - } else { - CGRect frame = target.frame; - int original_y = frame.origin.y; - frame.origin.y = self.view.frame.size.height; - target.frame = frame; - frame.origin.y = original_y; - target.hidden = NO; - - [UIView animateWithDuration:0.5 - delay:0.0 - options:UIViewAnimationOptionCurveEaseOut - animations:^{ - target.frame = frame; - } - completion:^(BOOL finished) { - target.frame = frame; // in case application did not finish - if (completion) - completion(finished); - }]; - } -} - - -@end diff --git a/Classes/CallPausedTableView.m b/Classes/CallPausedTableView.m deleted file mode 100644 index 2dc9b6c29..000000000 --- a/Classes/CallPausedTableView.m +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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 . - */ - -#import "CallPausedTableView.h" -#import "UICallPausedCell.h" -#import "LinphoneManager.h" -#import "Utils.h" - -@implementation CallPausedTableView - -#pragma mark - UI change - -- (void)update { - [self.tableView reloadData]; - CGRect newOrigin = self.tableView.frame; - newOrigin.size.height = - [self tableView:self.tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]] * - [self tableView:self.tableView numberOfRowsInSection:0]; - newOrigin.origin.y += self.tableView.frame.size.height - newOrigin.size.height; - self.tableView.frame = newOrigin; -} - -#pragma mark - UITableViewDataSource Functions -- (LinphoneCall *)conferenceCallForRow:(NSInteger)row { - const MSList *calls = linphone_core_get_calls(LC); - int i = -1; - while (calls) { - if (linphone_call_get_state(calls->data) == LinphoneCallPaused) { - i++; - if (i == row) - break; - } - calls = calls->next; - } - // we should reach this only when we are querying for conference - return (calls ? calls->data : NULL); -} - -#pragma mark - UITableViewDataSource Functions - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - NSString *kCellId = NSStringFromClass(UICallPausedCell.class); - UICallPausedCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellId]; - if (cell == nil) { - cell = [[UICallPausedCell alloc] initWithIdentifier:kCellId]; - } - [cell setCall:[self conferenceCallForRow:indexPath.row]]; - cell.contentView.userInteractionEnabled = false; - return cell; -} - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - const MSList *calls = linphone_core_get_calls(LC); - int count = 0; - int conference_in_pause = 0; - int call_in_conference = 0; - while (calls) { - LinphoneCall *call = calls->data; - BOOL callInConference = linphone_call_params_get_local_conference_mode(linphone_call_get_current_params(call)); - if (linphone_call_get_state(call) == LinphoneCallPaused) { - count++; - } - if(callInConference) { - call_in_conference++; - if (!linphone_core_is_in_conference(LC)) { - conference_in_pause = 1; - } - } - calls = calls->next; - } - if(call_in_conference == 1) { - conference_in_pause = 0; - } - return count + conference_in_pause; -} - -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { - return 1; -} - -- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { - return 1e-5; -} -- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { - return 1e-5; -} - -@end diff --git a/Classes/CallSideMenuView.m b/Classes/CallSideMenuView.m deleted file mode 100644 index 27ee4de48..000000000 --- a/Classes/CallSideMenuView.m +++ /dev/null @@ -1,230 +0,0 @@ -/* - * 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 . - */ - -#import "CallSideMenuView.h" -#import "LinphoneManager.h" -#import "PhoneMainView.h" - -@implementation CallSideMenuView { - NSTimer *updateTimer; -} - -#pragma mark - ViewController Functions - -- (void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - if (updateTimer != nil) { - [updateTimer invalidate]; - } - updateTimer = [NSTimer scheduledTimerWithTimeInterval:1 - target:self - selector:@selector(updateStats:) - userInfo:nil - repeats:YES]; - - [self updateStats:nil]; -} - -- (void)viewWillDisappear:(BOOL)animated { - [super viewWillDisappear:animated]; - if (updateTimer != nil) { - [updateTimer invalidate]; - updateTimer = nil; - } -} - -- (IBAction)onLateralSwipe:(id)sender { - [PhoneMainView.instance.mainViewController hideSideMenu:YES]; -} - -+ (NSString *)iceToString:(LinphoneIceState)state { - switch (state) { - case LinphoneIceStateNotActivated: - return NSLocalizedString(@"Not activated", @"ICE has not been activated for this call"); - break; - case LinphoneIceStateFailed: - return NSLocalizedString(@"Failed", @"ICE processing has failed"); - break; - case LinphoneIceStateInProgress: - return NSLocalizedString(@"In progress", @"ICE process is in progress"); - break; - case LinphoneIceStateHostConnection: - return NSLocalizedString(@"Direct connection", - @"ICE has established a direct connection to the remote host"); - break; - case LinphoneIceStateReflexiveConnection: - return NSLocalizedString( - @"NAT(s) connection", - @"ICE has established a connection to the remote host through one or several NATs"); - break; - case LinphoneIceStateRelayConnection: - return NSLocalizedString(@"Relay connection", @"ICE has established a connection through a relay"); - break; - } -} - -+ (NSString*)afinetToString:(int)remote_family { - return (remote_family == LinphoneAddressFamilyUnspec) ? @"Unspecified":(remote_family == LinphoneAddressFamilyInet) ? @"IPv4" : @"IPv6"; -} - -+ (NSString *)mediaEncryptionToString:(LinphoneMediaEncryption)enc { - switch (enc) { - case LinphoneMediaEncryptionDTLS: - return @"DTLS"; - case LinphoneMediaEncryptionSRTP: - return @"SRTP"; - case LinphoneMediaEncryptionZRTP: - return @"ZRTP"; - case LinphoneMediaEncryptionNone: - break; - } - return NSLocalizedString(@"None", nil); -} - -- (NSString *)updateStatsForCall:(LinphoneCall *)call stream:(LinphoneStreamType)stream { - NSMutableString *result = [[NSMutableString alloc] init]; - const PayloadType *payload = NULL; - const LinphoneCallStats *stats; - const LinphoneCallParams *params = linphone_call_get_current_params(call); - NSString *name; - - switch (stream) { - case LinphoneStreamTypeAudio: - name = @"Audio"; - payload = linphone_call_params_get_used_audio_codec(params); - stats = linphone_call_get_audio_stats(call); - break; - case LinphoneStreamTypeText: - name = @"Text"; - payload = linphone_call_params_get_used_text_codec(params); - stats = linphone_call_get_text_stats(call); - break; - case LinphoneStreamTypeVideo: - name = @"Video"; - payload = linphone_call_params_get_used_video_codec(params); - stats = linphone_call_get_video_stats(call); - break; - case LinphoneStreamTypeUnknown: - break; - } - if (payload == NULL) { - return result; - } - - [result appendString:@"\n"]; - [result appendString:name]; - [result appendString:@"\n"]; - - [result appendString:[NSString stringWithFormat:@"Codec: %s/%iHz", payload->mime_type, payload->clock_rate]]; - if (stream == LinphoneStreamTypeAudio) { - [result appendString:[NSString stringWithFormat:@"/%i channels", payload->channels]]; - } - [result appendString:@"\n"]; - // Encoder & decoder descriptions - const char *enc_desc = ms_factory_get_encoder(linphone_core_get_ms_factory(LC), payload->mime_type)->text; - const char *dec_desc = ms_factory_get_decoder(linphone_core_get_ms_factory(LC), payload->mime_type)->text; - if (strcmp(enc_desc, dec_desc) == 0) { - [result appendString:[NSString stringWithFormat:@"Encoder/Decoder: %s", enc_desc]]; - [result appendString:@"\n"]; - } else { - [result appendString:[NSString stringWithFormat:@"Encoder: %s", enc_desc]]; - [result appendString:@"\n"]; - [result appendString:[NSString stringWithFormat:@"Decoder: %s", dec_desc]]; - [result appendString:@"\n"]; - } - - if (stats != NULL) { - [result appendString:[NSString stringWithFormat:@"Download bandwidth: %1.1f kbits/s", - linphone_call_stats_get_download_bandwidth(stats)]]; - [result appendString:@"\n"]; - [result appendString:[NSString stringWithFormat:@"Upload bandwidth: %1.1f kbits/s", - linphone_call_stats_get_upload_bandwidth(stats)]]; - [result appendString:@"\n"]; - if (stream == LinphoneStreamTypeVideo) { - /*[result appendString:[NSString stringWithFormat:@"Estimated download bandwidth: %1.1f kbits/s", - linphone_call_stats_get_estimated_download_bandwidth(stats)]]; - [result appendString:@"\n"];*/ - } - [result - appendString:[NSString stringWithFormat:@"ICE state: %@", - [self.class iceToString:linphone_call_stats_get_ice_state(stats)]]]; - [result appendString:@"\n"]; - [result - appendString:[NSString - stringWithFormat:@"Afinet: %@", - [self.class afinetToString:linphone_call_stats_get_ip_family_of_remote( - stats)]]]; - [result appendString:@"\n"]; - - // RTP stats section (packet loss count, etc) - const rtp_stats_t rtp_stats = *linphone_call_stats_get_rtp_stats(stats); - [result - appendString:[NSString stringWithFormat: - @"RTP packets: %llu total, %lld cum loss, %llu discarded, %llu OOT, %llu bad", - rtp_stats.packet_recv, rtp_stats.cum_packet_loss, rtp_stats.discarded, - rtp_stats.outoftime, rtp_stats.bad]]; - [result appendString:@"\n"]; - [result appendString:[NSString stringWithFormat:@"Sender loss rate: %.2f%%", - linphone_call_stats_get_sender_loss_rate(stats)]]; - [result appendString:@"\n"]; - [result appendString:[NSString stringWithFormat:@"Receiver loss rate: %.2f%%", - linphone_call_stats_get_receiver_loss_rate(stats)]]; - [result appendString:@"\n"]; - - if (stream == LinphoneStreamTypeVideo) { - const LinphoneVideoDefinition *recv_definition = linphone_call_params_get_received_video_definition(params); - const LinphoneVideoDefinition *sent_definition = linphone_call_params_get_sent_video_definition(params); - float sentFPS = linphone_call_params_get_sent_framerate(params); - float recvFPS = linphone_call_params_get_received_framerate(params); - [result appendString:[NSString stringWithFormat:@"Sent video resolution: %dx%d (%.1fFPS)", linphone_video_definition_get_width(sent_definition), - linphone_video_definition_get_height(sent_definition), sentFPS]]; - [result appendString:@"\n"]; - [result appendString:[NSString stringWithFormat:@"Received video resolution: %dx%d (%.1fFPS)", - linphone_video_definition_get_width(recv_definition), - linphone_video_definition_get_height(recv_definition), recvFPS]]; - [result appendString:@"\n"]; - } - } - return result; -} - -- (void)updateStats:(NSTimer *)timer { - LinphoneCall *call = linphone_core_get_current_call(LC); - - if (!call) { - _statsLabel.text = NSLocalizedString(@"No call in progress", nil); - return; - } - - NSMutableString *stats = [[NSMutableString alloc] init]; - - LinphoneMediaEncryption enc = linphone_call_params_get_media_encryption(linphone_call_get_current_params(call)); - if (enc != LinphoneMediaEncryptionNone) { - [stats appendString:[NSString - stringWithFormat:@"Call encrypted using %@", [self.class mediaEncryptionToString:enc]]]; - } - - [stats appendString:[self updateStatsForCall:call stream:LinphoneStreamTypeAudio]]; - [stats appendString:[self updateStatsForCall:call stream:LinphoneStreamTypeVideo]]; - [stats appendString:[self updateStatsForCall:call stream:LinphoneStreamTypeText]]; - - _statsLabel.text = stats; -} - -@end diff --git a/Classes/CallSideMenuView.xib b/Classes/CallSideMenuView.xib deleted file mode 100644 index a0208f810..000000000 --- a/Classes/CallSideMenuView.xib +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Classes/CallView.h b/Classes/CallView.h deleted file mode 100644 index fa188895d..000000000 --- a/Classes/CallView.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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 . - */ - -#import - -#import "VideoZoomHandler.h" -#import "UICamSwitch.h" - -#import "UICompositeView.h" -#import "CallPausedTableView.h" - -#import "UIMutedMicroButton.h" -#import "UIPauseButton.h" -#import "UISpeakerButton.h" -#import "UIVideoButton.h" -#import "UIHangUpButton.h" -#import "UIDigitButton.h" -#import "UIRoundedImageView.h" -#import "UIBouncingView.h" - -@class VideoView; - -@interface CallView : TPMultiLayoutViewController { - @private - UITapGestureRecognizer *singleFingerTap; - NSTimer *hideControlsTimer; - NSTimer *videoDismissTimer; - BOOL videoHidden; - BOOL callRecording; - VideoZoomHandler *videoZoomHandler; -} - -@property(nonatomic, strong) IBOutlet CallPausedTableView *pausedCallsTable; - -@property(nonatomic, strong) IBOutlet UIView *videoGroup; -@property(nonatomic, strong) IBOutlet UIView *videoView; -@property(nonatomic, strong) IBOutlet UIView *videoPreview; -@property(nonatomic, strong) IBOutlet UICamSwitch *videoCameraSwitch; -@property(nonatomic, strong) IBOutlet UIActivityIndicatorView *videoWaitingForFirstImage; -@property(weak, nonatomic) IBOutlet UIView *callView; - -@property(nonatomic, strong) IBOutlet UIPauseButton *callPauseButton; -@property(nonatomic, strong) IBOutlet UIButton *optionsConferenceButton; -@property(nonatomic, strong) IBOutlet UIVideoButton *videoButton; -@property(nonatomic, strong) IBOutlet UIMutedMicroButton *microButton; -@property(nonatomic, strong) IBOutlet UISpeakerButton *speakerButton; -@property(nonatomic, strong) IBOutlet UIToggleButton *routesButton; -@property(nonatomic, strong) IBOutlet UIToggleButton *optionsButton; -@property(nonatomic, strong) IBOutlet UIHangUpButton *hangupButton; -@property(nonatomic, strong) IBOutlet UIView *numpadView; -@property(nonatomic, strong) IBOutlet UIView *routesView; -@property(nonatomic, strong) IBOutlet UIView *optionsView; -@property(nonatomic, strong) IBOutlet UIButton *routesEarpieceButton; -@property(nonatomic, strong) IBOutlet UIButton *routesSpeakerButton; -@property(nonatomic, strong) IBOutlet UIButton *routesBluetoothButton; -@property(nonatomic, strong) IBOutlet UIButton *optionsAddButton; -@property(nonatomic, strong) IBOutlet UIButton *optionsTransferButton; -@property(nonatomic, strong) IBOutlet UIToggleButton *numpadButton; -@property(weak, nonatomic) IBOutlet UIPauseButton *conferencePauseButton; -@property(weak, nonatomic) IBOutlet UIBouncingView *chatNotificationView; -@property(weak, nonatomic) IBOutlet UILabel *chatNotificationLabel; -@property (weak, nonatomic) IBOutlet UIButton *recordButton; -@property (weak, nonatomic) IBOutlet UIButton *recordButtonOnView; - -@property(weak, nonatomic) IBOutlet UIView *bottomBar; -@property(nonatomic, strong) IBOutlet UIDigitButton *oneButton; -@property(nonatomic, strong) IBOutlet UIDigitButton *twoButton; -@property(nonatomic, strong) IBOutlet UIDigitButton *threeButton; -@property(nonatomic, strong) IBOutlet UIDigitButton *fourButton; -@property(nonatomic, strong) IBOutlet UIDigitButton *fiveButton; -@property(nonatomic, strong) IBOutlet UIDigitButton *sixButton; -@property(nonatomic, strong) IBOutlet UIDigitButton *sevenButton; -@property(nonatomic, strong) IBOutlet UIDigitButton *eightButton; -@property(nonatomic, strong) IBOutlet UIDigitButton *nineButton; -@property(nonatomic, strong) IBOutlet UIDigitButton *starButton; -@property(nonatomic, strong) IBOutlet UIDigitButton *zeroButton; -@property(nonatomic, strong) IBOutlet UIDigitButton *hashButton; -@property(weak, nonatomic) IBOutlet UIRoundedImageView *avatarImage; -@property(weak, nonatomic) IBOutlet UILabel *nameLabel; -@property(weak, nonatomic) IBOutlet UILabel *durationLabel; -@property(weak, nonatomic) IBOutlet UIView *pausedByRemoteView; -@property(weak, nonatomic) IBOutlet UIView *noActiveCallView; -@property(weak, nonatomic) IBOutlet UIView *conferenceView; -@property(strong, nonatomic) IBOutlet CallPausedTableView *conferenceCallsTable; -@property (weak, nonatomic) IBOutlet UIView *waitView; -@property (weak, nonatomic) IBOutlet UIView *infoView; - -- (IBAction)onRoutesClick:(id)sender; -- (IBAction)onRoutesBluetoothClick:(id)sender; -- (IBAction)onRoutesEarpieceClick:(id)sender; -- (IBAction)onRoutesSpeakerClick:(id)sender; -- (IBAction)onOptionsClick:(id)sender; -- (IBAction)onOptionsTransferClick:(id)sender; -- (IBAction)onOptionsAddClick:(id)sender; -- (IBAction)onOptionsConferenceClick:(id)sender; -- (IBAction)onNumpadClick:(id)sender; -- (IBAction)onChatClick:(id)sender; -- (IBAction)onRecordClick:(id)sender; -- (IBAction)onRecordOnViewClick:(id)sender; - -@end diff --git a/Classes/CallView.m b/Classes/CallView.m deleted file mode 100644 index 2ae10de87..000000000 --- a/Classes/CallView.m +++ /dev/null @@ -1,1006 +0,0 @@ -/* - * 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 . - */ - -#import -#import -#import -#import -#import -#import -#import -#import "UICallConferenceCell.h" - -#import "CallView.h" -#import "CallSideMenuView.h" -#import "LinphoneManager.h" -#import "PhoneMainView.h" -#import "Utils.h" - -#include "linphone/linphonecore.h" - -#import "linphoneapp-Swift.h" - -const NSInteger SECURE_BUTTON_TAG = 5; - -@implementation CallView { - BOOL hiddenVolume; -} - -#pragma mark - Lifecycle Functions - -- (id)init { - self = [super initWithNibName:NSStringFromClass(self.class) bundle:[NSBundle mainBundle]]; - if (self != nil) { - singleFingerTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleControls:)]; - videoZoomHandler = [[VideoZoomHandler alloc] init]; - videoHidden = TRUE; - [self updateCallView]; - } - return self; -} - -#pragma mark - UICompositeViewDelegate Functions - -static UICompositeViewDescription *compositeDescription = nil; - -+ (UICompositeViewDescription *)compositeViewDescription { - if (compositeDescription == nil) { - compositeDescription = [[UICompositeViewDescription alloc] init:self.class - statusBar:StatusBarView.class - tabBar:nil - sideMenu:CallSideMenuView.class - fullscreen:false - isLeftFragment:YES - fragmentWith:nil]; - compositeDescription.darkBackground = true; - } - return compositeDescription; -} - -- (UICompositeViewDescription *)compositeViewDescription { - return self.class.compositeViewDescription; -} - -#pragma mark - ViewController Functions - -- (void)viewDidLoad { - [super viewDidLoad]; - - _routesEarpieceButton.enabled = !IPAD; - -// TODO: fixme! video preview frame is too big compared to openGL preview -// frame, so until this is fixed, temporary disabled it. -#if 0 - _videoPreview.layer.borderColor = UIColor.whiteColor.CGColor; - _videoPreview.layer.borderWidth = 1; -#endif - [singleFingerTap setNumberOfTapsRequired:1]; - [singleFingerTap setCancelsTouchesInView:FALSE]; - [self.videoView addGestureRecognizer:singleFingerTap]; - - [videoZoomHandler setup:_videoGroup]; - _videoGroup.alpha = 0; - - [_videoCameraSwitch setPreview:_videoPreview]; - - UIPanGestureRecognizer *dragndrop = - [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(moveVideoPreview:)]; - dragndrop.minimumNumberOfTouches = 1; - [_videoPreview addGestureRecognizer:dragndrop]; - - [_zeroButton setDigit:'0']; - [_zeroButton setDtmf:true]; - [_oneButton setDigit:'1']; - [_oneButton setDtmf:true]; - [_twoButton setDigit:'2']; - [_twoButton setDtmf:true]; - [_threeButton setDigit:'3']; - [_threeButton setDtmf:true]; - [_fourButton setDigit:'4']; - [_fourButton setDtmf:true]; - [_fiveButton setDigit:'5']; - [_fiveButton setDtmf:true]; - [_sixButton setDigit:'6']; - [_sixButton setDtmf:true]; - [_sevenButton setDigit:'7']; - [_sevenButton setDtmf:true]; - [_eightButton setDigit:'8']; - [_eightButton setDtmf:true]; - [_nineButton setDigit:'9']; - [_nineButton setDtmf:true]; - [_starButton setDigit:'*']; - [_starButton setDtmf:true]; - [_hashButton setDigit:'#']; - [_hashButton setDtmf:true]; -} - -- (void)dealloc { - [PhoneMainView.instance.view removeGestureRecognizer:singleFingerTap]; - // Remove all observer - [NSNotificationCenter.defaultCenter removeObserver:self]; -} - -- (void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - _waitView.hidden = TRUE; - CallManager.instance.nextCallIsTransfer = FALSE; - - callRecording = FALSE; - _recordButtonOnView.hidden = TRUE; - - // Update on show - [self hideRoutes:TRUE animated:FALSE]; - [self hideOptions:TRUE animated:FALSE]; - [self hidePad:TRUE animated:FALSE]; - [self hideSpeaker:LinphoneManager.instance.bluetoothAvailable]; - [self callDurationUpdate]; - [self onCurrentCallChange]; - // Set windows (warn memory leaks) - linphone_core_set_native_video_window_id(LC, (__bridge void *)(_videoView)); - linphone_core_set_native_preview_window_id(LC, (__bridge void *)(_videoPreview)); - - [self previewTouchLift]; - // Enable tap - [singleFingerTap setEnabled:TRUE]; - - [NSNotificationCenter.defaultCenter addObserver:self - selector:@selector(messageReceived:) - name:kLinphoneMessageReceived - object:nil]; - [NSNotificationCenter.defaultCenter addObserver:self - selector:@selector(bluetoothAvailabilityUpdateEvent:) - name:kLinphoneBluetoothAvailabilityUpdate - object:nil]; - [NSNotificationCenter.defaultCenter addObserver:self - selector:@selector(callUpdateEvent:) - name:kLinphoneCallUpdate - object:nil]; - - [NSNotificationCenter.defaultCenter addObserver:self - selector:@selector(participantListChanged:) - name:kLinphoneConfStateParticipantListChanged - object:nil]; - [NSNotificationCenter.defaultCenter addObserver:self - selector:@selector(confStateChanged:) - name:kLinphoneConfStateChanged - object:nil]; - - - - - [NSTimer scheduledTimerWithTimeInterval:1 - target:self - selector:@selector(callDurationUpdate) - userInfo:nil - repeats:YES]; - -} - -- (void)viewDidAppear:(BOOL)animated { - [super viewDidAppear:animated]; - - [[UIApplication sharedApplication] setIdleTimerDisabled:YES]; - [[UIDevice currentDevice] setProximityMonitoringEnabled:TRUE]; - - [PhoneMainView.instance setVolumeHidden:TRUE]; - hiddenVolume = TRUE; - - // we must wait didAppear to reset fullscreen mode because we cannot change it in viewwillappear - LinphoneCall *call = linphone_core_get_current_call(LC); - LinphoneCallState state = (call != NULL) ? linphone_call_get_state(call) : 0; - [self callUpdate:call state:state animated:FALSE]; -} - -- (void)viewWillDisappear:(BOOL)animated { - [super viewWillDisappear:animated]; -[[UIDevice currentDevice] setProximityMonitoringEnabled:FALSE]; - [self disableVideoDisplay:TRUE animated:NO]; - - if (hideControlsTimer != nil) { - [hideControlsTimer invalidate]; - hideControlsTimer = nil; - } - - if (hiddenVolume) { - [PhoneMainView.instance setVolumeHidden:FALSE]; - hiddenVolume = FALSE; - } - - if (videoDismissTimer) { - [self dismissVideoActionSheet:videoDismissTimer]; - [videoDismissTimer invalidate]; - videoDismissTimer = nil; - } - - // Remove observer - [NSNotificationCenter.defaultCenter removeObserver:self]; -} - -- (void)viewDidDisappear:(BOOL)animated { - [super viewDidDisappear:animated]; - - [[UIApplication sharedApplication] setIdleTimerDisabled:false]; - [[UIDevice currentDevice] setProximityMonitoringEnabled:FALSE]; - - [PhoneMainView.instance fullScreen:false]; - // Disable tap - [singleFingerTap setEnabled:FALSE]; - - if (linphone_core_get_calls_nb(LC) == 0) { - // reseting speaker button because no more call - _speakerButton.selected = FALSE; - } - - NSString *address = [LinphoneManager.instance lpConfigStringForKey:@"sas_dialog_denied"]; - if (address) { - UIConfirmationDialog *securityDialog = [UIConfirmationDialog ShowWithMessage:NSLocalizedString(@"Trust has been denied. Make a call to start the authentication process again.", nil) - cancelMessage:NSLocalizedString(@"CANCEL", nil) - confirmMessage:NSLocalizedString(@"CALL", nil) - onCancelClick:^() { - } - onConfirmationClick:^() { - LinphoneAddress *addr = linphone_address_new(address.UTF8String); - [CallManager.instance startCallWithAddr:addr isSas:TRUE]; - linphone_address_unref(addr); - } ]; - [securityDialog.securityImage setImage:[UIImage imageNamed:@"security_alert_indicator.png"]]; - securityDialog.securityImage.hidden = FALSE; - [securityDialog setSpecialColor]; - [LinphoneManager.instance lpConfigSetString:nil forKey:@"sas_dialog_denied"]; - } -} - -- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { - [super didRotateFromInterfaceOrientation:fromInterfaceOrientation]; - [self updateUnreadMessage:NO]; - [self previewTouchLift]; - [_recordButtonOnView setHidden:!callRecording]; - [self updateCallView]; - LinphoneCall *call = linphone_core_get_current_call(LC) ; - if (call && linphone_call_get_state(call) == LinphoneCallStatePausedByRemote) { - _pausedByRemoteView.hidden = NO; - [self updateInfoView:TRUE]; - } - _conferenceView.hidden = ![CallManager.instance isInConference]; - [self onCurrentCallChange]; -} - -#pragma mark - UI modification - -- (void)updateInfoView:(BOOL)pausedByRemote { - CGRect infoFrame = _infoView.frame; - if (pausedByRemote || !videoHidden) { - infoFrame.origin.y = 0; - } else { - infoFrame.origin.y = (_avatarImage.frame.origin.y-66)/2; - } - _infoView.frame = infoFrame; -} - -- (void)updateCallView { - CGRect pauseFrame = _callPauseButton.frame; - CGRect recordFrame = _recordButtonOnView.frame; - if (videoHidden) { - pauseFrame.origin.y = _bottomBar.frame.origin.y - pauseFrame.size.height - 60; - } else { - pauseFrame.origin.y = _videoCameraSwitch.frame.origin.y+_videoGroup.frame.origin.y; - } - recordFrame.origin.y = _bottomBar.frame.origin.y - pauseFrame.size.height - 60; - _callPauseButton.frame = pauseFrame; - _recordButtonOnView.frame = recordFrame; - [self updateInfoView:FALSE]; -} - -- (void)hideSpinnerIndicator:(LinphoneCall *)call { - _videoWaitingForFirstImage.hidden = TRUE; -} - -static void hideSpinner(LinphoneCall *call, void *user_data) { - CallView *thiz = (__bridge CallView *)user_data; - [thiz hideSpinnerIndicator:call]; -} - -- (void)updateBottomBar:(LinphoneCall *)call state:(LinphoneCallState)state { - [_speakerButton update]; - [_microButton update]; - [_callPauseButton update]; - [_conferencePauseButton update]; - [_videoButton update]; - [_hangupButton update]; - - _optionsButton.enabled = (!call || !linphone_core_sound_resources_locked(LC)); - _optionsTransferButton.enabled = call && !linphone_core_sound_resources_locked(LC); - // enable conference button if 2 calls are presents and at least one is not in the conference - int confSize = linphone_core_get_conference_size(LC) - ([CallManager.instance isInConference] ? 1 : 0); - _optionsConferenceButton.enabled = (linphone_core_get_calls_nb(LC) > 1) && (linphone_core_get_calls_nb(LC) != confSize) && !CallManager.instance.hasConferenceAsGuest; - - // Disable transfert in conference - if (linphone_core_get_current_call(LC) == NULL) { - [_optionsTransferButton setEnabled:FALSE]; - } else { - [_optionsTransferButton setEnabled:TRUE]; - } - - switch (state) { - case LinphoneCallEnd: - case LinphoneCallError: - case LinphoneCallIncoming: - case LinphoneCallOutgoing: - [self hidePad:TRUE animated:TRUE]; - [self hideOptions:TRUE animated:TRUE]; - [self hideRoutes:TRUE animated:TRUE]; - default: - break; - } -} - -- (void)toggleControls:(id)sender { - bool controlsHidden = (_bottomBar.alpha == 0.0); - [self hideControls:!controlsHidden sender:sender]; -} - -- (void)timerHideControls:(id)sender { - [self hideControls:TRUE sender:sender]; -} - -- (void)hideControls:(BOOL)hidden sender:(id)sender { - if (videoHidden && hidden) - return; - - if (hideControlsTimer) { - [hideControlsTimer invalidate]; - hideControlsTimer = nil; - } - - if ([[PhoneMainView.instance currentView] equal:CallView.compositeViewDescription]) { - // show controls - [UIView beginAnimations:nil context:nil]; - [UIView setAnimationDuration:0.35]; - _pausedCallsTable.tableView.alpha = _videoCameraSwitch.alpha = _callPauseButton.alpha = _routesView.alpha = - _optionsView.alpha = _numpadView.alpha = _bottomBar.alpha = _conferenceView.alpha = (hidden ? 0 : 1); - _infoView.alpha = (hidden ? 0 : .8f); - - if ([CallManager.instance inVideoConf]) { - _videoCameraSwitch.frame = CGRectMake(_videoCameraSwitch.frame.origin.x, _bottomBar.frame.origin.y - 75, _videoCameraSwitch.frame.size.width,_videoCameraSwitch.frame.size.height); - } - - if (CallManager.instance.isInConference) { - _callPauseButton.hidden = true; - _conferenceView.frame = CGRectMake(_conferenceView.frame.origin.x,_conferenceView.frame.origin.y,_conferenceView.frame.size.width,_conferenceCallsTable.tableView.frame.origin.y+[_conferenceCallsTable.tableView numberOfRowsInSection:0]*CONFERENCE_CELL_HEIGHT+10); - } - - - [UIView commitAnimations]; - - [PhoneMainView.instance hideTabBar:hidden]; - [PhoneMainView.instance hideStatusBar:hidden]; - - if (!hidden) { - // hide controls in 5 sec - hideControlsTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 - target:self - selector:@selector(timerHideControls:) - userInfo:nil - repeats:NO]; - } - } -} - -- (void)disableVideoDisplay:(BOOL)disabled animated:(BOOL)animation { - if (disabled == videoHidden && animation) - return; - videoHidden = disabled; - - if (!disabled) { - [videoZoomHandler resetZoom]; - } - if (animation) { - [UIView beginAnimations:nil context:nil]; - [UIView setAnimationDuration:1.0]; - } - - [_videoGroup setAlpha:disabled ? 0 : 1]; - - [self hideControls:!disabled sender:nil]; - - if (animation) { - [UIView commitAnimations]; - } - - // only show camera switch button if we have more than 1 camera - _videoCameraSwitch.hidden = (disabled || !LinphoneManager.instance.frontCamId); - _videoPreview.hidden = (disabled || !linphone_core_self_view_enabled(LC)); - - if (hideControlsTimer != nil) { - [hideControlsTimer invalidate]; - hideControlsTimer = nil; - } - - if(![PhoneMainView.instance isIphoneXDevice]){ - [PhoneMainView.instance fullScreen:!disabled]; - } - [PhoneMainView.instance hideTabBar:!disabled]; - - if (!disabled) { -#ifdef TEST_VIDEO_VIEW_CHANGE - [NSTimer scheduledTimerWithTimeInterval:5.0 - target:self - selector:@selector(_debugChangeVideoView) - userInfo:nil - repeats:YES]; -#endif - // [self batteryLevelChanged:nil]; - - [_videoWaitingForFirstImage setHidden:NO]; - [_videoWaitingForFirstImage startAnimating]; - - if ([CallManager.instance inVideoConf]) - [self hideSpinnerIndicator:nil]; - else { - LinphoneCall *call = linphone_core_get_current_call(LC); - // linphone_call_params_get_used_video_codec return 0 if no video stream enabled - if (call != NULL && linphone_call_params_get_used_video_codec(linphone_call_get_current_params(call))) { - linphone_call_set_next_video_frame_decoded_callback(call, hideSpinner, (__bridge void *)(self)); - } - } - } - - if ([CallManager.instance isInConference]) { - [_conferenceView removeFromSuperview]; - [_callView addSubview:_conferenceView]; - } else { - [_conferenceView removeFromSuperview]; - [self.view addSubview:_conferenceView]; - [self.view sendSubviewToBack:_conferenceView]; - } -} - -- (void)displayVideoCall:(BOOL)animated { - [self disableVideoDisplay:FALSE animated:animated]; -} - -- (void)displayAudioCall:(BOOL)animated { - [self disableVideoDisplay:TRUE animated:animated]; -} - -- (void)callDurationUpdate { - int duration = - linphone_core_get_current_call(LC) ? linphone_call_get_duration(linphone_core_get_current_call(LC)) : 0; - _durationLabel.text = [LinphoneUtils durationToString:duration]; - - [_pausedCallsTable update]; - [_conferenceCallsTable update]; -} - -- (void)onCurrentCallChange { - LinphoneCall *call = linphone_core_get_current_call(LC); - - _noActiveCallView.hidden = (call || CallManager.instance.isInConference); - _callView.hidden = !call && !CallManager.instance.isInConference; - _conferenceView.hidden = !CallManager.instance.isInConference; - _conferenceView.hidden = !CallManager.instance.isInConference; - _callPauseButton.hidden = !call; - - - [_callPauseButton setType:UIPauseButtonType_CurrentCall call:call]; - [_conferencePauseButton setType:UIPauseButtonType_Conference call:call]; - - if (call) { - const LinphoneAddress *addr = linphone_call_get_remote_address(call); - [ContactDisplay setDisplayNameLabel:_nameLabel forAddress:addr]; - char *uri = linphone_address_as_string_uri_only(addr); - ms_free(uri); - [_avatarImage setImage:[FastAddressBook imageForAddress:addr] bordered:YES withRoundedRadius:YES]; - } -} - -- (void)hidePad:(BOOL)hidden animated:(BOOL)animated { - if (hidden) { - [_numpadButton setOff]; - } else { - [_numpadButton setOn]; - } - if (hidden != _numpadView.hidden) { - if (animated) { - [self hideAnimation:hidden forView:_numpadView completion:nil]; - } else { - [_numpadView setHidden:hidden]; - } - } -} - -- (void)hideRoutes:(BOOL)hidden animated:(BOOL)animated { - if (hidden) { - [_routesButton setOff]; - } else { - [_routesButton setOn]; - } - - _routesBluetoothButton.selected = [CallManager.instance isBluetoothEnabled]; - _routesSpeakerButton.selected = [CallManager.instance isSpeakerEnabled]; - _routesEarpieceButton.selected = !_routesBluetoothButton.selected && !_routesSpeakerButton.selected; - - if (hidden != _routesView.hidden) { - if (animated) { - [self hideAnimation:hidden forView:_routesView completion:nil]; - } else { - [_routesView setHidden:hidden]; - } - } -} - -- (void)hideOptions:(BOOL)hidden animated:(BOOL)animated { - if (hidden) { - [_optionsButton setOff]; - } else { - [_optionsButton setOn]; - } - if (hidden != _optionsView.hidden) { - if (animated) { - [self hideAnimation:hidden forView:_optionsView completion:nil]; - } else { - [_optionsView setHidden:hidden]; - } - } -} - -- (void)hideSpeaker:(BOOL)hidden { - _speakerButton.hidden = hidden; - _routesButton.hidden = !hidden; -} - -#pragma mark - Event Functions - -- (void)bluetoothAvailabilityUpdateEvent:(NSNotification *)notif { - bool available = [[notif.userInfo objectForKey:@"available"] intValue]; - dispatch_async(dispatch_get_main_queue(), ^{ - [self hideSpeaker:available]; - }); -} - -- (void)callUpdateEvent:(NSNotification *)notif { - LinphoneCall *call = [[notif.userInfo objectForKey:@"call"] pointerValue]; - LinphoneCallState state = [[notif.userInfo objectForKey:@"state"] intValue]; - [self callUpdate:call state:state animated:TRUE]; -} - -- (void)callUpdate:(LinphoneCall *)call state:(LinphoneCallState)state animated:(BOOL)animated { - [self updateBottomBar:call state:state]; - if (hiddenVolume) { - [PhoneMainView.instance setVolumeHidden:FALSE]; - hiddenVolume = FALSE; - } - - // Update tables - [_pausedCallsTable update]; - [_conferenceCallsTable update]; - - [self onCurrentCallChange]; - - LinphoneCall *currentCall = linphone_core_get_current_call(LC); - BOOL shouldDisableVideo = currentCall ? !linphone_call_params_video_enabled(linphone_call_get_current_params(currentCall)): ![CallManager.instance inVideoConf]; - - - if (videoHidden != shouldDisableVideo) { - if (!shouldDisableVideo) { - [self displayVideoCall:animated]; - } else { - [self displayAudioCall:animated]; - } - } - - // Fake call update - if (call == NULL) { - return; - } - - if (state != LinphoneCallPausedByRemote) { - _pausedByRemoteView.hidden = YES; - } - - switch (state) { - case LinphoneCallIncomingReceived: - case LinphoneCallOutgoingInit: - case LinphoneCallConnected: - case LinphoneCallStreamsRunning: { - // check video, because video can be disabled because of the low bandwidth. - if (!linphone_call_params_video_enabled(linphone_call_get_current_params(call))) { - const LinphoneCallParams *param = linphone_call_get_current_params(call); - CallAppData *data = [CallManager getAppDataWithCall:call]; - if (state == LinphoneCallStreamsRunning && data && data.videoRequested && linphone_call_params_low_bandwidth_enabled(param)) { - // too bad video was not enabled because low bandwidth - UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Low bandwidth", nil) - message:NSLocalizedString(@"Video cannot be activated because of low bandwidth " - @"condition, only audio is available", - nil) - preferredStyle:UIAlertControllerStyleAlert]; - - UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Continue", nil) - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) {}]; - - [errView addAction:defaultAction]; - [self presentViewController:errView animated:YES completion:nil]; - data.videoRequested = FALSE; - [CallManager setAppDataWithCall:call appData:data]; - } - } - break; - } - case LinphoneCallUpdatedByRemote: { - const LinphoneCallParams *current = linphone_call_get_current_params(call); - const LinphoneCallParams *remote = linphone_call_get_remote_params(call); - - /* remote wants to add video */ - if ((linphone_core_video_display_enabled(LC) && !linphone_call_params_video_enabled(current) && - linphone_call_params_video_enabled(remote)) && - (!linphone_core_get_video_policy(LC)->automatically_accept || - (([UIApplication sharedApplication].applicationState != UIApplicationStateActive) && - floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max))) { - linphone_core_defer_call_update(LC, call); - [self displayAskToEnableVideoCall:call]; - } else if (linphone_call_params_video_enabled(current) && !linphone_call_params_video_enabled(remote) && ![CallManager.instance inVideoConf]) { - [self displayAudioCall:animated]; - } - break; - } - case LinphoneCallPausing: - case LinphoneCallPaused: - [self displayAudioCall:animated]; - break; - case LinphoneCallPausedByRemote: - if (![CallManager.instance inVideoConf]) { - [self displayAudioCall:animated]; - if (call == linphone_core_get_current_call(LC)) { - _pausedByRemoteView.hidden = NO; - [self updateInfoView:TRUE]; - } - } - break; - case LinphoneCallEnd: - case LinphoneCallError: - default: - break; - } -} - -#pragma mark - ActionSheet Functions - -- (void)displayAskToEnableVideoCall:(LinphoneCall *)call { - - if (CallManager.instance.inVideoConf) { // we are hosting a video conf, so just accept people wanting to activate video. - LinphoneCallParams *params = linphone_core_create_call_params(LC, call); - linphone_call_params_enable_video(params, TRUE); - linphone_call_accept_update(call, params); - return; - } - - if (linphone_core_get_video_policy(LC)->automatically_accept && - !([UIApplication sharedApplication].applicationState != UIApplicationStateActive)) - return; - - NSString *username = [FastAddressBook displayNameForAddress:linphone_call_get_remote_address(call)]; - NSString *title = [NSString stringWithFormat:NSLocalizedString(@"%@ would like to enable video", nil), username]; - if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive && - floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { - UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; - content.title = NSLocalizedString(@"Video request", nil); - content.body = title; - content.categoryIdentifier = @"video_request"; - content.userInfo = @{ - @"CallId" : [NSString stringWithUTF8String:linphone_call_log_get_call_id(linphone_call_get_call_log(call))] - }; - - UNNotificationRequest *req = - [UNNotificationRequest requestWithIdentifier:@"video_request" content:content trigger:NULL]; - [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:req - withCompletionHandler:^(NSError *_Nullable error) { - // Enable or disable features based on authorization. - if (error) { - LOGD(@"Error while adding notification request :"); - LOGD(error.description); - } - }]; - } else { - UIConfirmationDialog *sheet = [UIConfirmationDialog ShowWithMessage:title - cancelMessage:nil - confirmMessage:NSLocalizedString(@"ACCEPT", nil) - onCancelClick:^() { - LOGI(@"User declined video proposal"); - if (call == linphone_core_get_current_call(LC)) { - [CallManager.instance acceptVideoWithCall:call confirm:FALSE]; - [videoDismissTimer invalidate]; - videoDismissTimer = nil; - } - } - onConfirmationClick:^() { - LOGI(@"User accept video proposal"); - if (call == linphone_core_get_current_call(LC)) { - [CallManager.instance acceptVideoWithCall:call confirm:TRUE]; - [videoDismissTimer invalidate]; - videoDismissTimer = nil; - } - } - inController:self]; - videoDismissTimer = [NSTimer scheduledTimerWithTimeInterval:30 - target:self - selector:@selector(dismissVideoActionSheet:) - userInfo:sheet - repeats:NO]; - } -} - -- (void)dismissVideoActionSheet:(NSTimer *)timer { - UIConfirmationDialog *sheet = (UIConfirmationDialog *)timer.userInfo; - [sheet dismiss]; -} - -#pragma mark VideoPreviewMoving - -- (void)moveVideoPreview:(UIPanGestureRecognizer *)dragndrop { - CGPoint center = [dragndrop locationInView:_videoPreview.superview]; - _videoPreview.center = center; - if (dragndrop.state == UIGestureRecognizerStateEnded) { - [self previewTouchLift]; - } -} - -- (CGFloat)coerce:(CGFloat)value betweenMin:(CGFloat)min andMax:(CGFloat)max { - return MAX(min, MIN(value, max)); -} - -- (void)previewTouchLift { - CGRect previewFrame = _videoPreview.frame; - previewFrame.origin.x = [self coerce:previewFrame.origin.x - betweenMin:5 - andMax:(UIScreen.mainScreen.bounds.size.width - 5 - previewFrame.size.width)]; - previewFrame.origin.y = [self coerce:previewFrame.origin.y - betweenMin:5 - andMax:(UIScreen.mainScreen.bounds.size.height - 5 - previewFrame.size.height)]; - - if (!CGRectEqualToRect(previewFrame, _videoPreview.frame)) { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [UIView animateWithDuration:0.3 - animations:^{ - LOGD(@"Recentering preview to %@", NSStringFromCGRect(previewFrame)); - _videoPreview.frame = previewFrame; - }]; - }); - } -} - -#pragma mark - Action Functions - -- (IBAction)onNumpadClick:(id)sender { - if ([_numpadView isHidden]) { - [self hidePad:FALSE animated:ANIMATED]; - } else { - [self hidePad:TRUE animated:ANIMATED]; - } -} - -- (IBAction)onChatClick:(id)sender { - const LinphoneCall *currentCall = linphone_core_get_current_call(LC); - const LinphoneAddress *addr = currentCall ? linphone_call_get_remote_address(currentCall) : NULL; - // TODO encrpted or unencrpted - [LinphoneManager.instance lpConfigSetBool:TRUE forKey:@"create_chat"]; - [PhoneMainView.instance getOrCreateOneToOneChatRoom:addr waitView:_waitView isEncrypted:FALSE]; -} - -- (IBAction)onRecordClick:(id)sender { - if (![_optionsView isHidden]) - [self hideOptions:TRUE animated:ANIMATED]; - if (callRecording) { - [self onRecordOnViewClick:nil]; - } else { - LOGD(@"Recording Starts"); - - [_recordButton setImage:[UIImage imageNamed:@"rec_off_default.png"] forState:UIControlStateNormal]; - [_recordButtonOnView setHidden:FALSE]; - - LinphoneCall *call = linphone_core_get_current_call(LC); - linphone_call_start_recording(call); - - callRecording = TRUE; - } -} - -- (IBAction)onRecordOnViewClick:(id)sender { - LOGD(@"Recording Stops"); - [_recordButton setImage:[UIImage imageNamed:@"rec_on_default.png"] forState:UIControlStateNormal]; - [_recordButtonOnView setHidden:TRUE]; - - LinphoneCall *call = linphone_core_get_current_call(LC); - linphone_call_stop_recording(call); - - callRecording = FALSE; - - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); - NSString *writablePath = [paths objectAtIndex:0]; - writablePath = [writablePath stringByAppendingString:@"/"]; - NSArray *directoryContent = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:writablePath error:NULL]; - if (directoryContent) { - return; - } -} - -- (IBAction)onRoutesBluetoothClick:(id)sender { - [self hideRoutes:TRUE animated:TRUE]; - [CallManager.instance changeRouteToBluetooth]; -} - -- (IBAction)onRoutesEarpieceClick:(id)sender { - [self hideRoutes:TRUE animated:TRUE]; - [CallManager.instance changeRouteToDefault]; -} - -- (IBAction)onRoutesSpeakerClick:(id)sender { - [self hideRoutes:TRUE animated:TRUE]; - [CallManager.instance changeRouteToSpeaker]; -} - -- (IBAction)onRoutesClick:(id)sender { - if ([_routesView isHidden]) { - [self hideRoutes:FALSE animated:ANIMATED]; - } else { - [self hideRoutes:TRUE animated:ANIMATED]; - } -} - -- (IBAction)onOptionsClick:(id)sender { - int confSize = linphone_core_get_conference_size(LC) - (CallManager.instance.isInConference ? 1 : 0); - _optionsConferenceButton.enabled = (linphone_core_get_calls_nb(LC) > 1) && (linphone_core_get_calls_nb(LC) != confSize) && !CallManager.instance.hasConferenceAsGuest; - - if ([_optionsView isHidden]) { - [self hideOptions:FALSE animated:ANIMATED]; - } else { - [self hideOptions:TRUE animated:ANIMATED]; - } -} - -- (IBAction)onOptionsTransferClick:(id)sender { - [self hideOptions:TRUE animated:TRUE]; - DialerView *view = VIEW(DialerView); - [view setAddress:@""]; - CallManager.instance.nextCallIsTransfer = TRUE; - [PhoneMainView.instance changeCurrentView:view.compositeViewDescription]; -} - -- (IBAction)onOptionsAddClick:(id)sender { - [self hideOptions:TRUE animated:TRUE]; - DialerView *view = VIEW(DialerView); - [view setAddress:@""]; - CallManager.instance.nextCallIsTransfer = FALSE; - [PhoneMainView.instance changeCurrentView:view.compositeViewDescription]; -} - -- (IBAction)onOptionsConferenceClick:(id)sender { - [self hideOptions:TRUE animated:TRUE]; - [CallManager.instance groupCall]; -} - -#pragma mark - Animation - -- (void)hideAnimation:(BOOL)hidden forView:(UIView *)target completion:(void (^)(BOOL finished))completion { - if (hidden) { - int original_y = target.frame.origin.y; - CGRect newFrame = target.frame; - newFrame.origin.y = self.view.frame.size.height; - [UIView animateWithDuration:0.5 - delay:0.0 - options:UIViewAnimationOptionCurveEaseIn - animations:^{ - target.frame = newFrame; - } - completion:^(BOOL finished) { - CGRect originFrame = target.frame; - originFrame.origin.y = original_y; - target.hidden = YES; - target.frame = originFrame; - if (completion) - completion(finished); - }]; - } else { - CGRect frame = target.frame; - int original_y = frame.origin.y; - frame.origin.y = self.view.frame.size.height; - target.frame = frame; - frame.origin.y = original_y; - target.hidden = NO; - - [UIView animateWithDuration:0.5 - delay:0.0 - options:UIViewAnimationOptionCurveEaseOut - animations:^{ - target.frame = frame; - } - completion:^(BOOL finished) { - target.frame = frame; // in case application did not finish - if (completion) - completion(finished); - }]; - } -} - -#pragma mark - Bounce -- (void)messageReceived:(NSNotification *)notif { - [self updateUnreadMessage:TRUE]; -} -- (void)updateUnreadMessage:(BOOL)appear { - int unreadMessage = [LinphoneManager unreadMessageCount]; - if (unreadMessage > 0) { - _chatNotificationLabel.text = [NSString stringWithFormat:@"%i", unreadMessage]; - [_chatNotificationView startAnimating:appear]; - } else { - [_chatNotificationView stopAnimating:appear]; - } -} - -#pragma mark - Conference - -- (void)participantListChanged:(NSNotification *)notif { - [self confStateChanged:nil]; - [_conferenceCallsTable update]; - _conferenceView.frame = CGRectMake(_conferenceView.frame.origin.x,_conferenceView.frame.origin.y,_conferenceView.frame.size.width,_conferenceCallsTable.tableView.frame.origin.y+[_conferenceCallsTable.tableView numberOfRowsInSection:0]*CONFERENCE_CELL_HEIGHT+10); - [self onCurrentCallChange]; - _conferenceView.hidden = !CallManager.instance.isInConference; -} - -- (void)confStateChanged:(NSNotification *)notif { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - if ([CallManager.instance inVideoConf]) { - [self displayVideoCall:true]; - } else if (CallManager.instance.isInConference) { - [self displayAudioConference]; - } else { - [self displayAudioCall:true]; - _callPauseButton.hidden = NO; - _nameLabel.hidden = NO; - _durationLabel.hidden = NO; - _avatarImage.hidden = NO; - } - [_conferenceCallsTable update]; - _conferenceView.frame = CGRectMake(_conferenceView.frame.origin.x,_conferenceView.frame.origin.y,_conferenceView.frame.size.width,_conferenceCallsTable.tableView.frame.origin.y+[_conferenceCallsTable.tableView numberOfRowsInSection:0]*CONFERENCE_CELL_HEIGHT+10); - }); -} - --(void) displayAudioConference { - _callPauseButton.hidden = true; - _nameLabel.hidden = true; - _conferenceView.frame = CGRectMake(_conferenceView.frame.origin.x,_conferenceView.frame.origin.y,_conferenceView.frame.size.width,_conferenceCallsTable.tableView.frame.origin.y+[_conferenceCallsTable.tableView numberOfRowsInSection:0]*CONFERENCE_CELL_HEIGHT+10); - _durationLabel.hidden = true; - _avatarImage.hidden = true; - - [_conferenceView removeFromSuperview]; - [_callView addSubview:_conferenceView]; - - if ([CallManager.instance isInConference]) { - [_conferenceView removeFromSuperview]; - [_callView addSubview:_conferenceView]; - _conferenceView.hidden = NO; - } else { - [_conferenceView removeFromSuperview]; - [self.view addSubview:_conferenceView]; - [self.view sendSubviewToBack:_conferenceView]; - } -} - - - -@end diff --git a/Classes/ChatConversationImdnView.h b/Classes/ChatConversationImdnView.h index 78a9042b6..26607f58d 100644 --- a/Classes/ChatConversationImdnView.h +++ b/Classes/ChatConversationImdnView.h @@ -24,6 +24,7 @@ #import "UICompositeView.h" #import "UIRoundBorderedButton.h" +#import "UIChatBubbleTextCell.h" @interface ChatConversationImdnView : UIViewController { diff --git a/Classes/ChatConversationView.h b/Classes/ChatConversationView.h index 7c76cb286..7d8fd00dc 100644 --- a/Classes/ChatConversationView.h +++ b/Classes/ChatConversationView.h @@ -32,7 +32,6 @@ #import "UIImageViewDeletable.h" #import "UIConfirmationDialog.h" #import "UIInterfaceStyleButton.h" -#import "linphoneapp-Swift.h" #import "UIChatReplyBubbleView.h" #include "linphone/linphonecore.h" diff --git a/Classes/ChatConversationView.m b/Classes/ChatConversationView.m index 40e86856f..6ae1f25b4 100644 --- a/Classes/ChatConversationView.m +++ b/Classes/ChatConversationView.m @@ -28,6 +28,8 @@ #import "SVProgressHUD.h" #import "EphemeralSettingsView.h" #import "Utils.h" +#import "linphoneapp-Swift.h" + @implementation FileContext @@ -1853,7 +1855,7 @@ void on_chat_room_conference_alert(LinphoneChatRoom *cr, const LinphoneEventLog NSDictionary* userInfo = @{@"path": [NSString stringWithUTF8String:linphone_player_get_user_data(_sharedVoicePlayer)]}; [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneVoiceMessagePlayerLostFocus object:nil userInfo:userInfo]; } - [CallManager.instance changeRouteToSpeaker]; + [AudioRouteUtils routeAudioToSpeaker]; linphone_player_set_user_data(_sharedVoicePlayer, (void *)path); linphone_player_open(_sharedVoicePlayer, path); linphone_player_start(_sharedVoicePlayer); diff --git a/Classes/Contact.m b/Classes/Contact.m index fd27e9078..03a7e4174 100644 --- a/Classes/Contact.m +++ b/Classes/Contact.m @@ -120,6 +120,7 @@ return nil; } + - (NSString *)displayName { if (_friend) { const char *friend_name = linphone_friend_get_name(_friend); diff --git a/Classes/DevicesListView.m b/Classes/DevicesListView.m index 4913b6173..60e005dc9 100644 --- a/Classes/DevicesListView.m +++ b/Classes/DevicesListView.m @@ -20,6 +20,8 @@ #import "DevicesListView.h" #import "PhoneMainView.h" #import "UIDeviceCell.h" +#import "linphoneapp-Swift.h" + @implementation DevicesMenuEntry - (id)init:(LinphoneParticipantDevice *)dev isMe:(BOOL)isMe isFirst:(BOOL)first isUnique:(BOOL)unique index:(NSInteger)idx{ diff --git a/Classes/DialerView.m b/Classes/DialerView.m index df1a5d70f..d24f91ee2 100644 --- a/Classes/DialerView.m +++ b/Classes/DialerView.m @@ -21,6 +21,7 @@ #import "LinphoneManager.h" #import "PhoneMainView.h" +#import "linphoneapp-Swift.h" @implementation DialerView @@ -397,7 +398,7 @@ static UICompositeViewDescription *compositeDescription = nil; } - (IBAction)onBackClick:(id)event { - [PhoneMainView.instance popToView:CallView.compositeViewDescription]; + [PhoneMainView.instance popToView:ActiveCallOrConferenceView.compositeViewDescription]; } - (IBAction)onAddressChange:(id)sender { diff --git a/Classes/ImagePickerView.m b/Classes/ImagePickerView.m index ab1850f74..d4e4cebb7 100644 --- a/Classes/ImagePickerView.m +++ b/Classes/ImagePickerView.m @@ -301,96 +301,83 @@ static UICompositeViewDescription *compositeDescription = nil; inView:(UIView *)ipadView withDocumentMenuDelegate:(id)documentMenuDelegate { void (^block)(UIImagePickerControllerSourceType) = ^(UIImagePickerControllerSourceType type) { - ImagePickerView *view = VIEW(ImagePickerView); - view.sourceType = type; - - // Displays a control that allows the user to choose picture or - // movie capture, if both are available: - view.mediaTypes = [NSArray arrayWithObjects:(NSString *)kUTTypeMovie,(NSString *)kUTTypeImage,nil]; - - // Hides the controls for moving & scaling pictures, or for - // trimming movies. To instead show the controls, use YES. - view.allowsEditing = NO; - view.imagePickerDelegate = delegate; - - if (IPAD && ipadView && ipadPopoverView) { - UIView *iterview = ipadPopoverView; - CGRect ipadPopoverPosition = iterview.frame; - do { - ipadPopoverPosition = - [iterview.superview convertRect:ipadPopoverPosition toView:iterview.superview.superview]; - iterview = iterview.superview; - } while (iterview && iterview.superview != ipadView); - [view.popoverController presentPopoverFromRect:ipadPopoverPosition - inView:ipadView - permittedArrowDirections:UIPopoverArrowDirectionAny - animated:FALSE]; - } else { - [PhoneMainView.instance changeCurrentView:view.compositeViewDescription]; - } - }; - if ([PHPhotoLibrary authorizationStatus] == PHAuthorizationStatusAuthorized) { - DTActionSheet *sheet = [[DTActionSheet alloc] initWithTitle:NSLocalizedString(@"Select the source", nil)]; - if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { - [sheet addButtonWithTitle:NSLocalizedString(@"Camera", nil) - block:^() { - if([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] != AVAuthorizationStatusAuthorized ) { - [[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Camera's permission", nil) message:NSLocalizedString(@"Camera not authorized", nil) delegate:nil cancelButtonTitle:nil otherButtonTitles:@"Continue", nil] show]; - return; - } - block(UIImagePickerControllerSourceTypeCamera); - }]; - } - if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) { - [sheet addButtonWithTitle:NSLocalizedString(@"Photo library", nil) - block:^() { - block(UIImagePickerControllerSourceTypePhotoLibrary); - }]; - } + ImagePickerView *view = VIEW(ImagePickerView); + view.sourceType = type; - if (documentMenuDelegate) { - [sheet addButtonWithTitle:NSLocalizedString(@"Document",nil) block:^(){ - [self pickDocumentForDelegate:documentMenuDelegate]; - }]; + // Displays a control that allows the user to choose picture or + // movie capture, if both are available: + view.mediaTypes = [NSArray arrayWithObjects:(NSString *)kUTTypeMovie,(NSString *)kUTTypeImage,nil]; + + // Hides the controls for moving & scaling pictures, or for + // trimming movies. To instead show the controls, use YES. + view.allowsEditing = NO; + view.imagePickerDelegate = delegate; + + if (IPAD && ipadView && ipadPopoverView) { + UIView *iterview = ipadPopoverView; + CGRect ipadPopoverPosition = iterview.frame; + do { + ipadPopoverPosition = + [iterview.superview convertRect:ipadPopoverPosition toView:iterview.superview.superview]; + iterview = iterview.superview; + } while (iterview && iterview.superview != ipadView); + [view.popoverController presentPopoverFromRect:ipadPopoverPosition + inView:ipadView + permittedArrowDirections:UIPopoverArrowDirectionAny + animated:FALSE]; + } else { + [PhoneMainView.instance changeCurrentView:view.compositeViewDescription]; } - [sheet addCancelButtonWithTitle:NSLocalizedString(@"Cancel", nil) block:nil]; - - [sheet showInView:PhoneMainView.instance.view]; - } else { - [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) { - dispatch_async(dispatch_get_main_queue(), ^{ - if ([PHPhotoLibrary authorizationStatus] == PHAuthorizationStatusAuthorized) { - DTActionSheet *sheet = [[DTActionSheet alloc] initWithTitle:NSLocalizedString(@"Select the source", nil)]; - if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { - [sheet addButtonWithTitle:NSLocalizedString(@"Camera", nil) - block:^() { - if([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] != AVAuthorizationStatusAuthorized ) { - [[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Camera's permission", nil) message:NSLocalizedString(@"Camera not authorized", nil) delegate:nil cancelButtonTitle:nil otherButtonTitles:@"Continue", nil] show]; - return; - } - block(UIImagePickerControllerSourceTypeCamera); - }]; - } - if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) { - [sheet addButtonWithTitle:NSLocalizedString(@"Photo library", nil) - block:^() { - block(UIImagePickerControllerSourceTypePhotoLibrary); - }]; - } - if (documentMenuDelegate) { - [sheet addButtonWithTitle:NSLocalizedString(@"Document",nil) block:^(){ - [self pickDocumentForDelegate:documentMenuDelegate]; - }]; - } - [sheet addCancelButtonWithTitle:NSLocalizedString(@"Cancel", nil) block:nil]; - - [sheet showInView:PhoneMainView.instance.view]; - } else { - [[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Photo's permission", nil) message:NSLocalizedString(@"Photo not authorized", nil) delegate:nil cancelButtonTitle:nil otherButtonTitles:@"Continue", nil] show]; - } - }); - }]; - } + }; + DTActionSheet *sheet = [[DTActionSheet alloc] initWithTitle:NSLocalizedString(@"Select the source", nil)]; + if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { + [sheet addButtonWithTitle:NSLocalizedString(@"Camera", nil) + block:^() { + if([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] != AVAuthorizationStatusAuthorized ) { + [[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Camera's permission", nil) message:NSLocalizedString(@"Camera not authorized", nil) delegate:nil cancelButtonTitle:nil otherButtonTitles:@"Continue", nil] show]; + return; + } + if ([PHPhotoLibrary authorizationStatus] == PHAuthorizationStatusAuthorized) + block(UIImagePickerControllerSourceTypeCamera); + else { + [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) { + dispatch_async(dispatch_get_main_queue(), ^{ + if ([PHPhotoLibrary authorizationStatus] == PHAuthorizationStatusAuthorized) { + block(UIImagePickerControllerSourceTypeCamera); + } else { + [[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Photo's permission", nil) message:NSLocalizedString(@"Photo not authorized", nil) delegate:nil cancelButtonTitle:nil otherButtonTitles:@"Ok", nil] show]; + } + }); + }]; + } + }]; + } + if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) { + [sheet addButtonWithTitle:NSLocalizedString(@"Photo library", nil) + block:^() { + if ([PHPhotoLibrary authorizationStatus] == PHAuthorizationStatusAuthorized) + block(UIImagePickerControllerSourceTypePhotoLibrary); + else { + [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) { + dispatch_async(dispatch_get_main_queue(), ^{ + if ([PHPhotoLibrary authorizationStatus] == PHAuthorizationStatusAuthorized) { + block(UIImagePickerControllerSourceTypePhotoLibrary); + } else { + [[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Photo's permission", nil) message:NSLocalizedString(@"Photo not authorized", nil) delegate:nil cancelButtonTitle:nil otherButtonTitles:@"Ok", nil] show]; + } + }); + }]; + } + }]; + } + if (documentMenuDelegate) { + [sheet addButtonWithTitle:NSLocalizedString(@"Document",nil) block:^(){ + [self pickDocumentForDelegate:documentMenuDelegate]; + }]; + } + [sheet addCancelButtonWithTitle:NSLocalizedString(@"Cancel", nil) block:nil]; + + [sheet showInView:PhoneMainView.instance.view]; } +(void) pickDocumentForDelegate:(id)documentMenuDelegate { diff --git a/Classes/LinphoneAppDelegate.h b/Classes/LinphoneAppDelegate.h index d3820267b..9a4768d11 100644 --- a/Classes/LinphoneAppDelegate.h +++ b/Classes/LinphoneAppDelegate.h @@ -23,7 +23,6 @@ #import #import #import -#import "linphoneapp-Swift.h" @interface LinphoneAppDelegate : NSObject { diff --git a/Classes/LinphoneAppDelegate.m b/Classes/LinphoneAppDelegate.m index be99479de..1d4c152c3 100644 --- a/Classes/LinphoneAppDelegate.m +++ b/Classes/LinphoneAppDelegate.m @@ -140,7 +140,7 @@ if ((floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max)) { if ([LinphoneManager.instance lpConfigBoolForKey:@"autoanswer_notif_preference"]) { linphone_call_accept(call); - [PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription]; + [PhoneMainView.instance changeCurrentView:ActiveCallOrConferenceView.compositeViewDescription]; } else { [PhoneMainView.instance displayIncomingCall:call]; } @@ -321,6 +321,8 @@ return NO; } + VIEW(ActiveCallOrConferenceView); // to get created and all observers added + return YES; } @@ -409,16 +411,9 @@ } // used for callkit. Called when active video. -- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler -{ +- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler { - - if ([userActivity.activityType isEqualToString:@"INStartVideoCallIntent"]) { - LOGI(@"CallKit: satrt video."); - CallView *view = VIEW(CallView); - [view.videoButton setOn]; - } - if ([userActivity.activityType isEqualToString:@"INStartAudioCallIntent"]) { // tel URI handler. + if ([userActivity.activityType isEqualToString:@"INStartAudioCallIntent"]||[userActivity.activityType isEqualToString:@"INStartVideoCallIntent"]) { // tel URI handler. INInteraction *interaction = userActivity.interaction; INStartAudioCallIntent *startAudioCallIntent = (INStartAudioCallIntent *)interaction.intent; INPerson *contact = startAudioCallIntent.contacts[0]; @@ -537,8 +532,7 @@ if ([response.actionIdentifier isEqual:@"Answer"]) { // use the standard handler - [PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription]; - linphone_call_accept(call); + [CallManager.instance acceptCallWithCall:call hasVideo:NO]; } else if ([response.actionIdentifier isEqual:@"Decline"]) { linphone_call_decline(call, LinphoneReasonDeclined); } else if ([response.actionIdentifier isEqual:@"Reply"]) { @@ -575,7 +569,6 @@ return; [[UNUserNotificationCenter currentNotificationCenter] removeAllDeliveredNotifications]; - [PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription]; [CallManager.instance acceptVideoWithCall:call confirm:TRUE]; } else if ([response.actionIdentifier isEqual:@"Confirm"]) { if (linphone_core_get_current_call(LC) == call) @@ -608,7 +601,7 @@ } } else if ([response.notification.request.content.categoryIdentifier isEqual:@"video_request"]) { if (!call) return; - [PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription]; + [PhoneMainView.instance changeCurrentView:ActiveCallOrConferenceView.compositeViewDescription]; NSTimer *videoDismissTimer = nil; UIConfirmationDialog *sheet = [UIConfirmationDialog ShowWithMessage:response.notification.request.content.body cancelMessage:nil @@ -692,8 +685,7 @@ if ([notification.category isEqualToString:@"incoming_call"]) { if ([identifier isEqualToString:@"answer"]) { // use the standard handler - [PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription]; - linphone_call_accept(call); + [CallManager.instance acceptCallWithCall:call hasVideo:NO]; } else if ([identifier isEqualToString:@"decline"]) { LinphoneCall *call = linphone_core_get_current_call(LC); if (call) @@ -730,8 +722,7 @@ if ([notification.category isEqualToString:@"incoming_call"]) { if ([identifier isEqualToString:@"answer"]) { // use the standard handler - [PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription]; - linphone_call_accept(call); + [CallManager.instance acceptCallWithCall:call hasVideo:NO]; } else if ([identifier isEqualToString:@"decline"]) { LinphoneCall *call = linphone_core_get_current_call(LC); if (call) diff --git a/Classes/LinphoneCoreSettingsStore.m b/Classes/LinphoneCoreSettingsStore.m index 219841b65..077c5e84b 100644 --- a/Classes/LinphoneCoreSettingsStore.m +++ b/Classes/LinphoneCoreSettingsStore.m @@ -25,6 +25,8 @@ #include "linphone/lpconfig.h" #include #include +#import "linphoneapp-Swift.h" + @implementation LinphoneCoreSettingsStore diff --git a/Classes/LinphoneManager.h b/Classes/LinphoneManager.h index 53be84967..f0027fb6f 100644 --- a/Classes/LinphoneManager.h +++ b/Classes/LinphoneManager.h @@ -35,7 +35,6 @@ #include "bctoolbox/list.h" #import "OrderedDictionary.h" -#import "linphoneapp-Swift.h" extern NSString *const LINPHONERC_APPLICATION_KEY; @@ -49,7 +48,6 @@ extern NSString *const kLinphoneMainViewChange; extern NSString *const kLinphoneAddressBookUpdate; extern NSString *const kLinphoneLogsUpdate; extern NSString *const kLinphoneSettingsUpdate; -extern NSString *const kLinphoneBluetoothAvailabilityUpdate; extern NSString *const kLinphoneConfiguringStateUpdate; extern NSString *const kLinphoneGlobalStateUpdate; extern NSString *const kLinphoneNotifyReceived; @@ -204,7 +202,6 @@ typedef struct _LinphoneManagerSounds { @property (readonly) sqlite3* database; @property (readonly) LinphoneManagerSounds sounds; @property (readonly) NSMutableArray *logs; -@property (nonatomic, assign) BOOL bluetoothAvailable; @property (readonly) NSString* contactSipField; @property (readonly,copy) NSString* contactFilter; @property (copy) void (^silentPushCompletion)(UIBackgroundFetchResult); diff --git a/Classes/LinphoneManager.m b/Classes/LinphoneManager.m index fc2a91d95..a04fd5220 100644 --- a/Classes/LinphoneManager.m +++ b/Classes/LinphoneManager.m @@ -31,7 +31,6 @@ #import "LinphoneCoreSettingsStore.h" #import "LinphoneAppDelegate.h" #import "LinphoneManager.h" -#import "Utils/AudioHelper.h" #import "Utils/FileTransferDelegate.h" #include "linphone/factory.h" @@ -46,6 +45,7 @@ #import "ChatsListView.h" #import "ChatConversationView.h" #import +#import "linphoneapp-Swift.h" #define LINPHONE_LOGS_MAX_ENTRY 5000 @@ -233,11 +233,7 @@ struct codec_name_pref_table codec_pref_table[] = {{"speex", 8000, "speex_8k_pre - (id)init { if ((self = [super init])) { - [NSNotificationCenter.defaultCenter addObserver:self - selector:@selector(audioRouteChangeListenerCallback:) - name:AVAudioSessionRouteChangeNotification - object:nil]; - + NSString *path = [[NSBundle mainBundle] pathForResource:@"msg" ofType:@"wav"]; self.messagePlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL URLWithString:path] error:nil]; @@ -1767,20 +1763,7 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) { _configDb = linphone_config_new_for_shared_core(kLinphoneMsgNotificationAppGroupId.UTF8String, @"linphonerc".UTF8String, factory.UTF8String); linphone_config_clean_entry(_configDb, "misc", "max_calls"); } -#pragma mark - Audio route Functions -- (void)audioRouteChangeListenerCallback:(NSNotification *)notif { - if (IPAD) - return; - - _bluetoothAvailable = [CallManager.instance isBluetoothAvailable]; - - NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:_bluetoothAvailable], @"available", nil]; - [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneBluetoothAvailabilityUpdate - object:self - userInfo:dict]; - -} #pragma mark - Call Functions - (void)send:(NSString *)replyText toChatRoom:(LinphoneChatRoom *)room { @@ -2162,7 +2145,6 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) { if ([ct currentCalls] != nil) { if (call) { LOGI(@"Pausing SIP call because GSM call"); - CallManager.instance.speakerBeforePause = [CallManager.instance isSpeakerEnabled]; linphone_call_pause(call); [self startCallPausedLongRunningTask]; } else if (linphone_core_is_in_conference(theLinphoneCore)) { diff --git a/Classes/LinphoneUI/Base.lproj/StatusBarView.xib b/Classes/LinphoneUI/Base.lproj/StatusBarView.xib index 0830037cb..341bcf9a8 100644 --- a/Classes/LinphoneUI/Base.lproj/StatusBarView.xib +++ b/Classes/LinphoneUI/Base.lproj/StatusBarView.xib @@ -1,8 +1,10 @@ - - + + + - + + @@ -21,15 +23,15 @@ - - + + - - - - - - - - - - - - - - - - diff --git a/Classes/LinphoneUI/Base.lproj/UICallPausedCell.xib b/Classes/LinphoneUI/Base.lproj/UICallPausedCell.xib deleted file mode 100644 index 423378a9e..000000000 --- a/Classes/LinphoneUI/Base.lproj/UICallPausedCell.xib +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Classes/LinphoneUI/UIBackToCallButton.m b/Classes/LinphoneUI/UIBackToCallButton.m index 60c9f4df4..3a272f5a8 100644 --- a/Classes/LinphoneUI/UIBackToCallButton.m +++ b/Classes/LinphoneUI/UIBackToCallButton.m @@ -20,6 +20,7 @@ #import "UIBackToCallButton.h" #import "LinphoneManager.h" #import "PhoneMainView.h" +#import "linphoneapp-Swift.h" @implementation UIBackToCallButton @@ -46,7 +47,7 @@ } - (IBAction)onBackToCallClick:(id)sender { - [PhoneMainView.instance popToView:CallView.compositeViewDescription]; + [PhoneMainView.instance popToView:ActiveCallOrConferenceView.compositeViewDescription]; } @end diff --git a/Classes/LinphoneUI/UIBluetoothButton.m b/Classes/LinphoneUI/UIBluetoothButton.m index 53abc1818..f14af83b3 100644 --- a/Classes/LinphoneUI/UIBluetoothButton.m +++ b/Classes/LinphoneUI/UIBluetoothButton.m @@ -18,10 +18,8 @@ */ #import "UIBluetoothButton.h" -#import "../Utils/AudioHelper.h" #import "Utils.h" #import - #include "linphone/linphonecore.h" @implementation UIBluetoothButton diff --git a/Classes/LinphoneUI/UICallButton.m b/Classes/LinphoneUI/UICallButton.m index c84a7dafd..aa925fc22 100644 --- a/Classes/LinphoneUI/UICallButton.m +++ b/Classes/LinphoneUI/UICallButton.m @@ -21,6 +21,8 @@ #import "LinphoneManager.h" #import +#import "linphoneapp-Swift.h" + @implementation UICallButton diff --git a/Classes/LinphoneUI/UICallConferenceCell.h b/Classes/LinphoneUI/UICallConferenceCell.h deleted file mode 100644 index cf59dfe3c..000000000 --- a/Classes/LinphoneUI/UICallConferenceCell.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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 . - */ - -#import "UIRoundedImageView.h" -#import "LinphoneManager.h" -#import "UIInterfaceStyleButton.h" - -#define CONFERENCE_CELL_HEIGHT 60 - -@interface UICallConferenceCell : UITableViewCell - -@property(weak, nonatomic) IBOutlet UIRoundedImageView *avatarImage; -@property(weak, nonatomic) IBOutlet UILabel *nameLabel; -@property(weak, nonatomic) IBOutlet UILabel *durationLabel; -@property (weak, nonatomic) IBOutlet UIInterfaceStyleButton *kickButton; -@property(nonatomic, setter=setParticipant:) LinphoneParticipant *participant; - -- (id)initWithIdentifier:(NSString *)identifier; -- (IBAction)onKickClick:(id)sender; - -@end diff --git a/Classes/LinphoneUI/UICallConferenceCell.m b/Classes/LinphoneUI/UICallConferenceCell.m deleted file mode 100644 index 0e64ca4b1..000000000 --- a/Classes/LinphoneUI/UICallConferenceCell.m +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 . - */ - -#import "UICallConferenceCell.h" -#import "Utils.h" -#import "PhoneMainView.h" - -@implementation UICallConferenceCell - -- (id)initWithIdentifier:(NSString *)identifier { - self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]; - if (self != nil) { - NSArray *arrayOfViews = - [[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self.class) owner:self options:nil]; - if ([arrayOfViews count] >= 1) { - // resize cell to match .nib size. It is needed when resized the cell to - // correctly adapt its height too - UIView *sub = ((UIView *)[arrayOfViews objectAtIndex:0]); - [self setFrame:CGRectMake(0, 0, sub.frame.size.width, sub.frame.size.height)]; - [self addSubview:sub]; - } - } - return self; -} - -- (void)setParticipant:(LinphoneParticipant *)p { - _participant = p; - if (!p) { - return; - } - const LinphoneAddress *addr = linphone_participant_get_address(p); - [ContactDisplay setDisplayNameLabel:_nameLabel forAddress:addr]; - _durationLabel.text = [LinphoneUtils durationToString:[NSDate date].timeIntervalSince1970 - linphone_participant_get_creation_time(p)]; - _kickButton.hidden = CallManager.instance.isInConferenceAsGuest; -} - - -- (IBAction)onKickClick:(id)sender { - if (!_participant) { - return; - } - - if ([CallManager callKitEnabled]) { - LinphoneCall *call = [CallManager.instance getCallForParticipant:_participant]; - if (call) { - [CallManager.instance setHeldWithCall:call hold:true]; - } - } - linphone_conference_remove_participant_2([CallManager.instance getConference], _participant); - - -} -@end diff --git a/Classes/LinphoneUI/UICallPausedCell.h b/Classes/LinphoneUI/UICallPausedCell.h deleted file mode 100644 index 77bdedd71..000000000 --- a/Classes/LinphoneUI/UICallPausedCell.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 . - */ - -#import "UIRoundedImageView.h" -#import "LinphoneManager.h" -#import "UIPauseButton.h" - -@interface UICallPausedCell : UITableViewCell - -@property(weak, nonatomic) IBOutlet UIRoundedImageView *avatarImage; -@property(weak, nonatomic) IBOutlet UILabel *nameLabel; -@property(weak, nonatomic) IBOutlet UILabel *durationLabel; -@property(weak, nonatomic) IBOutlet UIPauseButton *pauseButton; - -- (id)initWithIdentifier:(NSString *)identifier; -- (void)setCall:(LinphoneCall *)call; - -@end diff --git a/Classes/LinphoneUI/UICallPausedCell.m b/Classes/LinphoneUI/UICallPausedCell.m deleted file mode 100644 index fe51d9c5d..000000000 --- a/Classes/LinphoneUI/UICallPausedCell.m +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 . - */ - -#import "UICallPausedCell.h" -#import "Utils.h" - -@implementation UICallPausedCell - -- (id)initWithIdentifier:(NSString *)identifier { - self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]; - if (self != nil) { - NSArray *arrayOfViews = - [[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self.class) owner:self options:nil]; - if ([arrayOfViews count] >= 1) { - // resize cell to match .nib size. It is needed when resized the cell to - // correctly adapt its height too - UIView *sub = ((UIView *)[arrayOfViews objectAtIndex:0]); - [self setFrame:CGRectMake(0, 0, sub.frame.size.width, sub.frame.size.height)]; - [self addSubview:sub]; - } - } - return self; -} - -- (void)setCall:(LinphoneCall *)call { - // if no call is provided, we assume that this is a conference - if (!call || linphone_call_get_conference(call)) { - [_pauseButton setType:UIPauseButtonType_Conference call:call]; - _nameLabel.text = NSLocalizedString(@"Conference", nil); - [_avatarImage setImage:[UIImage imageNamed:@"options_start_conference_default.png"] - bordered:NO - withRoundedRadius:YES]; - _durationLabel.text = @""; - } else { - [_pauseButton setType:UIPauseButtonType_Call call:call]; - const LinphoneAddress *addr = linphone_call_get_remote_address(call); - [ContactDisplay setDisplayNameLabel:_nameLabel forAddress:addr]; - [_avatarImage setImage:[FastAddressBook imageForAddress:addr] bordered:NO withRoundedRadius:YES]; - _durationLabel.text = [LinphoneUtils durationToString:linphone_call_get_duration(call)]; - } - [_pauseButton update]; -} - -@end diff --git a/Classes/LinphoneUI/UIChatBubblePhotoCell.m b/Classes/LinphoneUI/UIChatBubblePhotoCell.m index ccf47d7aa..01b13c93b 100644 --- a/Classes/LinphoneUI/UIChatBubblePhotoCell.m +++ b/Classes/LinphoneUI/UIChatBubblePhotoCell.m @@ -25,6 +25,8 @@ #import #import #import +#import "linphoneapp-Swift.h" + #define voicePlayer VIEW(ChatConversationView).sharedVoicePlayer #define chatView VIEW(ChatConversationView) diff --git a/Classes/LinphoneUI/UIChatBubbleTextCell.m b/Classes/LinphoneUI/UIChatBubbleTextCell.m index 4038ac799..079891029 100644 --- a/Classes/LinphoneUI/UIChatBubbleTextCell.m +++ b/Classes/LinphoneUI/UIChatBubbleTextCell.m @@ -25,6 +25,8 @@ #import #import #import +#import "linphoneapp-Swift.h" + @implementation UIChatBubbleTextCell diff --git a/Classes/LinphoneUI/UIHangUpButton.m b/Classes/LinphoneUI/UIHangUpButton.m deleted file mode 100644 index 6ab96297d..000000000 --- a/Classes/LinphoneUI/UIHangUpButton.m +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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 . - */ - -#import "UIHangUpButton.h" -#import "LinphoneManager.h" - -#import "linphoneapp-Swift.h" - -@implementation UIHangUpButton - -#pragma mark - Static Functions - -+ (bool)isInConference:(LinphoneCall *)call { - if (!call) - return false; - return linphone_call_params_get_local_conference_mode(linphone_call_get_current_params(call)); -} - -+ (int)callCount { - int count = 0; - const MSList *calls = linphone_core_get_calls(LC); - - while (calls != 0) { - if (![UIHangUpButton isInConference:((LinphoneCall *)calls->data)]) { - count++; - } - calls = calls->next; - } - return count; -} - -#pragma mark - Lifecycle Functions - -- (void)initUIHangUpButton { - [self addTarget:self action:@selector(touchUp:) forControlEvents:UIControlEventTouchUpInside]; -} - -- (id)init { - self = [super init]; - if (self) { - [self initUIHangUpButton]; - } - return self; -} - -- (id)initWithCoder:(NSCoder *)decoder { - self = [super initWithCoder:decoder]; - if (self) { - [self initUIHangUpButton]; - } - return self; -} - -- (id)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - [self initUIHangUpButton]; - } - return self; -} - -#pragma mark - - -- (void)update { - if (linphone_core_get_calls_nb(LC) == 1 || // One call - linphone_core_get_current_call(LC) != NULL || // In call - linphone_core_is_in_conference(LC) || // In conference - (linphone_core_get_conference_size(LC) > 0 && [UIHangUpButton callCount] == 0) // Only one conf - ) { - [self setEnabled:true]; - return; - } - [self setEnabled:false]; -} - -#pragma mark - Action Functions - -- (void)touchUp:(id)sender { - LinphoneCall *currentcall = linphone_core_get_current_call(LC); - if (linphone_core_is_in_conference(LC) || // In conference - (linphone_core_get_conference_size(LC) > 0 && [UIHangUpButton callCount] == 0) // Only one conf - ) { - LinphoneManager.instance.conf = TRUE; - linphone_core_terminate_conference(LC); - } else if (currentcall != NULL) { - [CallManager.instance terminateCallWithCall:currentcall]; - } else { - const MSList *calls = linphone_core_get_calls(LC); - if (bctbx_list_size(calls) == 1) { // Only one call - [CallManager.instance terminateCallWithCall:(calls->data)]; - } - } -} - -@end diff --git a/Classes/LinphoneUI/UIPauseButton.m b/Classes/LinphoneUI/UIPauseButton.m deleted file mode 100644 index 797e88675..000000000 --- a/Classes/LinphoneUI/UIPauseButton.m +++ /dev/null @@ -1,180 +0,0 @@ -/* - * 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 . - */ - -#import "UIPauseButton.h" -#import "LinphoneManager.h" -#import "Utils.h" - -@implementation UIPauseButton - -#pragma mark - Lifecycle Functions - -- (void)initUIPauseButton { - type = UIPauseButtonType_CurrentCall; -} - -- (id)init { - self = [super init]; - if (self) { - [self initUIPauseButton]; - } - return self; -} - -- (id)initWithCoder:(NSCoder *)decoder { - self = [super initWithCoder:decoder]; - if (self) { - [self initUIPauseButton]; - } - return self; -} - -- (id)initWithFrame:(CGRect)frame { - self = [super initWithFrame:frame]; - if (self) { - [self initUIPauseButton]; - } - return self; -} - -#pragma mark - Static Functions - -+ (bool)isInConference:(LinphoneCall *)call { - if (!call) - return false; - return linphone_call_params_get_local_conference_mode(linphone_call_get_current_params(call)); -} - -+ (LinphoneCall *)getCall { - LinphoneCall *currentCall = linphone_core_get_current_call(LC); - if (currentCall == nil && linphone_core_get_calls_nb(LC) == 1) { - currentCall = (LinphoneCall *)linphone_core_get_calls(LC)->data; - } - return currentCall; -} - -#pragma mark - - -- (void)setType:(UIPauseButtonType)atype call:(LinphoneCall *)acall { - type = atype; - call = acall; -} - -#pragma mark - UIToggleButtonDelegate Functions - -- (void)onOn { - switch (type) { - case UIPauseButtonType_Call: { - if (call != nil) { - if ([CallManager callKitEnabled]) { - [CallManager.instance setHeldWithCall:call hold:true]; - } else { - CallManager.instance.speakerBeforePause = [CallManager.instance isSpeakerEnabled]; - linphone_call_pause(call); - } - } else { - LOGW(@"Cannot toggle pause buttton, because no current call"); - } - break; - } - case UIPauseButtonType_Conference: { - linphone_core_leave_conference(CallManager.instance.getConference); - // Fake event - [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneCallUpdate object:self]; - break; - } - case UIPauseButtonType_CurrentCall: { - LinphoneCall *currentCall = [UIPauseButton getCall]; - if (currentCall != nil) { - if ([CallManager callKitEnabled]) { - [CallManager.instance setHeldWithCall:currentCall hold:true]; - } else { - CallManager.instance.speakerBeforePause = [CallManager.instance isSpeakerEnabled]; - linphone_call_pause(currentCall); - } - } else { - LOGW(@"Cannot toggle pause buttton, because no current call"); - } - break; - } - } -} - -- (void)onOff { - switch (type) { - case UIPauseButtonType_Call: { - if (call != nil) { - if ([CallManager callKitEnabled]) { - [CallManager.instance setHeldWithCall:call hold:false]; - } else { - linphone_call_resume(call); - } - } else { - LOGW(@"Cannot toggle pause buttton, because no current call"); - } - break; - } - case UIPauseButtonType_Conference: { - linphone_core_enter_conference(CallManager.instance.getConference); - // Fake event - [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneCallUpdate object:self]; - break; - } - case UIPauseButtonType_CurrentCall: { - LinphoneCall *currentCall = [UIPauseButton getCall]; - if ([CallManager callKitEnabled]) { - [CallManager.instance setHeldWithCall:currentCall hold:false]; - } else { - linphone_call_resume(currentCall); - } - break; - } - } -} - -- (bool)onUpdate { - bool ret = false; - LinphoneCall *c = call; - switch (type) { - case UIPauseButtonType_Conference: { - self.enabled = CallManager.instance.getConference && (linphone_conference_get_participant_count(CallManager.instance.getConference)> 0); - if (self.enabled) { - ret = (!CallManager.instance.isInConference); - } - break; - } - case UIPauseButtonType_CurrentCall: - c = [UIPauseButton getCall]; - case UIPauseButtonType_Call: { - if (c != nil) { - LinphoneCallState state = linphone_call_get_state(c); - ret = (state == LinphoneCallPaused || state == LinphoneCallPausing); - self.enabled = !linphone_core_sound_resources_locked(LC) && - (state == LinphoneCallPaused || state == LinphoneCallPausing || - state == LinphoneCallStreamsRunning); - } else { - self.enabled = FALSE; - } - break; - } - } - return ret; -} - -@end diff --git a/Classes/LinphoneUI/UISpeakerButton.m b/Classes/LinphoneUI/UISpeakerButton.m deleted file mode 100644 index 64c12d5b9..000000000 --- a/Classes/LinphoneUI/UISpeakerButton.m +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 . - */ - -#import -#import "UISpeakerButton.h" -#import "Utils.h" -#import "LinphoneManager.h" - -#include "linphone/linphonecore.h" - -@implementation UISpeakerButton - -INIT_WITH_COMMON_CF { - [NSNotificationCenter.defaultCenter addObserver:self - selector:@selector(audioRouteChangeListenerCallback:) - name:AVAudioSessionRouteChangeNotification - object:nil]; - return self; -} - -- (void)onOn { - [CallManager.instance changeRouteToSpeaker]; -} - -- (void)onOff { - [CallManager.instance changeRouteToDefault]; -} - - -- (void)dealloc { - [NSNotificationCenter.defaultCenter removeObserver:self]; -} - -#pragma mark - UIToggleButtonDelegate Functions - -- (void)audioRouteChangeListenerCallback:(NSNotification *)notif { - dispatch_async(dispatch_get_main_queue(), ^{ - [self update];}); -} - -- (bool)onUpdate { - return [CallManager.instance isSpeakerEnabled]; -} - -@end diff --git a/Classes/LinphoneUI/UIVideoButton.m b/Classes/LinphoneUI/UIVideoButton.m deleted file mode 100644 index af97c4295..000000000 --- a/Classes/LinphoneUI/UIVideoButton.m +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 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 . - */ - -#import "UIVideoButton.h" -#include "LinphoneManager.h" -#import "Log.h" - -@implementation UIVideoButton { - BOOL last_update_state; -} - -@synthesize waitView; - -INIT_WITH_COMMON_CF { - last_update_state = FALSE; - return self; -} - -- (void)onOn { - - if (!linphone_core_video_display_enabled(LC)) - return; - - [self setEnabled:FALSE]; - [waitView startAnimating]; - - LinphoneCall *call = linphone_core_get_current_call(LC); - if (call) { - CallAppData *data = [CallManager getAppDataWithCall:call]; - data.videoRequested = TRUE;/* will be used later to notify user if video was not activated because of the linphone core*/ - [CallManager setAppDataWithCall:call appData:data]; - LinphoneCallParams *call_params = linphone_core_create_call_params(LC,call); - linphone_call_params_enable_video(call_params, TRUE); - linphone_call_update(call, call_params); - linphone_call_params_unref(call_params); - } else if (self.inAudioConf) { - LinphoneConferenceParams *cp = linphone_core_create_conference_params(LC); - linphone_conference_params_set_video_enabled(cp, true); - linphone_conference_update_params(linphone_core_get_conference(LC), cp); - } else { - LOGW(@"Cannot toggle video button, because no current call"); - } -} - -- (void)onOff { - - if (!linphone_core_video_display_enabled(LC)) - return; - [CallManager.instance changeRouteToDefault]; - //[CallManager.instance enableSpeakerWithEnable:FALSE]; - [self setEnabled:FALSE]; - [waitView startAnimating]; - - LinphoneCall *call = linphone_core_get_current_call(LC); - if (call) { - LinphoneCallParams *call_params = linphone_core_create_call_params(LC,call); - linphone_call_params_enable_video(call_params, FALSE); - linphone_core_update_call(LC, call, call_params); - linphone_call_params_unref(call_params); - } else if (self.inVideoConf) { - LinphoneConferenceParams *cp = linphone_core_create_conference_params(LC); - linphone_conference_params_set_video_enabled(cp, false); - linphone_conference_update_params(linphone_core_get_conference(LC), cp); - } else { - LOGW(@"Cannot toggle video button, because no current call or no video conference"); - } -} - -- (bool)onUpdate { - bool video_enabled = false; - LinphoneCall *currentCall = linphone_core_get_current_call(LC); - if (linphone_core_video_supported(LC)) { - if (self.inAudioConf || self.inVideoConf || (linphone_core_video_display_enabled(LC) && currentCall && !linphone_core_sound_resources_locked(LC) && - linphone_call_get_state(currentCall) == LinphoneCallStreamsRunning)){ - video_enabled = TRUE; - } - } - - [self setEnabled:video_enabled]; - if (last_update_state != video_enabled) - [waitView stopAnimating]; - if (video_enabled) { - video_enabled = self.inVideoConf || (currentCall && linphone_call_params_video_enabled(linphone_call_get_current_params(currentCall))); - } - last_update_state = video_enabled; - - return video_enabled; -} - --(BOOL) inVideoConf { - return (linphone_core_is_in_conference(LC) && linphone_core_get_conference(LC) != nil && linphone_conference_params_is_video_enabled(linphone_conference_get_current_params(linphone_core_get_conference(LC)))); -} - --(BOOL) inAudioConf { - return (linphone_core_is_in_conference(LC) && linphone_core_get_conference(LC) != nil && !linphone_conference_params_is_video_enabled(linphone_conference_get_current_params(linphone_core_get_conference(LC)))); -} - -@end diff --git a/Classes/LinphoneUI/fr.lproj/UICallConferenceCell.strings b/Classes/LinphoneUI/fr.lproj/UICallConferenceCell.strings deleted file mode 100644 index 629130fa2..000000000 Binary files a/Classes/LinphoneUI/fr.lproj/UICallConferenceCell.strings and /dev/null differ diff --git a/Classes/LinphoneUI/fr.lproj/UICallPausedCell.strings b/Classes/LinphoneUI/fr.lproj/UICallPausedCell.strings deleted file mode 100644 index 4d0eb9bb9..000000000 Binary files a/Classes/LinphoneUI/fr.lproj/UICallPausedCell.strings and /dev/null differ diff --git a/Classes/LinphoneUI/hu.lproj/UICallConferenceCell.strings b/Classes/LinphoneUI/hu.lproj/UICallConferenceCell.strings deleted file mode 100644 index c90ef96ac..000000000 Binary files a/Classes/LinphoneUI/hu.lproj/UICallConferenceCell.strings and /dev/null differ diff --git a/Classes/LinphoneUI/hu.lproj/UICallPausedCell.strings b/Classes/LinphoneUI/hu.lproj/UICallPausedCell.strings deleted file mode 100644 index 7e626165f..000000000 Binary files a/Classes/LinphoneUI/hu.lproj/UICallPausedCell.strings and /dev/null differ diff --git a/Classes/Log.h b/Classes/Log.h index c2ee3d897..c838afa12 100644 --- a/Classes/Log.h +++ b/Classes/Log.h @@ -32,6 +32,11 @@ + (void)log:(OrtpLogLevel)severity file:(const char *)file line:(int)line format:(NSString *)format, ...; + (void)enableLogs:(OrtpLogLevel)level; + (void)directLog:(OrtpLogLevel)level text:(NSString *)text; ++ (void)d:(NSString *)text; ++ (void)i:(NSString *)text; ++ (void)w:(NSString *)text; ++ (void)e:(NSString *)text; ++ (void)f:(NSString *)text; void linphone_iphone_log_handler(const char *domain, OrtpLogLevel lev, const char *fmt, va_list args); @end diff --git a/Classes/PhoneMainView.h b/Classes/PhoneMainView.h index adfe87b5a..672030668 100644 --- a/Classes/PhoneMainView.h +++ b/Classes/PhoneMainView.h @@ -27,10 +27,6 @@ #import "AboutView.h" #import "AssistantLinkView.h" #import "AssistantView.h" -#import "CallIncomingView.h" -#import "CallOutgoingView.h" -#import "CallSideMenuView.h" -#import "CallView.h" #import "ChatConversationCreateView.h" #import "ChatConversationInfoView.h" #import "ChatConversationImdnView.h" @@ -78,7 +74,7 @@ @end -@interface PhoneMainView : UIViewController { +@interface PhoneMainView : UIViewController { @private NSMutableArray *inhibitedEvents; } @@ -96,6 +92,7 @@ - (void)changeCurrentView:(UICompositeViewDescription *)view; - (UIViewController*)popCurrentView; +- (UIViewController *)popView:(UICompositeViewDescription *)view; - (UIViewController *)popToView:(UICompositeViewDescription *)currentView; - (void) setPreviousViewName:(NSString*)previous; - (NSString*) getPreviousViewName; diff --git a/Classes/PhoneMainView.m b/Classes/PhoneMainView.m index 3d0ad4962..a45b51376 100644 --- a/Classes/PhoneMainView.m +++ b/Classes/PhoneMainView.m @@ -22,6 +22,8 @@ #import "LinphoneAppDelegate.h" #import "Log.h" #import "PhoneMainView.h" +#import "linphoneapp-Swift.h" + static RootViewManager *rootViewManagerInstance = nil; @@ -373,7 +375,9 @@ static RootViewManager *rootViewManagerInstance = nil; break; } case LinphoneCallOutgoingInit: { - [self changeCurrentView:CallOutgoingView.compositeViewDescription]; + OutgoingCallView *v = VIEW(OutgoingCallView); + [self changeCurrentView:OutgoingCallView.compositeViewDescription]; + [v setCallWithCall:call]; break; } case LinphoneCallPausedByRemote: @@ -381,39 +385,12 @@ static RootViewManager *rootViewManagerInstance = nil; if (![LinphoneManager.instance isCTCallCenterExist]) { /*only register CT call center CB for connected call*/ [LinphoneManager.instance setupGSMInteraction]; - [[UIDevice currentDevice] setProximityMonitoringEnabled:!([CallManager.instance isSpeakerEnabled] || [CallManager.instance isBluetoothEnabled])]; - } - break; - } - case LinphoneCallStreamsRunning: { - [self changeCurrentView:CallView.compositeViewDescription]; - break; - } - case LinphoneCallUpdatedByRemote: { - const LinphoneCallParams *current = linphone_call_get_current_params(call); - const LinphoneCallParams *remote = linphone_call_get_remote_params(call); - - if (linphone_call_params_video_enabled(current) && !linphone_call_params_video_enabled(remote)) { - [self changeCurrentView:CallView.compositeViewDescription]; } break; } case LinphoneCallError: { [self displayCallError:call message:message]; } - case LinphoneCallEnd: { - const MSList *calls = linphone_core_get_calls(LC); - if (!calls || calls->data == call) { - while ((currentView == CallView.compositeViewDescription) || - (currentView == CallIncomingView.compositeViewDescription) || - (currentView == CallOutgoingView.compositeViewDescription)) { - [self popCurrentView]; - } - } else { - [self changeCurrentView:CallView.compositeViewDescription]; - } - break; - } case LinphoneCallEarlyUpdatedByRemote: case LinphoneCallEarlyUpdating: case LinphoneCallIdle: @@ -633,6 +610,15 @@ static RootViewManager *rootViewManagerInstance = nil; return [mainViewController getCurrentViewController]; } +- (UIViewController *)popView:(UICompositeViewDescription *)view { + NSMutableArray *viewStack = [RootViewManager instance].viewDescriptionStack; + while (viewStack.count > 0 && [[viewStack lastObject] equal:view]) { + [viewStack removeLastObject]; + } + return [self popToView:viewStack.lastObject ?: DialerView.compositeViewDescription]; +} + + - (void)changeCurrentView:(UICompositeViewDescription *)view { [self _changeCurrentView:view transition:nil animated:ANIMATED]; } @@ -764,10 +750,10 @@ static RootViewManager *rootViewManagerInstance = nil; [CallManager.instance acceptCallWithCall:call hasVideo:YES]; } else { AudioServicesPlaySystemSound(lm.sounds.vibrate); - CallIncomingView *view = VIEW(CallIncomingView); + IncomingCallView *view = VIEW(IncomingCallView); [self changeCurrentView:view.compositeViewDescription]; - [view setCall:call]; - [view setDelegate:self]; + [view setCallWithCall:call]; + //CDFIX [view setDelegate:self]; } } } diff --git a/Classes/ProviderDelegate.swift b/Classes/ProviderDelegate.swift index 54113dafd..a6f324058 100644 --- a/Classes/ProviderDelegate.swift +++ b/Classes/ProviderDelegate.swift @@ -92,7 +92,7 @@ class ProviderDelegate: NSObject { provider.reportNewIncomingCall(with: uuid, update: update) { error in if error == nil { if CallManager.instance().endCallkit { - let call = CallManager.instance().lc?.getCallByCallid(callId: callId!) + let call = CallManager.instance().core?.getCallByCallid(callId: callId!) if (call?.state == .PushIncomingReceived) { try? call?.terminate() } @@ -184,7 +184,7 @@ extension ProviderDelegate: CXProviderDelegate { CallManager.instance().backgroundContextCameraIsEnabled = call!.params?.videoEnabled ?? false call?.cameraEnabled = false // Disable camera while app is not on foreground } - CallManager.instance().lc?.configureAudioSession() + CallManager.instance().core?.configureAudioSession() CallManager.instance().acceptCall(call: call!, hasVideo: call!.params?.videoEnabled ?? false) action.fulfill() } @@ -199,8 +199,8 @@ extension ProviderDelegate: CXProviderDelegate { } do { - if (CallManager.instance().lc?.isInConference ?? false && action.isOnHold) { - try CallManager.instance().lc?.leaveConference() + if (CallManager.instance().core?.isInConference ?? false && action.isOnHold) { + try CallManager.instance().core?.leaveConference() Log.directLog(BCTBX_LOG_DEBUG, text: "CallKit: call-id: [\(String(describing: callId))] leaving conference") NotificationCenter.default.post(name: Notification.Name("LinphoneCallUpdate"), object: self) return @@ -212,11 +212,10 @@ extension ProviderDelegate: CXProviderDelegate { if (call!.params?.localConferenceMode ?? false) { return } - CallManager.instance().speakerBeforePause = CallManager.instance().isSpeakerEnabled() try call!.pause() } else { - if (CallManager.instance().lc?.conference != nil && CallManager.instance().lc?.callsNb ?? 0 > 1) { - try CallManager.instance().lc?.enterConference() + if (CallManager.instance().core?.conference != nil && CallManager.instance().core?.callsNb ?? 0 > 1) { + try CallManager.instance().core?.enterConference() NotificationCenter.default.post(name: Notification.Name("LinphoneCallUpdate"), object: self) } else { try call!.resume() @@ -245,7 +244,7 @@ extension ProviderDelegate: CXProviderDelegate { action.fail() } - CallManager.instance().lc?.configureAudioSession() + CallManager.instance().core?.configureAudioSession() try CallManager.instance().doCall(addr: addr!, isSas: callInfo?.sasEnabled ?? false) } catch { Log.directLog(BCTBX_LOG_ERROR, text: "CallKit: Call started failed because \(error)") @@ -256,7 +255,7 @@ extension ProviderDelegate: CXProviderDelegate { func provider(_ provider: CXProvider, perform action: CXSetGroupCallAction) { Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: Call grouped callUUid : \(action.callUUID) with callUUID: \(String(describing: action.callUUIDToGroupWith)).") - CallManager.instance().addAllToConference() + CallManager.instance().addAllToLocalConference() action.fulfill() } @@ -264,7 +263,7 @@ extension ProviderDelegate: CXProviderDelegate { let uuid = action.callUUID let callId = callInfos[uuid]?.callId Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: Call muted with call-id: \(String(describing: callId)) an UUID: \(uuid.description).") - CallManager.instance().lc!.micEnabled = !CallManager.instance().lc!.micEnabled + CallManager.instance().core!.micEnabled = !CallManager.instance().core!.micEnabled action.fulfill() } @@ -297,12 +296,12 @@ extension ProviderDelegate: CXProviderDelegate { func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) { Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: audio session activated.") - CallManager.instance().lc?.activateAudioSession(actived: true) + CallManager.instance().core?.activateAudioSession(actived: true) } func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) { Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: audio session deactivated.") - CallManager.instance().lc?.activateAudioSession(actived: false) + CallManager.instance().core?.activateAudioSession(actived: false) } } diff --git a/Classes/CallConferenceTableView.h b/Classes/SwiftUtil/Extensions/IOS/OptionalExtensions.swift similarity index 79% rename from Classes/CallConferenceTableView.h rename to Classes/SwiftUtil/Extensions/IOS/OptionalExtensions.swift index 41698c257..58f10f627 100644 --- a/Classes/CallConferenceTableView.h +++ b/Classes/SwiftUtil/Extensions/IOS/OptionalExtensions.swift @@ -1,7 +1,7 @@ /* * Copyright (c) 2010-2020 Belledonne Communications SARL. * - * This file is part of linphone-iphone + * 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 @@ -17,10 +17,16 @@ * along with this program. If not, see . */ -#import -@interface CallConferenceTableView : UITableViewController - -- (void)update; - -@end +extension Optional { + var logable: Any { + switch self { + case .none: + return "|⭕️" + case let .some(value): + return value + } + } + + +} diff --git a/Classes/SwiftUtil/Extensions/IOS/UIApplication+Extension.swift b/Classes/SwiftUtil/Extensions/IOS/UIApplication+Extension.swift new file mode 100644 index 000000000..50f5e9353 --- /dev/null +++ b/Classes/SwiftUtil/Extensions/IOS/UIApplication+Extension.swift @@ -0,0 +1,39 @@ +/* +* Copyright (c) 2010-2020 Belledonne Communications SARL. +* +* This file is part of linhome +* +* 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 . +*/ + + + +import Foundation +import UIKit + + +extension UIApplication { + + class func getTopMostViewController() -> UIViewController? { + let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first + if var topController = keyWindow?.rootViewController { + while let presentedViewController = topController.presentedViewController { + topController = presentedViewController + } + return topController + } else { + return nil + } + } +} diff --git a/Classes/SwiftUtil/Extensions/IOS/UIColorExtensions.swift b/Classes/SwiftUtil/Extensions/IOS/UIColorExtensions.swift new file mode 100644 index 000000000..2d4b8efc9 --- /dev/null +++ b/Classes/SwiftUtil/Extensions/IOS/UIColorExtensions.swift @@ -0,0 +1,40 @@ +/* + * 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 . + */ + +import Foundation + +extension UIColor { + public convenience init(hex: String) { + let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted) + var int = UInt64() + Scanner(string: hex).scanHexInt64(&int) + let a, r, g, b: UInt64 + switch hex.count { + case 3: // RGB (12-bit) + (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17) + case 6: // RGB (24-bit) + (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF) + case 8: // ARGB (32-bit) + (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF) + default: + (a, r, g, b) = (255, 0, 0, 0) + } + self.init(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(a) / 255) + } +} diff --git a/Classes/SwiftUtil/Extensions/IOS/UIDeviceExtensions.swift b/Classes/SwiftUtil/Extensions/IOS/UIDeviceExtensions.swift new file mode 100644 index 000000000..fa00d6236 --- /dev/null +++ b/Classes/SwiftUtil/Extensions/IOS/UIDeviceExtensions.swift @@ -0,0 +1,57 @@ +/* +* Copyright (c) 2010-2020 Belledonne Communications SARL. +* +* This file is part of linhome +* +* 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 . +*/ + + + +import Foundation +import UIKit +import AVFoundation + +extension UIDevice { + static func ipad() -> Bool { + return UIDevice.current.userInterfaceIdiom == .pad + } + static func vibrate() { + if (!ipad()) { + AudioServicesPlaySystemSound(kSystemSoundID_Vibrate) + } + } + static func is5SorSEGen1() -> Bool { + return UIScreen.main.nativeBounds.height == 1136 + } + + static func hasNotch() -> Bool { + if (UserDefaults.standard.bool(forKey: "hasNotch")) { + return true + } + guard #available(iOS 11.0, *), let topPadding = UIApplication.shared.keyWindow?.safeAreaInsets.top, topPadding > 24 else { + return false + } + UserDefaults.standard.setValue(true, forKey: "hasNotch") + return true + } + + static func notchHeight() -> CGFloat { + guard #available(iOS 11.0, *), let topPadding = UIApplication.shared.keyWindow?.safeAreaInsets.top else { + return 0 + } + return topPadding + } + +} diff --git a/Classes/SwiftUtil/Extensions/IOS/UIImageExtensions.swift b/Classes/SwiftUtil/Extensions/IOS/UIImageExtensions.swift new file mode 100644 index 000000000..a409d782a --- /dev/null +++ b/Classes/SwiftUtil/Extensions/IOS/UIImageExtensions.swift @@ -0,0 +1,50 @@ +/* + * 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 . + */ + +import Foundation + +extension UIImage { + func tinted(with color: UIColor?) -> UIImage? { + if (color == nil) { + return self + } + defer { UIGraphicsEndImageContext() } + UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale) + color!.set() + self.withRenderingMode(.alwaysTemplate).draw(in: CGRect(origin: .zero, size: self.size)) + return UIGraphicsGetImageFromCurrentImageContext() + } + + func withInsets(insets: UIEdgeInsets) -> UIImage? { + UIGraphicsBeginImageContextWithOptions( + CGSize(width: self.size.width + insets.left + insets.right, + height: self.size.height + insets.top + insets.bottom), false, self.scale) + let _ = UIGraphicsGetCurrentContext() + let origin = CGPoint(x: insets.left, y: insets.top) + self.draw(at: origin) + let imageWithInsets = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return imageWithInsets + } + + func withPadding(padding: CGFloat) -> UIImage? { + let insets = UIEdgeInsets(top: padding, left: padding, bottom: padding, right: padding) + return withInsets(insets: insets) + } +} diff --git a/Classes/CallPausedTableView.h b/Classes/SwiftUtil/Extensions/IOS/UIImageViewExtensions.swift similarity index 78% rename from Classes/CallPausedTableView.h rename to Classes/SwiftUtil/Extensions/IOS/UIImageViewExtensions.swift index 622f19084..9f926344a 100644 --- a/Classes/CallPausedTableView.h +++ b/Classes/SwiftUtil/Extensions/IOS/UIImageViewExtensions.swift @@ -1,7 +1,7 @@ /* * Copyright (c) 2010-2020 Belledonne Communications SARL. * - * This file is part of linphone-iphone + * 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 @@ -17,10 +17,11 @@ * along with this program. If not, see . */ -#import +import Foundation -@interface CallPausedTableView : UITableViewController - -- (void)update; - -@end +extension UIImageView { + func tint(_ color:UIColor) { + self.image = self.image?.withRenderingMode(.alwaysTemplate) + tintColor = color + } +} diff --git a/Classes/LinphoneUI/UIVideoButton.h b/Classes/SwiftUtil/Extensions/IOS/UIVIewControllerExtensions.swift similarity index 72% rename from Classes/LinphoneUI/UIVideoButton.h rename to Classes/SwiftUtil/Extensions/IOS/UIVIewControllerExtensions.swift index 79def1378..8c27c7dec 100644 --- a/Classes/LinphoneUI/UIVideoButton.h +++ b/Classes/SwiftUtil/Extensions/IOS/UIVIewControllerExtensions.swift @@ -1,7 +1,7 @@ /* * Copyright (c) 2010-2020 Belledonne Communications SARL. * - * This file is part of linphone-iphone + * 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 @@ -17,13 +17,13 @@ * along with this program. If not, see . */ -#import +import Foundation +import SnapKit +import UIKit -#import "UIToggleButton.h" - -@interface UIVideoButton : UIToggleButton { +extension UIViewController { + func VIEW( _ desc: UICompositeViewDescription) -> T{ + return PhoneMainView.instance().mainViewController.getCachedController(desc.name) as! T + } + } - -@property(nonatomic, strong) IBOutlet UIActivityIndicatorView *waitView; - -@end diff --git a/Classes/SwiftUtil/Extensions/IOS/UIVIewExtensions.swift b/Classes/SwiftUtil/Extensions/IOS/UIVIewExtensions.swift new file mode 100644 index 000000000..7e99f532f --- /dev/null +++ b/Classes/SwiftUtil/Extensions/IOS/UIVIewExtensions.swift @@ -0,0 +1,326 @@ +/* + * 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 . + */ + +import Foundation +import SnapKit +import UIKit + +extension UIView { + + // Few constraints wrapper to abstract SnapKit functions + + func removeConstraints() -> UIView { + snp.removeConstraints() + return self + } + + func square(_ size:Int) -> UIView { + snp.makeConstraints { (make) in + make.width.equalTo(size) + make.height.equalTo(size) + } + return self + } + + func size(w:CGFloat,h:CGFloat) -> UIView { + snp.makeConstraints { (make) in + make.width.equalTo(w) + make.height.equalTo(h) + } + return self + } + + func height(_ h:CGFloat) -> UIView { + snp.makeConstraints { (make) in + make.height.equalTo(h) + } + return self + } + + func height(_ h:Int) -> UIView { + return height(CGFloat(h)) + } + + func width(_ h:CGFloat) -> UIView { + snp.makeConstraints { (make) in + make.width.equalTo(h) + } + return self + } + + func width(_ h:Int) -> UIView { + return width(CGFloat(h)) + } + + func maxHeight(_ h:CGFloat) -> UIView { + snp.makeConstraints { (make) in + make.height.lessThanOrEqualTo(h) + } + return self + } + + func minWidth(_ h:CGFloat) -> UIView { + snp.makeConstraints { (make) in + make.width.greaterThanOrEqualTo(h) + } + return self + } + + + func matchParentSideBorders(insetedByDx:CGFloat = 0) -> UIView { + snp.makeConstraints { (make) in + make.left.equalToSuperview().offset(insetedByDx) + make.right.equalToSuperview().offset(-insetedByDx) + } + return self + } + + func matchParentDimmensions() -> UIView { + snp.makeConstraints { (make) in + make.left.right.top.bottom.equalToSuperview() + } + return self + } + + func matchDimentionsOf(view:UIView) -> UIView { + snp.makeConstraints { (make) in + make.left.right.top.bottom.equalTo(view) + } + return self + } + + func matchParentHeight() -> UIView { + snp.makeConstraints { (make) in + make.top.bottom.equalToSuperview() + } + return self + } + + func matchParentHeightDividedBy(_ divider : CGFloat) -> UIView { + snp.makeConstraints { (make) in + make.height.equalToSuperview().dividedBy(divider) + } + return self + } + + func matchParentWidthDividedBy(_ divider : CGFloat) -> UIView { + snp.makeConstraints { (make) in + make.width.equalToSuperview().dividedBy(divider) + } + return self + } + + func center() -> UIView { + snp.makeConstraints { (make) in + make.center.equalToSuperview() + } + return self + } + + func alignParentTop(withMargin:CGFloat = 0.0) -> UIView { + snp.makeConstraints { (make) in + make.top.equalToSuperview().offset(withMargin) + } + return self + } + + func alignParentTop(withMargin:Int ) -> UIView { + return alignParentTop(withMargin:CGFloat(withMargin)) + } + + + func alignUnder(view:UIView, withMargin:CGFloat = 0.0) -> UIView { + snp.makeConstraints { (make) in + make.top.equalTo(view.snp.bottom).offset(withMargin) + } + return self + } + func alignUnder(view:UIView, withMargin:Int) -> UIView { + return alignUnder(view: view,withMargin:CGFloat(withMargin)) + } + + func matchRightOf(view:UIView, withMargin:CGFloat = 0) -> UIView { + snp.makeConstraints { (make) in + make.right.equalTo(view).offset(withMargin) + } + return self + } + + func updateAlignUnder(view:UIView, withMargin:CGFloat = 0.0) -> UIView { + snp.updateConstraints { (make) in + make.top.equalTo(view.snp.bottom).offset(withMargin) + } + return self + } + + func alignParentBottom(withMargin:CGFloat = 0.0) -> UIView { + snp.makeConstraints { (make) in + make.bottom.equalToSuperview().offset(-withMargin) + } + return self + } + + func alignParentBottom(withMargin:Int) -> UIView { + return alignParentBottom(withMargin:CGFloat(withMargin)) + } + + func alignAbove(view:UIView, withMargin:CGFloat = 0.0) -> UIView { + snp.makeConstraints { (make) in + make.bottom.equalTo(view.snp.top).offset(-withMargin) + } + return self + } + + func alignAbove(view:UIView, withMargin:Int) -> UIView { + return alignAbove(view: view,withMargin:CGFloat(withMargin)) + } + + func alignBottomWith(otherView:UIView) -> UIView { + snp.makeConstraints { (make) in + make.bottom.equalTo(otherView) + } + return self + } + + func marginLeft(_ m:CGFloat) -> UIView { + snp.makeConstraints { (make) in + make.left.equalToSuperview().offset(m) + } + return self + } + + func alignParentLeft(withMargin:CGFloat = 0.0) -> UIView { + snp.makeConstraints { (make) in + make.left.equalToSuperview().offset(withMargin) + } + return self + } + + func alignParentLeft(withMargin:Int) -> UIView { + return alignParentLeft(withMargin:CGFloat(withMargin)) + } + + func alignParentRight(withMargin:Int = 0) -> UIView { + snp.makeConstraints { (make) in + make.right.equalToSuperview().offset(-withMargin) + } + return self + } + + func alignParentRight(withMargin:CGFloat) -> UIView { + return alignParentRight(withMargin:Int(withMargin)) + } + + + func toRightOf(_ view:UIView, withLeftMargin:Int = 0) -> UIView { + snp.makeConstraints { (make) in + make.left.equalTo(view.snp.right).offset(withLeftMargin) + } + return self + } + + func toRightOf(_ view:UIView, withLeftMargin:CGFloat) -> UIView { + return toRightOf(view,withLeftMargin: Int(withLeftMargin)) + } + + func toLeftOf(_ view:UIView) -> UIView { + snp.makeConstraints { (make) in + make.right.equalTo(view.snp.left) + } + return self + } + + + func centerX(withDx:Int = 0) -> UIView { + snp.makeConstraints { (make) in + make.centerX.equalToSuperview().offset(withDx) + } + return self + } + + func centerY(withDy:Int = 0) -> UIView { + snp.makeConstraints { (make) in + make.centerY.equalToSuperview().offset(withDy) + } + return self + } + + func matchCenterXOf(view:UIView, withDx:Int = 0) -> UIView { + snp.makeConstraints { (make) in + make.centerX.equalTo(view).offset(withDx) + } + return self + } + + func matchCenterYOf(view:UIView, withDy:Int = 0) -> UIView { + snp.makeConstraints { (make) in + make.centerY.equalTo(view).offset(withDy) + } + return self + } + + func wrapContentY() -> UIView { + subviews.first?.snp.makeConstraints({ make in + make.top.equalToSuperview() + }) + subviews.last?.snp.makeConstraints({ make in + make.bottom.equalToSuperview() + }) + return self + } + + func wrapContentX() -> UIView { + subviews.first?.snp.makeConstraints({ make in + make.left.equalToSuperview() + }) + subviews.last?.snp.makeConstraints({ make in + make.right.equalToSuperview() + }) + return self + } + + func done() { + // to avoid the unused variable warning + } + + // Onclick + + class TapGestureRecognizer: UITapGestureRecognizer { + var action : (()->Void)? = nil + } + + func onClick(action : @escaping ()->Void ){ + let tap = TapGestureRecognizer(target: self , action: #selector(self.handleTap(_:))) + tap.action = action + tap.numberOfTapsRequired = 1 + tap.cancelsTouchesInView = false + + self.addGestureRecognizer(tap) + self.isUserInteractionEnabled = true + + } + @objc func handleTap(_ sender: TapGestureRecognizer) { + sender.action!() + } + + func VIEW( _ desc: UICompositeViewDescription) -> T{ + return PhoneMainView.instance().mainViewController.getCachedController(desc.name) as! T + } + +} diff --git a/Classes/SwiftUtil/Extensions/LinphoneCore/AddressExtensions.swift b/Classes/SwiftUtil/Extensions/LinphoneCore/AddressExtensions.swift new file mode 100644 index 000000000..39bd741f8 --- /dev/null +++ b/Classes/SwiftUtil/Extensions/LinphoneCore/AddressExtensions.swift @@ -0,0 +1,55 @@ +/* + * 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 . + */ + +import Foundation +import linphonesw + +extension Address { + + func initials() -> String? { + var initials = initials(displayName: addressBookEnhancedDisplayName()) + if (initials == nil || initials!.isEmpty) { + initials = String(username.prefix(1)) + } + return initials + } + + private func initials(displayName: String?) -> String? { // Basic ImproveMe + return displayName?.components(separatedBy: " ") + .reduce("") { + ($0.isEmpty ? "" : "\($0.first?.uppercased() ?? "")") + + ($1.isEmpty ? "" : "\($1.first?.uppercased() ?? "")") + } + } + + func addressBookEnhancedDisplayName() -> String? { + if let contact = FastAddressBook.getContactWith(getCobject) { + return contact.displayName + } else if (!displayName.isEmpty) { + return displayName + } else { + return username + } + } + + func contact() -> Contact? { + return FastAddressBook.getContactWith(getCobject) + } + +} diff --git a/Classes/SwiftUtil/Extensions/LinphoneCore/CallExtensions.swift b/Classes/SwiftUtil/Extensions/LinphoneCore/CallExtensions.swift new file mode 100644 index 000000000..197f48fbc --- /dev/null +++ b/Classes/SwiftUtil/Extensions/LinphoneCore/CallExtensions.swift @@ -0,0 +1,48 @@ +/* + * 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 . + */ + +import Foundation +import linphonesw + +extension Call { + func answerVideoUpdateRequest(accept:Bool) { + guard let params = try?core? .createCallParams(call: self) else { + Log.i("[Call] \(self) unable to answerVideoUpdateRequest : could not create params ") + return + } + if (accept) { + params.videoEnabled = true + core?.videoCaptureEnabled = true + core?.videoDisplayEnabled = true + } else { + params.videoEnabled = false + } + try?acceptUpdate(params: params) + } +} + +extension Call : CustomStringConvertible { + public var description: String { + if let callId = callLog?.callId { + return "" + } + return "" + } +} + diff --git a/Classes/SwiftUtil/Extensions/LinphoneCore/ConferenceExtensions.swift b/Classes/SwiftUtil/Extensions/LinphoneCore/ConferenceExtensions.swift new file mode 100644 index 000000000..483f3bc16 --- /dev/null +++ b/Classes/SwiftUtil/Extensions/LinphoneCore/ConferenceExtensions.swift @@ -0,0 +1,33 @@ +/* + * 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 . + */ + +import Foundation +import linphonesw + + + +extension Conference : CustomStringConvertible { + public var description: String { + if let username = conferenceAddress?.username { + return "<\(username)>" + } + return "" + } +} + diff --git a/Classes/SwiftUtil/Extensions/LinphoneCore/CoreExtensions.swift b/Classes/SwiftUtil/Extensions/LinphoneCore/CoreExtensions.swift new file mode 100644 index 000000000..ee0c99edb --- /dev/null +++ b/Classes/SwiftUtil/Extensions/LinphoneCore/CoreExtensions.swift @@ -0,0 +1,48 @@ +/* + * 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 . + */ + +import Foundation +import linphonesw + +extension Core { + static func get() -> Core { + return CallManager.instance().core! + } + + func showSwitchCameraButton() -> Bool { + return videoDevicesList.count > 2 // Count StaticImage camera + } + + func toggleCamera() { + Log.i("[Core] Current camera device is \(videoDevice)") + + videoDevicesList.forEach { + if ($0 != videoDevice && $0 != "StaticImage: Static picture") { + Log.i("[Core] New camera device will be \($0)") + try?setVideodevice(newValue: $0) + return + } + } + + let inConference = conference != nil && conference!.isIn + if !inConference, let call = currentCall { + try?call.update(params: nil) + } + } +} diff --git a/Classes/SwiftUtil/Extensions/LinphoneCore/IceState.swift b/Classes/SwiftUtil/Extensions/LinphoneCore/IceState.swift new file mode 100644 index 000000000..7274e2f7e --- /dev/null +++ b/Classes/SwiftUtil/Extensions/LinphoneCore/IceState.swift @@ -0,0 +1,35 @@ +/* + * 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 . + */ + +import Foundation +import linphonesw + +extension IceState { + func toString()->String { + switch (self) { + case .NotActivated: return NSLocalizedString("Not activated", tableName:"ICE has not been activated for this call",comment : "") + case .Failed: return NSLocalizedString("Failed", tableName:"ICE processing has failed",comment :"") + case .InProgress: return NSLocalizedString("In progress", tableName:"ICE process is in progress",comment :"") + case .HostConnection: return NSLocalizedString("Direct connection", tableName:"ICE has established a direct connection to the remote host",comment :"") + case .ReflexiveConnection: return NSLocalizedString( "NAT(s) connection", tableName:"ICE has established a connection to the remote host through one or several NATs",comment :"") + case .RelayConnection: return NSLocalizedString("Relay connection", tableName:"ICE has established a connection through a relay",comment :"") + } + + } +} diff --git a/Classes/SwiftUtil/Extensions/LinphoneCore/ParticipantExtensions.swift b/Classes/SwiftUtil/Extensions/LinphoneCore/ParticipantExtensions.swift new file mode 100644 index 000000000..9b7b07652 --- /dev/null +++ b/Classes/SwiftUtil/Extensions/LinphoneCore/ParticipantExtensions.swift @@ -0,0 +1,33 @@ +/* + * 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 . + */ + +import Foundation +import linphonesw + + + +extension Participant : CustomStringConvertible { + public var description: String { + if let address = address?.asStringUriOnly() { + return "" + } + return "" + } +} + diff --git a/Classes/LinphoneUI/UISpeakerButton.h b/Classes/SwiftUtil/Extensions/LinphoneCore/PayloadType.swift similarity index 85% rename from Classes/LinphoneUI/UISpeakerButton.h rename to Classes/SwiftUtil/Extensions/LinphoneCore/PayloadType.swift index 34361634e..197a8d733 100644 --- a/Classes/LinphoneUI/UISpeakerButton.h +++ b/Classes/SwiftUtil/Extensions/LinphoneCore/PayloadType.swift @@ -17,12 +17,11 @@ * along with this program. If not, see . */ -#import - -#import "UIToggleButton.h" - -@interface UISpeakerButton : UIToggleButton { +import Foundation +import linphonesw +import linphone +extension linphonesw.PayloadType { + + } - -@end diff --git a/Classes/SwiftUtil/ViewModel/MutableLiveData.swift b/Classes/SwiftUtil/ViewModel/MutableLiveData.swift new file mode 100644 index 000000000..125939785 --- /dev/null +++ b/Classes/SwiftUtil/ViewModel/MutableLiveData.swift @@ -0,0 +1,138 @@ +/* +* Copyright (c) 2010-2020 Belledonne Communications SARL. +* +* This file is part of linhome +* +* 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 . +*/ + + +import Foundation + + +class MutableLiveDataOnChangeClosure: NSObject { + let value: (Type?) -> Void + let onlyOnce: Bool + init(_ function: @escaping (Type?) -> Void, onlyOnce:Bool = false) { + value = function + self.onlyOnce = onlyOnce + } +} + +class MutableLiveData { + + private var _value : T? = nil + private var observers = [MutableLiveDataOnChangeClosure] () + private var _opposite : MutableLiveData? = nil + + init(_ initial:T) { + self.value = initial + } + + init() { + } + + var value : T? { + get { + return self._value + } + set { + self._value = newValue + self.notifyAllObservers(with: newValue) + } + } + + + func addObserver(observer: MutableLiveDataOnChangeClosure, andNotifyOnce: Bool = false) { + observers.append(observer) + if (andNotifyOnce) { + notifyValue() + } + } + + func removeObserver(observer: MutableLiveDataOnChangeClosure) { + observers = observers.filter({$0 !== observer}) + } + + + func clearObservers() { + observers.forEach { + removeObserver(observer: $0) + } + } + + + func notifyAllObservers(with newValue: T?) { + for observer in observers { + observer.value(newValue) + if (observer.onlyOnce) { + removeObserver(observer: observer) + } + } + } + + func notifyValue() { + for observer in observers { + observer.value(value) + if (observer.onlyOnce) { + removeObserver(observer: observer) + } + } + } + + func observe(onChange : @escaping (T?)->Void) { + let observer = MutableLiveDataOnChangeClosure({ value in + onChange(value) + }, onlyOnce: false) + addObserver(observer: observer) + } + + func readCurrentAndObserve(onChange : @escaping (T?)->Void) { + let observer = MutableLiveDataOnChangeClosure({ value in + onChange(value) + }, onlyOnce: false) + addObserver(observer: observer) + observer.value(value) + } + + func observeAsUniqueObserver (onChange : @escaping (T?)->Void, unique: Bool = false) { + let observer = MutableLiveDataOnChangeClosure({ value in + onChange(value) + }, onlyOnce: false) + if (unique) { + clearObservers() + } + addObserver(observer: observer) + } + + func observeOnce(onChange : @escaping (T?)->Void) { + let observer = MutableLiveDataOnChangeClosure({ value in + onChange(value) + }, onlyOnce: true) + addObserver(observer: observer) + } + + func opposite() -> MutableLiveData? { + if (_opposite != nil) { + return _opposite + } + _opposite = MutableLiveData(!(value! as! Bool)) + observe { (value) in + self._opposite!.value = !(value! as! Bool) + } + return _opposite + } + + +} diff --git a/Classes/Utils/AudioHelper.h b/Classes/Utils/AudioHelper.h deleted file mode 100644 index fdf2845b1..000000000 --- a/Classes/Utils/AudioHelper.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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 . - */ - -#ifndef AudioHelper_h -#define AudioHelper_h - -#import - -@import AVFoundation; - -@interface AudioHelper : NSObject - -+ (NSArray *)bluetoothRoutes; -+ (AVAudioSessionPortDescription *)bluetoothAudioDevice; -+ (AVAudioSessionPortDescription *)builtinAudioDevice; -+ (AVAudioSessionPortDescription *)speakerAudioDevice; -+ (AVAudioSessionPortDescription *)audioDeviceFromTypes:(NSArray *)types; -@end - -#endif /* AudioHelper_h */ diff --git a/Classes/Utils/FileTransferDelegate.m b/Classes/Utils/FileTransferDelegate.m index 0738c54b4..413c6c0b5 100644 --- a/Classes/Utils/FileTransferDelegate.m +++ b/Classes/Utils/FileTransferDelegate.m @@ -21,6 +21,8 @@ #import "LinphoneManager.h" #import "PhoneMainView.h" #import "Utils.h" +#import "linphoneapp-Swift.h" + @interface FileTransferDelegate () @property(strong) NSMutableData *data; diff --git a/Classes/Utils/Log.m b/Classes/Utils/Log.m index c7fe474c8..9c0b29c96 100644 --- a/Classes/Utils/Log.m +++ b/Classes/Utils/Log.m @@ -67,6 +67,24 @@ bctbx_log(BCTBX_LOG_DOMAIN, level, "%s", [text cStringUsingEncoding:NSUTF8StringEncoding]); } + ++(void)d:(NSString *)text { + [Log directLog:BCTBX_LOG_DEBUG text:text]; +} ++(void)i:(NSString *)text { + [Log directLog:BCTBX_LOG_MESSAGE text:text]; +} ++(void)w:(NSString *)text { + [Log directLog:BCTBX_LOG_WARNING text:text]; +} ++(void)e:(NSString *)text { + [Log directLog:BCTBX_LOG_ERROR text:text]; +} ++(void)f:(NSString *)text { + [Log directLog:BCTBX_LOG_FATAL text:text]; +} + + #pragma mark - Logs Functions callbacks void linphone_iphone_log_handler(const char *domain, OrtpLogLevel lev, const char *fmt, va_list args) { diff --git a/Classes/Voip/AudioRouteUtils.swift b/Classes/Voip/AudioRouteUtils.swift new file mode 100644 index 000000000..94a26f71e --- /dev/null +++ b/Classes/Voip/AudioRouteUtils.swift @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2010-2021 Belledonne Communications SARL. + * + * This file is part of linphone-android + * (see https://www.linphone.org). + * + * 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 . + */ + +import Foundation +import AVFoundation +import linphonesw + +@objc class AudioRouteUtils : NSObject { + + static let core = Core.get() + + static private func applyAudioRouteChange( call: Call?, types: [AudioDeviceType], output: Bool = true) { + let typesNames = types.map { String(describing: $0) }.joined(separator: "/") + + let currentCall = core.callsNb > 0 ? (call != nil) ? call : core.currentCall != nil ? core.currentCall : core.calls[0] : nil + if (currentCall == nil) { + Log.w("[Audio Route Helper] No call found, setting audio route on Core") + } + let conference = core.conference + let capability = output ? AudioDeviceCapabilities.CapabilityPlay : AudioDeviceCapabilities.CapabilityRecord + + var found = false + + core.audioDevices.forEach { (audioDevice) in + Log.i("[Audio Route Helper] cdes [\(audioDevice.deviceName)] [\(audioDevice.type)] [\(audioDevice.capabilities)] ") + } + + core.audioDevices.forEach { (audioDevice) in + if (!found && types.contains(audioDevice.type) && audioDevice.hasCapability(capability: capability)) { + if (conference != nil && conference?.isIn == true) { + Log.i("[Audio Route Helper] Found [\(audioDevice.type)] \(output ? "playback" : "recorder") audio device [\(audioDevice.deviceName)], routing conference audio to it") + if (output) { + conference?.outputAudioDevice = audioDevice + } else { + conference?.inputAudioDevice = audioDevice + } + } else if (currentCall != nil) { + Log.i("[Audio Route Helper] Found [\(audioDevice.type)] \(output ? "playback" : "recorder") audio device [\(audioDevice.deviceName)], routing call audio to it") + if (output) { + currentCall?.outputAudioDevice = audioDevice + } + else { + currentCall?.inputAudioDevice = audioDevice + } + } else { + Log.i("[Audio Route Helper] Found [\(audioDevice.type)] \(output ? "playback" : "recorder") audio device [\(audioDevice.deviceName)], changing core default audio device") + if (output) { + core.outputAudioDevice = audioDevice + } else { + core.inputAudioDevice = audioDevice + } + } + found = true + } + } + if (!found) { + Log.e("[Audio Route Helper] Couldn't find \(typesNames) audio device") + } + } + + static private func changeCaptureDeviceToMatchAudioRoute(call: Call?, types: [AudioDeviceType]) { + switch (types.first) { + case .Bluetooth :if (isBluetoothAudioRecorderAvailable()) { + Log.i("[Audio Route Helper] Bluetooth device is able to record audio, also change input audio device") + applyAudioRouteChange(call: call, types: [AudioDeviceType.Bluetooth], output: false) + } + case .Headset, .Headphones : if (isHeadsetAudioRecorderAvailable()) { + Log.i("[Audio Route Helper] Headphones/headset device is able to record audio, also change input audio device") + applyAudioRouteChange(call:call,types: [AudioDeviceType.Headphones, AudioDeviceType.Headset], output:false) + } + default: break + } + } + + static private func routeAudioTo( call: Call?, types: [AudioDeviceType]) { + let currentCall = call != nil ? call : core.currentCall != nil ? core.currentCall : core.calls[0] + if (call != nil || currentCall != nil) { + let callToUse = call != nil ? call : currentCall + applyAudioRouteChange(call: callToUse, types: types) + changeCaptureDeviceToMatchAudioRoute(call: callToUse, types: types) + } else { + applyAudioRouteChange(call: call, types: types) + changeCaptureDeviceToMatchAudioRoute(call: call, types: types) + } + } + + static func routeAudioToEarpiece(call: Call? = nil) { + routeAudioTo(call: call, types: [AudioDeviceType.Microphone]) // on iOS Earpiece = Microphone + } + + static func routeAudioToSpeaker(call: Call? = nil) { + routeAudioTo(call: call, types: [AudioDeviceType.Speaker]) + } + + @objc static func routeAudioToSpeaker() { + routeAudioTo(call: nil, types: [AudioDeviceType.Speaker]) + } + + static func routeAudioToBluetooth(call: Call? = nil) { + routeAudioTo(call: call, types: [AudioDeviceType.Bluetooth]) + } + + static func routeAudioToHeadset(call: Call? = nil) { + routeAudioTo(call: call, types: [AudioDeviceType.Headphones, AudioDeviceType.Headset]) + } + + static func isSpeakerAudioRouteCurrentlyUsed(call: Call? = nil) -> Bool { + + let currentCall = core.callsNb > 0 ? (call != nil) ? call : core.currentCall != nil ? core.currentCall : core.calls[0] : nil + if (currentCall == nil) { + Log.w("[Audio Route Helper] No call found, setting audio route on Core") + } + + let conference = core.conference + let audioDevice = conference != nil && conference?.isIn == true ? conference!.outputAudioDevice : currentCall != nil ? currentCall!.outputAudioDevice : core.outputAudioDevice + Log.i("[Audio Route Helper] Playback audio currently in use is [\(audioDevice?.deviceName ?? "n/a")] with type (\(audioDevice?.type ?? .Unknown)") + return audioDevice?.type == AudioDeviceType.Speaker + } + + static func isBluetoothAudioRouteCurrentlyUsed(call: Call? = nil) -> Bool { + if (core.callsNb == 0) { + Log.w("[Audio Route Helper] No call found, so bluetooth audio route isn't used") + return false + } + let currentCall = call != nil ? call : core.currentCall != nil ? core.currentCall : core.calls[0] + let conference = core.conference + + let audioDevice = conference != nil && conference?.isIn == true ? conference!.outputAudioDevice : currentCall?.outputAudioDevice + Log.i("[Audio Route Helper] Playback audio device currently in use is [\(audioDevice?.deviceName ?? "n/a")] with type (\(audioDevice?.type ?? .Unknown)") + return audioDevice?.type == AudioDeviceType.Bluetooth + } + + static func isBluetoothAudioRouteAvailable() -> Bool { + if let device = core.audioDevices.first(where: { $0.type == AudioDeviceType.Bluetooth && $0.hasCapability(capability: .CapabilityPlay) }) { + Log.i("[Audio Route Helper] Found bluetooth audio device [\(device.deviceName)]") + return true + } + return false + } + + static private func isBluetoothAudioRecorderAvailable() -> Bool { + if let device = core.audioDevices.first(where: { $0.type == AudioDeviceType.Bluetooth && $0.hasCapability(capability: .CapabilityRecord) }) { + Log.i("[Audio Route Helper] Found bluetooth audio recorder [\(device.deviceName)]") + return true + } + return false + } + + static func isHeadsetAudioRouteAvailable() -> Bool { + if let device = core.audioDevices.first(where: { ($0.type == AudioDeviceType.Headset||$0.type == AudioDeviceType.Headphones) && $0.hasCapability(capability: .CapabilityPlay) }) { + Log.i("[Audio Route Helper] Found headset/headphones audio device [\(device.deviceName)]") + return true + } + return false + } + + static private func isHeadsetAudioRecorderAvailable() -> Bool { + if let device = core.audioDevices.first(where: { ($0.type == AudioDeviceType.Headset||$0.type == AudioDeviceType.Headphones) && $0.hasCapability(capability: .CapabilityRecord) }) { + Log.i("[Audio Route Helper] Found headset/headphones audio recorder [\(device.deviceName)]") + return true + } + return false + } + + + + static func isReceiverEnabled() -> Bool { + if let outputDevice = core.outputAudioDevice { + return outputDevice.type == AudioDeviceType.Microphone + } + return false + } + +} diff --git a/Classes/Voip/Models/CallData.swift b/Classes/Voip/Models/CallData.swift new file mode 100644 index 000000000..49f5fa2a5 --- /dev/null +++ b/Classes/Voip/Models/CallData.swift @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linhome + * + * 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 . + */ + +import linphonesw +import Foundation + +class CallData { + + var call : Call + let address :String? + + let isPaused = MutableLiveData() + let isRemotelyPaused = MutableLiveData() + let canBePaused = MutableLiveData() + let isRecording = MutableLiveData() + let isRemotelyRecorded = MutableLiveData() + let isInRemoteConference = MutableLiveData() + let remoteConferenceSubject = MutableLiveData() + let isOutgoing = MutableLiveData() + let isIncoming = MutableLiveData() + let callState = MutableLiveData() + let iFrameReceived = MutableLiveData(false) + let outgoingEarlyMedia = MutableLiveData() + let enteredDTMF = MutableLiveData(" ") + + var chatRoom: ChatRoom? = nil + + private var callDelegate : CallDelegateStub? + + init (call:Call) { + self.call = call + address = call.remoteAddress?.asStringUriOnly() + callDelegate = CallDelegateStub( + onStateChanged : { (call: linphonesw.Call, state: linphonesw.Call.State, message: String) -> Void in + self.update() + }, + onNextVideoFrameDecoded : { (call: linphonesw.Call) -> Void in + self.iFrameReceived.value = true + }, + onRemoteRecording: { (call: linphonesw.Call, recording:Bool) -> Void in + self.isRemotelyRecorded.value = true + } + ) + call.addDelegate(delegate: callDelegate!) + update() + initChatRoom() + } + + + private func isCallPaused() -> Bool { + return [Call.State.Paused, Call.State.Pausing].contains(call.state) + } + + private func isCallRemotelyPaused() -> Bool { + return [Call.State.PausedByRemote].contains(call.state) + } + + private func isOutGoing() -> Bool { + return [Call.State.OutgoingInit, Call.State.OutgoingEarlyMedia, Call.State.OutgoingProgress, Call.State.OutgoingRinging].contains(call.state) + } + + private func isInComing() -> Bool { + return [Call.State.IncomingReceived, Call.State.IncomingEarlyMedia].contains(call.state) + } + + private func canCallBePaused() -> Bool { + return !call.mediaInProgress() && [Call.State.StreamsRunning, Call.State.PausedByRemote].contains(call.state) + } + + private func update() { + isPaused.value = isCallPaused() + isRemotelyPaused.value = isCallRemotelyPaused() + canBePaused.value = canCallBePaused() + let conference = call.conference + isInRemoteConference.value = conference != nil + if (conference != nil) { + remoteConferenceSubject.value = conference?.subject != nil && (conference?.subject.count)! > 0 ? conference!.subject : VoipTexts.conference_default_title + } + isOutgoing.value = isOutGoing() + isIncoming.value = isInComing() + if (call.mediaInProgress()) { + DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) { + self.update() + } + } + outgoingEarlyMedia.value = callState.value == .OutgoingEarlyMedia + isRecording.value = call.params?.isRecording + callState.value = call.state + } + + private func initChatRoom() { + let localSipUri = Core.get().defaultAccount?.params?.identityAddress?.asStringUriOnly() + let remoteSipUri = call.remoteAddress?.asStringUriOnly() + + guard + let localSipUri = Core.get().defaultAccount?.params?.identityAddress?.asStringUriOnly(), + let remoteSipUri = call.remoteAddress?.asStringUriOnly(), + let localAddress = try?Factory.Instance.createAddress(addr: localSipUri), + let remoteSipAddress = try?Factory.Instance.createAddress(addr: remoteSipUri) + else { + Log.e("[Call] Failed to get either local \(localSipUri.logable) or remote \(remoteSipUri.logable) SIP address!") + return + } + do { + chatRoom = Core.get().searchChatRoom(params: nil, localAddr: localAddress, remoteAddr: remoteSipAddress, participants: []) + if (chatRoom == nil) { + chatRoom = Core.get().searchChatRoom(params: nil, localAddr: localAddress, remoteAddr: nil, participants: [remoteSipAddress]) + } + if (chatRoom == nil) { + Log.w("[Call] Failed to find existing chat room for local address [$localSipUri] and remote address [$remoteSipUri]") + let chatRoomParams = try Core.get().createDefaultChatRoomParams() + // TODO: configure chat room params + chatRoom = try Core.get().createChatRoom(params: chatRoomParams, localAddr: localAddress, participants: [remoteSipAddress]) + } + + if (chatRoom == nil) { + Log.e("[Call] Failed to create a chat room for local address \(localSipUri) and remote address \(remoteSipUri)!") + } + } catch { + Log.e("[Call] Exception caught initiating a chat room for local address \(localSipUri) and remote address \(remoteSipUri) Error : \(error)!") + } + } + + func sendDTMF(dtmf:String) { + enteredDTMF.value = enteredDTMF.value! + dtmf + Core.get().playDtmf(dtmf: dtmf.utf8CString[0], durationMs: 1000) + try?call.sendDtmf(dtmf: dtmf.utf8CString[0]) + } + + func destroy() { + call.removeDelegate(delegate: callDelegate!) + isPaused.clearObservers() + isRemotelyPaused.clearObservers() + canBePaused.clearObservers() + isRecording.clearObservers() + isRemotelyRecorded.clearObservers() + isInRemoteConference.clearObservers() + remoteConferenceSubject.clearObservers() + isOutgoing.clearObservers() + isIncoming.clearObservers() + callState.clearObservers() + iFrameReceived.clearObservers() + outgoingEarlyMedia.clearObservers() + enteredDTMF.clearObservers() + } + + func toggleRecord() { + if (call.params?.isRecording == true) { + call.stopRecording() + } else { + call.startRecording() + } + isRecording.value = call.params?.isRecording + } + + func togglePause() { + if (isCallPaused()) { + try?call.resume() + } else { + try?call.pause() + } + isPaused.value = isCallPaused() + } + +} diff --git a/Classes/Voip/Models/CallStatisticsData.swift b/Classes/Voip/Models/CallStatisticsData.swift new file mode 100644 index 000000000..5d075d063 --- /dev/null +++ b/Classes/Voip/Models/CallStatisticsData.swift @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2010-2021 Belledonne Communications SARL. + * + * This file is part of linphone-android + * (see https://www.linphone.org). + * + * 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 . + */ + +import linphonesw + +class CallStatisticsData { + + var call : Call + var audioStats:[StatItemData] = [] + var videoStats:[StatItemData] = [] + let isVideoEnabled = MutableLiveData() + let statsUpdated = MutableLiveData(true) + + private var callDelegate : CallDelegateStub? + + init (call:Call) { + self.call = call + callDelegate = CallDelegateStub( + onStatsUpdated : { (call: Call, stats: CallStats) -> Void in + self.isVideoEnabled.value = call.currentParams?.videoEnabled + self.updateCallStats(stats: stats) + self.statsUpdated.value = true + } + + ) + call.addDelegate(delegate: callDelegate!) + initCallStats() + isVideoEnabled.value = call.currentParams?.videoEnabled + call.audioStats.map { updateCallStats(stats: $0) } + call.videoStats.map { updateCallStats(stats: $0) } + } + + private func initCallStats() { + + audioStats.append(StatItemData(type: StatType.CAPTURE)) + audioStats.append(StatItemData(type: StatType.PLAYBACK)) + audioStats.append(StatItemData(type: StatType.PAYLOAD)) + audioStats.append(StatItemData(type: StatType.ENCODER)) + audioStats.append(StatItemData(type: StatType.DECODER)) + audioStats.append(StatItemData(type: StatType.DOWNLOAD_BW)) + audioStats.append(StatItemData(type: StatType.UPLOAD_BW)) + audioStats.append(StatItemData(type: StatType.ICE)) + audioStats.append(StatItemData(type: StatType.IP_FAM)) + audioStats.append(StatItemData(type: StatType.SENDER_LOSS)) + audioStats.append(StatItemData(type: StatType.RECEIVER_LOSS)) + audioStats.append(StatItemData(type: StatType.JITTER)) + + videoStats.append(StatItemData(type: StatType.CAPTURE)) + videoStats.append(StatItemData(type: StatType.PLAYBACK)) + videoStats.append(StatItemData(type: StatType.PAYLOAD)) + videoStats.append(StatItemData(type: StatType.ENCODER)) + videoStats.append(StatItemData(type: StatType.DECODER)) + videoStats.append(StatItemData(type: StatType.DOWNLOAD_BW)) + videoStats.append(StatItemData(type: StatType.UPLOAD_BW)) + videoStats.append(StatItemData(type: StatType.ESTIMATED_AVAILABLE_DOWNLOAD_BW)) + videoStats.append(StatItemData(type: StatType.ICE)) + videoStats.append(StatItemData(type: StatType.IP_FAM)) + videoStats.append(StatItemData(type: StatType.SENDER_LOSS)) + videoStats.append(StatItemData(type: StatType.RECEIVER_LOSS)) + videoStats.append(StatItemData(type: StatType.SENT_RESOLUTION)) + videoStats.append(StatItemData(type: StatType.RECEIVED_RESOLUTION)) + videoStats.append(StatItemData(type: StatType.SENT_FPS)) + videoStats.append(StatItemData(type: StatType.RECEIVED_FPS)) + } + + private func updateCallStats(stats: CallStats) { + if (stats.type == StreamType.Audio) { + audioStats.forEach{ $0.update(call: call, stats: stats)} + } else if (stats.type == StreamType.Video) { + videoStats.forEach{ $0.update(call: call, stats: stats)} + } + } +} + + +enum StatType { + case CAPTURE + case PLAYBACK + case PAYLOAD + case ENCODER + case DECODER + case DOWNLOAD_BW + case UPLOAD_BW + case ICE + case IP_FAM + case SENDER_LOSS + case RECEIVER_LOSS + case JITTER + case SENT_RESOLUTION + case RECEIVED_RESOLUTION + case SENT_FPS + case RECEIVED_FPS + case ESTIMATED_AVAILABLE_DOWNLOAD_BW +} + +struct StatItemData { + var type:StatType + + let value = MutableLiveData() + + func update(call: Call, stats: CallStats) { + guard let payloadType = stats.type == StreamType.Audio ? call.currentParams?.usedAudioPayloadType : call.currentParams?.usedVideoPayloadType, let core = call.core else { + value.value = "n/a" + return + } + switch(type) { + case StatType.CAPTURE: value.value = stats.type == StreamType.Audio ? core.captureDevice : core.videoDevice + case StatType.PLAYBACK: value.value = stats.type == StreamType.Audio ? core.playbackDevice : core.videoDisplayFilter + case StatType.PAYLOAD: value.value = "\(payloadType.mimeType)/\(payloadType.clockRate / 1000) kHz" + case StatType.ENCODER: value.value = payloadType.description + case StatType.DECODER: value.value = payloadType.description + case StatType.DOWNLOAD_BW: value.value = "\(stats.downloadBandwidth) kbits/s" + case StatType.UPLOAD_BW: value.value = "\(stats.uploadBandwidth) kbits/s" + case StatType.ICE: value.value = stats.iceState.toString() + case StatType.IP_FAM: value.value = stats.ipFamilyOfRemote == AddressFamily.Inet6 ? "IPv6" : "IPv4" + case StatType.SENDER_LOSS: value.value = String(format: "%.2f%",stats.senderLossRate) + case StatType.RECEIVER_LOSS: value.value = String(format: "%.2f%",stats.receiverLossRate) + case StatType.JITTER: value.value = String(format: "%.2f ms",stats.jitterBufferSizeMs) + case StatType.SENT_RESOLUTION: value.value = call.currentParams?.sentVideoDefinition?.name + case StatType.RECEIVED_RESOLUTION: value.value = call.currentParams?.receivedVideoDefinition?.name + case StatType.SENT_FPS: value.value = "\(call.currentParams?.sentFramerate ?? 0.0)" + case StatType.RECEIVED_FPS: value.value = "\(call.currentParams?.receivedFramerate ?? 0.0)" + case StatType.ESTIMATED_AVAILABLE_DOWNLOAD_BW: value.value = "\(stats.estimatedDownloadBandwidth) kbit/s" + } + } + + + func getTypeTitle() -> String { + switch (type) { + case .CAPTURE: return VoipTexts.call_stats_capture_filter + case .PLAYBACK: return VoipTexts.call_stats_player_filter + case .PAYLOAD: return VoipTexts.call_stats_codec + case .ENCODER: return VoipTexts.call_stats_encoder_name + case .DECODER: return VoipTexts.call_stats_decoder_name + case .DOWNLOAD_BW: return VoipTexts.call_stats_download + case .UPLOAD_BW: return VoipTexts.call_stats_upload + case .ICE: return VoipTexts.call_stats_ice + case .IP_FAM: return VoipTexts.call_stats_ip + case .SENDER_LOSS: return VoipTexts.call_stats_sender_loss_rate + case .RECEIVER_LOSS: return VoipTexts.call_stats_receiver_loss_rate + case .JITTER: return VoipTexts.call_stats_jitter_buffer + case .SENT_RESOLUTION: return VoipTexts.call_stats_video_resolution_sent + case .RECEIVED_RESOLUTION: return VoipTexts.call_stats_video_resolution_received + case .SENT_FPS: return VoipTexts.call_stats_video_fps_sent + case .RECEIVED_FPS: return VoipTexts.call_stats_video_fps_received + case .ESTIMATED_AVAILABLE_DOWNLOAD_BW: return VoipTexts.call_stats_estimated_download + } + } + + + + + +} diff --git a/Classes/Voip/Models/CallsViewModel.swift b/Classes/Voip/Models/CallsViewModel.swift new file mode 100644 index 000000000..2caa75232 --- /dev/null +++ b/Classes/Voip/Models/CallsViewModel.swift @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linhome + * + * 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 . + */ + + +import Foundation +import linphonesw +import AVFoundation + + +class CallsViewModel { + + let currentCallData = MutableLiveData(nil) + let callsData = MutableLiveData<[CallData]>([]) + let inactiveCallsCount = MutableLiveData(0) + let currentCallUnreadChatMessageCount = MutableLiveData(0) + let chatAndCallsCount = MutableLiveData(0) + let callConnectedEvent = MutableLiveData() + let callUpdateEvent = MutableLiveData() + let noMoreCallEvent = MutableLiveData(false) + let core = Core.get() + + static let shared = CallsViewModel() + + private var coreDelegate : CoreDelegateStub? + + init () { + coreDelegate = CoreDelegateStub( + onCallStateChanged : { (core: Core, call: Call, state: Call.State, message:String) -> Void in + Log.i("[Calls] Call state changed: \(call) : \(state)") + let currentCall = core.currentCall + if (currentCall != nil && self.currentCallData.value??.call.getCobject != currentCall?.getCobject) { + self.updateCurrentCallData(currentCall: currentCall) + } else if (currentCall == nil && core.callsNb > 1) { + self.updateCurrentCallData(currentCall: currentCall) + } + if ([.End,.Released,.Error].contains(state)) { + self.removeCallFromList(call: call) + } else if ([.OutgoingInit].contains(state)) { + self.addCallToList(call:call) + } else if ([.IncomingReceived].contains(state)) { + self.addCallToList(call:call) + } else if (state == .UpdatedByRemote) { + let remoteVideo = call.remoteParams?.videoEnabled == true + let localVideo = call.currentParams?.videoEnabled == true + let autoAccept = call.core?.videoActivationPolicy?.automaticallyAccept == true + if (remoteVideo && !localVideo && !autoAccept) { + if (core.videoCaptureEnabled || core.videoDisplayEnabled) { + try?call.deferUpdate() + self.callUpdateEvent.value = call + } else { + call.answerVideoUpdateRequest(accept: false) + } + } + }else if (state == Call.State.Connected) { + self.callConnectedEvent.value = call + } else if (state == Call.State.StreamsRunning) { + self.callUpdateEvent.value = call + } + self.updateInactiveCallsCount() + self.callsData.notifyValue() + }, + + onMessageReceived : { (core: Core, room: ChatRoom, message: ChatMessage) -> Void in + self.updateUnreadChatCount() + }, + onChatRoomRead : { (core: Core, room: ChatRoom) -> Void in + self.updateUnreadChatCount() + }, + onLastCallEnded: { (core: Core) -> Void in + self.currentCallData.value??.destroy() + self.currentCallData.value = nil + self.noMoreCallEvent.value = true + } + ) + + Core.get().addDelegate(delegate: coreDelegate!) + + if let currentCall = core.currentCall { + currentCallData.value??.destroy() + currentCallData.value = CallData(call:currentCall) + } + + chatAndCallsCount.value = 0 + inactiveCallsCount.readCurrentAndObserve { (_) in + self.updateCallsAndChatCount() + } + currentCallUnreadChatMessageCount.readCurrentAndObserve { (_) in + self.updateCallsAndChatCount() + } + + initCallList() + updateInactiveCallsCount() + updateUnreadChatCount() + + } + + private func initCallList() { + core.calls.forEach { addCallToList(call: $0) } + } + + private func removeCallFromList(call: Call) { + Log.i("[Calls] Removing call \(call) from calls list") + if let removeCandidate = callsData.value?.filter{$0.call.getCobject == call.getCobject}.first { + removeCandidate.destroy() + } + + callsData.value = callsData.value?.filter(){$0.call.getCobject != call.getCobject} + callsData.notifyValue() + } + + private func addCallToList(call: Call) { + Log.i("[Calls] Adding call \(call) to calls list") + callsData.value?.append(CallData(call: call)) + callsData.notifyValue() + } + + private func updateUnreadChatCount() { + guard let unread = currentCallData.value??.chatRoom?.unreadMessagesCount else { + currentCallUnreadChatMessageCount.value = 0 + return + } + currentCallUnreadChatMessageCount.value = unread + } + + private func updateInactiveCallsCount() { + inactiveCallsCount.value = core.callsNb - 1 + } + + private func updateCallsAndChatCount() { + var value = 0 + if let calls = inactiveCallsCount.value { + value += calls + } + if let chats = currentCallUnreadChatMessageCount.value { + value += chats + } + chatAndCallsCount.value = value + } + + func mergeCallsIntoLocalConference() { + CallManager.instance().startLocalConference() + } + + private func updateCurrentCallData(currentCall: Call?) { + var callToUse = currentCall + if (currentCall == nil) { + Log.w("[Calls] Current call is now null") + + let firstCall = core.calls.first + if (firstCall != nil && currentCallData.value??.call.getCobject != firstCall?.getCobject) { + Log.i("[Calls] Using first call as \"current\" call") + callToUse = firstCall + } + } + + guard let callToUse = callToUse else { + Log.w("[Calls] No call found to be used as \"current\"") + return + } + + let firstToUse = callsData.value?.filter{$0.call.getCobject != callToUse.getCobject}.first + if (firstToUse != nil) { + currentCallData.value = firstToUse + } else { + Log.w("[Calls] Call not found in calls data list, shouldn't happen!") + currentCallData.value = CallData(call: callToUse) + } + + updateUnreadChatCount() + } + + +} diff --git a/Classes/Voip/Models/ConferenceParticipantData.swift b/Classes/Voip/Models/ConferenceParticipantData.swift new file mode 100644 index 000000000..2ccdf95d9 --- /dev/null +++ b/Classes/Voip/Models/ConferenceParticipantData.swift @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linhome + * + * 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 . + */ + +import linphonesw +import Foundation + +class ConferenceParticipantData { + + var conference:Conference + var participant:Participant + + let isAdmin = MutableLiveData() + let isMeAdmin = MutableLiveData() + + private var callDelegate : CallDelegateStub? + + init (conference:Conference, participant:Participant) { + self.conference = conference + self.participant = participant + isAdmin.value = participant.isAdmin + isMeAdmin.value = conference.me?.isAdmin + Log.i("[Conference Participant] Participant \(sipUri!) is admin=\(isAdmin.value!)") + + } + + var sipUri:String? { + get { + return self.participant.address?.asString() + } + } + + func destroy() { + isAdmin.clearObservers() + isMeAdmin.clearObservers() + } +} diff --git a/Classes/Voip/Models/ConferenceParticipantDeviceData.swift b/Classes/Voip/Models/ConferenceParticipantDeviceData.swift new file mode 100644 index 000000000..b10dd635f --- /dev/null +++ b/Classes/Voip/Models/ConferenceParticipantDeviceData.swift @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linhome + * + * 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 . + */ + +import linphonesw +import Foundation + +class ConferenceParticipantDeviceData { + + var participantDevice:ParticipantDevice + let isMe:Bool + + let videoEnabled = MutableLiveData() + let activeSpeaker = MutableLiveData() + let isInConference = MutableLiveData() + let core = Core.get() + + private var participantDeviceDelegate : ParticipantDeviceDelegate? + + init (participantDevice:ParticipantDevice, isMe:Bool) { + self.isMe = isMe + self.participantDevice = participantDevice + participantDeviceDelegate = ParticipantDeviceDelegateStub( + onIsSpeakingChanged: { (participantDevice, isSpeaking) in + Log.i("[Conference Participant Device] Participant \(participantDevice ) isspeaking = \(isSpeaking)") + self.activeSpeaker.value = isSpeaking + }, onConferenceJoined: { (participantDevice) in + Log.i("[Conference Participant Device] Participant \(participantDevice) has joined the conference") + self.isInConference.value = true + }, onConferenceLeft: { (participantDevice) in + Log.i("[Conference Participant Device] Participant \(participantDevice) has left the conference") + self.isInConference.value = false + }, onAudioDirectionChanged: { (participantDevice, direction) in + Log.i("[Conference Participant Device] Participant \(participantDevice) audio stream direction changed: \(direction)") + }, onVideoDirectionChanged: { (participantDevice, direction) in + Log.i("[Conference Participant Device] Participant \(participantDevice) video stream direction changed: \(direction)") + self.videoEnabled.value = direction == MediaDirection.SendOnly || direction == MediaDirection.SendRecv + }, onTextDirectionChanged: { (participantDevice, direction) in + Log.i("[Conference Participant Device] Participant \(participantDevice) text stream direction changed: \(direction)") + }) + + participantDevice.addDelegate(delegate: participantDeviceDelegate!) + activeSpeaker.value = false + + // TODO: What happens if we have disabled video locally? + videoEnabled.value = participantDevice.videoDirection == MediaDirection.SendOnly || participantDevice.videoDirection == MediaDirection.SendRecv + isInConference.value = participantDevice.isInConference + + } + + func destroy() { + participantDevice.removeDelegate(delegate: participantDeviceDelegate!) + } + + func switchCamera() { + Core.get().toggleCamera() + } + + func isSwitchCameraAvailable() -> Bool { + return isMe && Core.get().showSwitchCameraButton() + } + + func setVideoView(view:UIView) { + if (!isMe && participantDevice.videoDirection != MediaDirection.SendRecv) { + Log.e("[Conference Participant Device] Participant \(participantDevice) device video direction is \(participantDevice.videoDirection), don't set video window!") + return + } + Log.i("[Conference Participant Device] Setting textureView \(view) for participant \(participantDevice)") + if (isMe) { // TODO: remove + core.nativePreviewWindow = view + } else { + participantDevice.nativeVideoWindowId = UnsafeMutableRawPointer(Unmanaged.passRetained(view).toOpaque()) + } + } +} diff --git a/Classes/Voip/Models/ConferenceViewModel.swift b/Classes/Voip/Models/ConferenceViewModel.swift new file mode 100644 index 000000000..26206c306 --- /dev/null +++ b/Classes/Voip/Models/ConferenceViewModel.swift @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linhome + * + * 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 . + */ + + +import Foundation +import linphonesw +import AVFoundation + +class ConferenceViewModel { + + let core = Core.get() + static let shared = ConferenceViewModel() + + + let isConferencePaused = MutableLiveData() + let canResumeConference = MutableLiveData() + + let isMeConferenceFocus = MutableLiveData() + let isMeAdmin = MutableLiveData() + + let conferenceAddress = MutableLiveData
() + + let conferenceParticipants = MutableLiveData<[ConferenceParticipantData]>() + let conferenceParticipantDevices = MutableLiveData<[ConferenceParticipantDeviceData]>() + let conferenceDisplayMode = MutableLiveData() + + let isInConference = MutableLiveData() + + let isVideoConference = MutableLiveData() + + let isRecording = MutableLiveData() + let isRemotelyRecorded = MutableLiveData() + + let subject = MutableLiveData() + + let conference = MutableLiveData() + + let maxParticipantsForMosaicLayout = ConfigManager.instance().lpConfigIntForKey(key: "max_conf_part_mosaic_layout",defaultValue: 6) + + private var conferenceDelegate : ConferenceDelegateStub? + private var coreDelegate : CoreDelegateStub? + + init () { + conferenceDelegate = ConferenceDelegateStub(onParticipantAdded: { (conference: Conference, participant: Participant)in + if (conference.isMe(uri: participant.address!)) { + Log.i("[Conference] \(conference) Entered conference") + self.isConferencePaused.value = false + } else { + Log.i("[Conference] \(conference) Participant \(participant) added") + } + self.updateParticipantsList(conference) + self.updateParticipantsDevicesList(conference) + + let count = self.conferenceParticipantDevices.value!.count + if (count > self.maxParticipantsForMosaicLayout) { + Log.w("[Conference] \(conference) More than \(self.maxParticipantsForMosaicLayout) participants \(count), forcing active speaker layout") + self.conferenceDisplayMode.value = .ActiveSpeaker + } + }, onParticipantRemoved: {(conference: Conference, participant: Participant) in + if (conference.isMe(uri: participant.address!)) { + Log.i("[Conference] \(conference) \(participant) Left conference") + self.isConferencePaused.value = true + } else { + Log.i("[Conference] \(conference) \(participant) Participant removed") + } + self.updateParticipantsList(conference) + self.updateParticipantsDevicesList(conference) + }, onParticipantDeviceAdded: {(conference: Conference, participantDevice: ParticipantDevice) in + Log.i("[Conference] \(conference) Participant device \(participantDevice) added") + self.updateParticipantsDevicesList(conference) + }, onParticipantDeviceRemoved: { (conference: Conference, participantDevice: ParticipantDevice) in + Log.i("[Conference] \(conference) Participant device \(participantDevice) removed") + self.updateParticipantsDevicesList(conference) + }, onParticipantAdminStatusChanged: { (conference: Conference, participant: Participant) in + Log.i("[Conference] \(conference) Participant admin status changed") + self.isMeAdmin.value = conference.me?.isAdmin + self.updateParticipantsList(conference) + } + ) + + coreDelegate = CoreDelegateStub( + onConferenceStateChanged: { (core, conference, state) in + Log.i("[Conference] \(conference) Conference state changed: \(state)") + self.isConferencePaused.value = !conference.isIn + self.canResumeConference.value = true // TODO: How can this value be false? + self.isVideoConference.value = conference.currentParams?.isVideoEnabled == true + + if (state == Conference.State.Instantiated) { + self.conference.value = conference + self.isInConference.value = true + conference.addDelegate(delegate: self.conferenceDelegate!) + } else if (state == Conference.State.Created) { + self.updateParticipantsList(conference) + self.updateParticipantsDevicesList(conference) + self.isMeConferenceFocus.value = conference.me?.isFocus == true + self.isMeAdmin.value = conference.me?.isAdmin == true + self.conferenceAddress.value = conference.conferenceAddress + self.subject.value = conference.subject.isEmpty ? ( + conference.me?.isFocus == true ? ( + VoipTexts.conference_local_title + ) : ( + VoipTexts.conference_default_title + ) + ) : ( + conference.subject + ) + } else if (state == Conference.State.Terminated || state == Conference.State.TerminationFailed) { + self.isInConference.value = false + self.isVideoConference.value = false + conference.removeDelegate(delegate: self.conferenceDelegate!) + self.conferenceParticipants.value?.forEach{ $0.destroy()} + self.conferenceParticipantDevices.value?.forEach{ $0.destroy()} + self.conferenceParticipants.value = [] + self.conferenceParticipantDevices.value = [] + } + + let layout = conference.layout == .None ? .Grid : conference.layout + self.conferenceDisplayMode.value = layout + Log.i("[Conference] \(conference) Conference current layout is: \(layout)") + } + ) + + Core.get().addDelegate(delegate: coreDelegate!) + + + conferenceParticipants.value = [] + conferenceParticipantDevices.value = [] + conferenceDisplayMode.value = .Grid + + subject.value = VoipTexts.conference_default_title + + if let conference = core.conference != nil ? core.conference : core.currentCall?.conference { + Log.i("[Conference] Found an existing conference: \(conference)") + self.conference.value = conference + conference.addDelegate(delegate: self.conferenceDelegate!) + + + isInConference.value = true + isConferencePaused.value = !conference.isIn + isMeConferenceFocus.value = conference.me?.isFocus == true + isMeAdmin.value = conference.me?.isAdmin == true + isVideoConference.value = conference.currentParams?.isVideoEnabled == true + conferenceAddress.value = conference.conferenceAddress + if (!conference.subject.isEmpty) { + subject.value = conference.subject + } + + let layout = conference.layout == .None ? .Grid : conference.layout + conferenceDisplayMode.value = layout + Log.i("[Conference] \(conference) Conference current layout is: \(layout)") + + updateParticipantsList(conference) + updateParticipantsDevicesList(conference) + } + + + } + + + func destroy() { + core.removeDelegate(delegate: self.coreDelegate!) + self.conferenceParticipants.value?.forEach{ $0.destroy()} + self.conferenceParticipantDevices.value?.forEach{ $0.destroy()} + } + + + func pauseConference() { + let defaultProxyConfig = core.defaultProxyConfig + let localAddress = defaultProxyConfig?.identityAddress + let participants : [Address] = [] + let remoteConference = core.searchConference(params: nil, localAddr: localAddress, remoteAddr: conferenceAddress.value, participants: participants) + let localConference = core.searchConference(params: nil, localAddr: conferenceAddress.value, remoteAddr: conferenceAddress.value, participants: participants) + let conference = remoteConference != nil ? remoteConference : localConference + + if (conference != nil) { + Log.i("[Conference] Leaving conference with address \(conference) temporarily") + conference!.leave() + } else { + Log.w("[Conference] Unable to find conference with address \(conference)") + } + } + + func resumeConference() { + let defaultProxyConfig = core.defaultProxyConfig + let localAddress = defaultProxyConfig?.identityAddress + let participants : [Address] = [] + let remoteConference = core.searchConference(params: nil, localAddr: localAddress, remoteAddr: conferenceAddress.value, participants: participants) + let localConference = core.searchConference(params: nil, localAddr: conferenceAddress.value, remoteAddr: conferenceAddress.value, participants: participants) + + if let conference = remoteConference != nil ? remoteConference : localConference { + Log.i("[Conference] Entering again conference with address \(conference)") + conference.enter() + } else { + Log.w("[Conference] Unable to find conference with address \(conference)") + } + } + + func togglePlayPause () { + if (isConferencePaused.value == true) { + resumeConference() + isConferencePaused.value = false + } else { + pauseConference() + isConferencePaused.value = true + } + } + + func toggleRecording() { + guard let conference = core.conference else { + Log.e("[Conference] Failed to find conference!") + return + } + + if (conference.isRecording == true) { + conference.stopRecording() + } else { + let path = AppManager.recordingFilePathFromCall(address: conference.conferenceAddress?.asStringUriOnly() ?? "") + Log.i("[Conference] Starting recording \(conference) in file \(path)") + conference.startRecording(path: path) + } + isRecording.value = conference.isRecording + } + + private func updateParticipantsList(_ conference: Conference) { + self.conferenceParticipants.value?.forEach{ $0.destroy()} + var participants :[ConferenceParticipantData] = [] + + let participantsList = conference.participantList + Log.i("[Conference] \(conference) Conference has \(participantsList.count) participants") + + participantsList.forEach { (participant) in + let participantDevices = participant.devices + Log.i("[Conference] \(conference) Participant found: \(participant) with \(participantDevices.count) device(s)") + let participantData = ConferenceParticipantData(conference: conference, participant: participant) + participants.append(participantData) + } + + conferenceParticipants.value = participants + } + + private func updateParticipantsDevicesList(_ conference: Conference) { + self.conferenceParticipantDevices.value?.forEach{ $0.destroy()} + var devices :[ConferenceParticipantDeviceData] = [] + + let participantsList = conference.participantList + Log.i("[Conference] \(conference) Conference has \(participantsList.count) participants") + + participantsList.forEach { (participant) in + let participantDevices = participant.devices + Log.i("[Conference] \(conference) Participant found: \(participant) with \(participantDevices.count) device(s)") + + participantDevices.forEach { (device) in + Log.i("[Conference] \(conference) Participant device found: \(device.name) (\(device.address!.asStringUriOnly()))") + let deviceData = ConferenceParticipantDeviceData(participantDevice: device, isMe: false) + devices.append(deviceData) + } + + } + conference.me?.devices.forEach { (device) in + Log.i("[Conference] \(conference) Participant device for myself found: \(device.name) (\(device.address!.asStringUriOnly()))") + let deviceData = ConferenceParticipantDeviceData(participantDevice: device, isMe: true) + devices.append(deviceData) + } + + + conferenceParticipantDevices.value = devices + } + +} + +enum FlexDirection { + case ROW + case ROW_REVERSE + case COLUMN + case COLUMN_REVERSE +} diff --git a/Classes/Voip/Models/ControlsViewModel.swift b/Classes/Voip/Models/ControlsViewModel.swift new file mode 100644 index 000000000..51109541e --- /dev/null +++ b/Classes/Voip/Models/ControlsViewModel.swift @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linhome + * + * 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 . + */ + + +import Foundation +import linphonesw +import AVFoundation + + +class ControlsViewModel { + let core = Core.get() + + let isSpeakerSelected = MutableLiveData() + let isMicrophoneMuted = MutableLiveData() + let isMuteMicrophoneEnabled = MutableLiveData() + let isBluetoothHeadsetSelected = MutableLiveData() + let nonEarpieceOutputAudioDevice = MutableLiveData() + let audioRoutesSelected = MutableLiveData() + let audioRoutesEnabled = MutableLiveData() + + let isVideoUpdateInProgress = MutableLiveData() + let isVideoEnabled = MutableLiveData() + let isVideoAvailable = MutableLiveData() + + let fullScreenMode = MutableLiveData(false) + let numpadVisible = MutableLiveData(false) + let callStatsVisible = MutableLiveData(false) + let goToConferenceLayoutSettings = MutableLiveData(false) + let goToConferenceParticipantsListEvent = MutableLiveData(false) + let goToChatEvent = MutableLiveData(false) + let goToCallsListEvent = MutableLiveData(false) + let hideExtraButtons = MutableLiveData(true) + + let proximitySensorEnabled = MutableLiveData() + + + static let shared = ControlsViewModel() + private var coreDelegate : CoreDelegateStub? + private var previousCallState = Call.State.Idle + + + init () { + coreDelegate = CoreDelegateStub( + onCallStateChanged : { (core: Core, call: Call, state: Call.State, message:String) -> Void in + Log.i("[Call Controls] Call state changed: \(call) : \(state)") + if (state == Call.State.StreamsRunning) { + self.isVideoUpdateInProgress.value = false + } + self.updateUI() + self.setAudioRoutes(call,state) + self.previousCallState = state + }, + onAudioDeviceChanged : { (core: Core, audioDevice: AudioDevice) -> Void in + Log.i("[Call Controls] Audio device changed: \(audioDevice.deviceName)") + self.nonEarpieceOutputAudioDevice.value = audioDevice.type != AudioDeviceType.Microphone // on iOS Earpiece = Microphone + self.updateSpeakerState() + self.updateBluetoothHeadsetState() + } + ) + Core.get().addDelegate(delegate: coreDelegate!) + proximitySensorEnabled.value = shouldProximitySensorBeEnabled() + isVideoEnabled.readCurrentAndObserve { _ in + self.proximitySensorEnabled.value = self.shouldProximitySensorBeEnabled() + } + nonEarpieceOutputAudioDevice.readCurrentAndObserve { _ in + self.proximitySensorEnabled.value = self.shouldProximitySensorBeEnabled() + } + proximitySensorEnabled.readCurrentAndObserve { (enabled) in + UIDevice.current.isProximityMonitoringEnabled = enabled == true + } + updateUI() + } + + private func setAudioRoutes(_ call:Call,_ state:Call.State) { + if (state == .OutgoingProgress) { + if (core.callsNb == 1 && ConfigManager.instance().lpConfigBoolForKey(key: "route_audio_to_bluetooth_if_available",defaultValue:true)) { + AudioRouteUtils.routeAudioToBluetooth(call: call) + } + } + if (state == .StreamsRunning) { + if (core.callsNb == 1) { + // Only try to route bluetooth / headphone / headset when the call is in StreamsRunning for the first time + if (previousCallState == Call.State.Connected) { + Log.i("[Context] First call going into StreamsRunning state for the first time, trying to route audio to headset or bluetooth if available") + if (AudioRouteUtils.isHeadsetAudioRouteAvailable()) { + AudioRouteUtils.routeAudioToHeadset(call: call) + } else if (ConfigManager.instance().lpConfigBoolForKey(key: "route_audio_to_bluetooth_if_available",defaultValue:true) && + AudioRouteUtils.isBluetoothAudioRouteAvailable()) { + AudioRouteUtils.routeAudioToBluetooth(call: call) + } + } + } + + if (ConfigManager.instance().lpConfigBoolForKey(key: "route_audio_to_speaker_when_video_enabled",defaultValue:true) && call.currentParams?.videoEnabled == true) { + // Do not turn speaker on when video is enabled if headset or bluetooth is used + if (!AudioRouteUtils.isHeadsetAudioRouteAvailable() && + !AudioRouteUtils.isBluetoothAudioRouteCurrentlyUsed(call: call) + ) { + Log.i("[Context] Video enabled and no wired headset not bluetooth in use, routing audio to speaker") + AudioRouteUtils.routeAudioToSpeaker(call: call) + } + } + } + } + + + private func shouldProximitySensorBeEnabled() -> Bool { + return isVideoEnabled.value != true && nonEarpieceOutputAudioDevice.value != true + } + + + func hangUp() { + if (core.currentCall != nil) { + try?core.currentCall?.terminate() + } else if (core.conference?.isIn == true) { + try?core.terminateConference() + } else { + try?core.terminateAllCalls() + } + } + + func toggleVideo() { + if let conference = core.conference, conference.isIn { + if let params = try?core.createConferenceParams() { + let videoEnabled = conference.currentParams?.isVideoEnabled == true + params.videoEnabled = !videoEnabled + _ = conference.updateParams(params: params) + } + } else if let currentCall = core.currentCall { + let state = currentCall.state + if (state == Call.State.End || state == Call.State.Released || state == Call.State.Error) { + return + } + isVideoUpdateInProgress.value = true + if let params = try?core.createCallParams(call: currentCall) { + params.videoEnabled = !(currentCall.currentParams?.videoEnabled == true) + try?currentCall.update(params: params) + if (params.videoEnabled) { + currentCall.requestNotifyNextVideoFrameDecoded() + } + } + } + } + + + private func updateUI() { + updateVideoAvailable() + updateVideoEnabled() + updateMicState() + updateSpeakerState() + updateAudioRoutesState() + proximitySensorEnabled.value = shouldProximitySensorBeEnabled() + } + + private func updateAudioRoutesState() { + let bluetoothDeviceAvailable = AudioRouteUtils.isBluetoothAudioRouteAvailable() + audioRoutesEnabled.value = bluetoothDeviceAvailable + + if (!bluetoothDeviceAvailable) { + audioRoutesSelected.value = false + audioRoutesEnabled.value = false + } + } + + private func updateSpeakerState() { + isSpeakerSelected.value = AudioRouteUtils.isSpeakerAudioRouteCurrentlyUsed() + } + + private func updateBluetoothHeadsetState() { + isBluetoothHeadsetSelected.value = AudioRouteUtils.isBluetoothAudioRouteCurrentlyUsed() + } + + private func updateVideoAvailable() { + let currentCall = core.currentCall + isVideoAvailable.value = (core.videoCaptureEnabled || core.videoPreviewEnabled) && + ((currentCall != nil && currentCall?.mediaInProgress() != true) || core.conference?.isIn == true) + } + + private func updateVideoEnabled() { + let enabled = isVideoCallOrConferenceActive() + isVideoEnabled.value = enabled + } + + func updateMicState() { + isMicrophoneMuted.value = !micAuthorized() || !core.micEnabled + isMuteMicrophoneEnabled.value = core.currentCall != nil || core.conference?.isIn == true + } + + func micAuthorized() -> Bool { + return AVCaptureDevice.authorizationStatus(for: .audio) == .authorized + } + + func isVideoCallOrConferenceActive() -> Bool { + if let conference = core.conference, conference.isIn { + return conference.currentParams?.videoEnabled == true + } else { + return core.currentCall?.currentParams?.videoEnabled == true + } + } + + func toggleFullScreen() { + if (isVideoEnabled.value == true) { + fullScreenMode.value = fullScreenMode.value != true + } + } + + func toggleMuteMicrophone() { + if (!micAuthorized()) { + AVAudioSession.sharedInstance().requestRecordPermission { granted in + if granted { + self.core.micEnabled = !self.core.micEnabled + self.updateMicState() + } + } + } + core.micEnabled = !core.micEnabled + updateMicState() + } + + func forceEarpieceAudioRoute() { + if (AudioRouteUtils.isHeadsetAudioRouteAvailable()) { + Log.i("[Call Controls] Headset found, route audio to it instead of earpiece") + AudioRouteUtils.routeAudioToHeadset() + } else { + AudioRouteUtils.routeAudioToEarpiece() + } + } + + func forceSpeakerAudioRoute() { + AudioRouteUtils.routeAudioToSpeaker() + } + + func forceBluetoothAudioRoute() { + AudioRouteUtils.routeAudioToBluetooth() + } + + func toggleSpeaker() { + if (AudioRouteUtils.isSpeakerAudioRouteCurrentlyUsed()) { + forceEarpieceAudioRoute() + } else { + forceSpeakerAudioRoute() + } + } + + func toggleRoutesMenu() { + audioRoutesSelected.value = audioRoutesSelected.value != true + } + +} diff --git a/Classes/CallSideMenuView.h b/Classes/Voip/Theme/ButtonTheme.swift similarity index 70% rename from Classes/CallSideMenuView.h rename to Classes/Voip/Theme/ButtonTheme.swift index 2a97f78d5..88b8306bb 100644 --- a/Classes/CallSideMenuView.h +++ b/Classes/Voip/Theme/ButtonTheme.swift @@ -1,7 +1,7 @@ /* * Copyright (c) 2010-2020 Belledonne Communications SARL. * - * This file is part of linphone-iphone + * 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 @@ -17,15 +17,17 @@ * along with this program. If not, see . */ -#import -#import "SideMenuTableView.h" -#import "PhoneMainView.h" +import Foundation +import UIKit -@interface CallSideMenuView : UIViewController -@property(weak, nonatomic) IBOutlet UILabel *statsLabel; +struct ButtonTheme { + var tintableStateIcons: [UInt: TintableIcon] // State indexed + var backgroundStateColors: [UInt: LightDarkColor] // State indexed +} -- (IBAction)onLateralSwipe:(id)sender; - -@end +struct TintableIcon { + var name:String + var tintColor: LightDarkColor? = nil +} diff --git a/Classes/Voip/Theme/LightDarkColor.swift b/Classes/Voip/Theme/LightDarkColor.swift new file mode 100644 index 000000000..a01e375b2 --- /dev/null +++ b/Classes/Voip/Theme/LightDarkColor.swift @@ -0,0 +1,42 @@ +/* + * 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 . + */ + +import Foundation + +class LightDarkColor { + var light: UIColor + var dark : UIColor + init(_ l:UIColor,_ d:UIColor){ + light = l + dark = d + } + + func get() -> UIColor { + if #available(iOS 13.0, *) { + if UITraitCollection.current.userInterfaceStyle == .light { + return light + } else { + return dark + } + } else { + return light + } + } + +} diff --git a/Classes/Voip/Theme/TextStyle.swift b/Classes/Voip/Theme/TextStyle.swift new file mode 100644 index 000000000..ceff5a5eb --- /dev/null +++ b/Classes/Voip/Theme/TextStyle.swift @@ -0,0 +1,61 @@ +/* + * 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 . + */ + +import Foundation +import UIKit + +struct TextStyle { + var fgColor:LightDarkColor + var bgColor:LightDarkColor + var allCaps:Bool + var align:NSTextAlignment + var font:String + var size:Float + + func boldEd() -> TextStyle { + return self.font.contains("Bold") ? self : TextStyle(fgColor: self.fgColor,bgColor: self.bgColor,allCaps: self.allCaps,align: self.align,font: self.font.replacingOccurrences(of: "Regular", with: "Bold"), size: self.size) + } +} + + +extension UILabel { + func applyStyle(_ style:TextStyle) { + textColor = style.fgColor.get() + backgroundColor = style.bgColor.get() + if (style.allCaps) { + text = self.text?.uppercased() + tag = 1 + } + textAlignment = style.align + let fontSizeMultiplier: Float = (UIDevice.ipad() ? 1.25 : UIDevice.is5SorSEGen1() ? 0.9 : 1.0) + font = UIFont.init(name: style.font, size: CGFloat(style.size*fontSizeMultiplier)) + } +} + +extension UIButton { + func applyTitleStyle(_ style:TextStyle) { + titleLabel?.applyStyle(style) + if (style.allCaps) { + setTitle(self.title(for: .normal)?.uppercased(), for: .normal) + tag = 1 + } + setTitleColor(style.fgColor.get(), for: .normal) + contentHorizontalAlignment = style.align == .left ? .left : style.align == .center ? .center : style.align == .right ? .right : .left + } +} diff --git a/Classes/Voip/Theme/VoipTexts.swift b/Classes/Voip/Theme/VoipTexts.swift new file mode 100644 index 000000000..4df37ec4d --- /dev/null +++ b/Classes/Voip/Theme/VoipTexts.swift @@ -0,0 +1,137 @@ +/* + * 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 . + */ + +import Foundation +import UIKit + +class VoipTexts { // From android key names. Added intentionnally with NSLocalizedString calls for each key, so it can be picked up by translation system (Weblate or Xcode). + + static let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as! String + + // Calls + static let call_incoming_title = NSLocalizedString("Incoming Call",comment:"") + static let call_outgoing_title = NSLocalizedString("Outgoing Call",comment:"") + static let call_notification_paused = NSLocalizedString("Paused call",comment:"") + static let call_notification_outgoing = NSLocalizedString("Outgoing call",comment:"") + static let call_notification_active = NSLocalizedString("Call running",comment:"") + static let call_error_declined = NSLocalizedString("Call has been declined",comment:"") + static let call_error_user_busy = NSLocalizedString("User is busy",comment:"") + static let call_error_user_not_found = NSLocalizedString("User hasn't been found",comment:"") + static let call_error_incompatible_media_params = NSLocalizedString("Incompatible media parameters",comment:"") + static let call_error_network_unreachable = NSLocalizedString("Network is unreachable",comment:"") + static let call_error_io_error = NSLocalizedString("Service unavailable or network error",comment:"") + static let call_error_server_timeout = NSLocalizedString("Server timeout",comment:"") + static let call_error_temporarily_unavailable = NSLocalizedString("Temporarily unavailable",comment:"") + static let call_error_generic = NSLocalizedString("Error: %s",comment:"") + static let call_video_update_requested_dialog = NSLocalizedString("Correspondent would like to turn the video on",comment:"") + static let call_action_participants_list = NSLocalizedString("Participants list",comment:"") + static let call_action_chat = NSLocalizedString("Chat",comment:"") + static let call_action_calls_list = NSLocalizedString("Calls list",comment:"") + static let call_action_numpad = NSLocalizedString("Numpad",comment:"") + static let call_action_change_conf_layout = NSLocalizedString("Change layout",comment:"") + static let call_action_transfer_call = NSLocalizedString("Transfer call",comment:"") + static let call_action_add_call = NSLocalizedString("Start new call",comment:"") + + static let call_action_statistics = NSLocalizedString("Call statistics",comment:"") + static let call_context_action_resume = NSLocalizedString("Resume call",comment:"") + static let call_context_action_pause = NSLocalizedString("Pause call",comment:"") + static let call_context_action_transfer = NSLocalizedString("Transfer call",comment:"") + static let call_context_action_answer = NSLocalizedString("Answer call",comment:"") + static let call_context_action_hangup = NSLocalizedString("Terminate call",comment:"") + static let call_remote_recording = NSLocalizedString("This call is being recorded.",comment:"") + static let call_remotely_paused_title = NSLocalizedString("Call has been paused by remote.",comment:"") + + // Conference + static let conference_schedule_title = NSLocalizedString("Start a conference",comment:"") + static let conference_schedule_later = NSLocalizedString("Do you want to schedule this conference for later?",comment:"") + static let conference_schedule_mandatory_field = NSLocalizedString("Mandatory",comment:"") + static let conference_schedule_subject_title = NSLocalizedString("Subject",comment:"") + static let conference_schedule_subject_hint = NSLocalizedString("Conference subject",comment:"") + static let conference_schedule_address_title = NSLocalizedString("Conference address",comment:"") + static let conference_schedule_description_title = NSLocalizedString("Add a description",comment:"") + static let conference_schedule_description_hint = NSLocalizedString("Description",comment:"") + static let conference_schedule_date = NSLocalizedString("Date",comment:"") + static let conference_schedule_time = NSLocalizedString("Time",comment:"") + static let conference_schedule_duration = NSLocalizedString("Duration",comment:"") + static let conference_schedule_timezone = NSLocalizedString("Timezone",comment:"") + static let conference_schedule_send_invite_chat = NSLocalizedString("Send invite via \(appName)",comment:"") + static let conference_schedule_send_invite_email = NSLocalizedString("Send invite via email",comment:"") + static let conference_schedule_encryption = NSLocalizedString("Would you like to encrypt the conference?",comment:"") + static let conference_schedule_send_invite_chat_summary = NSLocalizedString("Invite will be sent out from my \(appName) account",comment:"") + static let conference_schedule_participants_list = NSLocalizedString("Participants list",comment:"") + static let conference_schedule_summary = NSLocalizedString("Conference info",comment:"") + static let conference_schedule_create = NSLocalizedString("Create conference",comment:"") + static let conference_schedule = NSLocalizedString("Schedule conference",comment:"") + static let conference_schedule_address_copied_to_clipboard = NSLocalizedString("Conference address copied into clipboard",comment:"") + static let conference_schedule_creation_failure = NSLocalizedString("Failed to create conference!",comment:"") + static let conference_schedule_info_not_sent_to_participant = NSLocalizedString("Failed to send conference info to a participant",comment:"") + static let conference_paused_title = NSLocalizedString("You are currently out of the conference.",comment:"") + static let conference_paused_subtitle = NSLocalizedString("Click on play button to join it back.",comment:"") + static let conference_default_title = NSLocalizedString("Remote conference",comment:"") + static let conference_local_title = NSLocalizedString("Local conference",comment:"") + static let conference_invite_title = NSLocalizedString("Conference invite:",comment:"") + static let conference_description_title = NSLocalizedString("Description:",comment:"") + static let conference_invite_join = NSLocalizedString("Join",comment:"") + static let conference_invite_participants_count = NSLocalizedString("%d participants",comment:"") + static let conference_display_mode_mosaic = NSLocalizedString("Mosaic mode",comment:"") + static let conference_display_mode_active_speaker = NSLocalizedString("Active speaker mode",comment:"") + static let conference_display_no_active_speaker = NSLocalizedString("No active speaker",comment:"") + static let conference_waiting_room_start_call = NSLocalizedString("Start",comment:"") + static let conference_waiting_room_cancel_call = NSLocalizedString("Cancel",comment:"") + static let conference_scheduled = NSLocalizedString("Conferences",comment:"") + static let conference_too_many_participants_for_mosaic_layout = NSLocalizedString("You can't change conference layout as there is too many participants",comment:"") + static let conference_participant_paused = NSLocalizedString("(paused)",comment:"") + + + // Call Stats + + static let call_stats_audio = "Audio" + static let call_stats_video = "Video" + static let call_stats_codec = "Codec:" + static let call_stats_ip = "IP Family:" + static let call_stats_upload = "Upload bandwidth:" + static let call_stats_download = "Download bandwidth:" + static let call_stats_estimated_download = "Estimated download bandwidth:" + static let call_stats_ice = "ICE connectivity:" + static let call_stats_video_resolution_sent = "Sent video resolution:" + static let call_stats_video_resolution_received = "Received video resolution:" + static let call_stats_video_fps_sent = "Sent video fps:" + static let call_stats_video_fps_received = "Received video fps:" + static let call_stats_sender_loss_rate = "Sender loss rate:" + static let call_stats_receiver_loss_rate = "Receiver loss rate:" + static let call_stats_jitter_buffer = "Jitter buffer:" + static let call_stats_encoder_name = "Encoder:" + static let call_stats_decoder_name = "Decoder:" + static let call_stats_player_filter = "Player filter:" + static let call_stats_capture_filter = "Capture filter:" + + + // Added in iOS + static let camera_required_for_video = NSLocalizedString("Camera use is not Authorized for \(appName). This permission is required to activate Video.",comment:"") + static let ok = NSLocalizedString("ok",comment:"") + static let cancel = NSLocalizedString("cancel",comment:"") + + static let dialog_accept = NSLocalizedString("Accept",comment:"") + static let dialog_decline = NSLocalizedString("Decline",comment:"") + + // Participants list : + static let chat_room_group_info_admin = NSLocalizedString("Admin",comment:"") + + +} diff --git a/Classes/Voip/Theme/VoipTheme.swift b/Classes/Voip/Theme/VoipTheme.swift new file mode 100644 index 000000000..1a5d77d9d --- /dev/null +++ b/Classes/Voip/Theme/VoipTheme.swift @@ -0,0 +1,342 @@ +/* + * 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 . + */ + +import Foundation +import UIKit + +class VoipTheme { // Names & values replicated from Android + + // Voip Colors + static let voip_gray_blue_color = UIColor(hex:"#798791") + static let voip_light_gray = UIColor(hex:"#D0D8DE") + static let voip_dark_gray = UIColor(hex:"#4B5964") + static let voip_gray = UIColor(hex:"#96A5B1") + static let voip_gray_background = UIColor(hex:"#D8D8D8") + static let voip_call_record_background = UIColor(hex:"#EBEBEB") + static let voip_calls_list_inactive_background = UIColor(hex:"#F0F1F2") + static let voip_translucent_popup_background = UIColor(hex:"#A64B5964") + static let voip_translucent_popup_alt_background = UIColor(hex:"#E64B5964") + static let voip_numpad_background = UIColor(hex:"#E4E4E4") + static let voip_contact_avatar_background_alt = UIColor(hex:"#AFAFAF") + static let voip_contact_avatar_calls_list = UIColor(hex:"#A1A1A1") + static let voip_conference_participant_paused_background = UIColor(hex:"#303030") + static let voip_drawable_color = UIColor(hex:"#A6B2BC") + static let voip_dark_color = UIColor(hex:"#252E35") + static let voip_dark_color2 = UIColor(hex:"#3F464B") + static let voip_dark_color3 = UIColor(hex:"#475663") + static let voip_dark_color4 = UIColor(hex:"#2D3841") + + // General colors (used by VoIP) + + static let primary_color = UIColor(hex:"#ff5e00") + static let primary_dark_color = UIColor(hex:"#e65000") + static let green_color = UIColor(hex:"#96c11f") + static let dark_green_color = UIColor(hex:"#7d9f21") + static let toolbar_color = UIColor(hex:"#e1e1e1") + static let form_field_gray_background = UIColor(hex:"#F7F7F7") + static let light_grey_color = UIColor(hex:"#c4c4c4") + static let header_background_color = UIColor(hex:"#f3f3f3") + static let dark_grey_color = UIColor(hex:"#444444") + + // Light / Dark variations + static let voipBackgroundColor = LightDarkColor(voip_gray_blue_color,voip_dark_color) + static let voipBackgroundBWColor = LightDarkColor(UIColor.white,voip_dark_color) + static let voipParticipantBackgroundColor = LightDarkColor(voip_gray_background,voip_dark_color2) + static let voipExtraButtonsBackgroundColor = LightDarkColor(voip_gray,voip_dark_color3) + static let voipToolbarBackgroundColor = LightDarkColor(toolbar_color,voip_dark_color4) + static let voipDrawableColor = LightDarkColor(voip_dark_gray,.white) + static let voipDrawableColorHighlighted = LightDarkColor(voip_gray,voip_gray) + static let voipTextColor = LightDarkColor(voip_dark_gray,UIColor.white) + static let voipFormBackgroundColor = LightDarkColor(form_field_gray_background,voip_dark_color4) + static let voipFormFieldBackgroundColor = LightDarkColor(light_grey_color,voip_dark_color4) + static let voipFormDisabledFieldBackgroundColor = LightDarkColor(header_background_color,voip_dark_color4) + static let primarySubtextLightColor = LightDarkColor(light_grey_color,toolbar_color) + static let primaryTextColor = LightDarkColor(dark_grey_color,.white) + + + + + // Text styles + static let fontName = "Roboto" + static let call_header_title = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .center, font: fontName+"-Bold", size: 18.0) + static let call_header_subtitle = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .center, font: fontName+"-Regular", size: 14.0) + static let call_generated_avatar_large = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: true, align: .center, font: fontName+"-Regular", size: 53.0) + static let call_generated_avatar_medium = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: true, align: .center, font: fontName+"-Regular", size: 27.0) + static let call_generated_avatar_small = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: true, align: .center, font: fontName+"-Regular", size: 16.0) + + static let dtmf_label = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .center, font: fontName+"-Regular", size: 30.0) + static let call_remote_name = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .left, font: fontName+"-Regular", size: 18.0) + static let call_remote_recording = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .center, font: fontName+"-Regular", size: 16.0) + static let call_or_conference_title = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .center, font: fontName+"-Bold", size: 30.0) + static let call_or_conference_subtitle = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .center, font: fontName+"-Bold", size: 20.0) + static let basic_popup_title = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .center, font: fontName+"-Regular", size: 21.0) + static let big_button = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: true, align: .center, font: fontName+"-Bold", size: 17.0) + + static let call_display_name_duration = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .left, font: fontName+"-Regular", size: 17.0) + static let call_sip_address = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .left, font: fontName+"-Regular", size: 14.0) + static let voip_extra_button = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .center, font: fontName+"-Regular", size: 12.0) + static let unread_count_font = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .center, font: fontName+"-Regular", size: 11.0) + static let call_stats_font = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .center, font: fontName+"-Regular", size: 12.0) + static let call_stats_font_title = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .center, font: fontName+"-Regular", size: 18.0) + static let calls_list_header_font = TextStyle(fgColor: voipTextColor, bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .center, font: fontName+"-Regular", size: 20.0) + + static let call_list_active_name_font = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .center, font: fontName+"-Regular", size: 18.0) + static let call_list_active_sip_uri_font = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .center, font: fontName+"-Regular", size: 12.0) + + static let call_list_name_font = TextStyle(fgColor: voipTextColor, bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .center, font: fontName+"-Regular", size: 18.0) + static let call_list_sip_uri_font = TextStyle(fgColor: voipTextColor, bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .center, font: fontName+"-Regular", size: 12.0) + + static let call_context_menu_item_font = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: true, align: .left, font: fontName+"-Bold", size: 16.0) + + static let conference_participant_admin_label = TextStyle(fgColor: primarySubtextLightColor, bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .left, font: fontName+"-Bold", size: 13.0) + static let conference_participant_name_font = TextStyle(fgColor: LightDarkColor(dark_grey_color,dark_grey_color), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .center, font: fontName+"-Regular", size: 18.0) + static let conference_participant_sip_uri_font = TextStyle(fgColor: LightDarkColor(primary_color,primary_color), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .center, font: fontName+"-Regular", size: 12.0) + static let conference_participant_name_font_grid = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .left, font: fontName+"-Bold", size: 15.0) + static let conference_participant_name_font_as = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .left, font: fontName+"-Bold", size: 12.0) + + + static let conference_mode_title = TextStyle(fgColor: LightDarkColor(dark_grey_color,dark_grey_color), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .left, font: fontName+"-Regular", size: 17.0) + static let conference_mode_title_selected = conference_mode_title.boldEd() + + + + // Buttons Background (State colors) + + static let button_background = [ + UIButton.State.normal.rawValue : LightDarkColor(voip_gray,voip_gray), + UIButton.State.highlighted.rawValue : LightDarkColor(voip_dark_gray,voip_dark_gray), + UIButton.State.selected.union(.highlighted).rawValue : LightDarkColor(voip_dark_gray,voip_dark_gray), + UIButton.State.disabled.rawValue : LightDarkColor(voip_light_gray,voip_light_gray) + ] + + static let button_background_reverse = [ + UIButton.State.normal.rawValue : LightDarkColor(voip_dark_gray,voip_dark_gray), + UIButton.State.highlighted.rawValue : LightDarkColor(voip_gray,voip_gray), + UIButton.State.selected.union(.highlighted).rawValue : LightDarkColor(voip_gray,voip_gray), + UIButton.State.disabled.rawValue : LightDarkColor(voip_light_gray,voip_light_gray) + ] + + static let button_call_recording_background = [ + UIButton.State.normal.rawValue : LightDarkColor(voip_call_record_background,voip_call_record_background), + UIButton.State.selected.rawValue : LightDarkColor(primary_color,primary_color), + UIButton.State.disabled.rawValue : LightDarkColor(voip_light_gray,voip_light_gray) + ] + + static let button_toggle_background = [ + UIButton.State.normal.rawValue : LightDarkColor(voip_gray,voip_gray), + UIButton.State.selected.rawValue : LightDarkColor(primary_color,primary_color), + UIButton.State.highlighted.rawValue : LightDarkColor(voip_dark_gray,voip_dark_gray), + UIButton.State.disabled.rawValue : LightDarkColor(voip_light_gray,voip_light_gray) + ] + + static let button_toggle_background_reverse = [ + UIButton.State.normal.rawValue : LightDarkColor(voip_dark_gray,voip_dark_gray), + UIButton.State.selected.rawValue : LightDarkColor(primary_color,primary_color), + UIButton.State.highlighted.rawValue : LightDarkColor(voip_gray,voip_gray), + UIButton.State.disabled.rawValue : LightDarkColor(voip_light_gray,voip_light_gray) + ] + + + + static let primary_colors_background = [ + UIButton.State.normal.rawValue : LightDarkColor(primary_color,primary_color), + UIButton.State.highlighted.rawValue : LightDarkColor(primary_dark_color,primary_dark_color), + ] + + static let primary_colors_background_gray = [ + UIButton.State.normal.rawValue : LightDarkColor(voip_gray,voip_gray), + UIButton.State.highlighted.rawValue : LightDarkColor(voip_dark_gray,voip_dark_gray), + ] + + static let numpad_digit_background = [ + UIButton.State.normal.rawValue : LightDarkColor(voip_numpad_background,voip_numpad_background), + UIButton.State.highlighted.rawValue : LightDarkColor(voip_gray_blue_color,voip_gray_blue_color) + ] + + static let button_round_background = [ + UIButton.State.normal.rawValue : LightDarkColor(primary_color,primary_color), + UIButton.State.highlighted.rawValue : LightDarkColor(dark_grey_color,dark_grey_color), + UIButton.State.disabled.rawValue : LightDarkColor(voip_light_gray,voip_light_gray) + ] + + static let button_call_context_menu_background = [ + UIButton.State.normal.rawValue : LightDarkColor(voip_gray,voip_gray), + UIButton.State.highlighted.rawValue : LightDarkColor(primary_color,primary_color), + ] + + // Buttons Icons (State colors) + Background colors + + + static let call_terminate = ButtonTheme( + tintableStateIcons:[UIButton.State.normal.rawValue : TintableIcon(name: "voip_hangup",tintColor: LightDarkColor(.white,.white))], + backgroundStateColors: [ + UIButton.State.normal.rawValue : LightDarkColor(primary_color,primary_color), + UIButton.State.highlighted.rawValue : LightDarkColor(primary_dark_color,primary_dark_color) + ]) + + static let call_record = ButtonTheme( + tintableStateIcons:[ + UIButton.State.normal.rawValue : TintableIcon(name: "voip_call_record",tintColor: LightDarkColor(voip_gray_blue_color,voip_gray_blue_color)), + UIButton.State.selected.rawValue : TintableIcon(name: "voip_call_record",tintColor: LightDarkColor(.white,.white)), + UIButton.State.highlighted.rawValue : TintableIcon(name: "voip_call_record",tintColor: LightDarkColor(primary_color,primary_color)), + ], + backgroundStateColors: button_call_recording_background) + + static let call_pause = ButtonTheme( + tintableStateIcons:[ + UIButton.State.normal.rawValue : TintableIcon(name: "voip_pause",tintColor: LightDarkColor(.white,.white)), + ], + backgroundStateColors: button_toggle_background) + + static let call_accept = ButtonTheme( + tintableStateIcons:[UIButton.State.normal.rawValue : TintableIcon(name: "voip_call",tintColor: LightDarkColor(.white,.white))], + backgroundStateColors: [ + UIButton.State.normal.rawValue : LightDarkColor(green_color,green_color), + UIButton.State.highlighted.rawValue : LightDarkColor(dark_green_color,dark_green_color) + ]) + + static let call_mute = ButtonTheme( + tintableStateIcons:[ + UIButton.State.normal.rawValue : TintableIcon(name: "voip_micro_on",tintColor: LightDarkColor(.white,.white)), + UIButton.State.selected.rawValue : TintableIcon(name: "voip_micro_off",tintColor: LightDarkColor(.white,.white)), + ], + backgroundStateColors: button_background_reverse) + + static let call_speaker = ButtonTheme( + tintableStateIcons:[ + UIButton.State.normal.rawValue : TintableIcon(name: "voip_speaker_off",tintColor: LightDarkColor(.white,.white)), + UIButton.State.selected.rawValue : TintableIcon(name: "voip_speaker_on",tintColor: LightDarkColor(.white,.white)), + ], + backgroundStateColors: button_background_reverse) + + static let call_audio_route = ButtonTheme( + tintableStateIcons:[ + UIButton.State.normal.rawValue : TintableIcon(name: "voip_audio_routes",tintColor: LightDarkColor(.white,.white)), + ], + backgroundStateColors: button_toggle_background_reverse) + + + static let call_video = ButtonTheme( + tintableStateIcons:[ + UIButton.State.normal.rawValue : TintableIcon(name: "voip_camera_off",tintColor: LightDarkColor(.white,.white)), + UIButton.State.selected.rawValue : TintableIcon(name: "voip_camera_on",tintColor: LightDarkColor(.white,.white)), + ], + backgroundStateColors: button_background_reverse) + + static let call_numpad = ButtonTheme( + tintableStateIcons:[ + UIButton.State.normal.rawValue : TintableIcon(name: "voip_call_numpad",tintColor: LightDarkColor(.white,.white)), + UIButton.State.highlighted.rawValue : TintableIcon(name: "voip_call_numpad",tintColor: LightDarkColor(voip_dark_gray,voip_dark_gray)), + UIButton.State.disabled.rawValue : TintableIcon(name: "voip_call_numpad",tintColor: LightDarkColor(voip_light_gray,voip_light_gray)), + ], + backgroundStateColors: button_background) + + // AUdio routes + static let route_bluetooth = ButtonTheme( + tintableStateIcons:[ + UIButton.State.normal.rawValue : TintableIcon(name: "voip_bluetooth",tintColor: LightDarkColor(.white,.white)), + ], + backgroundStateColors: button_toggle_background_reverse) + + static let route_earpiece = ButtonTheme( + tintableStateIcons:[ + UIButton.State.normal.rawValue : TintableIcon(name: "voip_earpiece",tintColor: LightDarkColor(.white,.white)), + ], + backgroundStateColors: button_toggle_background_reverse) + + static let route_speaker = ButtonTheme( + tintableStateIcons:[ + UIButton.State.normal.rawValue : TintableIcon(name: "voip_speaker_on",tintColor: LightDarkColor(.white,.white)), + ], + backgroundStateColors: button_toggle_background_reverse) + + + + static let call_more = ButtonTheme( + tintableStateIcons:[ + UIButton.State.normal.rawValue : TintableIcon(name: "voip_call_more",tintColor: LightDarkColor(.white,.white)) + ], + backgroundStateColors: button_background) + + + static let voip_cancel = ButtonTheme( + tintableStateIcons:[ + UIButton.State.normal.rawValue : TintableIcon(name: "voip_cancel",tintColor: voipDrawableColor), + UIButton.State.highlighted.rawValue : TintableIcon(name: "voip_cancel",tintColor: voipDrawableColorHighlighted) + ], + backgroundStateColors: [UIButton.State.normal.rawValue : LightDarkColor(.clear,.clear)]) + + + static let voip_cancel_light = ButtonTheme( + tintableStateIcons:[ + UIButton.State.normal.rawValue : TintableIcon(name: "voip_cancel",tintColor: LightDarkColor(voip_gray,voip_gray)), + UIButton.State.highlighted.rawValue : TintableIcon(name: "voip_cancel",tintColor: LightDarkColor(voip_dark_gray,voip_dark_gray)) + ], + backgroundStateColors: [UIButton.State.normal.rawValue : LightDarkColor(.clear,.clear)]) + + static let voip_edit = ButtonTheme( + tintableStateIcons:[ + UIButton.State.normal.rawValue : TintableIcon(name: "voip_edit",tintColor: LightDarkColor(dark_grey_color,dark_grey_color)), + UIButton.State.highlighted.rawValue : TintableIcon(name: "voip_edit",tintColor: voipDrawableColorHighlighted) + ], + backgroundStateColors: [UIButton.State.normal.rawValue : LightDarkColor(.clear,.clear)]) + + static let radio_button = ButtonTheme( + tintableStateIcons:[ + UIButton.State.normal.rawValue : TintableIcon(name: "voip_radio_off",tintColor: LightDarkColor(dark_grey_color,dark_grey_color)), + UIButton.State.selected.rawValue : TintableIcon(name: "voip_radio_on",tintColor: LightDarkColor(primary_color,primary_color)) + ], + backgroundStateColors: [UIButton.State.normal.rawValue : LightDarkColor(.clear,.clear)]) + + static let voip_call_list_active_menu = ButtonTheme( + tintableStateIcons:[ + UIButton.State.normal.rawValue : TintableIcon(name: "voip_call_list_menu",tintColor: LightDarkColor(.white,.white)), + UIButton.State.highlighted.rawValue : TintableIcon(name: "voip_call_list_menu",tintColor: voipDrawableColorHighlighted) + ], + backgroundStateColors: [UIButton.State.normal.rawValue : LightDarkColor(.clear,.clear)]) + + static let voip_call_list_menu = ButtonTheme( + tintableStateIcons:[ + UIButton.State.normal.rawValue : TintableIcon(name: "voip_call_list_menu",tintColor: voipTextColor), + UIButton.State.highlighted.rawValue : TintableIcon(name: "voip_call_list_menu",tintColor: voipDrawableColorHighlighted) + ], + backgroundStateColors: [UIButton.State.normal.rawValue : LightDarkColor(.clear,.clear)]) + + + static func call_action(_ iconName:String) -> ButtonTheme { + return ButtonTheme( + tintableStateIcons:[ + UIButton.State.normal.rawValue : TintableIcon(name: iconName,tintColor: LightDarkColor(.white,.white)), + UIButton.State.highlighted.rawValue : TintableIcon(name: iconName,tintColor: LightDarkColor(voip_dark_gray,voip_dark_gray)), + UIButton.State.disabled.rawValue : TintableIcon(name: iconName,tintColor: LightDarkColor(voip_light_gray,voip_light_gray)), + ], + backgroundStateColors: [:]) + } + + static let call_add = ButtonTheme( + tintableStateIcons:[UIButton.State.normal.rawValue : TintableIcon(name: "voip_call_add",tintColor: LightDarkColor(.white,.white))], + backgroundStateColors: button_round_background) + + static let call_merge = ButtonTheme( + tintableStateIcons:[UIButton.State.normal.rawValue : TintableIcon(name: "voip_merge_calls",tintColor: LightDarkColor(.white,.white))], + backgroundStateColors: button_round_background) + +} + + diff --git a/Classes/Voip/Views/CompositeViewControllers/ActiveCallOrConferenceView.swift b/Classes/Voip/Views/CompositeViewControllers/ActiveCallOrConferenceView.swift new file mode 100644 index 000000000..f85be7062 --- /dev/null +++ b/Classes/Voip/Views/CompositeViewControllers/ActiveCallOrConferenceView.swift @@ -0,0 +1,348 @@ +/* + * 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 . + */ + + +import UIKit +import linphonesw + + +class ActiveCallOrConferenceView: UIViewController, UICompositeViewDelegate { // Replaces CallView + + // Layout constants + let content_inset = 12.0 + + var callPausedByRemoteView : PausedCallOrConferenceView? = nil + var conferencePausedView : PausedCallOrConferenceView? = nil + + var currentCallView : ActiveCallView? = nil + var conferenceGridView: VoipConferenceGridView? = nil + var conferenceActiveSpeakerView: VoipConferenceActiveSpeakerView? = nil + + let extraButtonsView = VoipExtraButtonsView() + var numpadView : NumpadView? = nil + var currentCallStatsVew : CallStatsView? = nil + var shadingMask = UIView() + var videoAcceptDialog : VoipDialog? = nil + var dismissableView : DismissableView? = nil + var participantsListView : ParticipantsListView? = nil + + var audioRoutesView : AudioRoutesView? = nil + + + static let compositeDescription = UICompositeViewDescription(ActiveCallOrConferenceView.self, statusBar: StatusBarView.self, tabBar: nil, sideMenu: nil, fullscreen: false, isLeftFragment: false,fragmentWith: nil) + static func compositeViewDescription() -> UICompositeViewDescription! { return compositeDescription } + func compositeViewDescription() -> UICompositeViewDescription! { return type(of: self).compositeDescription } + + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = VoipTheme.voipBackgroundColor.get() + + // Hangup + let hangup = CallControlButton(width: 65, imageInset:IncomingOutgoingCommonView.answer_decline_inset, buttonTheme: VoipTheme.call_terminate, onClickAction: { + ControlsViewModel.shared.hangUp() + }) + view.addSubview(hangup) + hangup.alignParentLeft(withMargin:SharedLayoutConstants.margin_call_view_side_controls_buttons).alignParentBottom(withMargin:SharedLayoutConstants.buttons_bottom_margin).done() + + + // Controls + let controlsView = ControlsView(showVideo: true) + view.addSubview(controlsView) + controlsView.alignParentBottom(withMargin:SharedLayoutConstants.buttons_bottom_margin).centerX().done() + + + // Container fiew + let fullScreenMutableContainerView = UIView() + fullScreenMutableContainerView.backgroundColor = .clear + self.view.addSubview(fullScreenMutableContainerView) + fullScreenMutableContainerView.matchParentSideBorders(insetedByDx: content_inset).matchParentHeight().alignAbove(view:controlsView,withMargin:SharedLayoutConstants.buttons_bottom_margin).done() + + // Current (Single) Call (VoipCallView) + currentCallView = ActiveCallView() + currentCallView!.isHidden = true + fullScreenMutableContainerView.addSubview(currentCallView!) + CallsViewModel.shared.currentCallData.readCurrentAndObserve { (currentCallData) in + self.updateNavigation() + self.currentCallView!.isHidden = currentCallData == nil || ConferenceViewModel.shared.isInConference.value == true + self.currentCallView!.callData = currentCallData != nil ? currentCallData! : nil + currentCallData??.isRemotelyPaused.readCurrentAndObserve { remotelyPaused in + self.callPausedByRemoteView?.isHidden = remotelyPaused != true + } + if (currentCallData == nil) { + self.callPausedByRemoteView?.isHidden = true + } else { + currentCallData??.isIncoming.readCurrentAndObserve { _ in self.updateNavigation() } + currentCallData??.isOutgoing.readCurrentAndObserve { _ in self.updateNavigation() } + } + self.extraButtonsView.isHidden = true + self.conferencePausedView?.isHidden = true + } + + currentCallView!.matchParentDimmensions().done() + + // Paused by remote (Call) + callPausedByRemoteView = PausedCallOrConferenceView(iconName: "voip_conference_paused_big",titleText: VoipTexts.call_remotely_paused_title,subTitleText: nil) + view.addSubview(callPausedByRemoteView!) + callPausedByRemoteView?.matchParentSideBorders().matchParentHeight().alignAbove(view:controlsView,withMargin:SharedLayoutConstants.buttons_bottom_margin).done() + callPausedByRemoteView?.isHidden = true + + // Conference paused + conferencePausedView = PausedCallOrConferenceView(iconName: "voip_conference_paused_big",titleText: VoipTexts.conference_paused_title,subTitleText: VoipTexts.conference_paused_subtitle) + view.addSubview(conferencePausedView!) + conferencePausedView?.matchParentSideBorders().matchParentHeight().alignAbove(view:controlsView,withMargin:SharedLayoutConstants.buttons_bottom_margin).done() + conferencePausedView?.isHidden = true + + // Conference grid + conferenceGridView = VoipConferenceGridView() + fullScreenMutableContainerView.addSubview(conferenceGridView!) + conferenceGridView?.matchParentDimmensions().done() + conferenceGridView?.isHidden = true + ConferenceViewModel.shared.isInConference.readCurrentAndObserve { (isInConference) in + self.updateNavigation() + if (isInConference == true) { + self.currentCallView!.isHidden = true + self.extraButtonsView.isHidden = true + self.conferencePausedView?.isHidden = true + self.conferenceGridView!.isHidden = false + self.conferenceActiveSpeakerView!.isHidden = true + self.conferenceGridView?.conferenceViewModel = ConferenceViewModel.shared + } else { + self.conferenceGridView?.isHidden = true + } + } + + // Conference active speaker + conferenceActiveSpeakerView = VoipConferenceActiveSpeakerView() + fullScreenMutableContainerView.addSubview(conferenceActiveSpeakerView!) + conferenceActiveSpeakerView?.matchParentDimmensions().done() + conferenceActiveSpeakerView?.isHidden = true + + // Conference mode switching + + ConferenceViewModel.shared.conferenceDisplayMode.readCurrentAndObserve { (conferenceMode) in + if (ConferenceViewModel.shared.isInConference.value == true) { + self.conferenceGridView!.isHidden = conferenceMode != .Grid + self.conferenceActiveSpeakerView!.isHidden = conferenceMode != .ActiveSpeaker + self.conferenceActiveSpeakerView?.conferenceViewModel = ConferenceViewModel.shared + } else { + self.conferenceActiveSpeakerView?.isHidden = true + } + } + + ConferenceViewModel.shared.isInConference.readCurrentAndObserve { (isInConference) in + self.updateNavigation() + } + + // Calls List + ControlsViewModel.shared.goToCallsListEvent.observe { (_) in + self.dismissableView = CallsListView() + self.view.addSubview(self.dismissableView!) + self.dismissableView?.matchParentDimmensions().done() + } + + // Conference Participants List + ControlsViewModel.shared.goToConferenceParticipantsListEvent.observe { (_) in + self.participantsListView = ParticipantsListView() + self.view.addSubview(self.participantsListView!) + self.participantsListView?.matchParentDimmensions().done() + } + + // Goto chat + ControlsViewModel.shared.goToChatEvent.observe { (_) in + self.goToChat() + } + + // Conference mode selection + ControlsViewModel.shared.goToConferenceLayoutSettings.observe { (_) in + self.dismissableView = VoipConferenceDisplayModeSelectionView() + self.view.addSubview(self.dismissableView!) + self.dismissableView?.matchParentDimmensions().done() + let activeDisplayMode = ConferenceViewModel.shared.conferenceDisplayMode.value! + let indexPath = IndexPath(row: activeDisplayMode == .Grid ? 0 : 1, section: 0) + (self.dismissableView as! VoipConferenceDisplayModeSelectionView).optionsListView.selectRow(at:indexPath, animated: true, scrollPosition: .bottom) + + } + + // Shading mask, everything before will be shaded upon displaying of the mask + shadingMask.backgroundColor = VoipTheme.voip_translucent_popup_background + shadingMask.isHidden = true + self.view.addSubview(shadingMask) + shadingMask.matchParentDimmensions().done() + + // Extra Buttons + let showextraButtons = CallControlButton(imageInset:IncomingOutgoingCommonView.answer_decline_inset, buttonTheme: VoipTheme.call_more, onClickAction: { + self.showModalSubview(view: self.extraButtonsView) + ControlsViewModel.shared.audioRoutesSelected.value = false + }) + view.addSubview(showextraButtons) + showextraButtons.alignParentRight(withMargin:SharedLayoutConstants.margin_call_view_side_controls_buttons).alignParentBottom(withMargin:SharedLayoutConstants.buttons_bottom_margin).done() + + let boucingCounter = BouncingCounter(inButton:showextraButtons) + view.addSubview(boucingCounter) + boucingCounter.dataSource = CallsViewModel.shared.chatAndCallsCount + + view.addSubview(extraButtonsView) + extraButtonsView.matchParentSideBorders(insetedByDx: content_inset).alignParentBottom().done() + ControlsViewModel.shared.hideExtraButtons.readCurrentAndObserve { (_) in + self.hideModalSubview(view: self.extraButtonsView) + } + self.view.onClick { + if (!self.extraButtonsView.isHidden) { + self.hideModalSubview(view: self.extraButtonsView) + } + ControlsViewModel.shared.audioRoutesSelected.value = false + } + + // Numpad + ControlsViewModel.shared.numpadVisible.readCurrentAndObserve { (visible) in + if (visible == true && CallsViewModel.shared.currentCallData.value != nil ) { + self.numpadView?.removeFromSuperview() + self.shadingMask.isHidden = false + self.numpadView = NumpadView(superView: self.view,callData: CallsViewModel.shared.currentCallData.value!!, onDismissAction: { + self.numpadView?.removeFromSuperview() + self.shadingMask.isHidden = true + }) + } + } + + // Call stats + ControlsViewModel.shared.callStatsVisible.readCurrentAndObserve { (visible) in + if (visible == true && CallsViewModel.shared.currentCallData.value != nil ) { + self.currentCallStatsVew?.removeFromSuperview() + self.shadingMask.isHidden = false + self.currentCallStatsVew = CallStatsView(superView: self.view,callData: CallsViewModel.shared.currentCallData.value!!, onDismissAction: { + self.currentCallStatsVew?.removeFromSuperview() + self.shadingMask.isHidden = true + }) + } + } + + // Video activation dialog request + CallsViewModel.shared.callUpdateEvent.observe { (call) in + let core = Core.get() + if (call?.state == .StreamsRunning) { + self.videoAcceptDialog?.removeFromSuperview() + self.videoAcceptDialog = nil + } else if (call?.state == .UpdatedByRemote) { + if (core.videoCaptureEnabled || core.videoDisplayEnabled) { + if (call?.currentParams?.videoEnabled != call?.remoteParams?.videoEnabled) { + let accept = ButtonAttributes(text:VoipTexts.dialog_accept, action: {call?.answerVideoUpdateRequest(accept: true)}, isDestructive:false) + let cancel = ButtonAttributes(text:VoipTexts.dialog_decline, action: {call?.answerVideoUpdateRequest(accept: false)}, isDestructive:true) + self.videoAcceptDialog = VoipDialog(message:VoipTexts.call_video_update_requested_dialog, givenButtons: [cancel,accept]) + self.videoAcceptDialog?.show() + } + } else { + Log.w("[Call] Video display & capture are disabled, don't show video dialog") + } + } + } + + // Audio Routes + audioRoutesView = AudioRoutesView() + view.addSubview(audioRoutesView!) + audioRoutesView!.alignBottomWith(otherView: controlsView).done() + ControlsViewModel.shared.audioRoutesSelected.readCurrentAndObserve { (audioRoutesSelected) in + self.audioRoutesView!.isHidden = audioRoutesSelected != true + } + audioRoutesView!.alignAbove(view:controlsView,withMargin:SharedLayoutConstants.buttons_bottom_margin).centerX().done() + + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(true) + extraButtonsView.refresh() + ControlsViewModel.shared.callStatsVisible.notifyValue() + CallsViewModel.shared.currentCallData.notifyValue() + ControlsViewModel.shared.audioRoutesSelected.value = false + } + + override func viewWillDisappear(_ animated: Bool) { + dismissableView?.removeFromSuperview() + dismissableView = nil + + participantsListView?.removeFromSuperview() + participantsListView = nil + + ControlsViewModel.shared.fullScreenMode.value = false + super.viewWillDisappear(animated) + } + + func showModalSubview(view:UIView) { + view.isHidden = false + shadingMask.isHidden = false + } + func hideModalSubview(view:UIView) { + view.isHidden = true + shadingMask.isHidden = true + } + + func updateNavigation() { + if (Core.get().callsNb == 0) { + PhoneMainView.instance().popView(self.compositeViewDescription()) + } else { + if let data = CallsViewModel.shared.currentCallData.value { + if (data?.isOutgoing.value == true || data?.isIncoming.value == true) { + PhoneMainView.instance().popView(self.compositeViewDescription()) + } else { + PhoneMainView.instance().changeCurrentView(self.compositeViewDescription()) + } + } else { + PhoneMainView.instance().changeCurrentView(self.compositeViewDescription()) + } + } + } + + func goToChat() { + let core = Core.get() + guard + let localSipUri = core.defaultAccount?.params?.identityAddress?.asStringUriOnly(), + let remoteSipUri = ConferenceViewModel.shared.isInConference.value == true ? ConferenceViewModel.shared.conferenceAddress.value?.asStringUriOnly() : core.currentCall?.remoteAddress?.asStringUriOnly(), + let localAddress = try?Factory.Instance.createAddress(addr: localSipUri), + let remoteSipAddress = try?Factory.Instance.createAddress(addr: remoteSipUri), + let chatRoomParams = try?core.createDefaultChatRoomParams() + else { + return + } + + var chatRoom = core.searchChatRoom(params: nil, localAddr: localAddress, remoteAddr: remoteSipAddress, participants: []) + if (chatRoom == nil) { + chatRoom = core.searchChatRoom(params: nil, localAddr: localAddress, remoteAddr: nil, participants: [remoteSipAddress]) + } + if (chatRoom == nil) { + Log.w("[Call] Failed to find existing chat room for local address \(localSipUri) and remote address \(remoteSipUri)") + + // TODO: configure chat room params + if (ConferenceViewModel.shared.isInConference.value == true) { + // TODO: compute conference participants addresses list + } else { + chatRoom = try?core.createChatRoom(params: chatRoomParams, localAddr: localAddress, participants: [remoteSipAddress]) + } + } + + if (chatRoom != nil) { + PhoneMainView.instance().go(to: chatRoom?.getCobject) + } else { + Log.w("[Call] Failed to create chat room for local address \(localSipUri) and remote address \(remoteSipUri)") + } + + } + + +} diff --git a/Classes/Voip/Views/CompositeViewControllers/IncomingCallView.swift b/Classes/Voip/Views/CompositeViewControllers/IncomingCallView.swift new file mode 100644 index 000000000..6009bf0a5 --- /dev/null +++ b/Classes/Voip/Views/CompositeViewControllers/IncomingCallView.swift @@ -0,0 +1,81 @@ +/* + * 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 . + */ + + +import UIKit +import Foundation +import linphonesw + +@objc class IncomingCallView: IncomingOutgoingCommonView, UICompositeViewDelegate { + + // Layout constants + let buttons_distance_from_center_x = 38 + + static let compositeDescription = UICompositeViewDescription(IncomingCallView.self, statusBar: nil, tabBar: nil, sideMenu: nil, fullscreen: true, isLeftFragment: false,fragmentWith: nil) + static func compositeViewDescription() -> UICompositeViewDescription! { return compositeDescription } + func compositeViewDescription() -> UICompositeViewDescription! { return type(of: self).compositeDescription } + + var earlyMediaView : UIView? = nil + + override func viewDidLoad() { + + super.viewDidLoad(forCallType: VoipTexts.call_incoming_title) + + // Accept + let accept = CallControlButton(width: CallControlButton.hungup_width, imageInset:IncomingOutgoingCommonView.answer_decline_inset, buttonTheme: VoipTheme.call_accept, onClickAction: { + self.callData.map { CallManager.instance().acceptCall(call: $0.call.getCobject, hasVideo: false)} + }) + view.addSubview(accept) + accept.centerX(withDx: buttons_distance_from_center_x).alignParentBottom(withMargin:SharedLayoutConstants.buttons_bottom_margin).done() + + // Decline + let decline = CallControlButton(width: CallControlButton.hungup_width, imageInset:IncomingOutgoingCommonView.answer_decline_inset, buttonTheme: VoipTheme.call_terminate, onClickAction: { + self.callData.map { CallManager.instance().terminateCall(call: $0.call.getCobject)} + }) + view.addSubview(decline) + decline.centerX(withDx: -buttons_distance_from_center_x).alignParentBottom(withMargin:SharedLayoutConstants.buttons_bottom_margin).done() + } + + @objc override func setCall(call:OpaquePointer) { + super.setCall(call: call) + callData?.iFrameReceived.observe(onChange: { (video) in + if (video == true) { + Core.get().nativeVideoWindow = self.earlyMediaView + self.earlyMediaView?.isHidden = false + } + }) + callData?.callState.readCurrentAndObserve(onChange: { (state) in + if (ConfigManager.instance().lpConfigBoolForKey(key: "pref_accept_early_media") && state == .IncomingReceived) { + try?self.callData?.call.acceptEarlyMedia() + self.callData?.call.requestNotifyNextVideoFrameDecoded() + } + }) + callData?.isIncoming.readCurrentAndObserve { (incoming) in + if (incoming != true) { + PhoneMainView.instance().popView(self.compositeViewDescription()) + } + } + + if (ConfigManager.instance().lpConfigBoolForKey(key: "auto_answer")) { + CallManager.instance().acceptCall(call: call, hasVideo: false) // TODO check with old version for Video accept separate button - Not implemented in Android + } + } + + +} diff --git a/Classes/Voip/Views/CompositeViewControllers/OutgoingCallView.swift b/Classes/Voip/Views/CompositeViewControllers/OutgoingCallView.swift new file mode 100644 index 000000000..d2898a148 --- /dev/null +++ b/Classes/Voip/Views/CompositeViewControllers/OutgoingCallView.swift @@ -0,0 +1,102 @@ +/* + * 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 . + */ + + +import UIKit +import Foundation +import linphonesw + +@objc class OutgoingCallView: IncomingOutgoingCommonView, UICompositeViewDelegate { + + // Layout constants + let numpad_icon_padding = 10.0 + + var numpadView : NumpadView? = nil + var showNumPad : CallControlButton? = nil + var shadingMask = UIView() + + + static let compositeDescription = UICompositeViewDescription(OutgoingCallView.self, statusBar: nil, tabBar: nil, sideMenu: nil, fullscreen: true, isLeftFragment: false,fragmentWith: nil) + static func compositeViewDescription() -> UICompositeViewDescription! { return compositeDescription } + func compositeViewDescription() -> UICompositeViewDescription! { return OutgoingCallView.compositeDescription } + + override func viewDidLoad() { + super.viewDidLoad(forCallType: VoipTexts.call_outgoing_title) + + // Cancel + let cancelCall = CallControlButton(width: CallControlButton.hungup_width, imageInset:IncomingOutgoingCommonView.answer_decline_inset, buttonTheme: VoipTheme.call_terminate, onClickAction: { + self.callData.map { CallManager.instance().terminateCall(call: $0.call.getCobject)} + }) + view.addSubview(cancelCall) + cancelCall.alignParentLeft(withMargin:SharedLayoutConstants.margin_call_view_side_controls_buttons).alignParentBottom(withMargin:SharedLayoutConstants.buttons_bottom_margin).done() + + // Controls + let controlsView = ControlsView(showVideo: false) + view.addSubview(controlsView) + controlsView.alignParentBottom(withMargin:SharedLayoutConstants.buttons_bottom_margin).centerX().done() + + // Shading mask, everything after will be shaded upon displayed + shadingMask.backgroundColor = VoipTheme.voip_translucent_popup_background + shadingMask.isHidden = true + self.view.addSubview(shadingMask) + shadingMask.matchParentDimmensions().done() + + // Numpad + showNumPad = CallControlButton(imageInset:UIEdgeInsets(top: numpad_icon_padding, left: numpad_icon_padding, bottom: numpad_icon_padding, right: numpad_icon_padding), buttonTheme: VoipTheme.call_numpad, onClickAction: { + self.numpadView?.removeFromSuperview() + self.shadingMask.isHidden = false + self.numpadView = NumpadView(superView: self.view,callData: self.callData!, onDismissAction: { + self.numpadView?.removeFromSuperview() + self.shadingMask.isHidden = true + }) + }) + view.addSubview(showNumPad!) + showNumPad?.alignParentRight(withMargin:SharedLayoutConstants.margin_call_view_side_controls_buttons).alignParentBottom(withMargin:SharedLayoutConstants.buttons_bottom_margin).done() + showNumPad!.isHidden = true + + // Audio Routes + let audioRoutesView = AudioRoutesView() + view.addSubview(audioRoutesView) + audioRoutesView.alignBottomWith(otherView: controlsView).done() + ControlsViewModel.shared.audioRoutesSelected.readCurrentAndObserve { (audioRoutesSelected) in + audioRoutesView.isHidden = audioRoutesSelected != true + } + audioRoutesView.alignAbove(view:controlsView,withMargin:SharedLayoutConstants.buttons_bottom_margin).matchRightOf(view: controlsView, withMargin:+ControlsView.controls_button_spacing).done() + } + + + override func viewWillAppear(_ animated: Bool) { + ControlsViewModel.shared.audioRoutesSelected.value = false + super.viewWillAppear(animated) + } + + @objc override func setCall(call:OpaquePointer) { + super.setCall(call: call) + self.callData?.outgoingEarlyMedia.readCurrentAndObserve(onChange: { (outgoingEM) in + self.showNumPad!.isHidden = outgoingEM != true + }) + callData?.isOutgoing.readCurrentAndObserve { (outgoing) in + if (outgoing != true) { + PhoneMainView.instance().popView(self.compositeViewDescription()) + } + } + } + + +} diff --git a/Classes/Voip/Views/Fragments/ActiveCall/ActiveCallView.swift b/Classes/Voip/Views/Fragments/ActiveCall/ActiveCallView.swift new file mode 100644 index 000000000..2b8194b70 --- /dev/null +++ b/Classes/Voip/Views/Fragments/ActiveCall/ActiveCallView.swift @@ -0,0 +1,231 @@ +/* + * 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 . + */ + + +import UIKit +import Foundation +import SnapKit +import linphonesw + +class ActiveCallView: UIView { // = currentCall + + // Layout constants : + static let top_displayname_margin_top = 20.0 + let sip_address_margin_top = 4.0 + static let remote_recording_margin_top = 10.0 + static let remote_recording_height = 30 + static let bottom_displayname_margin_bottom = 10.0 + static let bottom_displayname_margin_left = 12.0 + static let center_view_margin_top = 15.0 + static let center_view_corner_radius = 20.0 + let record_pause_button_size = 40 + let record_pause_button_inset = UIEdgeInsets(top: 7, left: 7, bottom: 7, right: 7) + let record_pause_button_margin = 10.0 + static let local_video_width = 150.0 + static let local_video_margins = 15.0 + + + let displayNameTop = StyledLabel(VoipTheme.call_display_name_duration) + let duration = CallTimer(nil, VoipTheme.call_display_name_duration) + let sipAddress = StyledLabel(VoipTheme.call_sip_address) + let remotelyRecordedIndicator = RemotelyRecordingView(height: ActiveCallView.remote_recording_height,text: VoipTexts.call_remote_recording) + let avatar = Avatar(diameter: CGFloat(Avatar.diameter_for_call_views), color:VoipTheme.voipBackgroundColor, textStyle: VoipTheme.call_generated_avatar_large) + let displayNameBottom = StyledLabel(VoipTheme.call_remote_name) + var recordCallButtons : [CallControlButton] = [] + var pauseCallButtons : [CallControlButton] = [] + let remoteVideo = UIView() + let localVideo = LocalVideoView(width: local_video_width) + + var callData: CallData? = nil { + didSet { + duration.call = callData?.call + callData?.call.remoteAddress.map { + avatar.fillFromAddress(address: $0) + if let displayName = $0.addressBookEnhancedDisplayName() { + displayNameTop.text = displayName+" - " + displayNameBottom.text = displayName + } + sipAddress.text = $0.asStringUriOnly() + } + self.remotelyRecordedIndicator.isRemotelyRecorded = callData?.isRemotelyRecorded + callData?.isRecording.readCurrentAndObserve { (selected) in + self.recordCallButtons.forEach { + $0.isSelected = selected == true + } + } + callData?.isPaused.readCurrentAndObserve { (paused) in + self.pauseCallButtons.forEach { + $0.isSelected = paused == true + } + if (paused == true) { + self.localVideo.isHidden = true + } + } + + Core.get().nativeVideoWindow = remoteVideo + Core.get().nativePreviewWindow = localVideo + + ControlsViewModel.shared.isVideoEnabled.readCurrentAndObserve{ (video) in + self.remoteVideo.isHidden = video != true + self.localVideo.isHidden = video != true + self.recordCallButtons.first?.isHidden = video != true + self.pauseCallButtons.first?.isHidden = video != true + self.recordCallButtons.last?.isHidden = video == true + self.pauseCallButtons.last?.isHidden = video == true + } + + } + } + + init() { + super.init(frame: .zero) + let stack = UIStackView() + stack.distribution = .equalSpacing + stack.alignment = .bottom + stack.spacing = record_pause_button_margin + stack.axis = .vertical + + let displayNameDurationSipAddress = UIView() + + displayNameDurationSipAddress.addSubview(displayNameTop) + displayNameTop.alignParentLeft().done() + + displayNameDurationSipAddress.addSubview(duration) + duration.toRightOf(displayNameTop).alignParentRight().done() + + displayNameDurationSipAddress.addSubview(sipAddress) + sipAddress.matchParentSideBorders().alignUnder(view: displayNameTop,withMargin:sip_address_margin_top).done() + + let upperSection = UIStackView() + upperSection.distribution = .equalSpacing + upperSection.alignment = .center + upperSection.spacing = record_pause_button_margin + upperSection.axis = .horizontal + + upperSection.addArrangedSubview(displayNameDurationSipAddress) + displayNameDurationSipAddress.wrapContentY().done() + + let recordPauseView = UIStackView() + recordPauseView.spacing = record_pause_button_margin + + // Record (with video) + var recordCall = CallControlButton(width: record_pause_button_size, height: record_pause_button_size, imageInset:record_pause_button_inset, buttonTheme: VoipTheme.call_record, onClickAction: { + self.callData.map { $0.toggleRecord() } + }) + recordCallButtons.append(recordCall) + recordPauseView.addArrangedSubview(recordCall) + + // Pause (with video) + var pauseCall = CallControlButton(width: record_pause_button_size, height: record_pause_button_size, imageInset:record_pause_button_inset, buttonTheme: VoipTheme.call_pause, onClickAction: { + self.callData.map { $0.togglePause() } + }) + pauseCallButtons.append(pauseCall) + recordPauseView.addArrangedSubview(pauseCall) + upperSection.addArrangedSubview(recordPauseView) + + + stack.addArrangedSubview(upperSection) + upperSection.matchParentSideBorders().alignParentTop(withMargin:ActiveCallView.top_displayname_margin_top).done() + + + stack.addArrangedSubview(remotelyRecordedIndicator) + remotelyRecordedIndicator.matchParentSideBorders().alignUnder(view:upperSection, withMargin:ActiveCallView.remote_recording_margin_top).height(CGFloat(ActiveCallView.remote_recording_height)).done() + + // Center Section : Avatar + video + record/pause buttons + videos + let centerSection = UIView() + centerSection.layer.cornerRadius = ActiveCallView.center_view_corner_radius + centerSection.clipsToBounds = true + centerSection.backgroundColor = VoipTheme.voipParticipantBackgroundColor.get() + + // Record (w/o video) + recordCall = CallControlButton(width: record_pause_button_size, height: record_pause_button_size, imageInset:record_pause_button_inset, buttonTheme: VoipTheme.call_record, onClickAction: { + self.callData.map { $0.toggleRecord() } + }) + recordCallButtons.append(recordCall) + centerSection.addSubview(recordCall) + recordCall.alignParentLeft(withMargin:record_pause_button_margin).alignParentTop(withMargin:record_pause_button_margin).done() + + // Pause (w/o video) + pauseCall = CallControlButton(width: record_pause_button_size, height: record_pause_button_size, imageInset:record_pause_button_inset, buttonTheme: VoipTheme.call_pause, onClickAction: { + self.callData.map { $0.togglePause() } + }) + pauseCallButtons.append(pauseCall) + centerSection.addSubview(pauseCall) + pauseCall.alignParentRight(withMargin:record_pause_button_margin).alignParentTop(withMargin:record_pause_button_margin).done() + + // Avatar + centerSection.addSubview(avatar) + avatar.square(Avatar.diameter_for_call_views).center().done() + + // Remote Video Display + centerSection.addSubview(remoteVideo) + remoteVideo.isHidden = true + remoteVideo.matchParentDimmensions().done() + + // Local Video Display + centerSection.addSubview(localVideo) + localVideo.backgroundColor = .black + localVideo.alignParentBottom(withMargin: ActiveCallView.local_video_margins).alignParentRight(withMargin: ActiveCallView.local_video_margins).done() + localVideo.isHidden = true + localVideo.dragZone = centerSection + + // Full screen video togggle + remoteVideo.onClick { + ControlsViewModel.shared.toggleFullScreen() + } + ControlsViewModel.shared.fullScreenMode.observe { (fullScreen) in + if (self.isHidden) { + return + } + self.remoteVideo.removeConstraints().done() + self.localVideo.removeConstraints().done() + if (fullScreen == true) { + self.remoteVideo.removeFromSuperview() + self.localVideo.removeFromSuperview() + PhoneMainView.instance().mainViewController.view?.addSubview(self.remoteVideo) + PhoneMainView.instance().mainViewController.view?.addSubview(self.localVideo) + } else { + self.remoteVideo.removeFromSuperview() + self.localVideo.removeFromSuperview() + centerSection.addSubview(self.remoteVideo) + centerSection.addSubview(self.localVideo) + } + self.remoteVideo.matchParentDimmensions().done() + self.localVideo.alignParentBottom(withMargin: ActiveCallView.local_video_margins).alignParentRight(withMargin: ActiveCallView.local_video_margins).done() + self.localVideo.setSizeConstraint() + } + + + // Bottom display name + centerSection.addSubview(displayNameBottom) + displayNameBottom.alignParentLeft(withMargin:ActiveCallView.bottom_displayname_margin_left).alignParentRight().alignParentBottom(withMargin:ActiveCallView.bottom_displayname_margin_bottom).done() + + stack.addArrangedSubview(centerSection) + centerSection.matchParentSideBorders().alignUnder(view:upperSection,withMargin: ActiveCallView.center_view_margin_top).alignParentBottom().done() + + addSubview(stack) + stack.matchParentDimmensions().done() + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/Classes/Voip/Views/Fragments/AudioRoutesView.swift b/Classes/Voip/Views/Fragments/AudioRoutesView.swift new file mode 100644 index 000000000..1618e3727 --- /dev/null +++ b/Classes/Voip/Views/Fragments/AudioRoutesView.swift @@ -0,0 +1,80 @@ +/* + * 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 . + */ + +import Foundation +import UIKit + +class AudioRoutesView: UIStackView { + + // Layout constants + let corner_radius = 6.7 + let margin = 10.0 + + init () { + super.init(frame: .zero) + axis = .vertical + distribution = .equalCentering + alignment = .center + spacing = ControlsView.controls_button_spacing + backgroundColor = VoipTheme.voip_gray + layer.cornerRadius = corner_radius + clipsToBounds = true + + // bluetooth + let blueTooth = CallControlButton(buttonTheme: VoipTheme.route_bluetooth, onClickAction: { + ControlsViewModel.shared.forceBluetoothAudioRoute() + }) + addArrangedSubview(blueTooth) + + ControlsViewModel.shared.isBluetoothHeadsetSelected.readCurrentAndObserve { (selected) in + blueTooth.isSelected = selected == true + } + + // Earpiece + let earpiece = CallControlButton(buttonTheme: VoipTheme.route_earpiece, onClickAction: { + ControlsViewModel.shared.forceEarpieceAudioRoute() + }) + addArrangedSubview(earpiece) + ControlsViewModel.shared.isSpeakerSelected.readCurrentAndObserve { (isSpeakerSelected) in + earpiece.isSelected = isSpeakerSelected != true && ControlsViewModel.shared.isBluetoothHeadsetSelected.value != true + } + ControlsViewModel.shared.isBluetoothHeadsetSelected.readCurrentAndObserve { (isBluetoothHeadsetSelected) in + earpiece.isSelected = isBluetoothHeadsetSelected != true && ControlsViewModel.shared.isSpeakerSelected.value != true + } + + // Speaker + let speaker = CallControlButton(buttonTheme: VoipTheme.route_speaker, onClickAction: { + ControlsViewModel.shared.forceSpeakerAudioRoute() + }) + addArrangedSubview(speaker) + ControlsViewModel.shared.isSpeakerSelected.readCurrentAndObserve { (selected) in + speaker.isSelected = selected == true + } + + size(w:CGFloat(CallControlButton.default_size)+margin, h : 3*CGFloat(CallControlButton.default_size)+2*CGFloat(ControlsView.controls_button_spacing)+margin).done() + + } + + required init(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + + diff --git a/Classes/Voip/Views/Fragments/CallStatsView.swift b/Classes/Voip/Views/Fragments/CallStatsView.swift new file mode 100644 index 000000000..142cc03d6 --- /dev/null +++ b/Classes/Voip/Views/Fragments/CallStatsView.swift @@ -0,0 +1,105 @@ +/* + * 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 . + */ + +import Foundation +import linphonesw + +@objc class CallStatsView: UIView { + + // Layout constants + let side_margins = 10.0 + let margin_top = 50 + let corner_radius = 20.0 + let view_height = 600 + let audio_video_margin = 20 + + init(superView:UIView, callData:CallData, onDismissAction : @escaping ()->Void) { + super.init(frame:.zero) + backgroundColor = VoipTheme.voip_translucent_popup_background + layer.cornerRadius = corner_radius + clipsToBounds = true + superView.addSubview(self) + snp.makeConstraints { make in + make.left.equalToSuperview().offset(side_margins) + make.right.equalToSuperview().offset(-side_margins) + make.height.equalTo(view_height) + make.bottom.equalToSuperview().offset(-side_margins) + } + callData.callState.observe { state in + if (state == Call.State.End) { + onDismissAction() + } + } + + let hide = CallControlButton(buttonTheme: VoipTheme.voip_cancel_light, onClickAction: { + onDismissAction() + }) + addSubview(hide) + hide.alignParentRight(withMargin: side_margins).alignParentTop(withMargin: side_margins).done() + + + let model = CallStatisticsData(call: callData.call) + let audioTitle = StyledLabel(VoipTheme.call_stats_font_title,NSLocalizedString("Audio", comment: "")) + addSubview(audioTitle) + audioTitle.matchParentSideBorders().alignParentTop(withMargin: margin_top).done() + + let audioStats = StyledLabel(VoipTheme.call_stats_font) + + audioStats.numberOfLines = 0 + addSubview(audioStats) + audioStats.matchParentSideBorders().alignUnder(view: audioTitle).done() + + let videoTitle = StyledLabel(VoipTheme.call_stats_font_title,NSLocalizedString("Video", comment: "")) + addSubview(videoTitle) + videoTitle.alignUnder(view: audioStats, withMargin:audio_video_margin).matchParentSideBorders().done() + + let videoStats = StyledLabel(VoipTheme.call_stats_font) + + videoStats.numberOfLines = 0 + addSubview(videoStats) + videoStats.matchParentSideBorders().alignUnder(view: videoTitle).done() + + model.isVideoEnabled.readCurrentAndObserve { (video) in + videoTitle.isHidden = video != true + videoStats.isHidden = video != true + } + + model.statsUpdated.readCurrentAndObserve { (updated) in + var stats = "" + model.audioStats.forEach { + stats += "\n\($0.getTypeTitle())\($0.value.value ?? "n/a")" + } + audioStats.text = stats + stats = "" + model.videoStats.forEach { + stats += "\n\($0.getTypeTitle())\($0.value.value ?? "n/a")" + } + videoStats.text = stats + } + + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + } + +} + + + diff --git a/Classes/Voip/Views/Fragments/CallsList/CallsListView.swift b/Classes/Voip/Views/Fragments/CallsList/CallsListView.swift new file mode 100644 index 000000000..6fad29f47 --- /dev/null +++ b/Classes/Voip/Views/Fragments/CallsList/CallsListView.swift @@ -0,0 +1,139 @@ +/* + * 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 . + */ + + +import UIKit +import Foundation +import linphonesw + +@objc class CallsListView: DismissableView, UITableViewDataSource { + + // Layout constants + let buttons_distance_from_center_x = 38 + let buttons_size = 60 + + let callsListTableView = UITableView() + let menuView = VoipCallContextMenu() + + var callsDataObserver : MutableLiveDataOnChangeClosure<[CallData]>? = nil + + init() { + super.init(title: VoipTexts.call_action_calls_list) + + // New Call + let newCall = CallControlButton(width: buttons_size,height: buttons_size, buttonTheme: VoipTheme.call_add, onClickAction: { + let view: DialerView = self.VIEW(DialerView.compositeViewDescription()); + view.setAddress("") + CallManager.instance().nextCallIsTransfer = false + PhoneMainView.instance().changeCurrentView(view.compositeViewDescription()) + }) + addSubview(newCall) + newCall.centerX(withDx: -buttons_distance_from_center_x).alignParentBottom(withMargin:SharedLayoutConstants.buttons_bottom_margin).done() + + // Merge Calls + let mergeIntoLocalConference = CallControlButton(width: buttons_size,height: buttons_size, buttonTheme: VoipTheme.call_merge, onClickAction: { + self.removeFromSuperview() + CallsViewModel.shared.mergeCallsIntoLocalConference() + }) + addSubview(mergeIntoLocalConference) + mergeIntoLocalConference.centerX(withDx: buttons_distance_from_center_x).alignParentBottom(withMargin:SharedLayoutConstants.buttons_bottom_margin).done() + + + CallsViewModel.shared.callsData.readCurrentAndObserve{ (callsData) in + if let callsData = callsData { + mergeIntoLocalConference.isEnabled = callsData.count >= 2 && Core.get().conference?.isIn != true + } else { + mergeIntoLocalConference.isEnabled = false + } + self.callsListTableView.reloadData() + } + + + // CallsList + super.contentView.addSubview(callsListTableView) + callsListTableView.matchParentDimmensions().done() + callsListTableView.dataSource = self + callsListTableView.register(VoipCallCell.self, forCellReuseIdentifier: "VoipCallCell") + callsListTableView.allowsSelection = false + if #available(iOS 15.0, *) { + callsListTableView.allowsFocus = false + } + callsListTableView.separatorStyle = .singleLine + callsListTableView.separatorColor = .white + callsListTableView.onClick { + self.hideMenu() + } + + // Floating menu + super.contentView.addSubview(menuView) + + menuView.isHidden = true + + } + + + func toggleMenu(forCell:VoipCallCell) { + if (menuView.isHidden) { + showMenu(forCell: forCell) + } else if (menuView.callData?.call.callLog?.callId != forCell.callData?.call.callLog?.callId) { + hideMenu() + showMenu(forCell: forCell) + } else { + hideMenu() + } + } + + func showMenu(forCell:VoipCallCell) { + menuView.removeConstraints().alignUnder(view: forCell).alignParentRight().done() + menuView.callData = forCell.callData + menuView.isHidden = false + } + + func hideMenu() { + menuView.isHidden = true + } + + // TableView datasource delegate + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + guard let callsData = CallsViewModel.shared.callsData.value else { + return 0 + } + return callsData.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell:VoipCallCell = tableView.dequeueReusableCell(withIdentifier: "VoipCallCell") as! VoipCallCell + guard let callData = CallsViewModel.shared.callsData.value?[indexPath.row] else { + return cell + } + cell.selectionStyle = .none + cell.callData = callData + cell.owningCallsListView = self + return cell + } + + // View controller + + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/Classes/Voip/Views/Fragments/CallsList/VoipCallCell.swift b/Classes/Voip/Views/Fragments/CallsList/VoipCallCell.swift new file mode 100644 index 000000000..22c92def4 --- /dev/null +++ b/Classes/Voip/Views/Fragments/CallsList/VoipCallCell.swift @@ -0,0 +1,106 @@ +/* + * 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 . + */ + + +import UIKit +import Foundation +import SnapKit +import linphonesw + +class VoipCallCell: UITableViewCell { + + // Layout Constants + let cell_height = 80.0 + let call_status_icon_size = 65.0 + static let avatar_size = 45.0 + let avatar_left_margin = 40.0 + let texts_left_margin = 20.0 + let side_menu_icon_size = 80.0 + + + var onMenuClickAction : (()->Void) = {} + let callStatusIcon = UIImageView() + let avatar = Avatar(diameter:VoipCallCell.avatar_size,color:LightDarkColor(VoipTheme.voip_contact_avatar_calls_list,VoipTheme.voip_contact_avatar_calls_list), textStyle: VoipTheme.call_generated_avatar_small) + let conferenceAvatar = UIImageView(image:UIImage(named:"voip_multiple_contacts_avatar")) + let displayName = StyledLabel(VoipTheme.call_list_active_name_font) + let sipAddress = StyledLabel(VoipTheme.call_list_active_sip_uri_font) + var menuButton : CallControlButton? = nil + var owningCallsListView : CallsListView? = nil + + var callData: CallData? = nil { + didSet { + if let data = callData { + contentView.backgroundColor = data.isPaused.value == true ? VoipTheme.voip_calls_list_inactive_background : VoipTheme.voip_dark_gray + callStatusIcon.image = + data.isIncoming.value == true ? UIImage(named:"voip_call_header_incoming") : + data.isOutgoing.value == true ? UIImage(named:"voip_call_header_outgoing") : + data.isPaused.value == true ? UIImage(named:"voip_call_header_paused") : + UIImage(named:"voip_call_header_active") + if (data.isInRemoteConference.value == true) { + avatar.isHidden = true + conferenceAvatar.isHidden = false + displayName.text = data.remoteConferenceSubject.value + } else { + displayName.text = data.call.remoteAddress?.addressBookEnhancedDisplayName() + avatar.fillFromAddress(address: data.call.remoteAddress!) + avatar.isHidden = false + conferenceAvatar.isHidden = true + } + sipAddress.text = data.call.remoteAddress?.asStringUriOnly() + displayName.applyStyle(data.isPaused.value == true ? VoipTheme.call_list_name_font : VoipTheme.call_list_active_name_font) + sipAddress.applyStyle(data.isPaused.value == true ? VoipTheme.call_list_sip_uri_font : VoipTheme.call_list_active_sip_uri_font) + menuButton?.applyTintedIcons(tintedIcons: data.isPaused.value == true ? VoipTheme.voip_call_list_menu.tintableStateIcons : VoipTheme.voip_call_list_active_menu.tintableStateIcons) + } + } + } + + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + contentView.height(cell_height).matchParentSideBorders().done() + + contentView.addSubview(callStatusIcon) + callStatusIcon.size(w: call_status_icon_size, h: call_status_icon_size).done() + + contentView.addSubview(avatar) + avatar.size(w: VoipCallCell.avatar_size, h: VoipCallCell.avatar_size).centerY().alignParentLeft(withMargin: avatar_left_margin).done() + + contentView.addSubview(conferenceAvatar) + conferenceAvatar.size(w: VoipCallCell.avatar_size, h: VoipCallCell.avatar_size).centerY().alignParentLeft(withMargin: avatar_left_margin).done() + + let nameAddress = UIView() + nameAddress.addSubview(displayName) + nameAddress.addSubview(sipAddress) + displayName.alignParentTop().done() + sipAddress.alignUnder(view: displayName).done() + contentView.addSubview(nameAddress) + nameAddress.toRightOf(avatar,withLeftMargin:texts_left_margin).toRightOf(conferenceAvatar,withLeftMargin:texts_left_margin).wrapContentY().centerY().done() + + menuButton = CallControlButton(buttonTheme: VoipTheme.voip_call_list_active_menu, onClickAction: { + self.owningCallsListView?.toggleMenu(forCell: self) + }) + contentView.addSubview(menuButton!) + menuButton!.size(w: side_menu_icon_size, h: side_menu_icon_size).alignParentRight().centerY().done() + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/Classes/Voip/Views/Fragments/CallsList/VoipCallContextMenu.swift b/Classes/Voip/Views/Fragments/CallsList/VoipCallContextMenu.swift new file mode 100644 index 000000000..79cdaa063 --- /dev/null +++ b/Classes/Voip/Views/Fragments/CallsList/VoipCallContextMenu.swift @@ -0,0 +1,160 @@ +/* + * 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 . + */ + + +import UIKit +import Foundation +import SnapKit +import linphonesw + +class VoipCallContextMenu: UIStackView { + + //Layout constants + static let item_height = 50.0 + let width = 250.0 + let margin_bw_items = 1.0 + static let texts_margin_left = 10.0 + + + let resume : ButtonWithStateBackgrounds + let pause : ButtonWithStateBackgrounds + let transfer : ButtonWithStateBackgrounds + let answer : ButtonWithStateBackgrounds + let terminate : ButtonWithStateBackgrounds + + var callData: CallData? = nil { + didSet { + callData?.callState.readCurrentAndObserve(onChange: { (state) in + self.resume.isHidden = false + self.pause.isHidden = false + self.transfer.isHidden = false + self.answer.isHidden = false + self.terminate.isHidden = false + var count = 5.0 + + if let callData = self.callData { + if (callData.isPaused.value == true || + callData.isIncoming.value == true || + callData.isOutgoing.value == true || + callData.isInRemoteConference.value == true + ) { + self.pause.isHidden = true + count -= 1 + } + + if (callData.isIncoming.value == true || + callData.isOutgoing.value == true || + callData.isInRemoteConference.value == true + ) { + self.resume.isHidden = true + self.transfer.isHidden = true + count -= 2 + } else if (callData.isPaused.value == false) { + self.resume.isHidden = true + count -= 1 + } + + if (callData.isIncoming.value == false) { + count -= 1 + self.answer.isHidden = true + } + self.size(w:self.width,h:count*VoipCallContextMenu.item_height).done() + } + + }) + } + } + + + init () { + + resume = VoipCallContextMenu.getButton(title: VoipTexts.call_context_action_resume) + pause = VoipCallContextMenu.getButton(title: VoipTexts.call_context_action_pause) + transfer = VoipCallContextMenu.getButton(title: VoipTexts.call_context_action_transfer) + answer = VoipCallContextMenu.getButton(title: VoipTexts.call_context_action_answer) + terminate = VoipCallContextMenu.getButton(title: VoipTexts.call_context_action_hangup) + + super.init(frame: .zero) + backgroundColor = .white + axis = .vertical + spacing = margin_bw_items + + + addArrangedSubview(resume) + addArrangedSubview(pause) + addArrangedSubview(transfer) + addArrangedSubview(answer) + addArrangedSubview(terminate) + + resume.onClick { + self.isHidden = true + guard let call = self.callData?.call else { return } + if (CallManager.callKitEnabled()) { + CallManager.instance().setHeld(call:call,hold:false); + } else { + try?call.resume() + } + } + pause.onClick { + self.isHidden = true + guard let call = self.callData?.call else { return } + if (CallManager.callKitEnabled()) { + CallManager.instance().setHeld(call:call,hold:true); + } else { + try?call.pause() + } + } + transfer.onClick { + let view: DialerView = self.VIEW(DialerView.compositeViewDescription()); + view.setAddress("") + CallManager.instance().nextCallIsTransfer = true + PhoneMainView.instance().changeCurrentView(view.compositeViewDescription()) + } + answer.onClick { + self.isHidden = true + guard let call = self.callData?.call else { return } + if (CallManager.callKitEnabled()) { + CallManager.instance().acceptCall(call: call, hasVideo: false) + } else { + try?call.accept() + } + } + terminate.onClick { + self.isHidden = true + guard let call = self.callData?.call else { return } + try?call.terminate() + } + } + + required init(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + static func getButton(title:String) -> ButtonWithStateBackgrounds { + let button = ButtonWithStateBackgrounds(backgroundStateColors: VoipTheme.button_call_context_menu_background) + button.setTitle(title, for: .normal) + button.applyTitleStyle(VoipTheme.call_context_menu_item_font) + button.titleEdgeInsets = UIEdgeInsets(top: 0, left: texts_margin_left, bottom: 0, right: 0) + button.height(VoipCallContextMenu.item_height).done() + return button + } + + + +} diff --git a/Classes/Voip/Views/Fragments/Conference/VoipActiveSpeakerParticipantCell.swift b/Classes/Voip/Views/Fragments/Conference/VoipActiveSpeakerParticipantCell.swift new file mode 100644 index 000000000..02c2f384f --- /dev/null +++ b/Classes/Voip/Views/Fragments/Conference/VoipActiveSpeakerParticipantCell.swift @@ -0,0 +1,121 @@ +/* + * 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 . + */ + + +import UIKit +import Foundation +import SnapKit +import linphonesw + +class VoipActiveSpeakerParticipantCell: UICollectionViewCell { + + // Layout Constants + let corner_radius = 20.0 + static let avatar_size = 80.0 + let switch_camera_button_margins = 8.0 + let switch_camera_button_size = 30 + + + let videoView = UIView() + let avatar = Avatar(diameter:VoipGridParticipantCell.avatar_size,color:VoipTheme.voipBackgroundColor, textStyle: VoipTheme.call_generated_avatar_medium) + let pause = UIImageView(image: UIImage(named: "voip_pause")?.tinted(with: .white)) + let switchCamera = UIImageView(image: UIImage(named:"voip_change_camera")?.tinted(with:.white)) + let displayName = StyledLabel(VoipTheme.conference_participant_name_font_as) + let pauseLabel = StyledLabel(VoipTheme.conference_participant_name_font_as,VoipTexts.conference_participant_paused) + + var participantData: ConferenceParticipantDeviceData? = nil { + didSet { + if let data = participantData { + data.isInConference.readCurrentAndObserve { (isIn) in + self.updateBackground() + self.pause.isHidden = isIn == true + self.pauseLabel.isHidden = self.pause.isHidden + } + data.videoEnabled.readCurrentAndObserve { (videoEnabled) in + self.updateBackground() + if (videoEnabled == true) { + data.setVideoView(view: self.videoView) + self.avatar.isHidden = true + } else { + self.avatar.isHidden = false + } + self.switchCamera.isHidden = videoEnabled != true || !data.isSwitchCameraAvailable() + } + data.participantDevice.address.map { + avatar.fillFromAddress(address: $0) + if let displayName = $0.addressBookEnhancedDisplayName() { + self.displayName.text = displayName + } + } + } + } + } + + func updateBackground() { + if let data = participantData { + if (data.isInConference.value != true) { + self.contentView.backgroundColor = VoipTheme.voip_conference_participant_paused_background + } else if (data.videoEnabled.value == true) { + self.contentView.backgroundColor = .black + } else { + self.contentView.backgroundColor = VoipTheme.voipParticipantBackgroundColor.get() + + } + } + } + + + override init(frame:CGRect) { + super.init(frame:.zero) + layer.cornerRadius = corner_radius + clipsToBounds = true + + contentView.addSubview(videoView) + videoView.matchParentDimmensions().done() + + contentView.addSubview(avatar) + avatar.size(w: VoipGridParticipantCell.avatar_size, h: VoipGridParticipantCell.avatar_size).center().done() + + contentView.addSubview(pause) + pause.layer.cornerRadius = VoipGridParticipantCell.avatar_size/2 + pause.clipsToBounds = true + pause.backgroundColor = VoipTheme.voip_gray + pause.size(w: VoipGridParticipantCell.avatar_size, h: VoipGridParticipantCell.avatar_size).center().done() + + contentView.addSubview(switchCamera) + switchCamera.alignParentTop(withMargin: switch_camera_button_margins).alignParentRight(withMargin: switch_camera_button_margins).square(switch_camera_button_size).done() + switchCamera.contentMode = .scaleAspectFit + + switchCamera.onClick { + Core.get().toggleCamera() + } + + contentView.addSubview(displayName) + displayName.matchParentSideBorders(insetedByDx:ActiveCallView.bottom_displayname_margin_left).alignParentBottom(withMargin:ActiveCallView.bottom_displayname_margin_bottom).done() + + contentView.addSubview(pauseLabel) + pauseLabel.toRightOf(displayName).alignParentBottom(withMargin:ActiveCallView.bottom_displayname_margin_bottom).done() + + contentView.matchParentDimmensions().done() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/Classes/Voip/Views/Fragments/Conference/VoipConferenceActiveSpeakerView.swift b/Classes/Voip/Views/Fragments/Conference/VoipConferenceActiveSpeakerView.swift new file mode 100644 index 000000000..e0bc2d937 --- /dev/null +++ b/Classes/Voip/Views/Fragments/Conference/VoipConferenceActiveSpeakerView.swift @@ -0,0 +1,260 @@ +/* + * 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 . + */ + + +import UIKit +import Foundation +import SnapKit +import linphonesw + +class VoipConferenceActiveSpeakerView: UIView, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout { + + // Layout constants : + let inter_cell = 10.0 + let record_pause_button_margin = 10.0 + let duration_margin_top = 4.0 + let record_pause_button_size = 40 + let record_pause_button_inset = UIEdgeInsets(top: 7, left: 7, bottom: 7, right: 7) + let grid_height = 150.0 + let cell_width = 100.0 + + + let subjectLabel = StyledLabel(VoipTheme.call_display_name_duration) + let duration = CallTimer(nil, VoipTheme.call_display_name_duration) + + let remotelyRecording = RemotelyRecordingView(height: ActiveCallView.remote_recording_height,text: VoipTexts.call_remote_recording) + var recordCallButtons : [CallControlButton] = [] + var pauseCallButtons : [CallControlButton] = [] + + let activeSpeakerView = UIView() + let activeSpeakerVideoView = UIView() + let activeSpeakerAvatar = Avatar(diameter: CGFloat(Avatar.diameter_for_call_views), color:VoipTheme.voipBackgroundColor, textStyle: VoipTheme.call_generated_avatar_large) + let activeSpeakerDisplayName = StyledLabel(VoipTheme.call_remote_name) + var activeSpeakerMonitorTimer : Timer? = nil + + var grid : UICollectionView + + + var conferenceViewModel: ConferenceViewModel? = nil { + didSet { + if let model = conferenceViewModel { + model.subject.readCurrentAndObserve { (subject) in + self.subjectLabel.text = subject + } + duration.conference = model.conference.value + self.remotelyRecording.isRemotelyRecorded = model.isRemotelyRecorded + model.conferenceParticipantDevices.readCurrentAndObserve { (_) in + self.grid.reloadData() + } + model.isConferencePaused.readCurrentAndObserve { (paused) in + self.pauseCallButtons.forEach { + $0.isSelected = paused == true + } + } + model.isRecording.readCurrentAndObserve { (selected) in + self.recordCallButtons.forEach { + $0.isSelected = selected == true + } + } + Core.get().nativeVideoWindow = self.activeSpeakerVideoView + activeSpeakerMonitorTimer?.invalidate() + activeSpeakerMonitorTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in + var thereIsAnActiveSpeaker = false + model.conferenceParticipantDevices.value?.forEach { (data) in + if (data.activeSpeaker.value == true) { + thereIsAnActiveSpeaker = true + data.participantDevice.address.map { + self.activeSpeakerAvatar.isHidden = false + self.activeSpeakerAvatar.fillFromAddress(address: $0) + self.activeSpeakerDisplayName.text = $0.addressBookEnhancedDisplayName() + } + self.activeSpeakerVideoView.isHidden = data.videoEnabled.value != true + return + } + } + if (!thereIsAnActiveSpeaker) { + self.activeSpeakerAvatar.isHidden = true + self.activeSpeakerVideoView.isHidden = true + self.activeSpeakerDisplayName.text = VoipTexts.conference_display_no_active_speaker + } + } + } else { + activeSpeakerMonitorTimer?.invalidate() + } + self.grid.reloadData() + + } + } + + init() { + + let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout() + layout.minimumInteritemSpacing = 0 + layout.minimumLineSpacing = 0 + layout.scrollDirection = .horizontal + layout.itemSize = CGSize(width:cell_width, height:grid_height) + grid = UICollectionView(frame:.zero, collectionViewLayout: layout) + + super.init(frame: .zero) + + let headerView = UIStackView() + addSubview(headerView) + headerView.matchParentSideBorders().alignParentTop().done() + + headerView.distribution = .equalSpacing + headerView.alignment = .bottom + headerView.spacing = record_pause_button_margin + headerView.axis = .vertical + + let subjectDuration = UIView() + + subjectDuration.addSubview(subjectLabel) + subjectLabel.alignParentLeft().done() + + subjectDuration.addSubview(duration) + duration.alignParentLeft().alignUnder(view: subjectLabel,withMargin:duration_margin_top).done() + + let upperSection = UIStackView() + upperSection.distribution = .equalSpacing + upperSection.alignment = .center + upperSection.spacing = record_pause_button_margin + upperSection.axis = .horizontal + + upperSection.addArrangedSubview(subjectDuration) + subjectDuration.wrapContentY().done() + + // Record (with video) + let recordCall = CallControlButton(width: record_pause_button_size, height: record_pause_button_size, imageInset:record_pause_button_inset, buttonTheme: VoipTheme.call_record, onClickAction: { + self.conferenceViewModel?.toggleRecording() + }) + + let recordPauseView = UIStackView() + recordPauseView.spacing = record_pause_button_margin + recordCallButtons.append(recordCall) + recordPauseView.addArrangedSubview(recordCall) + + // Pause (with video) + let pauseCall = CallControlButton(width: record_pause_button_size, height: record_pause_button_size, imageInset:record_pause_button_inset, buttonTheme: VoipTheme.call_pause, onClickAction: { + self.conferenceViewModel?.togglePlayPause() + + }) + pauseCallButtons.append(pauseCall) + recordPauseView.addArrangedSubview(pauseCall) + + upperSection.addArrangedSubview(recordPauseView) + + headerView.addArrangedSubview(upperSection) + upperSection.matchParentSideBorders().alignParentTop(withMargin:ActiveCallView.top_displayname_margin_top).done() + + headerView.addArrangedSubview(remotelyRecording) + remotelyRecording.matchParentSideBorders().alignUnder(view:upperSection, withMargin:ActiveCallView.remote_recording_margin_top).height(CGFloat(ActiveCallView.remote_recording_height)).done() + + + // Container view that can toggle full screen by ckick + let fullScreenMutableView = UIView() + addSubview(fullScreenMutableView) + fullScreenMutableView.backgroundColor = VoipTheme.voipBackgroundColor.get() + fullScreenMutableView.matchParentSideBorders().alignUnder(view:headerView,withMargin: ActiveCallView.center_view_margin_top).alignParentBottom().done() + + + // Active speaker + fullScreenMutableView.addSubview(activeSpeakerView) + activeSpeakerView.layer.cornerRadius = ActiveCallView.center_view_corner_radius + activeSpeakerView.clipsToBounds = true + activeSpeakerView.backgroundColor = VoipTheme.voipParticipantBackgroundColor.get() + activeSpeakerView.matchParentSideBorders().alignParentTop().done() + + activeSpeakerView.addSubview(activeSpeakerAvatar) + activeSpeakerAvatar.square(Avatar.diameter_for_call_views).center().done() + + activeSpeakerView.addSubview(activeSpeakerVideoView) + activeSpeakerVideoView.matchParentDimmensions().done() + + activeSpeakerView.addSubview(activeSpeakerDisplayName) + activeSpeakerDisplayName.alignParentLeft(withMargin:ActiveCallView.bottom_displayname_margin_left).alignParentRight().alignParentBottom(withMargin:ActiveCallView.bottom_displayname_margin_bottom).done() + + // CollectionView + grid.dataSource = self + grid.delegate = self + grid.register(VoipActiveSpeakerParticipantCell.self, forCellWithReuseIdentifier: "VoipActiveSpeakerParticipantCell") + grid.backgroundColor = .clear + grid.isScrollEnabled = true + fullScreenMutableView.addSubview(grid) + + grid.matchParentSideBorders().height(grid_height).alignParentBottom().alignUnder(view: activeSpeakerView, withMargin:ActiveCallView.center_view_margin_top).done() + + // Full screen video togggle + activeSpeakerView.onClick { + ControlsViewModel.shared.toggleFullScreen() + } + + ControlsViewModel.shared.fullScreenMode.observe { (fullScreen) in + if (self.isHidden) { + return + } + fullScreenMutableView.removeConstraints().done() + if (fullScreen == true) { + fullScreenMutableView.removeFromSuperview() + PhoneMainView.instance().mainViewController.view?.addSubview(fullScreenMutableView) + fullScreenMutableView.matchParentDimmensions().done() + } else { + fullScreenMutableView.removeFromSuperview() + self.addSubview(fullScreenMutableView) + fullScreenMutableView.matchParentSideBorders().alignUnder(view:headerView,withMargin: ActiveCallView.center_view_margin_top).alignParentBottom().done() + } + } + + } + + + // UICollectionView related delegates + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + return inter_cell + } + + func collectionView(_ collectionView: UICollectionView, layout + collectionViewLayout: UICollectionViewLayout, + minimumLineSpacingForSectionAt section: Int) -> CGFloat { + return inter_cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + guard let participantsCount = conferenceViewModel?.conferenceParticipantDevices.value?.count else { + return .zero + } + return participantsCount + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell:VoipActiveSpeakerParticipantCell = collectionView.dequeueReusableCell(withReuseIdentifier: "VoipActiveSpeakerParticipantCell", for: indexPath) as! VoipActiveSpeakerParticipantCell + guard let participantData = conferenceViewModel?.conferenceParticipantDevices.value?[indexPath.row] else { + return cell + } + cell.participantData = participantData + return cell + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + + +} diff --git a/Classes/Voip/Views/Fragments/Conference/VoipConferenceDisplayModeSelectionView.swift b/Classes/Voip/Views/Fragments/Conference/VoipConferenceDisplayModeSelectionView.swift new file mode 100644 index 000000000..42a694bbd --- /dev/null +++ b/Classes/Voip/Views/Fragments/Conference/VoipConferenceDisplayModeSelectionView.swift @@ -0,0 +1,140 @@ +/* + * 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 . + */ + + +import UIKit +import Foundation +import linphonesw + +@objc class VoipConferenceDisplayModeSelectionView: DismissableView, UITableViewDataSource, UITableViewDelegate{ + + // Layout constants + let buttons_distance_from_center_x = 38 + let buttons_size = 60 + + let optionsListView = UITableView() + + init() { + super.init(title: VoipTexts.call_action_change_conf_layout) + + super.contentView.addSubview(optionsListView) + optionsListView.matchParentDimmensions().done() + optionsListView.dataSource = self + optionsListView.delegate = self + optionsListView.register(ConferenceDisplayModeSelectionCell.self, forCellReuseIdentifier: "ConferenceDisplayModeSelectionCell") + optionsListView.separatorStyle = .singleLine + optionsListView.separatorColor = VoipTheme.light_grey_color + optionsListView.isScrollEnabled = false + } + + // TableView datasource delegate + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return 2 + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell:ConferenceDisplayModeSelectionCell = tableView.dequeueReusableCell(withIdentifier: "ConferenceDisplayModeSelectionCell") as! ConferenceDisplayModeSelectionCell + cell.selectionStyle = .none + if (indexPath.row == 0) { + cell.setOption(title: VoipTexts.conference_display_mode_mosaic, onSelectAction: { + ConferenceViewModel.shared.conference.value?.layout = .Grid + ConferenceViewModel.shared.conferenceDisplayMode.value = .Grid + }, image:(UIImage(named: "voip_conference_mosaic")?.tinted(with: VoipTheme.voipDrawableColor.get())!)!) + cell.isUserInteractionEnabled = ConferenceViewModel.shared.conferenceParticipantDevices.value!.count <= ConferenceViewModel.shared.maxParticipantsForMosaicLayout + } + if (indexPath.row == 1) { + cell.setOption(title: VoipTexts.conference_display_mode_active_speaker, onSelectAction: { + ConferenceViewModel.shared.conference.value?.layout = .ActiveSpeaker + ConferenceViewModel.shared.conferenceDisplayMode.value = .ActiveSpeaker + }, image:(UIImage(named: "voip_conference_active_speaker")?.tinted(with: VoipTheme.voipDrawableColor.get())!)!) + cell.isUserInteractionEnabled = true + } + + cell.separatorInset = .zero + cell.selectionStyle = .none + return cell + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let cell = tableView.cellForRow(at: indexPath) as! ConferenceDisplayModeSelectionCell + cell.onSelectAction?() + cell.isSelected = true + if (indexPath.row == 0) { + tableView.deselectRow(at: IndexPath(row: 1, section: 0), animated: false) + } + if (indexPath.row == 1) { + tableView.deselectRow(at: IndexPath(row: 0, section: 0), animated: false) + } + } + + func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) { + let cell = tableView.cellForRow(at: indexPath) as! ConferenceDisplayModeSelectionCell + cell.isSelected = false + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +class ConferenceDisplayModeSelectionCell : UITableViewCell { + + let cell_height = 60.0 + let icon_size = 40.0 + let side_margins = 20.0 + + let radio = CallControlButton(buttonTheme: VoipTheme.radio_button) + let label = StyledLabel(VoipTheme.conference_mode_title) + let icon = UIImageView() + + var onSelectAction : (()->Void)? = nil + + override var isSelected: Bool { + didSet { + radio.isSelected = isSelected + label.applyStyle(isSelected ? VoipTheme.conference_mode_title_selected : VoipTheme.conference_mode_title) + } + } + + + func setOption(title:String, onSelectAction:@escaping ()->Void, image:UIImage) { + self.onSelectAction = onSelectAction + label.text = title + icon.image = image + } + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + contentView.height(cell_height).matchParentSideBorders().done() + contentView.addSubview(radio) + radio.alignParentLeft(withMargin: side_margins).centerY().done() + contentView.addSubview(label) + label.toRightOf(radio).centerY().done() + contentView.addSubview(icon) + icon.size(w: icon_size, h: icon_size).alignParentRight(withMargin: side_margins).centerY().done() + radio.isUserInteractionEnabled = false + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/Classes/Voip/Views/Fragments/Conference/VoipConferenceGridView.swift b/Classes/Voip/Views/Fragments/Conference/VoipConferenceGridView.swift new file mode 100644 index 000000000..1b6509c03 --- /dev/null +++ b/Classes/Voip/Views/Fragments/Conference/VoipConferenceGridView.swift @@ -0,0 +1,242 @@ +/* + * 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 . + */ + + +import UIKit +import Foundation +import SnapKit +import linphonesw + +class VoipConferenceGridView: UIView, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout { + + // Layout constants : + let inter_cell = 10.0 + let record_pause_button_margin = 10.0 + let duration_margin_top = 4.0 + let record_pause_button_size = 40 + let record_pause_button_inset = UIEdgeInsets(top: 7, left: 7, bottom: 7, right: 7) + + + let subjectLabel = StyledLabel(VoipTheme.call_display_name_duration) + let duration = CallTimer(nil, VoipTheme.call_display_name_duration) + + let remotelyRecording = RemotelyRecordingView(height: ActiveCallView.remote_recording_height,text: VoipTexts.call_remote_recording) + var recordCallButtons : [CallControlButton] = [] + var pauseCallButtons : [CallControlButton] = [] + var grid : UICollectionView + + + var conferenceViewModel: ConferenceViewModel? = nil { + didSet { + if let model = conferenceViewModel { + model.subject.readCurrentAndObserve { (subject) in + self.subjectLabel.text = subject + } + duration.conference = model.conference.value + self.remotelyRecording.isRemotelyRecorded = model.isRemotelyRecorded + model.conferenceParticipantDevices.readCurrentAndObserve { (_) in + self.grid.reloadData() + } + model.isConferencePaused.readCurrentAndObserve { (paused) in + self.pauseCallButtons.forEach { + $0.isSelected = paused == true + } + } + model.isRecording.readCurrentAndObserve { (selected) in + self.recordCallButtons.forEach { + $0.isSelected = selected == true + } + } + } + self.grid.reloadData() + } + } + + init() { + + let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout() + layout.minimumInteritemSpacing = 0 + layout.minimumLineSpacing = 0 + grid = UICollectionView(frame:.zero, collectionViewLayout: layout) + + super.init(frame: .zero) + + let headerView = UIStackView() + addSubview(headerView) + + headerView.distribution = .equalSpacing + headerView.alignment = .bottom + headerView.spacing = record_pause_button_margin + headerView.axis = .vertical + + let subjectDuration = UIView() + + subjectDuration.addSubview(subjectLabel) + subjectLabel.alignParentLeft().done() + + subjectDuration.addSubview(duration) + duration.alignParentLeft().alignUnder(view: subjectLabel,withMargin:duration_margin_top).done() + + let upperSection = UIStackView() + upperSection.distribution = .equalSpacing + upperSection.alignment = .center + upperSection.spacing = record_pause_button_margin + upperSection.axis = .horizontal + + upperSection.addArrangedSubview(subjectDuration) + subjectDuration.wrapContentY().done() + + // Record (with video) + let recordCall = CallControlButton(width: record_pause_button_size, height: record_pause_button_size, imageInset:record_pause_button_inset, buttonTheme: VoipTheme.call_record, onClickAction: { + self.conferenceViewModel?.toggleRecording() + }) + + let recordPauseView = UIStackView() + recordPauseView.spacing = record_pause_button_margin + recordCallButtons.append(recordCall) + recordPauseView.addArrangedSubview(recordCall) + + // Pause (with video) + let pauseCall = CallControlButton(width: record_pause_button_size, height: record_pause_button_size, imageInset:record_pause_button_inset, buttonTheme: VoipTheme.call_pause, onClickAction: { + self.conferenceViewModel?.togglePlayPause() + + }) + pauseCallButtons.append(pauseCall) + recordPauseView.addArrangedSubview(pauseCall) + + upperSection.addArrangedSubview(recordPauseView) + + headerView.addArrangedSubview(upperSection) + upperSection.matchParentSideBorders().alignParentTop(withMargin:ActiveCallView.top_displayname_margin_top).done() + + headerView.addArrangedSubview(remotelyRecording) + remotelyRecording.matchParentSideBorders().alignUnder(view:upperSection, withMargin:ActiveCallView.remote_recording_margin_top).height(CGFloat(ActiveCallView.remote_recording_height)).done() + + // CollectionView + grid.dataSource = self + grid.delegate = self + grid.register(VoipGridParticipantCell.self, forCellWithReuseIdentifier: "VoipGridParticipantCell") + grid.backgroundColor = VoipTheme.voipBackgroundColor.get() + grid.isScrollEnabled = false + addSubview(grid) + grid.matchParentSideBorders().alignUnder(view:headerView,withMargin: ActiveCallView.center_view_margin_top).alignParentBottom().done() + + headerView.matchParentSideBorders().alignParentTop().done() + + + // Full screen video togggle + grid.onClick { + ControlsViewModel.shared.toggleFullScreen() + } + + ControlsViewModel.shared.fullScreenMode.observe { (fullScreen) in + if (self.isHidden) { + return + } + self.grid.removeConstraints().done() + if (fullScreen == true) { + self.grid.removeFromSuperview() + PhoneMainView.instance().mainViewController.view?.addSubview(self.grid) + self.grid.matchParentDimmensions().center().done() + self.grid.reloadData() // Cauz of the frames + } else { + self.grid.removeFromSuperview() + self.addSubview(self.grid) + self.grid.matchParentSideBorders().alignUnder(view:headerView,withMargin: ActiveCallView.center_view_margin_top).alignParentBottom().done() + self.grid.reloadData() + } + } + } + + + // UICollectionView related delegates + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + return inter_cell + } + + func collectionView(_ collectionView: UICollectionView, layout + collectionViewLayout: UICollectionViewLayout, + minimumLineSpacingForSectionAt section: Int) -> CGFloat { + return inter_cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + guard let participantsCount = conferenceViewModel?.conferenceParticipantDevices.value?.count else { + return .zero + } + return participantsCount + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell:VoipGridParticipantCell = collectionView.dequeueReusableCell(withReuseIdentifier: "VoipGridParticipantCell", for: indexPath) as! VoipGridParticipantCell + guard let participantData = conferenceViewModel?.conferenceParticipantDevices.value?[indexPath.row] else { + return cell + } + cell.participantData = participantData + return cell + } + + func collectionView(_ collectionView: UICollectionView, + layout collectionViewLayout: UICollectionViewLayout, + sizeForItemAt indexPath: IndexPath) -> CGSize { + + guard let participantsCount = conferenceViewModel?.conferenceParticipantDevices.value?.count else { + return .zero + } + + var cellSize : CGSize = .zero + let availableSize = collectionView.frame.size + + if (participantsCount == 1) { + cellSize = availableSize + } else if (participantsCount == 2) { + cellSize = CGSize(width:availableSize.width, height:availableSize.height/2) + cellSize.height -= inter_cell/2 + } else if (participantsCount == 3) { + cellSize = CGSize(width:availableSize.width, height:availableSize.height/3) + cellSize.height -= 2*inter_cell/3 + } else if (participantsCount == 4) { + cellSize = CGSize(width:availableSize.width/2, height:availableSize.height/2) + cellSize.height -= inter_cell/2 + cellSize.width -= inter_cell/2 + } else if (participantsCount == 5) { + if (indexPath.row == 4) { // last (local) participant takes full width (under discussion) + cellSize = CGSize(width:availableSize.width, height:availableSize.height/3) + } else { + cellSize = CGSize(width:availableSize.width/2, height:availableSize.height/3) + cellSize.width -= inter_cell/2 + } + cellSize.height -= 2*inter_cell/3 + } else { + cellSize = CGSize(width:availableSize.width/2, height:availableSize.height/CGFloat((participantsCount/2))) + cellSize.height -= 2*inter_cell/3 + cellSize.width -= inter_cell/2 + } + return cellSize + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + + +} diff --git a/Classes/Voip/Views/Fragments/Conference/VoipGridParticipantCell.swift b/Classes/Voip/Views/Fragments/Conference/VoipGridParticipantCell.swift new file mode 100644 index 000000000..6339a0d15 --- /dev/null +++ b/Classes/Voip/Views/Fragments/Conference/VoipGridParticipantCell.swift @@ -0,0 +1,129 @@ +/* + * 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 . + */ + + +import UIKit +import Foundation +import SnapKit +import linphonesw + +class VoipGridParticipantCell: UICollectionViewCell { + + // Layout Constants + let corner_radius = 20.0 + static let avatar_size = 80.0 + let switch_camera_button_margins = 8.0 + let switch_camera_button_size = 30 + + + let videoView = UIView() + let avatar = Avatar(diameter:VoipGridParticipantCell.avatar_size,color:VoipTheme.voipBackgroundColor, textStyle: VoipTheme.call_generated_avatar_medium) + let pause = UIImageView(image: UIImage(named: "voip_pause")?.tinted(with: .white)) + let switchCamera = UIImageView(image: UIImage(named:"voip_change_camera")?.tinted(with:.white)) + let displayName = StyledLabel(VoipTheme.conference_participant_name_font_grid) + let pauseLabel = StyledLabel(VoipTheme.conference_participant_name_font_grid,VoipTexts.conference_participant_paused) + + var participantData: ConferenceParticipantDeviceData? = nil { + didSet { + if let data = participantData { + data.isInConference.readCurrentAndObserve { (isIn) in + self.updateBackground() + self.pause.isHidden = isIn == true + self.pauseLabel.isHidden = self.pause.isHidden + } + data.videoEnabled.readCurrentAndObserve { (videoEnabled) in + self.updateBackground() + if (videoEnabled == true) { + data.setVideoView(view: self.videoView) + self.avatar.isHidden = true + } else { + self.avatar.isHidden = false + } + self.switchCamera.isHidden = videoEnabled != true || !data.isSwitchCameraAvailable() + } + data.participantDevice.address.map { + avatar.fillFromAddress(address: $0) + if let displayName = $0.addressBookEnhancedDisplayName() { + self.displayName.text = displayName + } + } + data.activeSpeaker.readCurrentAndObserve { (active) in + if (active == true) { + self.layer.borderWidth = 2 + } else { + self.layer.borderWidth = 0 + } + } + } + } + } + + func updateBackground() { + if let data = participantData { + if (data.isInConference.value != true) { + self.contentView.backgroundColor = VoipTheme.voip_conference_participant_paused_background + } else if (data.videoEnabled.value == true) { + self.contentView.backgroundColor = .black + } else { + self.contentView.backgroundColor = VoipTheme.voipParticipantBackgroundColor.get() + + } + } + } + + + override init(frame:CGRect) { + super.init(frame:.zero) + layer.cornerRadius = corner_radius + clipsToBounds = true + layer.borderColor = VoipTheme.primary_color.cgColor + + contentView.addSubview(videoView) + videoView.matchParentDimmensions().done() + + contentView.addSubview(avatar) + avatar.size(w: VoipGridParticipantCell.avatar_size, h: VoipGridParticipantCell.avatar_size).center().done() + + contentView.addSubview(pause) + pause.layer.cornerRadius = VoipGridParticipantCell.avatar_size/2 + pause.clipsToBounds = true + pause.backgroundColor = VoipTheme.voip_gray + pause.size(w: VoipGridParticipantCell.avatar_size, h: VoipGridParticipantCell.avatar_size).center().done() + + contentView.addSubview(switchCamera) + switchCamera.alignParentTop(withMargin: switch_camera_button_margins).alignParentRight(withMargin: switch_camera_button_margins).square(switch_camera_button_size).done() + switchCamera.contentMode = .scaleAspectFit + + switchCamera.onClick { + Core.get().toggleCamera() + } + + contentView.addSubview(displayName) + displayName.alignParentLeft(withMargin:ActiveCallView.bottom_displayname_margin_left).alignParentBottom(withMargin:ActiveCallView.bottom_displayname_margin_bottom).done() + + contentView.addSubview(pauseLabel) + pauseLabel.toRightOf(displayName).alignParentBottom(withMargin:ActiveCallView.bottom_displayname_margin_bottom).done() + + contentView.matchParentDimmensions().done() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/Classes/Voip/Views/Fragments/ControlsView.swift b/Classes/Voip/Views/Fragments/ControlsView.swift new file mode 100644 index 000000000..58c285aa5 --- /dev/null +++ b/Classes/Voip/Views/Fragments/ControlsView.swift @@ -0,0 +1,109 @@ +/* + * 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 . + */ + +import Foundation +import UIKit + +class ControlsView: UIStackView { + + // Layout constants + static let controls_button_spacing = 5.0 + + init (showVideo:Bool) { + super.init(frame: .zero) + axis = .horizontal + distribution = .equalSpacing + alignment = .center + spacing = ControlsView.controls_button_spacing + + // Mute + let mute = CallControlButton(buttonTheme: VoipTheme.call_mute, onClickAction: { + ControlsViewModel.shared.toggleMuteMicrophone() + }) + addArrangedSubview(mute) + ControlsViewModel.shared.isMicrophoneMuted.readCurrentAndObserve { (muted) in + mute.isSelected = muted == true + } + ControlsViewModel.shared.isMuteMicrophoneEnabled.readCurrentAndObserve { (enabled) in + mute.isEnabled = enabled == true + } + + // Speaker + let speaker = CallControlButton(buttonTheme: VoipTheme.call_speaker, onClickAction: { + ControlsViewModel.shared.toggleSpeaker() + }) + addArrangedSubview(speaker) + ControlsViewModel.shared.isSpeakerSelected.readCurrentAndObserve { (selected) in + speaker.isSelected = selected == true + } + + // Audio routes + let routes = CallControlButton(buttonTheme: VoipTheme.call_audio_route, onClickAction: { + ControlsViewModel.shared.toggleRoutesMenu() + }) + addArrangedSubview(routes) + ControlsViewModel.shared.audioRoutesSelected.readCurrentAndObserve { (selected) in + routes.isSelected = selected == true + } + + ControlsViewModel.shared.audioRoutesEnabled.readCurrentAndObserve { (routesEnabled) in + speaker.isHidden = routesEnabled == true + routes.isHidden = !speaker.isHidden + } + + // Video + if (showVideo) { + let video = CallControlButton(buttonTheme: VoipTheme.call_video, onClickAction: { + if AVCaptureDevice.authorizationStatus(for: .video) == .authorized { + ControlsViewModel.shared.toggleVideo() + } else { + AVCaptureDevice.requestAccess(for: .video, completionHandler: { (granted: Bool) in + if granted { + ControlsViewModel.shared.toggleVideo() + } else { + VoipDialog(message:VoipTexts.camera_required_for_video).show() + } + }) + } + }) + addArrangedSubview(video) + video.showActivityIndicatorDataSource = ControlsViewModel.shared.isVideoUpdateInProgress + ControlsViewModel.shared.isVideoEnabled.readCurrentAndObserve { (selected) in + video.isSelected = selected == true + } + ControlsViewModel.shared.isVideoAvailable.readCurrentAndObserve { (available) in + video.isEnabled = available == true && ControlsViewModel.shared.isVideoUpdateInProgress.value != true + } + ControlsViewModel.shared.isVideoUpdateInProgress.readCurrentAndObserve { (updateInProgress) in + video.isEnabled = updateInProgress != true && ControlsViewModel.shared.isVideoAvailable.value == true + } + + } + + height(CallControlButton.default_size).done() + + } + + required init(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + + diff --git a/Classes/Voip/Views/Fragments/DismissableView.swift b/Classes/Voip/Views/Fragments/DismissableView.swift new file mode 100644 index 000000000..9bb1f7d9b --- /dev/null +++ b/Classes/Voip/Views/Fragments/DismissableView.swift @@ -0,0 +1,61 @@ +/* + * 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 . + */ + + +import UIKit +import Foundation + +class DismissableView: UIView { + + // Layout constants + let header_height = 60.0 + let title_left_margin = 20 + let dismiss_right_margin = 10 + let dismiss_icon_inset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) + let headerView = UIView() + let contentView = UIView() + var dismiss : CallControlButton? = nil + + init(title:String) { + super.init(frame:.zero) + + headerView.backgroundColor = VoipTheme.voipToolbarBackgroundColor.get() + self.addSubview(headerView) + headerView.matchParentSideBorders().alignParentTop().height(header_height).done() + + dismiss = CallControlButton(imageInset:dismiss_icon_inset,buttonTheme: VoipTheme.voip_cancel, onClickAction: { + self.removeFromSuperview() + }) + headerView.addSubview(dismiss!) + dismiss?.alignParentRight(withMargin: dismiss_right_margin).centerY().done() + + let title = StyledLabel(VoipTheme.calls_list_header_font,title) + headerView.addSubview(title) + title.alignParentTop().alignParentLeft(withMargin: title_left_margin).centerY().done() + + self.addSubview(contentView) + contentView.alignUnder(view: headerView).matchParentSideBorders().alignParentBottom().done() + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/Classes/Voip/Views/Fragments/IncomingOuntgoingCommonView.swift b/Classes/Voip/Views/Fragments/IncomingOuntgoingCommonView.swift new file mode 100644 index 000000000..40a21da83 --- /dev/null +++ b/Classes/Voip/Views/Fragments/IncomingOuntgoingCommonView.swift @@ -0,0 +1,98 @@ +/* + * 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 . + */ + + +import UIKit +import Foundation +import linphonesw + +@objc class IncomingOutgoingCommonView: UIViewController { // Shared between IncomingCallView and OutgoingCallVIew (all upper part except control buttons) + + // Layout constants + static let spinner_size = 50 + static let spinner_margin_top = 8.0 + static let call_type_margin_top = 10.0 + static let duration_margin_top = 10.0 + static let display_name_height = 20.0 + static let display_name_margin_top = 18.0 + static let sip_address_height = 16.0 + static let sip_address_margin_top = 6.0 + static let answer_decline_inset = UIEdgeInsets(top: 2, left: 7, bottom: 2, right: 7) + + let spinner = RotatingSpinner() + let duration = CallTimer(nil, VoipTheme.call_header_subtitle) + let avatar = Avatar(diameter: CGFloat(Avatar.diameter_for_call_views),color:VoipTheme.voipParticipantBackgroundColor, textStyle: VoipTheme.call_generated_avatar_large) + let displayName = StyledLabel(VoipTheme.call_header_title) + let sipAddress = StyledLabel(VoipTheme.call_header_subtitle) + + var callData: CallData? = nil { + didSet { + duration.call = callData?.call + callData?.call.remoteAddress.map { + avatar.fillFromAddress(address: $0) + displayName.text = $0.addressBookEnhancedDisplayName() + sipAddress.text = $0.asStringUriOnly() + } + } + } + + func viewDidLoad(forCallType:String) { + super.viewDidLoad() + + view.backgroundColor = VoipTheme.voipBackgroundColor.get() + + view.addSubview(spinner) + spinner.square(IncomingOutgoingCommonView.spinner_size).matchParentSideBorders().alignParentTop(withMargin:IncomingOutgoingCommonView.spinner_margin_top + UIDevice.notchHeight()).done() + + let callType = StyledLabel(VoipTheme.call_header_title,forCallType) + view.addSubview(callType) + callType.matchParentSideBorders().alignUnder(view:spinner,withMargin:IncomingOutgoingCommonView.call_type_margin_top).done() + + self.view.addSubview(duration) + duration.matchParentSideBorders().alignUnder(view:callType,withMargin:IncomingOutgoingCommonView.duration_margin_top).done() + + // Center : Avatar + Display name + SIP Address + let centerSection = UIView() + centerSection.addSubview(avatar) + avatar.square(Avatar.diameter_for_call_views).center().done() + centerSection.addSubview(displayName) + displayName.height(IncomingOutgoingCommonView.display_name_height).matchParentSideBorders().alignUnder(view:avatar,withMargin:IncomingOutgoingCommonView.display_name_margin_top).done() + centerSection.addSubview(sipAddress) + sipAddress.height(IncomingOutgoingCommonView.sip_address_height).matchParentSideBorders().alignUnder(view:displayName,withMargin:IncomingOutgoingCommonView.sip_address_margin_top).done() + self.view.addSubview(centerSection) + centerSection.matchParentSideBorders().center().done() + + + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + spinner.startRotation() + } + + override func viewWillDisappear(_ animated: Bool) { + spinner.stopRotation() + super.viewWillDisappear(animated) + } + + @objc func setCall(call:OpaquePointer) { + callData = CallData(call: Call.getSwiftObject(cObject: call)) + } + +} diff --git a/Classes/Voip/Views/Fragments/LocalVideoView.swift b/Classes/Voip/Views/Fragments/LocalVideoView.swift new file mode 100644 index 000000000..dba74002d --- /dev/null +++ b/Classes/Voip/Views/Fragments/LocalVideoView.swift @@ -0,0 +1,87 @@ +/* + * 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 . + */ + + +import UIKit +import Foundation +import SnapKit +import linphonesw + +class LocalVideoView: UIView { + + //Layout constants + let corner_radius = 15.0 + let aspect_ratio = 4.0/3.0 + let switch_camera_button_margins = 8.0 + let switch_camera_button_size = 30 + + var width : CGFloat + + var dragZone : UIView? { + didSet { + let panGesture = UIPanGestureRecognizer(target: self, action: #selector(drag)) + isUserInteractionEnabled = true + addGestureRecognizer(panGesture) + } + } + + let switchCamera = UIImageView(image: UIImage(named:"voip_change_camera")?.tinted(with:.white)) + + var callData: CallData? = nil { + didSet { + callData?.isRemotelyRecorded.readCurrentAndObserve(onChange: { (isRemotelyRecording) in + self.isHidden = !(isRemotelyRecording == true) + }) + } + } + + required init?(coder: NSCoder) { + width = 0.0 + super.init(coder: coder) + } + + init (width:CGFloat) { + self.width = width + super.init(frame: .zero) + layer.cornerRadius = corner_radius + clipsToBounds = true + + addSubview(switchCamera) + switchCamera.alignParentTop(withMargin: switch_camera_button_margins).alignParentRight(withMargin: switch_camera_button_margins).square(switch_camera_button_size).done() + switchCamera.contentMode = .scaleAspectFit + + switchCamera.onClick { + Core.get().toggleCamera() + } + setSizeConstraint() + } + + func setSizeConstraint() { + size(w: width, h: width*aspect_ratio).done() + } + + + @objc func drag(_ sender:UIPanGestureRecognizer){ + dragZone?.bringSubviewToFront(self) + let translation = sender.translation(in: dragZone) + center = CGPoint(x: center.x + translation.x, y: center.y + translation.y) + sender.setTranslation(CGPoint.zero, in: dragZone) + } + +} diff --git a/Classes/Voip/Views/Fragments/NumpadView.swift b/Classes/Voip/Views/Fragments/NumpadView.swift new file mode 100644 index 000000000..e949e82f8 --- /dev/null +++ b/Classes/Voip/Views/Fragments/NumpadView.swift @@ -0,0 +1,123 @@ +/* + * 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 . + */ + +import Foundation +import linphonesw + +@objc class NumpadView: UIView { + + // Layout constants + let side_margins = 10.0 + let margin_top = 100.0 + let button_size = 70 + let button_vertical_space = 17.0 + let button_horizontal_space = 14.0 + let digit_icon_inset = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20) + let corner_radius = 20.0 + let pad_height = 550 + let side_padding = 50.0 + + + init(superView:UIView, callData:CallData, onDismissAction : @escaping ()->Void) { + super.init(frame:.zero) + backgroundColor = VoipTheme.voip_translucent_popup_background + layer.cornerRadius = corner_radius + clipsToBounds = true + superView.addSubview(self) + snp.makeConstraints { make in + make.left.equalToSuperview().offset(side_margins) + make.right.equalToSuperview().offset(-side_margins) + make.height.equalTo(pad_height) + make.bottom.equalToSuperview().offset(-side_margins) + } + callData.callState.observe { state in + if (state == Call.State.End) { + onDismissAction() + } + } + + // Hide numpad button + let hide = CallControlButton(buttonTheme: VoipTheme.voip_cancel_light, onClickAction: { + onDismissAction() + }) + addSubview(hide) + hide.alignParentRight(withMargin: side_margins).alignParentTop(withMargin: side_margins).done() + + // DTMF History : + + let eneteredDtmf = StyledLabel(VoipTheme.dtmf_label) + addSubview(eneteredDtmf) + _ = eneteredDtmf.matchParentSideBorders().alignUnder(view:hide,withMargin:side_margins) + callData.enteredDTMF.readCurrentAndObserve { (dtmfs) in + eneteredDtmf.text = dtmfs + } + + // Digit buttons + + let allRows = UIStackView() + allRows.axis = .vertical + allRows.distribution = .equalSpacing + allRows.alignment = .center + allRows.spacing = button_vertical_space + allRows.layoutMargins = UIEdgeInsets(top: 0, left: side_padding, bottom: 0, right: side_padding) + allRows.isLayoutMarginsRelativeArrangement = true + addSubview(allRows) + _ = allRows.matchParentSideBorders().alignUnder(view:eneteredDtmf,withMargin: side_margins) + + + for key in [["1","2","3"],["4","5","6"],["7","8","9"],["*","0","#"]] { + let newRow = addRow(allRows: allRows) + for subkey in key { + let digit = CallControlButton(width:button_size, height:button_size, imageInset: digit_icon_inset, buttonTheme: ButtonTheme(tintableStateIcons:[UIButton.State.normal.rawValue : TintableIcon(name: "voip_numpad_\(iconNameForDigit(digit: subkey))")],backgroundStateColors:VoipTheme.numpad_digit_background), onClickAction: { + callData.sendDTMF(dtmf: "\(subkey)") + }) + newRow.addArrangedSubview(digit) + } + } + } + + func iconNameForDigit(digit:String) -> String { + if (digit == "*") { + return "star" + } + if (digit == "#") { + return "hash" + } + return digit + } + + func addRow(allRows:UIStackView) -> UIStackView { + let row = UIStackView() + row.axis = .horizontal + row.distribution = .equalSpacing + row.alignment = .center + row.spacing = button_vertical_space + row.isLayoutMarginsRelativeArrangement = true + allRows.addArrangedSubview(row) + return row + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + } + +} + + + diff --git a/Classes/Voip/Views/Fragments/ParticipantsList/ParticipantsListView.swift b/Classes/Voip/Views/Fragments/ParticipantsList/ParticipantsListView.swift new file mode 100644 index 000000000..b81357bb7 --- /dev/null +++ b/Classes/Voip/Views/Fragments/ParticipantsList/ParticipantsListView.swift @@ -0,0 +1,97 @@ +/* + * 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 . + */ + + +import UIKit +import Foundation +import linphonesw + +@objc class ParticipantsListView: DismissableView, UITableViewDataSource { + + // Layout constants + + + let participantsListTableView = UITableView() + + var callsDataObserver : MutableLiveDataOnChangeClosure<[CallData]>? = nil + + init() { + super.init(title: VoipTexts.call_action_participants_list) + + + let edit = CallControlButton(buttonTheme: VoipTheme.voip_edit, onClickAction: { + // Todo (not implemented in Android yet as of 22.11.21) + }) + super.headerView.addSubview(edit) + edit.centerY().done() + super.dismiss?.toRightOf(edit,withLeftMargin: dismiss_right_margin).centerY().done() + + + // ParticipantsList + super.contentView.addSubview(participantsListTableView) + participantsListTableView.matchParentDimmensions().done() + participantsListTableView.dataSource = self + participantsListTableView.register(VoipParticipantCell.self, forCellReuseIdentifier: "VoipParticipantCell") + participantsListTableView.allowsSelection = false + if #available(iOS 15.0, *) { + participantsListTableView.allowsFocus = false + } + participantsListTableView.separatorStyle = .singleLine + participantsListTableView.separatorColor = .white + + + CallsViewModel.shared.callsData.readCurrentAndObserve{ (callsData) in + self.participantsListTableView.reloadData() + } + + ConferenceViewModel.shared.isMeAdmin.readCurrentAndObserve { (meAdmin) in + edit.isHidden = meAdmin != true + } + + } + + + // TableView datasource delegate + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + guard let participants = ConferenceViewModel.shared.conferenceParticipants.value else { + return 0 + } + return participants.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell:VoipParticipantCell = tableView.dequeueReusableCell(withIdentifier: "VoipParticipantCell") as! VoipParticipantCell + guard let participantData = ConferenceViewModel.shared.conferenceParticipants.value?[indexPath.row] else { + return cell + } + cell.selectionStyle = .none + cell.participantData = participantData + cell.owningParticpantsListView = self + return cell + } + + // View controller + + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/Classes/Voip/Views/Fragments/ParticipantsList/VoipParticipantCell.swift b/Classes/Voip/Views/Fragments/ParticipantsList/VoipParticipantCell.swift new file mode 100644 index 000000000..c57a4e7a0 --- /dev/null +++ b/Classes/Voip/Views/Fragments/ParticipantsList/VoipParticipantCell.swift @@ -0,0 +1,113 @@ +/* + * 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 . + */ + + +import UIKit +import Foundation +import SnapKit +import linphonesw + +class VoipParticipantCell: UITableViewCell { + + // Layout Constants + + let dismiss_icon_inset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) + let dismiss_right_margin = 10 + let check_box_size = 20.0 + let cell_height = 80.0 + let avatar_left_margin = 15.0 + let texts_left_margin = 20.0 + + + let avatar = Avatar(diameter:VoipCallCell.avatar_size,color:VoipTheme.primaryTextColor, textStyle: VoipTheme.call_generated_avatar_small) + let displayName = StyledLabel(VoipTheme.conference_participant_name_font) + let sipAddress = StyledLabel(VoipTheme.conference_participant_sip_uri_font) + let isAdminView = UIView() + var removePart : CallControlButton? + + + var owningParticpantsListView : ParticipantsListView? = nil + + var participantData: ConferenceParticipantData? = nil { + didSet { + if let data = participantData { + avatar.fillFromAddress(address: data.participant.address!) + displayName.text = data.participant.address?.addressBookEnhancedDisplayName() + sipAddress.text = data.participant.address?.asStringUriOnly() + data.isAdmin.readCurrentAndObserve { (isAdmin) in self.isAdminView.isHidden = isAdmin != true + + } + data.isMeAdmin.readCurrentAndObserve { (isMeAdmin) in + self.removePart!.isHidden = isMeAdmin != true + self.isAdminView.alpha = isMeAdmin == true ? 1.0 : 0.6 + self.isAdminView.isUserInteractionEnabled = isMeAdmin == true + } + self.isAdminView.onClick { + data.conference.setParticipantAdminStatus(participant: data.participant, isAdmin: data.isAdmin.value != true) + self.owningParticpantsListView?.participantsListTableView.reloadData() + } + self.removePart?.onClick { + try?data.conference.removeParticipant(participant: data.participant) + self.owningParticpantsListView?.participantsListTableView.reloadData() + } + } + } + } + + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + contentView.height(cell_height).matchParentSideBorders().done() + + + contentView.addSubview(avatar) + avatar.size(w: VoipCallCell.avatar_size, h: VoipCallCell.avatar_size).centerY().alignParentLeft(withMargin: avatar_left_margin).done() + + let nameAddress = UIView() + nameAddress.addSubview(displayName) + nameAddress.addSubview(sipAddress) + displayName.alignParentTop().done() + sipAddress.alignUnder(view: displayName).done() + contentView.addSubview(nameAddress) + nameAddress.toRightOf(avatar,withLeftMargin:texts_left_margin).wrapContentY().centerY().done() + + removePart = CallControlButton(imageInset:dismiss_icon_inset,buttonTheme: VoipTheme.voip_cancel, onClickAction: { + self.removeFromSuperview() + }) + contentView.addSubview(removePart!) + removePart!.alignParentRight(withMargin: dismiss_right_margin).centerY().done() + + let isAdminLabel = StyledLabel(VoipTheme.conference_participant_admin_label,VoipTexts.chat_room_group_info_admin) + isAdminView.addSubview(isAdminLabel) + isAdminLabel.alignParentRight().centerY().done() + + let isAdminCheck = UIImageView(image: UIImage(named:("check_unselected"))) + isAdminView.addSubview(isAdminCheck) + isAdminCheck.size(w: check_box_size, h: check_box_size).toLeftOf(isAdminLabel).done() + + contentView.addSubview(isAdminView) + isAdminView.height(check_box_size).toLeftOf(removePart!).centerY().done() + + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/Classes/Voip/Views/Fragments/PausedCallOrConferenceView.swift b/Classes/Voip/Views/Fragments/PausedCallOrConferenceView.swift new file mode 100644 index 000000000..45a1a9a98 --- /dev/null +++ b/Classes/Voip/Views/Fragments/PausedCallOrConferenceView.swift @@ -0,0 +1,70 @@ +/* + * 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 . + */ + + +import UIKit +import Foundation +import SnapKit +import linphonesw + +class PausedCallOrConferenceView: UIView { + + // Layout constants + let icon_size = 200 + let icon_padding = 80.0 + let title_margin_top = 20 + + var icon : UIImageView? = nil + let title = StyledLabel(VoipTheme.call_or_conference_title) + let subtitle = StyledLabel(VoipTheme.call_or_conference_subtitle) + + required init?(coder: NSCoder) { + super.init(coder: coder) + } + + init (iconName:String, titleText:String, subTitleText:String? = nil) { + super.init(frame: .zero) + + backgroundColor = VoipTheme.voip_translucent_popup_background + + let centeredView = UIView() + icon = UIImageView(image: UIImage(named:iconName)?.withPadding(padding: icon_padding)) + icon!.backgroundColor = VoipTheme.primary_color + icon!.layer.cornerRadius = CGFloat(icon_size/2) + icon!.clipsToBounds = true + icon!.contentMode = .scaleAspectFit + centeredView.addSubview(icon!) + icon!.square(icon_size).centerX().done() + + title.numberOfLines = 0 + centeredView.addSubview(title) + title.alignUnder(view:icon!, withMargin:title_margin_top).matchParentSideBorders().done() + title.text = titleText + + subtitle.numberOfLines = 0 + centeredView.addSubview(subtitle) + subtitle.alignUnder(view: title).matchParentSideBorders().done() + subtitle.text = subTitleText + + self.addSubview(centeredView) + centeredView.center().matchParentSideBorders().wrapContentY().done() + + } + +} diff --git a/Classes/Voip/Views/Fragments/RemotelyRecording.swift b/Classes/Voip/Views/Fragments/RemotelyRecording.swift new file mode 100644 index 000000000..a3e957865 --- /dev/null +++ b/Classes/Voip/Views/Fragments/RemotelyRecording.swift @@ -0,0 +1,61 @@ +/* + * 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 . + */ + + +import UIKit +import Foundation +import SnapKit +import linphonesw + +class RemotelyRecordingView: UIView { + + let label = StyledLabel(VoipTheme.call_remote_recording) + let icon = UIImageView(image: UIImage(named:"voip_remote_recording")) + + var isRemotelyRecorded: MutableLiveData? = nil { + didSet { + isRemotelyRecorded?.readCurrentAndObserve(onChange: { (isRemotelyRecording) in + self.isHidden = !(isRemotelyRecording == true) + }) + } + } + + + required init?(coder: NSCoder) { + super.init(coder: coder) + } + + init (height:Int, text:String) { + super.init(frame: .zero) + backgroundColor = VoipTheme.dark_grey_color + layer.cornerRadius = CGFloat(height/2) + clipsToBounds = true + + addSubview(label) + label.center().height(CGFloat(height)).done() + label.text = text + + addSubview(icon) + icon.square(height).toLeftOf(label).done() + + isHidden = true + + } + +} diff --git a/Classes/Voip/Views/Fragments/VoipExtraButtonsView.swift b/Classes/Voip/Views/Fragments/VoipExtraButtonsView.swift new file mode 100644 index 000000000..2a7deca61 --- /dev/null +++ b/Classes/Voip/Views/Fragments/VoipExtraButtonsView.swift @@ -0,0 +1,133 @@ +/* + * 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 . + */ + + +import UIKit +import Foundation +import SnapKit +import linphonesw + +class VoipExtraButtonsView: UIStackView { + + //Layout constants + let height = 200.0 + let corner_radius = 20.0 + + required init(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + init () { + super.init(frame: .zero) + + axis = .vertical + distribution = .fillEqually + alignment = .center + + layer.cornerRadius = corner_radius + clipsToBounds = true + + backgroundColor = VoipTheme.voipExtraButtonsBackgroundColor.get() + height(height).done() + + let row1 = UIStackView() + row1.axis = .horizontal + row1.distribution = .fillEqually + row1.alignment = .center + + + // First row + let numpad = VoipExtraButton(text: VoipTexts.call_action_numpad, buttonTheme: VoipTheme.call_action("voip_call_numpad"),onClickAction: { + ControlsViewModel.shared.numpadVisible.notifyAllObservers(with: true) + }) + row1.addArrangedSubview(numpad) + + let stats = VoipExtraButton(text: VoipTexts.call_action_statistics, buttonTheme: VoipTheme.call_action("voip_call_stats"),onClickAction: { + ControlsViewModel.shared.callStatsVisible.notifyAllObservers(with: true) + }) + row1.addArrangedSubview(stats) + + let chats = VoipExtraButton(text: VoipTexts.call_action_chat, buttonTheme: VoipTheme.call_action("voip_call_chat"),withbBoucinCounterDataSource:CallsViewModel.shared.currentCallUnreadChatMessageCount, onClickAction: { + ControlsViewModel.shared.goToChatEvent.notifyAllObservers(with: true) + }) + row1.addArrangedSubview(chats) + + addArrangedSubview(row1) + row1.matchParentSideBorders().done() + + // Second row + + let row2 = UIStackView() + row2.axis = .horizontal + row2.distribution = .fillEqually + row2.alignment = .center + + let transfer = VoipExtraButton(text: VoipTexts.call_action_transfer_call, buttonTheme: VoipTheme.call_action("voip_call_forward"),onClickAction: { + let view: DialerView = self.VIEW(DialerView.compositeViewDescription()); + view.setAddress("") + CallManager.instance().nextCallIsTransfer = true + PhoneMainView.instance().changeCurrentView(view.compositeViewDescription()) + }) + row2.addArrangedSubview(transfer) + + let participants = VoipExtraButton(text: VoipTexts.call_action_participants_list, buttonTheme: VoipTheme.call_action("voip_call_participants"),onClickAction: { + ControlsViewModel.shared.goToConferenceParticipantsListEvent.notifyAllObservers(with: true) + }) + row2.addArrangedSubview(participants) + + + let addcall = VoipExtraButton(text: VoipTexts.call_action_add_call, buttonTheme: VoipTheme.call_action("voip_call_add"),onClickAction: { + let view: DialerView = self.VIEW(DialerView.compositeViewDescription()); + view.setAddress("") + CallManager.instance().nextCallIsTransfer = false + PhoneMainView.instance().changeCurrentView(view.compositeViewDescription()) + }) + row2.addArrangedSubview(addcall) + + + let layoutselect = VoipExtraButton(text: VoipTexts.call_action_change_conf_layout, buttonTheme: VoipTheme.call_action("voip_conference_mosaic"),onClickAction: { + ControlsViewModel.shared.goToConferenceLayoutSettings.notifyAllObservers(with: true) + }) + row2.addArrangedSubview(layoutselect) + + let calls = VoipExtraButton(text: VoipTexts.call_action_calls_list, buttonTheme: VoipTheme.call_action("voip_calls_list"), withbBoucinCounterDataSource: CallsViewModel.shared.inactiveCallsCount, onClickAction: { + ControlsViewModel.shared.goToCallsListEvent.notifyAllObservers(with: true) + }) + row2.addArrangedSubview(calls) + + addArrangedSubview(row2) + row2.matchParentSideBorders().done() + + ConferenceViewModel.shared.isInConference.readCurrentAndObserve { (isIn) in + participants.isHidden = isIn != true + layoutselect.isHidden = isIn != true + transfer.isHidden = isIn == true + addcall.isHidden = isIn == true + } + + } + + func refresh() { + CallsViewModel.shared.currentCallUnreadChatMessageCount.notifyValue() + CallsViewModel.shared.inactiveCallsCount.notifyValue() + } + + + +} diff --git a/Classes/LinphoneUI/UIHangUpButton.h b/Classes/Voip/Views/SharedLayoutConstants.swift similarity index 79% rename from Classes/LinphoneUI/UIHangUpButton.h rename to Classes/Voip/Views/SharedLayoutConstants.swift index 784369828..163d8ba07 100644 --- a/Classes/LinphoneUI/UIHangUpButton.h +++ b/Classes/Voip/Views/SharedLayoutConstants.swift @@ -1,7 +1,7 @@ /* * Copyright (c) 2010-2020 Belledonne Communications SARL. * - * This file is part of linphone-iphone + * 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 @@ -17,13 +17,11 @@ * along with this program. If not, see . */ -#import -#import "UIIconButton.h" +import Foundation + +class SharedLayoutConstants { + static let buttons_bottom_margin = 15 + static let margin_call_view_side_controls_buttons = 12 -@interface UIHangUpButton : UIIconButton { } - -- (void)update; - -@end diff --git a/Classes/Voip/VoipDialog.swift b/Classes/Voip/VoipDialog.swift new file mode 100644 index 000000000..8488e8764 --- /dev/null +++ b/Classes/Voip/VoipDialog.swift @@ -0,0 +1,107 @@ +/* + * 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 . + */ + +import Foundation +import UIKit + +class VoipDialog : UIView{ + + // Layout constants + let center_corner_radius = 7.0 + let title_margin_top = 20 + let button_margin = 20.0 + let button_width = 135.0 + let button_height = 40.0 + let button_radius = 3.0 + let button_spacing = 15.0 + + let center_view_sides_margin = 13.0 + + + let title = StyledLabel(VoipTheme.basic_popup_title) + + init(message:String, givenButtons:[ButtonAttributes]? = nil) { + + super.init(frame: .zero) + backgroundColor = VoipTheme.voip_translucent_popup_background + + let centerView = UIView() + centerView.backgroundColor = VoipTheme.dark_grey_color.withAlphaComponent(0.8) + centerView.layer.cornerRadius = center_corner_radius + centerView.clipsToBounds = true + addSubview(centerView) + + title.numberOfLines = 0 + centerView.addSubview(title) + title.alignParentTop(withMargin:title_margin_top).matchParentSideBorders().done() + title.text = message + + let buttonsView = UIStackView() + buttonsView.axis = .horizontal + buttonsView.spacing = button_spacing + + var buttons = givenButtons + + if (buttons == nil) { // assuming info popup, just putting an ok button + let ok = ButtonAttributes(text:VoipTexts.ok, action: {}, isDestructive:false) + buttons = [ok] + } + + buttons?.forEach { + let b = ButtonWithStateBackgrounds(backgroundStateColors: $0.isDestructive ? VoipTheme.primary_colors_background_gray : VoipTheme.primary_colors_background) + b.setTitle($0.text, for: .normal) + b.layer.cornerRadius = button_radius + b.clipsToBounds = true + buttonsView.addArrangedSubview(b) + b.applyTitleStyle(VoipTheme.big_button) + let action = $0.action + b.onClick { + self.removeFromSuperview() + action() + } + b.size(w: button_width,h: button_height).done() + } + centerView.addSubview(buttonsView) + buttonsView.alignUnder(view:title,withMargin:button_margin).alignParentBottom(withMargin:button_margin).centerX().done() + + + + centerView.matchParentSideBorders(insetedByDx: center_view_sides_margin).center().done() + } + + func show() { + rootVC()?.view.addSubview(self) + matchParentDimmensions().done() + } + + private func rootVC() -> UIViewController? { + return UIApplication.getTopMostViewController() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + } + +} + +struct ButtonAttributes { + let text:String + let action: (()->Void) + let isDestructive: Bool +} diff --git a/Classes/Voip/Widgets/Avatar.swift b/Classes/Voip/Widgets/Avatar.swift new file mode 100644 index 000000000..8e4084fa5 --- /dev/null +++ b/Classes/Voip/Widgets/Avatar.swift @@ -0,0 +1,60 @@ +/* + * 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 . + */ + +import Foundation +import linphonesw + +class Avatar : UIImageView { + + static let diameter_for_call_views = 191 + + required init?(coder: NSCoder) { + initialsLabel = StyledLabel(VoipTheme.call_generated_avatar_large) + super.init(coder: coder) + } + + let initialsLabel: StyledLabel + + init (diameter: CGFloat, color:LightDarkColor,textStyle:TextStyle) { + initialsLabel = StyledLabel(textStyle) + super.init(frame: .zero) + layer.cornerRadius = diameter/2.0 + clipsToBounds = true + self.backgroundColor = color.get() + addSubview(initialsLabel) + _ = initialsLabel.matchParentSideBorders().matchParentHeight() + } + + + + func fillFromAddress(address:Address) { + if let image = address.contact()?.avatar() { + self.image = image + initialsLabel.isHidden = true + } else { + self.image = nil + initialsLabel.text = address.initials() + initialsLabel.isHidden = false + } + } + + + +} + diff --git a/Classes/Voip/Widgets/BouncingCounter.swift b/Classes/Voip/Widgets/BouncingCounter.swift new file mode 100644 index 000000000..a07f5124b --- /dev/null +++ b/Classes/Voip/Widgets/BouncingCounter.swift @@ -0,0 +1,75 @@ +/* + * 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 . + */ + + +import Foundation +import UIKit +import SwiftUI + +class BouncingCounter : UIBouncingView { + + // Layout constants + let size = 20.0 + let center_offset = 20 + + let owningButton : UIButton + let label : StyledLabel + + var dataSource : MutableLiveData? { + didSet { + if let dataSource = dataSource { + self.size(w:self.size,h:self.size).matchCenterXOf(view: self.owningButton, withDx: self.center_offset).matchCenterYOf(view: self.owningButton, withDy: -self.center_offset).done() + dataSource.readCurrentAndObserve { (value) in + if (value! > 0) { + self.label.text = value! < 100 ? String(value!) : "99+" + self.isHidden = true // to force legacy startAnimating to unhide and animate + self.startAnimating(true) + } else { + self.isHidden = false // to force legacy startAnimating to hide and animate + self.stopAnimating(true) + } + } + } else { + self.isHidden = false + self.stopAnimating(true) + } + } + } + + + init (inButton:UIButton) { + owningButton = inButton + label = StyledLabel(VoipTheme.unread_count_font) + super.init(frame:.zero) + addSubview(label) + label.matchParentDimmensions().done() + backgroundColor = VoipTheme.primary_color + layer.masksToBounds = true + layer.cornerRadius = size/2 + self.isHidden = true + } + + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + + +} diff --git a/Classes/Voip/Widgets/ButtonWithStateBackgrounds.swift b/Classes/Voip/Widgets/ButtonWithStateBackgrounds.swift new file mode 100644 index 000000000..fa437dc8f --- /dev/null +++ b/Classes/Voip/Widgets/ButtonWithStateBackgrounds.swift @@ -0,0 +1,49 @@ +/* + * 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 . + */ + + +import Foundation +import UIKit +import SwiftUI + +class ButtonWithStateBackgrounds : UIButton { + + required init?(coder: NSCoder) { + super.init(coder: coder) + } + + init (backgroundStateColors: [UInt: LightDarkColor]) { + super.init(frame: .zero) + backgroundStateColors.keys.forEach { (stateRawValue) in + setBackgroundColor(color: backgroundStateColors[stateRawValue]!.get(), forState: UIButton.State(rawValue: stateRawValue)) + } + } + + func setBackgroundColor(color: UIColor, forState: UIControl.State) { + UIGraphicsBeginImageContext(CGSize(width: 1, height: 1)) + UIGraphicsGetCurrentContext()!.setFillColor(color.cgColor) + UIGraphicsGetCurrentContext()!.fill(CGRect(x: 0, y: 0, width: 1, height: 1)) + let colorImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + self.setBackgroundImage(colorImage, for: forState) + } + + + +} diff --git a/Classes/Voip/Widgets/CallControlButton.swift b/Classes/Voip/Widgets/CallControlButton.swift new file mode 100644 index 000000000..03a8a4835 --- /dev/null +++ b/Classes/Voip/Widgets/CallControlButton.swift @@ -0,0 +1,92 @@ +/* + * 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 . + */ + + +import Foundation +import UIKit +import SwiftUI + +class CallControlButton : ButtonWithStateBackgrounds { + + // Layout constants + static let default_size = 50 + static let hungup_width = 65 + + var showActivityIndicatorDataSource : MutableLiveData? = nil { + didSet { + if let showActivityIndicatorDataSource = self.showActivityIndicatorDataSource { + let spinner = UIActivityIndicatorView(style: .white) + spinner.color = VoipTheme.primary_color + self.addSubview(spinner) + spinner.matchParentDimmensions().center().done() + + showActivityIndicatorDataSource.readCurrentAndObserve { (show) in + if (show == true) { + spinner.startAnimating() + spinner.isHidden = false + self.isEnabled = false + } else { + spinner.stopAnimating() + spinner.isHidden = true + self.isEnabled = true + } + } + } + } + } + + var onClickAction : (()->Void)? = nil + + required init?(coder: NSCoder) { + super.init(coder: coder) + } + + init (width:Int = CallControlButton.default_size, height:Int = CallControlButton.default_size, imageInset:UIEdgeInsets = UIEdgeInsets(top: 2, left: 2, bottom: 2, right: 2), buttonTheme: ButtonTheme, onClickAction : (()->Void)? = nil ) { + super.init(backgroundStateColors: buttonTheme.backgroundStateColors) + + layer.cornerRadius = CGFloat(height/2) + clipsToBounds = true + contentMode = .scaleAspectFit + + applyTintedIcons(tintedIcons: buttonTheme.tintableStateIcons) + + imageView?.contentMode = .scaleAspectFit + + imageEdgeInsets = imageInset + + size(w: CGFloat(width), h: CGFloat(height)).done() + + self.onClickAction = onClickAction + onClick { + self.onClickAction?() + } + + } + + func applyTintedIcons(tintedIcons: [UInt: TintableIcon]) { + tintedIcons.keys.forEach { (stateRawValue) in + let tintedIcon = tintedIcons[stateRawValue]! + UIImage(named:tintedIcon.name).map { + setImage($0.tinted(with: tintedIcon.tintColor?.get()),for: UIButton.State(rawValue: stateRawValue)) + } + } + } + + +} diff --git a/Classes/LinphoneUI/UIPauseButton.h b/Classes/Voip/Widgets/RotatingSpinner.swift similarity index 50% rename from Classes/LinphoneUI/UIPauseButton.h rename to Classes/Voip/Widgets/RotatingSpinner.swift index 0146c3174..a03f12671 100644 --- a/Classes/LinphoneUI/UIPauseButton.h +++ b/Classes/Voip/Widgets/RotatingSpinner.swift @@ -1,7 +1,7 @@ /* * Copyright (c) 2010-2020 Belledonne Communications SARL. * - * This file is part of linphone-iphone + * 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 @@ -17,22 +17,32 @@ * along with this program. If not, see . */ -#import "UIToggleButton.h" +import Foundation -#include "linphone/linphonecore.h" - -typedef enum _UIPauseButtonType { - UIPauseButtonType_CurrentCall, - UIPauseButtonType_Call, - UIPauseButtonType_Conference -} UIPauseButtonType; - -@interface UIPauseButton : UIToggleButton { - @private - UIPauseButtonType type; - LinphoneCall* call; +class RotatingSpinner : UIImageView { + + init () { + super.init(frame: .zero) + self.image = UIImage(named: "voip_spinner") + self.tint(UIColor.white) + self.contentMode = .scaleAspectFit + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + } + + func startRotation() { + let rotation : CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z") + rotation.toValue = NSNumber(value: Double.pi * 2) + rotation.duration = 2.2 + rotation.isCumulative = true + rotation.repeatCount = Float.greatestFiniteMagnitude + self.layer.add(rotation, forKey: "rotationAnimation") + } + + func stopRotation() { + self.layer.removeAllAnimations() + } } -- (void)setType:(UIPauseButtonType) type call:(LinphoneCall*)call; - -@end diff --git a/Classes/Voip/Widgets/StyledLabel.swift b/Classes/Voip/Widgets/StyledLabel.swift new file mode 100644 index 000000000..29c15da3e --- /dev/null +++ b/Classes/Voip/Widgets/StyledLabel.swift @@ -0,0 +1,34 @@ +/* + * 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 . + */ + +import Foundation + +class StyledLabel: UILabel { + + required init?(coder: NSCoder) { + super.init(coder: coder) + } + + init (_ style:TextStyle, _ text:String? = nil) { + super.init(frame: .zero) + self.text = text + applyStyle(style) + } + +} diff --git a/Classes/Voip/Widgets/UICallTimer.swift b/Classes/Voip/Widgets/UICallTimer.swift new file mode 100644 index 000000000..1eaf13ef9 --- /dev/null +++ b/Classes/Voip/Widgets/UICallTimer.swift @@ -0,0 +1,70 @@ +/* + * 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 . + */ + +import Foundation +import linphonesw + +class CallTimer : StyledLabel { + + let min_width = 50.0 + + let formatter = DateComponentsFormatter() + var call:Call? = nil { + didSet { + if (self.call != nil) { + self.format() + } + } + } + + var conference:Conference? = nil { + didSet { + if (self.call != nil) { + self.format() + } + } + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + } + + init (_ text:String?, _ style:TextStyle, _ call:Call? = nil) { + super.init(style,text) + self.call = call + formatter.unitsStyle = .positional + formatter.allowedUnits = [.minute, .second ] + formatter.zeroFormattingBehavior = [ .pad ] + Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in + self.format() + } + minWidth(min_width).done() + + } + + func format() { + guard let duration = self.call != nil ? self.call!.duration : self.conference != nil ? self.conference!.duration: nil else { + return + } + formatter.string(from: TimeInterval(duration)).map { + self.text = $0.hasPrefix("0:") ? "0" + $0 : $0 + } + } + +} diff --git a/Classes/Voip/Widgets/VoipExtraButton.swift b/Classes/Voip/Widgets/VoipExtraButton.swift new file mode 100644 index 000000000..67fce96c1 --- /dev/null +++ b/Classes/Voip/Widgets/VoipExtraButton.swift @@ -0,0 +1,75 @@ +/* + * 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 . + */ + + +import Foundation +import UIKit +import SwiftUI + +class VoipExtraButton : UIButton { + + // Layout constants + let width = 60.0 + let image_size = 50.0 + let bouncing_label_size = 17.0 + + var boucingCounter : BouncingCounter? = nil + + + required init?(coder: NSCoder) { + super.init(coder: coder) + } + + init ( text:String, buttonTheme: ButtonTheme, withbBoucinCounterDataSource:MutableLiveData? = nil, onClickAction : @escaping ()->Void ) { + super.init(frame: .zero) + + + contentMode = .scaleToFill + + buttonTheme.tintableStateIcons.keys.forEach { (stateRawValue) in + let tintedIcon = buttonTheme.tintableStateIcons[stateRawValue]! + UIImage(named:tintedIcon.name).map { + setImage($0.tinted(with: tintedIcon.tintColor?.get()),for: UIButton.State(rawValue: stateRawValue)) + } + setTitleColor(tintedIcon.tintColor?.get(), for: UIButton.State(rawValue: stateRawValue)) + } + imageView?.contentMode = .scaleAspectFit + imageView?.size(w: image_size,h: image_size).centerX().alignParentTop().done() + titleLabel?.alignUnder(view: imageView!).centerX().done() + + + size(w: width,h: image_size).done() + setTitle(text, for: .normal) + applyTitleStyle(VoipTheme.voip_extra_button) + + onClick { + ControlsViewModel.shared.hideExtraButtons.value = true + onClickAction() + } + + if (withbBoucinCounterDataSource != nil) { + boucingCounter = BouncingCounter(inButton:self) + addSubview(boucingCounter!) + boucingCounter?.dataSource = withbBoucinCounterDataSource + } + + } + + +} diff --git a/Classes/ar.lproj/CallIncomingView.strings b/Classes/ar.lproj/CallIncomingView.strings deleted file mode 100644 index c5adc5123..000000000 Binary files a/Classes/ar.lproj/CallIncomingView.strings and /dev/null differ diff --git a/Classes/ar.lproj/CallView.strings b/Classes/ar.lproj/CallView.strings deleted file mode 100644 index 6544e6570..000000000 Binary files a/Classes/ar.lproj/CallView.strings and /dev/null differ diff --git a/Classes/fr.lproj/CallIncomingView.strings b/Classes/fr.lproj/CallIncomingView.strings deleted file mode 100644 index b59db541e..000000000 Binary files a/Classes/fr.lproj/CallIncomingView.strings and /dev/null differ diff --git a/Classes/fr.lproj/CallOutgoingView.strings b/Classes/fr.lproj/CallOutgoingView.strings deleted file mode 100644 index f507fda06..000000000 Binary files a/Classes/fr.lproj/CallOutgoingView.strings and /dev/null differ diff --git a/Classes/fr.lproj/CallView.strings b/Classes/fr.lproj/CallView.strings deleted file mode 100644 index 8303fcfed..000000000 Binary files a/Classes/fr.lproj/CallView.strings and /dev/null differ diff --git a/Classes/fr.lproj/CallView~ipad.strings b/Classes/fr.lproj/CallView~ipad.strings deleted file mode 100644 index b4c093b13..000000000 Binary files a/Classes/fr.lproj/CallView~ipad.strings and /dev/null differ diff --git a/Classes/hu.lproj/CallIncomingView.strings b/Classes/hu.lproj/CallIncomingView.strings deleted file mode 100644 index de84a65e8..000000000 Binary files a/Classes/hu.lproj/CallIncomingView.strings and /dev/null differ diff --git a/Classes/hu.lproj/CallOutgoingView.strings b/Classes/hu.lproj/CallOutgoingView.strings deleted file mode 100644 index 912463c42..000000000 Binary files a/Classes/hu.lproj/CallOutgoingView.strings and /dev/null differ diff --git a/Classes/hu.lproj/CallView.strings b/Classes/hu.lproj/CallView.strings deleted file mode 100644 index 95effba3d..000000000 Binary files a/Classes/hu.lproj/CallView.strings and /dev/null differ diff --git a/Classes/hu.lproj/CallView~ipad.strings b/Classes/hu.lproj/CallView~ipad.strings deleted file mode 100644 index 3bd40d4d4..000000000 Binary files a/Classes/hu.lproj/CallView~ipad.strings and /dev/null differ diff --git a/Classes/linphone-Bridging-Header.h b/Classes/linphone-Bridging-Header.h index a520acc8e..c09603899 100644 --- a/Classes/linphone-Bridging-Header.h +++ b/Classes/linphone-Bridging-Header.h @@ -7,5 +7,8 @@ #import #import "FastAddressBook.h" #import "Log.h" -#import "AudioHelper.h" - +#import "LinphoneUI/UICompositeView.h" +#import "Contact.h" +#import "StatusBarView.h" +#import "LinphoneUI/UIBouncingView.h" +#import "PhoneMainView.h" diff --git a/Classes/ru.lproj/CallIncomingView.strings b/Classes/ru.lproj/CallIncomingView.strings deleted file mode 100644 index f433ea682..000000000 Binary files a/Classes/ru.lproj/CallIncomingView.strings and /dev/null differ diff --git a/Classes/ru.lproj/CallView.strings b/Classes/ru.lproj/CallView.strings deleted file mode 100644 index c3217f4ee..000000000 Binary files a/Classes/ru.lproj/CallView.strings and /dev/null differ diff --git a/Podfile b/Podfile index d6a747f18..4bdf972fb 100644 --- a/Podfile +++ b/Podfile @@ -26,6 +26,7 @@ target 'linphone' do # Pods for linphone pod 'SVProgressHUD' + pod 'SnapKit' all_pods end diff --git a/Resources/fonts/Roboto-Bold.ttf b/Resources/fonts/Roboto-Bold.ttf new file mode 100644 index 000000000..8d6cf0551 Binary files /dev/null and b/Resources/fonts/Roboto-Bold.ttf differ diff --git a/Resources/fonts/Roboto-Italic.ttf b/Resources/fonts/Roboto-Italic.ttf new file mode 100644 index 000000000..737244bc0 Binary files /dev/null and b/Resources/fonts/Roboto-Italic.ttf differ diff --git a/Resources/fonts/Roboto-Regular.ttf b/Resources/fonts/Roboto-Regular.ttf new file mode 100644 index 000000000..3a4973c78 Binary files /dev/null and b/Resources/fonts/Roboto-Regular.ttf differ diff --git a/Resources/images/voip_audio_routes.png b/Resources/images/voip_audio_routes.png new file mode 100644 index 000000000..fae4e84e3 Binary files /dev/null and b/Resources/images/voip_audio_routes.png differ diff --git a/Resources/images/voip_bluetooth.png b/Resources/images/voip_bluetooth.png new file mode 100644 index 000000000..3010fb418 Binary files /dev/null and b/Resources/images/voip_bluetooth.png differ diff --git a/Resources/images/voip_call.png b/Resources/images/voip_call.png new file mode 100644 index 000000000..1a95c052e Binary files /dev/null and b/Resources/images/voip_call.png differ diff --git a/Resources/images/voip_call_add.png b/Resources/images/voip_call_add.png new file mode 100644 index 000000000..8237f3305 Binary files /dev/null and b/Resources/images/voip_call_add.png differ diff --git a/Resources/images/voip_call_chat.png b/Resources/images/voip_call_chat.png new file mode 100644 index 000000000..4ff01de6f Binary files /dev/null and b/Resources/images/voip_call_chat.png differ diff --git a/Resources/images/voip_call_forward.png b/Resources/images/voip_call_forward.png new file mode 100644 index 000000000..cc61de482 Binary files /dev/null and b/Resources/images/voip_call_forward.png differ diff --git a/Resources/images/voip_call_header_active.png b/Resources/images/voip_call_header_active.png new file mode 100644 index 000000000..3ef0106bc Binary files /dev/null and b/Resources/images/voip_call_header_active.png differ diff --git a/Resources/images/voip_call_header_incoming.png b/Resources/images/voip_call_header_incoming.png new file mode 100644 index 000000000..7a8d458ad Binary files /dev/null and b/Resources/images/voip_call_header_incoming.png differ diff --git a/Resources/images/voip_call_header_outgoing.png b/Resources/images/voip_call_header_outgoing.png new file mode 100644 index 000000000..474abe754 Binary files /dev/null and b/Resources/images/voip_call_header_outgoing.png differ diff --git a/Resources/images/voip_call_header_paused.png b/Resources/images/voip_call_header_paused.png new file mode 100644 index 000000000..fdcfaf5d2 Binary files /dev/null and b/Resources/images/voip_call_header_paused.png differ diff --git a/Resources/images/voip_call_list_menu.png b/Resources/images/voip_call_list_menu.png new file mode 100644 index 000000000..fdd82bea6 Binary files /dev/null and b/Resources/images/voip_call_list_menu.png differ diff --git a/Resources/images/voip_call_more.png b/Resources/images/voip_call_more.png new file mode 100644 index 000000000..73bb13b36 Binary files /dev/null and b/Resources/images/voip_call_more.png differ diff --git a/Resources/images/voip_call_numpad.png b/Resources/images/voip_call_numpad.png new file mode 100644 index 000000000..a0fd33835 Binary files /dev/null and b/Resources/images/voip_call_numpad.png differ diff --git a/Resources/images/voip_call_participants.png b/Resources/images/voip_call_participants.png new file mode 100644 index 000000000..b2b766b14 Binary files /dev/null and b/Resources/images/voip_call_participants.png differ diff --git a/Resources/images/voip_call_record.png b/Resources/images/voip_call_record.png new file mode 100644 index 000000000..9c392dc47 Binary files /dev/null and b/Resources/images/voip_call_record.png differ diff --git a/Resources/images/voip_call_stats.png b/Resources/images/voip_call_stats.png new file mode 100644 index 000000000..3dd39d43b Binary files /dev/null and b/Resources/images/voip_call_stats.png differ diff --git a/Resources/images/voip_calls_list.png b/Resources/images/voip_calls_list.png new file mode 100644 index 000000000..a3d69b84e Binary files /dev/null and b/Resources/images/voip_calls_list.png differ diff --git a/Resources/images/voip_camera_off.png b/Resources/images/voip_camera_off.png new file mode 100644 index 000000000..dd41c338b Binary files /dev/null and b/Resources/images/voip_camera_off.png differ diff --git a/Resources/images/voip_camera_on.png b/Resources/images/voip_camera_on.png new file mode 100644 index 000000000..7109c577d Binary files /dev/null and b/Resources/images/voip_camera_on.png differ diff --git a/Resources/images/voip_cancel.png b/Resources/images/voip_cancel.png new file mode 100644 index 000000000..493b35e79 Binary files /dev/null and b/Resources/images/voip_cancel.png differ diff --git a/Resources/images/voip_change_camera.png b/Resources/images/voip_change_camera.png new file mode 100644 index 000000000..d6dc15cb8 Binary files /dev/null and b/Resources/images/voip_change_camera.png differ diff --git a/Resources/images/voip_chat_rooms_list.png b/Resources/images/voip_chat_rooms_list.png new file mode 100644 index 000000000..edf722c7e Binary files /dev/null and b/Resources/images/voip_chat_rooms_list.png differ diff --git a/Resources/images/voip_conference_active_speaker.png b/Resources/images/voip_conference_active_speaker.png new file mode 100644 index 000000000..18f21106a Binary files /dev/null and b/Resources/images/voip_conference_active_speaker.png differ diff --git a/Resources/images/voip_conference_mosaic.png b/Resources/images/voip_conference_mosaic.png new file mode 100644 index 000000000..8fa0137b7 Binary files /dev/null and b/Resources/images/voip_conference_mosaic.png differ diff --git a/Resources/images/voip_conference_new.png b/Resources/images/voip_conference_new.png new file mode 100644 index 000000000..8985782ae Binary files /dev/null and b/Resources/images/voip_conference_new.png differ diff --git a/Resources/images/voip_conference_paused_big.png b/Resources/images/voip_conference_paused_big.png new file mode 100644 index 000000000..745f17220 Binary files /dev/null and b/Resources/images/voip_conference_paused_big.png differ diff --git a/Resources/images/voip_conference_play_big.png b/Resources/images/voip_conference_play_big.png new file mode 100644 index 000000000..303d05faa Binary files /dev/null and b/Resources/images/voip_conference_play_big.png differ diff --git a/Resources/images/voip_copy.png b/Resources/images/voip_copy.png new file mode 100644 index 000000000..43639693e Binary files /dev/null and b/Resources/images/voip_copy.png differ diff --git a/Resources/images/voip_delete.png b/Resources/images/voip_delete.png new file mode 100644 index 000000000..3022d156d Binary files /dev/null and b/Resources/images/voip_delete.png differ diff --git a/Resources/images/voip_dropdown.png b/Resources/images/voip_dropdown.png new file mode 100644 index 000000000..d9fccac91 Binary files /dev/null and b/Resources/images/voip_dropdown.png differ diff --git a/Resources/images/voip_earpiece.png b/Resources/images/voip_earpiece.png new file mode 100644 index 000000000..e95a30801 Binary files /dev/null and b/Resources/images/voip_earpiece.png differ diff --git a/Resources/images/voip_edit.png b/Resources/images/voip_edit.png new file mode 100644 index 000000000..c24930212 Binary files /dev/null and b/Resources/images/voip_edit.png differ diff --git a/Resources/images/voip_export.png b/Resources/images/voip_export.png new file mode 100644 index 000000000..3fdfa078a Binary files /dev/null and b/Resources/images/voip_export.png differ diff --git a/Resources/images/voip_hangup.png b/Resources/images/voip_hangup.png new file mode 100644 index 000000000..a2ceab5d8 Binary files /dev/null and b/Resources/images/voip_hangup.png differ diff --git a/Resources/images/voip_info.png b/Resources/images/voip_info.png new file mode 100644 index 000000000..ae8dd86f8 Binary files /dev/null and b/Resources/images/voip_info.png differ diff --git a/Resources/images/voip_mandatory.png b/Resources/images/voip_mandatory.png new file mode 100644 index 000000000..4be37f7e4 Binary files /dev/null and b/Resources/images/voip_mandatory.png differ diff --git a/Resources/images/voip_menu_more.png b/Resources/images/voip_menu_more.png new file mode 100644 index 000000000..7f7f7d8fd Binary files /dev/null and b/Resources/images/voip_menu_more.png differ diff --git a/Resources/images/voip_merge_calls.png b/Resources/images/voip_merge_calls.png new file mode 100644 index 000000000..6c4da5988 Binary files /dev/null and b/Resources/images/voip_merge_calls.png differ diff --git a/Resources/images/voip_micro_off.png b/Resources/images/voip_micro_off.png new file mode 100644 index 000000000..57569b4f2 Binary files /dev/null and b/Resources/images/voip_micro_off.png differ diff --git a/Resources/images/voip_micro_on.png b/Resources/images/voip_micro_on.png new file mode 100644 index 000000000..6552d35d5 Binary files /dev/null and b/Resources/images/voip_micro_on.png differ diff --git a/Resources/images/voip_multiple_contacts_avatar.png b/Resources/images/voip_multiple_contacts_avatar.png new file mode 100644 index 000000000..78b10f11b Binary files /dev/null and b/Resources/images/voip_multiple_contacts_avatar.png differ diff --git a/Resources/images/voip_numpad_0.png b/Resources/images/voip_numpad_0.png new file mode 100644 index 000000000..115bdb17d Binary files /dev/null and b/Resources/images/voip_numpad_0.png differ diff --git a/Resources/images/voip_numpad_1.png b/Resources/images/voip_numpad_1.png new file mode 100644 index 000000000..4d8b7f5cc Binary files /dev/null and b/Resources/images/voip_numpad_1.png differ diff --git a/Resources/images/voip_numpad_2.png b/Resources/images/voip_numpad_2.png new file mode 100644 index 000000000..6b561c468 Binary files /dev/null and b/Resources/images/voip_numpad_2.png differ diff --git a/Resources/images/voip_numpad_3.png b/Resources/images/voip_numpad_3.png new file mode 100644 index 000000000..386715586 Binary files /dev/null and b/Resources/images/voip_numpad_3.png differ diff --git a/Resources/images/voip_numpad_4.png b/Resources/images/voip_numpad_4.png new file mode 100644 index 000000000..e3dfdcc51 Binary files /dev/null and b/Resources/images/voip_numpad_4.png differ diff --git a/Resources/images/voip_numpad_5.png b/Resources/images/voip_numpad_5.png new file mode 100644 index 000000000..a18af28e5 Binary files /dev/null and b/Resources/images/voip_numpad_5.png differ diff --git a/Resources/images/voip_numpad_6.png b/Resources/images/voip_numpad_6.png new file mode 100644 index 000000000..79279cb99 Binary files /dev/null and b/Resources/images/voip_numpad_6.png differ diff --git a/Resources/images/voip_numpad_7.png b/Resources/images/voip_numpad_7.png new file mode 100644 index 000000000..c68656fd3 Binary files /dev/null and b/Resources/images/voip_numpad_7.png differ diff --git a/Resources/images/voip_numpad_8.png b/Resources/images/voip_numpad_8.png new file mode 100644 index 000000000..8d84c96ba Binary files /dev/null and b/Resources/images/voip_numpad_8.png differ diff --git a/Resources/images/voip_numpad_9.png b/Resources/images/voip_numpad_9.png new file mode 100644 index 000000000..af3e0e0bf Binary files /dev/null and b/Resources/images/voip_numpad_9.png differ diff --git a/Resources/images/voip_numpad_hash.png b/Resources/images/voip_numpad_hash.png new file mode 100644 index 000000000..790e7d12e Binary files /dev/null and b/Resources/images/voip_numpad_hash.png differ diff --git a/Resources/images/voip_numpad_star.png b/Resources/images/voip_numpad_star.png new file mode 100644 index 000000000..5a2649de4 Binary files /dev/null and b/Resources/images/voip_numpad_star.png differ diff --git a/Resources/images/voip_pause.png b/Resources/images/voip_pause.png new file mode 100644 index 000000000..e888da937 Binary files /dev/null and b/Resources/images/voip_pause.png differ diff --git a/Resources/images/voip_radio_off.png b/Resources/images/voip_radio_off.png new file mode 100644 index 000000000..b703dea80 Binary files /dev/null and b/Resources/images/voip_radio_off.png differ diff --git a/Resources/images/voip_radio_on.png b/Resources/images/voip_radio_on.png new file mode 100644 index 000000000..feaf00e11 Binary files /dev/null and b/Resources/images/voip_radio_on.png differ diff --git a/Resources/images/voip_remote_recording.png b/Resources/images/voip_remote_recording.png new file mode 100644 index 000000000..3e4e94009 Binary files /dev/null and b/Resources/images/voip_remote_recording.png differ diff --git a/Resources/images/voip_single_contact_avatar.png b/Resources/images/voip_single_contact_avatar.png new file mode 100644 index 000000000..5d158fa49 Binary files /dev/null and b/Resources/images/voip_single_contact_avatar.png differ diff --git a/Resources/images/voip_speaker_off.png b/Resources/images/voip_speaker_off.png new file mode 100644 index 000000000..c018c9899 Binary files /dev/null and b/Resources/images/voip_speaker_off.png differ diff --git a/Resources/images/voip_speaker_on.png b/Resources/images/voip_speaker_on.png new file mode 100644 index 000000000..07f13c9b6 Binary files /dev/null and b/Resources/images/voip_speaker_on.png differ diff --git a/Resources/images/voip_spinner.png b/Resources/images/voip_spinner.png new file mode 100644 index 000000000..8238de56b Binary files /dev/null and b/Resources/images/voip_spinner.png differ diff --git a/linphone-Info.plist b/linphone-Info.plist index 8bd93f866..b9d24c1fb 100644 --- a/linphone-Info.plist +++ b/linphone-Info.plist @@ -102,6 +102,8 @@ Share photos with your friends and customize avatars NSContactsUsageDescription Make calls with your friends + NSLocalNetworkUsageDescription + Stream audio and video through the local network NSLocationWhenInUseUsageDescription Linphone will not use, store or communicate your location. The authorization allows us to detect Wifi connection changes NSMicrophoneUsageDescription @@ -110,8 +112,6 @@ Add tranfered files to your library NSPhotoLibraryUsageDescription Share photos with your friends and customize avatars - NSLocalNetworkUsageDescription - Stream audio and video through the local network NSUbiquitousContainers iCloud.org.linphone.phone @@ -126,6 +126,12 @@ NSVoIPUsageDescription Make audio/video calls + UIAppFonts + + Roboto-Regular.ttf + Roboto-Bold.ttf + Roboto-Italic.ttf + UIApplicationShortcutItems diff --git a/linphone.xcodeproj/project.pbxproj b/linphone.xcodeproj/project.pbxproj index 3fbac849c..f0ab4df60 100644 --- a/linphone.xcodeproj/project.pbxproj +++ b/linphone.xcodeproj/project.pbxproj @@ -7,13 +7,13 @@ objects = { /* Begin PBXBuildFile section */ + 017AC1D70F142AE8EAC13BDB /* Pods_msgNotificationContent.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A164BAF39B3A5B9F905917A7 /* Pods_msgNotificationContent.framework */; }; 152F22361B15E889008C0621 /* libxml2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 152F22351B15E889008C0621 /* libxml2.dylib */; }; 1D3623260D0F684500981E51 /* LinphoneAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D3623250D0F684500981E51 /* LinphoneAppDelegate.m */; }; 1D60589B0D05DD56006BFB54 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; }; 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 2214EB7A12F846B1002A5394 /* UICallButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 2214EB7912F846B1002A5394 /* UICallButton.m */; }; - 2214EB8912F84EBB002A5394 /* UIHangUpButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 2214EB8812F84EBB002A5394 /* UIHangUpButton.m */; }; 2214EBF312F86360002A5394 /* UIMutedMicroButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 2214EBF212F86360002A5394 /* UIMutedMicroButton.m */; }; 22276E8913C73DC000210156 /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 22276E8813C73DC000210156 /* CoreMedia.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 22405EEE1600B4E400B92522 /* AssetsLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 22405EED1600B4E400B92522 /* AssetsLibrary.framework */; }; @@ -25,8 +25,6 @@ 2274401A106F31BD006EC466 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 22744019106F31BD006EC466 /* CoreAudio.framework */; }; 2274402F106F335E006EC466 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2274402E106F335E006EC466 /* AudioToolbox.framework */; }; 228697C411AC29B800E9E0CA /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 228697C311AC29B800E9E0CA /* CFNetwork.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; - 22968A5F12F875C600588287 /* UISpeakerButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 22968A5E12F875C600588287 /* UISpeakerButton.m */; }; - 22AA8B0113D83F6300B30535 /* UICamSwitch.m in Sources */ = {isa = PBXBuildFile; fileRef = 22AA8B0013D83F6300B30535 /* UICamSwitch.m */; }; 22C755601317E59C007BC101 /* UIBluetoothButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 22C7555F1317E59C007BC101 /* UIBluetoothButton.m */; }; 22D1B68112A3E0BE001AE361 /* libresolv.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 22D1B68012A3E0BE001AE361 /* libresolv.dylib */; }; 22E0A822111C44E100B04932 /* AboutView.m in Sources */ = {isa = PBXBuildFile; fileRef = 22E0A81C111C44E100B04932 /* AboutView.m */; }; @@ -53,10 +51,8 @@ 24E1C7C01F9A235600D3F981 /* Contacts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 24E1C7B91F9A235500D3F981 /* Contacts.framework */; }; 288765FD0DF74451002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765FC0DF74451002DB57D /* CoreGraphics.framework */; }; 340751971506459A00B89C47 /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 340751961506459A00B89C47 /* CoreTelephony.framework */; }; - 340751E7150F38FD00B89C47 /* UIVideoButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 340751E6150F38FD00B89C47 /* UIVideoButton.m */; }; 34216F401547EBCD00EA9777 /* VideoZoomHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 34216F3F1547EBCD00EA9777 /* VideoZoomHandler.m */; }; 344ABDF114850AE9007420B6 /* libc++.1.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 344ABDEF14850AE9007420B6 /* libc++.1.dylib */; settings = {ATTRIBUTES = (Weak, ); }; }; - 369CCF81C921CD7C4E49A637 /* Pods_msgNotificationContent.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 82E9DEDA2A78C6DBBD1A54DB /* Pods_msgNotificationContent.framework */; }; 570742581D5A0691004B9C84 /* ShopView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 570742561D5A0691004B9C84 /* ShopView.xib */; }; 570742611D5A09B8004B9C84 /* ShopView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5707425F1D5A09B8004B9C84 /* ShopView.m */; }; 570742671D5A63DB004B9C84 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 570742661D5A63DB004B9C84 /* StoreKit.framework */; }; @@ -109,7 +105,6 @@ 61AE364F20C00B370089D9D3 /* ShareViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 61AE364E20C00B370089D9D3 /* ShareViewController.m */; }; 61AE365220C00B370089D9D3 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 61AE365020C00B370089D9D3 /* MainInterface.storyboard */; }; 61AE365620C00B370089D9D3 /* linphoneExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 61AE364B20C00B370089D9D3 /* linphoneExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 61AEBEA321906AFC00F35E7F /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; 61AEBEBD2191990A00F35E7F /* DevicesListView.m in Sources */ = {isa = PBXBuildFile; fileRef = 61AEBEBC2191990A00F35E7F /* DevicesListView.m */; }; 61AEBEBF2191991F00F35E7F /* DevicesListView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 61AEBEBE2191991F00F35E7F /* DevicesListView.xib */; }; 61AEBEC62191E47500F35E7F /* chevron_list_close.png in Resources */ = {isa = PBXBuildFile; fileRef = 61AEBEC52191E47500F35E7F /* chevron_list_close.png */; }; @@ -563,11 +558,7 @@ 6341807C1BBC103100F71761 /* ChatConversationCreateTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6341807B1BBC103100F71761 /* ChatConversationCreateTableView.m */; }; 63423C0A1C4501D000D9A050 /* Contact.m in Sources */ = {isa = PBXBuildFile; fileRef = 63423C091C4501D000D9A050 /* Contact.m */; }; 634610061B61330300548952 /* UILabel+Boldify.m in Sources */ = {isa = PBXBuildFile; fileRef = 634610051B61330300548952 /* UILabel+Boldify.m */; }; - 6346100F1B61409800548952 /* CallOutgoingView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6346100E1B61409800548952 /* CallOutgoingView.m */; }; - 634610121B6140A500548952 /* CallOutgoingView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 634610101B6140A500548952 /* CallOutgoingView.xib */; }; 635173F91BA082A40095EB0A /* UIChatBubblePhotoCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 635173F81BA082A40095EB0A /* UIChatBubblePhotoCell.m */; }; - 6352A5751BE0D4B800594C1C /* CallSideMenuView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6352A5731BE0D4B800594C1C /* CallSideMenuView.m */; }; - 6352A5761BE0D4B800594C1C /* CallSideMenuView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6352A5741BE0D4B800594C1C /* CallSideMenuView.xib */; }; 635775251B6673EC00C8B704 /* HistoryDetailsTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 635775241B6673EC00C8B704 /* HistoryDetailsTableView.m */; }; 636316D11A1DEBCB0009B839 /* AboutView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 636316D31A1DEBCB0009B839 /* AboutView.xib */; }; 636316D41A1DEC650009B839 /* SettingsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 636316D61A1DEC650009B839 /* SettingsView.xib */; }; @@ -577,7 +568,6 @@ 6377AC801BDE4069007F7625 /* UIBackToCallButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 6377AC7F1BDE4069007F7625 /* UIBackToCallButton.m */; }; 6381DA7D1C1AD5EA00DF3BBD /* UIBouncingView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6381DA7C1C1AD5EA00DF3BBD /* UIBouncingView.m */; }; 638F1A621C2021B2004B8E02 /* DialerView~ipad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 638F1A601C2021B2004B8E02 /* DialerView~ipad.xib */; }; - 638F1A881C2167C2004B8E02 /* CallView~ipad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 638F1A861C2167C2004B8E02 /* CallView~ipad.xib */; }; 638F1A911C21993D004B8E02 /* UICompositeView~ipad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 638F1A8F1C21993D004B8E02 /* UICompositeView~ipad.xib */; }; 639CEAFD1A1DF4D9004DE38F /* StatusBarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 639CEAFF1A1DF4D9004DE38F /* StatusBarView.xib */; }; 639CEB001A1DF4E4004DE38F /* UIHistoryCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 639CEB021A1DF4E4004DE38F /* UIHistoryCell.xib */; }; @@ -585,7 +575,6 @@ 639CEB091A1DF4FA004DE38F /* UIChatCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 639CEB0B1A1DF4FA004DE38F /* UIChatCell.xib */; }; 639E9C801C0DB13D00019A75 /* UICheckBoxTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 639E9C7F1C0DB13D00019A75 /* UICheckBoxTableView.m */; }; 639E9C931C0DB7BE00019A75 /* FirstLoginView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 639E9C951C0DB7BE00019A75 /* FirstLoginView.xib */; }; - 639E9C9D1C0DB7DF00019A75 /* UICallPausedCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 639E9C9F1C0DB7DF00019A75 /* UICallPausedCell.xib */; }; 639E9CA01C0DB7E500019A75 /* UIChatBubblePhotoCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 639E9CA21C0DB7E500019A75 /* UIChatBubblePhotoCell.xib */; }; 639E9CA31C0DB7EA00019A75 /* UIChatBubbleTextCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 639E9CA51C0DB7EA00019A75 /* UIChatBubbleTextCell.xib */; }; 639E9CA61C0DB7F200019A75 /* UIChatCreateCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 639E9CA81C0DB7F200019A75 /* UIChatCreateCell.xib */; }; @@ -609,7 +598,6 @@ 63B81A101B57DA33009604A6 /* UIScrollView+TPKeyboardAvoidingAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 63B81A0B1B57DA33009604A6 /* UIScrollView+TPKeyboardAvoidingAdditions.m */; }; 63B8D68C1BCBE65600C12B09 /* ChatConversationCreateView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 63B8D68E1BCBE65600C12B09 /* ChatConversationCreateView.xib */; }; 63B8D6A21BCBF43100C12B09 /* UIChatCreateCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 63B8D6A01BCBF43100C12B09 /* UIChatCreateCell.m */; }; - 63BC49E21BA2CDFC004EC273 /* UICallPausedCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 63BC49E11BA2CDFC004EC273 /* UICallPausedCell.m */; }; 63BE7A781D75BDF6000990EF /* ShopTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 63BE7A771D75BDF6000990EF /* ShopTableView.m */; }; 63C441C31BBC23ED0053DC5E /* UIAssistantTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 63C441C21BBC23ED0053DC5E /* UIAssistantTextField.m */; }; 63CD4B4F1A5AAC8C00B84282 /* DTAlertView.m in Sources */ = {isa = PBXBuildFile; fileRef = 63CD4B4E1A5AAC8C00B84282 /* DTAlertView.m */; }; @@ -622,16 +610,12 @@ 63E27A321C4FECD000D332AE /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 63E27A311C4FECD000D332AE /* LaunchScreen.xib */; }; 63E27A521C50EDB000D332AE /* hold.mkv in Resources */ = {isa = PBXBuildFile; fileRef = 63E27A511C50EB2700D332AE /* hold.mkv */; }; 63E59A3F1ADE70D900646FB3 /* InAppProductsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 63E59A3E1ADE70D900646FB3 /* InAppProductsManager.m */; }; - 63E802DB1C625AEF000D5509 /* BuildFile in Resources */ = {isa = PBXBuildFile; }; + 63E802DB1C625AEF000D5509 /* (null) in Resources */ = {isa = PBXBuildFile; }; 63EC8D391D7438660066547B /* AssistantLinkView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 63EC8D3B1D7438660066547B /* AssistantLinkView.xib */; }; 63F1DF441BCE618E00EDED90 /* UIAddressTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 63F1DF431BCE618E00EDED90 /* UIAddressTextField.m */; }; - 63F1DF4B1BCE983200EDED90 /* CallConferenceTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 63F1DF4A1BCE983200EDED90 /* CallConferenceTableView.m */; }; - 63F1DF4F1BCE985F00EDED90 /* UICallConferenceCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 63F1DF4D1BCE985F00EDED90 /* UICallConferenceCell.m */; }; - 63F1DF511BCE986A00EDED90 /* UICallConferenceCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 63F1DF531BCE986A00EDED90 /* UICallConferenceCell.xib */; }; 63FB30351A680E73008CA393 /* UIRoundedImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 63FB30341A680E73008CA393 /* UIRoundedImageView.m */; }; 70E542F313E147E3002BA2C0 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70E542F213E147E3002BA2C0 /* OpenGLES.framework */; }; 70E542F513E147EB002BA2C0 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70E542F413E147EB002BA2C0 /* QuartzCore.framework */; }; - 8C1B67061E671826001EA2FE /* AudioHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C1B67051E671826001EA2FE /* AudioHelper.m */; }; 8C2595DF1DEDCC8E007A6424 /* CallKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8C2595DE1DEDCC8E007A6424 /* CallKit.framework */; }; 8C2A81951F87B8000012A66B /* chat_group_avatar@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8C2A81931F87B7FF0012A66B /* chat_group_avatar@2x.png */; }; 8C2A81961F87B8000012A66B /* chat_group_avatar.png in Resources */ = {isa = PBXBuildFile; fileRef = 8C2A81941F87B8000012A66B /* chat_group_avatar.png */; }; @@ -670,8 +654,11 @@ 8CF25D961F9F336100BEA0C1 /* check_unselected.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CF25D941F9F336100BEA0C1 /* check_unselected.png */; }; 8CF25D9D1F9F76BD00BEA0C1 /* chat_group_informations.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CF25D9B1F9F76BC00BEA0C1 /* chat_group_informations.png */; }; 8CF25D9E1F9F76BD00BEA0C1 /* chat_group_informations@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CF25D9C1F9F76BD00BEA0C1 /* chat_group_informations@2x.png */; }; - 93566413F75DA69D2811A716 /* Pods_msgNotificationService.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F30EA7BEA39DA427CE0754E /* Pods_msgNotificationService.framework */; }; - A634ABAFCB39B6AAE4CA991D /* Pods_linphone.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65CEDD144CABFAA70A29AF27 /* Pods_linphone.framework */; }; + 9C0B30F54D61774AFD1473CE /* Pods_linphone.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B464E44A606CB50A65A96FE2 /* Pods_linphone.framework */; }; + C60B66682721AFFA0026AC7D /* CallStatisticsData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C60B66672721AFFA0026AC7D /* CallStatisticsData.swift */; }; + C60D265627299C94006238BB /* ControlsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C60D265527299C94006238BB /* ControlsViewModel.swift */; }; + C60D265827299F70006238BB /* CoreExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C60D265727299F6F006238BB /* CoreExtensions.swift */; }; + C60D265C272AA0BD006238BB /* UIImageExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C60D265B272AA0BD006238BB /* UIImageExtensions.swift */; }; C61B1BF22667D075001A4E4A /* menu_security_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C61B1BF12667D075001A4E4A /* menu_security_default.png */; }; C61B1BF42667D202001A4E4A /* more_menu_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C61B1BF32667D202001A4E4A /* more_menu_default.png */; }; C61B1BF72667EC6B001A4E4A /* ephemeral_messages_color_A.png in Resources */ = {isa = PBXBuildFile; fileRef = C61B1BF62667EC6B001A4E4A /* ephemeral_messages_color_A.png */; }; @@ -681,14 +668,105 @@ C622E3F226A81290004F5434 /* vr_off.png in Resources */ = {isa = PBXBuildFile; fileRef = C622E3EC26A8128F004F5434 /* vr_off.png */; }; C622E3F326A81290004F5434 /* vr_pause.png in Resources */ = {isa = PBXBuildFile; fileRef = C622E3ED26A8128F004F5434 /* vr_pause.png */; }; C622E3F426A81290004F5434 /* vr_play.png in Resources */ = {isa = PBXBuildFile; fileRef = C622E3EE26A81290004F5434 /* vr_play.png */; }; + C6277DA8274BF1CE00406FB9 /* voip_radio_on.png in Resources */ = {isa = PBXBuildFile; fileRef = C6277DA6274BF1CD00406FB9 /* voip_radio_on.png */; }; + C6277DA9274BF1CE00406FB9 /* voip_radio_off.png in Resources */ = {isa = PBXBuildFile; fileRef = C6277DA7274BF1CD00406FB9 /* voip_radio_off.png */; }; + C6278497273C21E1002FAA29 /* LocalVideoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6278496273C21E1002FAA29 /* LocalVideoView.swift */; }; C64A854E2667B67200252AD2 /* EphemeralSettingsView.m in Sources */ = {isa = PBXBuildFile; fileRef = C64A854D2667B67200252AD2 /* EphemeralSettingsView.m */; }; C64A85502667B67A00252AD2 /* EphemeralSettingsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C64A854F2667B67A00252AD2 /* EphemeralSettingsView.xib */; }; C64A85522667B74100252AD2 /* ephemeral_messages_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C64A85512667B74100252AD2 /* ephemeral_messages_default.png */; }; + C6586149273E595700A0DBFC /* VoipExtraButtonsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6586148273E595700A0DBFC /* VoipExtraButtonsView.swift */; }; + C658614C273E5B5E00A0DBFC /* VoipExtraButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = C658614B273E5B5E00A0DBFC /* VoipExtraButton.swift */; }; + C65A5D3027216B86005BA038 /* ActiveCallOrConferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C65A5D2F27216B86005BA038 /* ActiveCallOrConferenceView.swift */; }; + C65A5D3B27216CC0005BA038 /* MutableLiveData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C65A5D3727216CC0005BA038 /* MutableLiveData.swift */; }; + C65A5D3F27216E3A005BA038 /* CallData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C65A5D3E27216E3A005BA038 /* CallData.swift */; }; + C65A5D45272196AE005BA038 /* OptionalExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C65A5D44272196AE005BA038 /* OptionalExtensions.swift */; }; C666756F264C925800A0273C /* VFSUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6DA657B261C950C0020CB43 /* VFSUtil.swift */; }; C6667571264C925B00A0273C /* VFSUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6DA657B261C950C0020CB43 /* VFSUtil.swift */; }; C66B03BB26E8EB1A009B5EDC /* UIChatReplyBubbleView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C66B03BD26E8EB1A009B5EDC /* UIChatReplyBubbleView.xib */; }; C66B040A26EFDA55009B5EDC /* reply_cancel.png in Resources */ = {isa = PBXBuildFile; fileRef = C66B040926EFDA54009B5EDC /* reply_cancel.png */; }; C66B040E26F095D1009B5EDC /* cancel_forward.png in Resources */ = {isa = PBXBuildFile; fileRef = C66B040D26F095CE009B5EDC /* cancel_forward.png */; }; + C6710F4F2722903200ED888F /* RotatingSpinner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6710F4E2722903200ED888F /* RotatingSpinner.swift */; }; + C6710F512722932600ED888F /* UIImageViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6710F502722932600ED888F /* UIImageViewExtensions.swift */; }; + C6710F53272297C400ED888F /* VoipTexts.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6710F52272297C400ED888F /* VoipTexts.swift */; }; + C6710F5527229D5900ED888F /* LightDarkColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6710F5427229D5900ED888F /* LightDarkColor.swift */; }; + C6710F5727229DEE00ED888F /* TextStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6710F5627229DEE00ED888F /* TextStyle.swift */; }; + C6710F592722A9B800ED888F /* StyledLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6710F582722A9B800ED888F /* StyledLabel.swift */; }; + C6710F5C2722AAED00ED888F /* UIDeviceExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6710F5B2722AAED00ED888F /* UIDeviceExtensions.swift */; }; + C6710F612722AECB00ED888F /* Roboto-Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = C6710F5E2722AECB00ED888F /* Roboto-Italic.ttf */; }; + C6710F622722AECB00ED888F /* Roboto-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = C6710F5F2722AECB00ED888F /* Roboto-Bold.ttf */; }; + C6710F632722AECB00ED888F /* Roboto-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = C6710F602722AECB00ED888F /* Roboto-Regular.ttf */; }; + C6710F652722B13000ED888F /* voip_spinner.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F642722B13000ED888F /* voip_spinner.png */; }; + C6710F9F2722B20000ED888F /* voip_numpad_1.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F662722B1EF00ED888F /* voip_numpad_1.png */; }; + C6710FA02722B20000ED888F /* voip_numpad_0.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F672722B1EF00ED888F /* voip_numpad_0.png */; }; + C6710FA12722B20000ED888F /* voip_copy.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F682722B1EF00ED888F /* voip_copy.png */; }; + C6710FA22722B20000ED888F /* voip_merge_calls.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F692722B1EF00ED888F /* voip_merge_calls.png */; }; + C6710FA32722B20000ED888F /* voip_call_numpad.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F6A2722B1EF00ED888F /* voip_call_numpad.png */; }; + C6710FA42722B20000ED888F /* voip_single_contact_avatar.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F6B2722B1F000ED888F /* voip_single_contact_avatar.png */; }; + C6710FA52722B20000ED888F /* voip_mandatory.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F6C2722B1F000ED888F /* voip_mandatory.png */; }; + C6710FA62722B20000ED888F /* voip_bluetooth.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F6D2722B1F000ED888F /* voip_bluetooth.png */; }; + C6710FA72722B20000ED888F /* voip_call_more.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F6E2722B1F000ED888F /* voip_call_more.png */; }; + C6710FA82722B20000ED888F /* voip_multiple_contacts_avatar.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F6F2722B1F100ED888F /* voip_multiple_contacts_avatar.png */; }; + C6710FA92722B20000ED888F /* voip_call_stats.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F702722B1F100ED888F /* voip_call_stats.png */; }; + C6710FAA2722B20000ED888F /* voip_conference_active_speaker.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F712722B1F100ED888F /* voip_conference_active_speaker.png */; }; + C6710FAB2722B20000ED888F /* voip_edit.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F722722B1F200ED888F /* voip_edit.png */; }; + C6710FAC2722B20000ED888F /* voip_call_chat.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F732722B1F200ED888F /* voip_call_chat.png */; }; + C6710FAD2722B20000ED888F /* voip_numpad_5.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F742722B1F200ED888F /* voip_numpad_5.png */; }; + C6710FAE2722B20000ED888F /* voip_hangup.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F752722B1F300ED888F /* voip_hangup.png */; }; + C6710FAF2722B20000ED888F /* voip_conference_new.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F762722B1F300ED888F /* voip_conference_new.png */; }; + C6710FB02722B20000ED888F /* voip_chat_rooms_list.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F772722B1F300ED888F /* voip_chat_rooms_list.png */; }; + C6710FB12722B20000ED888F /* voip_micro_on.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F782722B1F300ED888F /* voip_micro_on.png */; }; + C6710FB22722B20000ED888F /* voip_calls_list.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F792722B1F400ED888F /* voip_calls_list.png */; }; + C6710FB32722B20000ED888F /* voip_pause.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F7A2722B1F400ED888F /* voip_pause.png */; }; + C6710FB42722B20000ED888F /* voip_conference_mosaic.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F7B2722B1F500ED888F /* voip_conference_mosaic.png */; }; + C6710FB52722B20000ED888F /* voip_delete.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F7C2722B1F500ED888F /* voip_delete.png */; }; + C6710FB62722B20000ED888F /* voip_audio_routes.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F7D2722B1F500ED888F /* voip_audio_routes.png */; }; + C6710FB72722B20000ED888F /* voip_numpad_6.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F7E2722B1F600ED888F /* voip_numpad_6.png */; }; + C6710FB82722B20000ED888F /* voip_numpad_3.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F7F2722B1F600ED888F /* voip_numpad_3.png */; }; + C6710FB92722B20000ED888F /* voip_call_add.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F802722B1F600ED888F /* voip_call_add.png */; }; + C6710FBA2722B20000ED888F /* voip_numpad_9.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F812722B1F700ED888F /* voip_numpad_9.png */; }; + C6710FBB2722B20000ED888F /* voip_call.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F822722B1F700ED888F /* voip_call.png */; }; + C6710FBC2722B20000ED888F /* voip_change_camera.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F832722B1F700ED888F /* voip_change_camera.png */; }; + C6710FBD2722B20000ED888F /* voip_call_header_paused.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F842722B1F800ED888F /* voip_call_header_paused.png */; }; + C6710FBE2722B20000ED888F /* voip_export.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F852722B1F800ED888F /* voip_export.png */; }; + C6710FBF2722B20000ED888F /* voip_earpiece.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F862722B1F800ED888F /* voip_earpiece.png */; }; + C6710FC02722B20000ED888F /* voip_numpad_hash.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F872722B1F800ED888F /* voip_numpad_hash.png */; }; + C6710FC12722B20000ED888F /* voip_micro_off.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F882722B1F900ED888F /* voip_micro_off.png */; }; + C6710FC22722B20000ED888F /* voip_numpad_2.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F892722B1F900ED888F /* voip_numpad_2.png */; }; + C6710FC32722B20000ED888F /* voip_cancel.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F8A2722B1F900ED888F /* voip_cancel.png */; }; + C6710FC42722B20000ED888F /* voip_numpad_star.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F8B2722B1FA00ED888F /* voip_numpad_star.png */; }; + C6710FC52722B20000ED888F /* voip_camera_off.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F8C2722B1FA00ED888F /* voip_camera_off.png */; }; + C6710FC62722B20000ED888F /* voip_menu_more.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F8D2722B1FA00ED888F /* voip_menu_more.png */; }; + C6710FC72722B20000ED888F /* voip_numpad_4.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F8E2722B1FB00ED888F /* voip_numpad_4.png */; }; + C6710FC82722B20000ED888F /* voip_numpad_7.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F8F2722B1FB00ED888F /* voip_numpad_7.png */; }; + C6710FC92722B20000ED888F /* voip_speaker_on.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F902722B1FB00ED888F /* voip_speaker_on.png */; }; + C6710FCA2722B20000ED888F /* voip_call_header_incoming.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F912722B1FC00ED888F /* voip_call_header_incoming.png */; }; + C6710FCB2722B20000ED888F /* voip_remote_recording.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F922722B1FC00ED888F /* voip_remote_recording.png */; }; + C6710FCC2722B20000ED888F /* voip_call_list_menu.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F932722B1FC00ED888F /* voip_call_list_menu.png */; }; + C6710FCD2722B20000ED888F /* voip_call_participants.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F942722B1FD00ED888F /* voip_call_participants.png */; }; + C6710FCE2722B20000ED888F /* voip_speaker_off.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F952722B1FD00ED888F /* voip_speaker_off.png */; }; + C6710FCF2722B20000ED888F /* voip_numpad_8.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F962722B1FD00ED888F /* voip_numpad_8.png */; }; + C6710FD02722B20000ED888F /* voip_call_header_active.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F972722B1FE00ED888F /* voip_call_header_active.png */; }; + C6710FD12722B20000ED888F /* voip_dropdown.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F982722B1FE00ED888F /* voip_dropdown.png */; }; + C6710FD22722B20000ED888F /* voip_call_record.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F992722B1FE00ED888F /* voip_call_record.png */; }; + C6710FD32722B20000ED888F /* voip_conference_paused_big.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F9A2722B1FE00ED888F /* voip_conference_paused_big.png */; }; + C6710FD42722B20000ED888F /* voip_camera_on.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F9B2722B1FF00ED888F /* voip_camera_on.png */; }; + C6710FD52722B20000ED888F /* voip_call_header_outgoing.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F9C2722B1FF00ED888F /* voip_call_header_outgoing.png */; }; + C6710FD62722B20000ED888F /* voip_conference_play_big.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F9D2722B20000ED888F /* voip_conference_play_big.png */; }; + C6710FD72722B20000ED888F /* voip_info.png in Resources */ = {isa = PBXBuildFile; fileRef = C6710F9E2722B20000ED888F /* voip_info.png */; }; + C6710FD92722BD0100ED888F /* UICallTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6710FD82722BD0100ED888F /* UICallTimer.swift */; }; + C6710FDC2722C3BB00ED888F /* UIVIewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6710FDB2722C3BB00ED888F /* UIVIewExtensions.swift */; }; + C6710FDE2722D44B00ED888F /* IncomingOuntgoingCommonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6710FDD2722D44A00ED888F /* IncomingOuntgoingCommonView.swift */; }; + C6710FE12722F0E400ED888F /* Avatar.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6710FE02722F0E400ED888F /* Avatar.swift */; }; + C6710FE527230B5800ED888F /* AddressExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6710FE427230B5800ED888F /* AddressExtensions.swift */; }; + C6710FE72723234400ED888F /* OutgoingCallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6710FE62723234400ED888F /* OutgoingCallView.swift */; }; + C6710FE92723DD7D00ED888F /* CallControlButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6710FE82723DD7D00ED888F /* CallControlButton.swift */; }; + C6710FEB2726874D00ED888F /* ButtonTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6710FEA2726874D00ED888F /* ButtonTheme.swift */; }; + C67C97B1274FB4C10074A0D8 /* VoipConferenceDisplayModeSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C67C97B0274FB4C10074A0D8 /* VoipConferenceDisplayModeSelectionView.swift */; }; + C67C97B4274FC5EF0074A0D8 /* AudioRoutesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C67C97B3274FC5EE0074A0D8 /* AudioRoutesView.swift */; }; + C67C97B8274FD76B0074A0D8 /* AudioRouteUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C67C97B7274FD76B0074A0D8 /* AudioRouteUtils.swift */; }; + C6824FBA27219D890043D4FC /* IncomingCallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6824FB927219D890043D4FC /* IncomingCallView.swift */; }; + C683B20E2722702300D4E15C /* VoipTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = C683B20D2722702300D4E15C /* VoipTheme.swift */; }; + C683B213272276CF00D4E15C /* UIColorExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C683B212272276CF00D4E15C /* UIColorExtensions.swift */; }; C6A1BB3526E8815400540D50 /* menu_info.png in Resources */ = {isa = PBXBuildFile; fileRef = C6A1BB3126E8815300540D50 /* menu_info.png */; }; C6A1BB3626E8815400540D50 /* menu_forward_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C6A1BB3226E8815400540D50 /* menu_forward_default.png */; }; C6A1BB3726E8815400540D50 /* menu_copy_text_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C6A1BB3326E8815400540D50 /* menu_copy_text_default.png */; }; @@ -698,13 +776,46 @@ C6A1BB4126E889AD00540D50 /* forward_message_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C6A1BB4026E889AD00540D50 /* forward_message_default.png */; }; C6A1BB4326E88F7C00540D50 /* menu_resend_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C6A1BB4226E88F7C00540D50 /* menu_resend_default.png */; }; C6A1BB4526E890BD00540D50 /* file_voice_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C6A1BB4426E890BD00540D50 /* file_voice_default.png */; }; + C6B04D61274B954500F70559 /* ParticipantsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6B04D60274B954500F70559 /* ParticipantsListView.swift */; }; + C6B04D63274B95D500F70559 /* VoipParticipantCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6B04D62274B95D400F70559 /* VoipParticipantCell.swift */; }; + C6B04D67274BD61300F70559 /* VoipConferenceActiveSpeakerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6B04D66274BD61200F70559 /* VoipConferenceActiveSpeakerView.swift */; }; + C6B04D69274BD6A100F70559 /* VoipActiveSpeakerParticipantCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6B04D68274BD6A100F70559 /* VoipActiveSpeakerParticipantCell.swift */; }; C6B4444226AAD0980076C517 /* file_video_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C6B4443D26AAD0970076C517 /* file_video_default.png */; }; C6B4444326AAD0980076C517 /* file_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C6B4443E26AAD0970076C517 /* file_default.png */; }; C6B4444426AAD0980076C517 /* file_picture_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C6B4443F26AAD0970076C517 /* file_picture_default.png */; }; C6B4444526AAD0980076C517 /* file_audio_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C6B4444026AAD0970076C517 /* file_audio_default.png */; }; C6B4444626AAD0980076C517 /* file_pdf_default.png in Resources */ = {isa = PBXBuildFile; fileRef = C6B4444126AAD0970076C517 /* file_pdf_default.png */; }; C6B4444826AADA530076C517 /* SwiftUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6B4444726AADA530076C517 /* SwiftUtil.swift */; }; + C6C65E89272723DC00E48FC6 /* UIVIewControllerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6C65E88272723DC00E48FC6 /* UIVIewControllerExtensions.swift */; }; + C6C65E8B2727274A00E48FC6 /* NumpadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6C65E8A2727274A00E48FC6 /* NumpadView.swift */; }; + C6C98CCE27438A3F00059B55 /* DismissableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6C98CCD27438A3F00059B55 /* DismissableView.swift */; }; + C6C98CD027439A7F00059B55 /* VoipCallCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6C98CCF27439A7F00059B55 /* VoipCallCell.swift */; }; + C6C98CD22743FD0B00059B55 /* VoipCallContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6C98CD12743FD0B00059B55 /* VoipCallContextMenu.swift */; }; + C6C98CD527453ED900059B55 /* ConferenceViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6C98CD427453ED700059B55 /* ConferenceViewModel.swift */; }; + C6C98CD727453F9600059B55 /* ConferenceParticipantData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6C98CD627453F9600059B55 /* ConferenceParticipantData.swift */; }; + C6C98CDB274541E400059B55 /* ConferenceParticipantDeviceData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6C98CDA274541E400059B55 /* ConferenceParticipantDeviceData.swift */; }; + C6C98CDD274547C500059B55 /* ParticipantExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6C98CDC274547C500059B55 /* ParticipantExtensions.swift */; }; + C6C98CDF2745590500059B55 /* ConferenceExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6C98CDE2745590400059B55 /* ConferenceExtensions.swift */; }; + C6C98CE1274568F800059B55 /* VoipConferenceGridView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6C98CE0274568F700059B55 /* VoipConferenceGridView.swift */; }; + C6D09F3D273EE467003C2173 /* BouncingCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6D09F3C273EE467003C2173 /* BouncingCounter.swift */; }; + C6D09F3F274273FB003C2173 /* CallStatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6D09F3E274273FB003C2173 /* CallStatsView.swift */; }; + C6D09F4127428626003C2173 /* IceState.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6D09F4027428626003C2173 /* IceState.swift */; }; + C6D09F43274288D4003C2173 /* PayloadType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6D09F42274288D4003C2173 /* PayloadType.swift */; }; + C6D09F4B27438707003C2173 /* CallsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6D09F4A27438706003C2173 /* CallsListView.swift */; }; + C6D1EC4A274D212B0091881C /* UICamSwitch.m in Sources */ = {isa = PBXBuildFile; fileRef = C6D1EC49274D212B0091881C /* UICamSwitch.m */; }; + C6D52B45274648E500904660 /* VoipGridParticipantCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6D52B44274648E500904660 /* VoipGridParticipantCell.swift */; }; C6DA657C261C950C0020CB43 /* VFSUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6DA657B261C950C0020CB43 /* VFSUtil.swift */; }; + C6EA2F4827514D09008E60F8 /* voip_call_forward.png in Resources */ = {isa = PBXBuildFile; fileRef = C6EA2F4727514D08008E60F8 /* voip_call_forward.png */; }; + C6F2D4EF27392D970071BA52 /* CallsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F2D4EE27392D960071BA52 /* CallsViewModel.swift */; }; + C6F2D4F1273935860071BA52 /* CallExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F2D4F0273935860071BA52 /* CallExtensions.swift */; }; + C6F2D4F32739475C0071BA52 /* ActiveCallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F2D4F22739475C0071BA52 /* ActiveCallView.swift */; }; + C6F2D4F72739861F0071BA52 /* RemotelyRecording.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F2D4F62739861F0071BA52 /* RemotelyRecording.swift */; }; + C6F2D4F9273A4CD70071BA52 /* SharedLayoutConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F2D4F8273A4CD60071BA52 /* SharedLayoutConstants.swift */; }; + C6F2D4FD273A85A90071BA52 /* PausedCallOrConferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F2D4FC273A85A90071BA52 /* PausedCallOrConferenceView.swift */; }; + C6F2D4FF273AF01A0071BA52 /* ControlsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F2D4FE273AF01A0071BA52 /* ControlsView.swift */; }; + C6F2D501273B0EFC0071BA52 /* VoipDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F2D500273B0EFC0071BA52 /* VoipDialog.swift */; }; + C6F2D503273BAC030071BA52 /* ButtonWithStateBackgrounds.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F2D502273BAC030071BA52 /* ButtonWithStateBackgrounds.swift */; }; + C6F2D505273BB3BB0071BA52 /* UIApplication+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F2D504273BB3BB0071BA52 /* UIApplication+Extension.swift */; }; C90FAA7915AF54E6002091CB /* HistoryDetailsView.m in Sources */ = {isa = PBXBuildFile; fileRef = C90FAA7715AF54E6002091CB /* HistoryDetailsView.m */; }; CF15F21E20E4F9A3008B1DE6 /* UIImageViewDeletable.m in Sources */ = {isa = PBXBuildFile; fileRef = CF15F21C20E4F9A3008B1DE6 /* UIImageViewDeletable.m */; }; CF15F21F20E4F9A3008B1DE6 /* UIImageViewDeletable.xib in Resources */ = {isa = PBXBuildFile; fileRef = CF15F21D20E4F9A3008B1DE6 /* UIImageViewDeletable.xib */; }; @@ -721,7 +832,6 @@ CFBD7A2A20E504AE007C5286 /* delete_img.png in Resources */ = {isa = PBXBuildFile; fileRef = CFBD7A2320E504AD007C5286 /* delete_img.png */; }; D306459E1611EC2A00BB571E /* UILoadingImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = D306459D1611EC2900BB571E /* UILoadingImageView.m */; }; D3128FE115AABC7E00A2147A /* ContactDetailsView.m in Sources */ = {isa = PBXBuildFile; fileRef = D3128FDF15AABC7E00A2147A /* ContactDetailsView.m */; }; - D31AAF5E159B3919002C6B02 /* CallPausedTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = D31AAF5D159B3919002C6B02 /* CallPausedTableView.m */; }; D31B4B21159876C0002E6C72 /* UICompositeView.m in Sources */ = {isa = PBXBuildFile; fileRef = D31B4B1F159876C0002E6C72 /* UICompositeView.m */; }; D31C9C98158A1CDF00756B45 /* UIHistoryCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D31C9C97158A1CDE00756B45 /* UIHistoryCell.m */; }; D326483815887D5200930C67 /* OrderedDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = D326483715887D5200930C67 /* OrderedDictionary.m */; }; @@ -736,7 +846,6 @@ D35860D615B549B500513429 /* Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = D35860D515B549B500513429 /* Utils.m */; }; D35E7597159460580066B1C1 /* ChatsListView.m in Sources */ = {isa = PBXBuildFile; fileRef = D35E7595159460560066B1C1 /* ChatsListView.m */; }; D35E759F159460B70066B1C1 /* SettingsView.m in Sources */ = {isa = PBXBuildFile; fileRef = D35E759D159460B50066B1C1 /* SettingsView.m */; }; - D36FB2D51589EF7C0036F6F2 /* UIPauseButton.m in Sources */ = {isa = PBXBuildFile; fileRef = D36FB2D41589EF7C0036F6F2 /* UIPauseButton.m */; }; D378AB2A15DCDB4A0098505D /* ImagePickerView.m in Sources */ = {isa = PBXBuildFile; fileRef = D378AB2915DCDB490098505D /* ImagePickerView.m */; }; D37C639B15AADEF6009D0BAC /* ContactDetailsTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = D37C639A15AADEF5009D0BAC /* ContactDetailsTableView.m */; }; D37DC6C11594AE1800B2A5EB /* LinphoneCoreSettingsStore.m in Sources */ = {isa = PBXBuildFile; fileRef = D37DC6C01594AE1800B2A5EB /* LinphoneCoreSettingsStore.m */; }; @@ -769,10 +878,8 @@ D38187C115FE345B00C3EDCA /* DialerView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D38187C415FE345B00C3EDCA /* DialerView.xib */; }; D38187CD15FE346700C3EDCA /* HistoryDetailsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D38187D015FE346700C3EDCA /* HistoryDetailsView.xib */; }; D38187D115FE346B00C3EDCA /* HistoryListView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D38187D415FE346B00C3EDCA /* HistoryListView.xib */; }; - D38187D915FE347700C3EDCA /* CallIncomingView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D38187DC15FE347700C3EDCA /* CallIncomingView.xib */; }; D38187DD15FE348A00C3EDCA /* AssistantView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D38187E015FE348A00C3EDCA /* AssistantView.xib */; }; D38187F815FE355D00C3EDCA /* TabBarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D38187FB15FE355D00C3EDCA /* TabBarView.xib */; }; - D381881915FE3FCA00C3EDCA /* CallView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D381881C15FE3FCA00C3EDCA /* CallView.xib */; }; D3A55FBC15877E5E003FD403 /* UIContactCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D3A55FBB15877E5E003FD403 /* UIContactCell.m */; }; D3A8BB7015A6C7D500F96BE5 /* UIChatBubbleTextCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D3A8BB6F15A6C7D500F96BE5 /* UIChatBubbleTextCell.m */; }; D3C6526715AC1A8F0092A874 /* UIContactDetailsCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D3C6526615AC1A8F0092A874 /* UIContactDetailsCell.m */; }; @@ -783,11 +890,10 @@ D3ED3E871586291E006C0DE4 /* TabBarView.m in Sources */ = {isa = PBXBuildFile; fileRef = D3ED3E851586291B006C0DE4 /* TabBarView.m */; }; D3ED3EA71587334E006C0DE4 /* HistoryListTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = D3ED3EA51587334C006C0DE4 /* HistoryListTableView.m */; }; D3ED3EB81587392C006C0DE4 /* HistoryListView.m in Sources */ = {isa = PBXBuildFile; fileRef = D3ED3EB615873929006C0DE4 /* HistoryListView.m */; }; - D3F26BF115986B73005F9CAB /* CallIncomingView.m in Sources */ = {isa = PBXBuildFile; fileRef = D3F26BEF15986B71005F9CAB /* CallIncomingView.m */; }; D3F795D615A582810077328B /* ChatConversationView.m in Sources */ = {isa = PBXBuildFile; fileRef = D3F795D415A582800077328B /* ChatConversationView.m */; }; D3F7998115BD32370018C273 /* TPMultiLayoutViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D3F7998015BD32370018C273 /* TPMultiLayoutViewController.m */; }; - D3F83EEC1582021700336684 /* CallView.m in Sources */ = {isa = PBXBuildFile; fileRef = D3F83EEA1582021700336684 /* CallView.m */; }; D3F83F8E15822ABE00336684 /* PhoneMainView.m in Sources */ = {isa = PBXBuildFile; fileRef = D3F83F8D15822ABD00336684 /* PhoneMainView.m */; }; + E42B8736B8196388DDF2771A /* Pods_msgNotificationService.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 72B39681098B0AF5B59B3A61 /* Pods_msgNotificationService.framework */; }; EA0007A62356008F003CC6BF /* msgNotificationService.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = EA5F25D9232BD3E200475F2E /* msgNotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; EA3650DB2330D2E30001148A /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA5F25DB232BD3E200475F2E /* NotificationService.swift */; }; EA88A405242A6216007FEC61 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 63AADBC41B6A0FF200AA16FD /* Localizable.strings */; }; @@ -859,8 +965,10 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 046DEFE77AD0675DA9932C4C /* Pods-liblinphoneTesterTests.distributionadhoc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-liblinphoneTesterTests.distributionadhoc.xcconfig"; path = "Pods/Target Support Files/Pods-liblinphoneTesterTests/Pods-liblinphoneTesterTests.distributionadhoc.xcconfig"; sourceTree = ""; }; - 13B1BD646346F33BF57412F2 /* Pods-messagesNotification.distribution.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-messagesNotification.distribution.xcconfig"; path = "Pods/Target Support Files/Pods-messagesNotification/Pods-messagesNotification.distribution.xcconfig"; sourceTree = ""; }; + 02DBDD5A09F46796AEC2485B /* Pods-msgNotificationContent.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-msgNotificationContent.release.xcconfig"; path = "Target Support Files/Pods-msgNotificationContent/Pods-msgNotificationContent.release.xcconfig"; sourceTree = ""; }; + 063D57B2E4769739DC5DA5C0 /* Pods-msgNotificationService.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-msgNotificationService.release.xcconfig"; path = "Target Support Files/Pods-msgNotificationService/Pods-msgNotificationService.release.xcconfig"; sourceTree = ""; }; + 0DF941C97B75E7BA39A90600 /* Pods-linphone.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-linphone.release.xcconfig"; path = "Target Support Files/Pods-linphone/Pods-linphone.release.xcconfig"; sourceTree = ""; }; + 1060E68152C51FCE5ACBF779 /* Pods-msgNotificationContent.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-msgNotificationContent.debug.xcconfig"; path = "Target Support Files/Pods-msgNotificationContent/Pods-msgNotificationContent.debug.xcconfig"; sourceTree = ""; }; 152F22351B15E889008C0621 /* libxml2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxml2.dylib; path = usr/lib/libxml2.dylib; sourceTree = SDKROOT; }; 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 1D3623240D0F684500981E51 /* LinphoneAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LinphoneAppDelegate.h; sourceTree = ""; }; @@ -869,8 +977,6 @@ 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 2214EB7812F846B1002A5394 /* UICallButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UICallButton.h; sourceTree = ""; }; 2214EB7912F846B1002A5394 /* UICallButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UICallButton.m; sourceTree = ""; }; - 2214EB8712F84EBB002A5394 /* UIHangUpButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIHangUpButton.h; sourceTree = ""; }; - 2214EB8812F84EBB002A5394 /* UIHangUpButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIHangUpButton.m; sourceTree = ""; }; 2214EBF112F86360002A5394 /* UIMutedMicroButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIMutedMicroButton.h; sourceTree = ""; }; 2214EBF212F86360002A5394 /* UIMutedMicroButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIMutedMicroButton.m; sourceTree = ""; }; 22276E8613C73D8A00210156 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; }; @@ -888,15 +994,12 @@ 22744043106F33FC006EC466 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 22744056106F9BC9006EC466 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; 228697C311AC29B800E9E0CA /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; }; - 22968A5D12F875C600588287 /* UISpeakerButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UISpeakerButton.h; sourceTree = ""; }; - 22968A5E12F875C600588287 /* UISpeakerButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UISpeakerButton.m; sourceTree = ""; }; - 22AA8AFF13D83F6300B30535 /* UICamSwitch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UICamSwitch.h; sourceTree = ""; }; - 22AA8B0013D83F6300B30535 /* UICamSwitch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UICamSwitch.m; sourceTree = ""; }; 22B5EFA210CE50BD00777D97 /* AddressBookUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBookUI.framework; path = System/Library/Frameworks/AddressBookUI.framework; sourceTree = SDKROOT; }; 22B5F03410CE6B2F00777D97 /* AddressBook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBook.framework; path = System/Library/Frameworks/AddressBook.framework; sourceTree = SDKROOT; }; 22C7555E1317E59C007BC101 /* UIBluetoothButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIBluetoothButton.h; sourceTree = ""; }; 22C7555F1317E59C007BC101 /* UIBluetoothButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIBluetoothButton.m; sourceTree = ""; }; 22D1B68012A3E0BE001AE361 /* libresolv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libresolv.dylib; path = usr/lib/libresolv.dylib; sourceTree = SDKROOT; }; + 22D3BF4C45858F6B07F4D2A4 /* Pods-linphone.distributionadhoc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-linphone.distributionadhoc.xcconfig"; path = "Target Support Files/Pods-linphone/Pods-linphone.distributionadhoc.xcconfig"; sourceTree = ""; }; 22E0A81C111C44E100B04932 /* AboutView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AboutView.m; sourceTree = ""; }; 22E0A81D111C44E100B04932 /* AboutView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AboutView.h; sourceTree = ""; }; 22F2508B107141E100AC9B3F /* DialerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DialerView.h; sourceTree = ""; }; @@ -905,8 +1008,6 @@ 244523AD1E8266CC0037A187 /* chat_error.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = chat_error.png; sourceTree = ""; }; 244523AE1E8266CC0037A187 /* chat_read.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = chat_read.png; sourceTree = ""; }; 244523BC1E8D3A6C0037A187 /* chat_unsecure.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = chat_unsecure.png; sourceTree = ""; }; - 24585CBE78DA4F005C7F9D71 /* Pods-liblinphoneTester.distribution.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-liblinphoneTester.distribution.xcconfig"; path = "Pods/Target Support Files/Pods-liblinphoneTester/Pods-liblinphoneTester.distribution.xcconfig"; sourceTree = ""; }; - 248C326F4AD75E654C1CB37A /* Pods-liblinphoneTesterTests.distribution.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-liblinphoneTesterTests.distribution.xcconfig"; path = "Pods/Target Support Files/Pods-liblinphoneTesterTests/Pods-liblinphoneTesterTests.distribution.xcconfig"; sourceTree = ""; }; 249660941FD6A359001D55AA /* Photos.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Photos.framework; path = System/Library/Frameworks/Photos.framework; sourceTree = SDKROOT; }; 24A3459D1D95797700881A5C /* UIShopTableCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = UIShopTableCell.xib; sourceTree = ""; }; 24A345A51D95798A00881A5C /* UIShopTableCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIShopTableCell.m; sourceTree = ""; }; @@ -927,16 +1028,14 @@ 288765FC0DF74451002DB57D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 32CA4F630368D1EE00C91783 /* linphone_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = linphone_Prefix.pch; sourceTree = ""; }; - 34027665305514025971F85C /* Pods-msgNotificationContent.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-msgNotificationContent.release.xcconfig"; path = "Pods/Target Support Files/Pods-msgNotificationContent/Pods-msgNotificationContent.release.xcconfig"; sourceTree = ""; }; 340751961506459A00B89C47 /* CoreTelephony.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreTelephony.framework; path = System/Library/Frameworks/CoreTelephony.framework; sourceTree = SDKROOT; }; - 340751E5150F38FC00B89C47 /* UIVideoButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIVideoButton.h; sourceTree = ""; }; - 340751E6150F38FD00B89C47 /* UIVideoButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIVideoButton.m; sourceTree = ""; }; + 3411568BE5527EB500F75EBB /* Pods-msgNotificationService.distributionadhoc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-msgNotificationService.distributionadhoc.xcconfig"; path = "Target Support Files/Pods-msgNotificationService/Pods-msgNotificationService.distributionadhoc.xcconfig"; sourceTree = ""; }; 34216F3E1547EBCD00EA9777 /* VideoZoomHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VideoZoomHandler.h; path = LinphoneUI/VideoZoomHandler.h; sourceTree = ""; }; 34216F3F1547EBCD00EA9777 /* VideoZoomHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = VideoZoomHandler.m; path = LinphoneUI/VideoZoomHandler.m; sourceTree = ""; }; 344ABDEF14850AE9007420B6 /* libc++.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libc++.1.dylib"; path = "usr/lib/libc++.1.dylib"; sourceTree = SDKROOT; }; 344ABDF014850AE9007420B6 /* libstdc++.6.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libstdc++.6.dylib"; path = "usr/lib/libstdc++.6.dylib"; sourceTree = SDKROOT; }; - 38A3AE51B9E09ABF29222E5F /* Pods-liblinphoneTester.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-liblinphoneTester.release.xcconfig"; path = "Pods/Target Support Files/Pods-liblinphoneTester/Pods-liblinphoneTester.release.xcconfig"; sourceTree = ""; }; - 38DF35D11A7C0F45E990C83A /* Pods-linphone.distribution.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-linphone.distribution.xcconfig"; path = "Pods/Target Support Files/Pods-linphone/Pods-linphone.distribution.xcconfig"; sourceTree = ""; }; + 507103607396F28FF4427108 /* Pods-msgNotificationContent.distribution.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-msgNotificationContent.distribution.xcconfig"; path = "Target Support Files/Pods-msgNotificationContent/Pods-msgNotificationContent.distribution.xcconfig"; sourceTree = ""; }; + 53432234870660E9876CBCA8 /* Pods-linphone.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-linphone.debug.xcconfig"; path = "Target Support Files/Pods-linphone/Pods-linphone.debug.xcconfig"; sourceTree = ""; }; 570742571D5A0691004B9C84 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/ShopView.xib; sourceTree = ""; }; 5707425F1D5A09B8004B9C84 /* ShopView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShopView.m; sourceTree = ""; }; 570742601D5A09B8004B9C84 /* ShopView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShopView.h; sourceTree = ""; }; @@ -956,6 +1055,7 @@ 614C087723D1A35F00217F80 /* ProviderDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProviderDelegate.swift; sourceTree = ""; }; 614C087923D1A37400217F80 /* CallManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallManager.swift; sourceTree = ""; }; 614D09CD21E74D5400C43EDF /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 6150F32455334A0A7B3D46C8 /* Pods-msgNotificationContent.distributionadhoc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-msgNotificationContent.distributionadhoc.xcconfig"; path = "Target Support Files/Pods-msgNotificationContent/Pods-msgNotificationContent.distributionadhoc.xcconfig"; sourceTree = ""; }; 61586B7A217A16EE0038AC45 /* menu_about.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = menu_about.png; sourceTree = ""; }; 61586B82217A16FD0038AC45 /* menu_about@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menu_about@2x.png"; sourceTree = ""; }; 61586B84217A17070038AC45 /* menu_assistant.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = menu_assistant.png; sourceTree = ""; }; @@ -1000,10 +1100,6 @@ 6187B1B624B3271500D580FB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/AssistantLinkView.strings; sourceTree = ""; }; 6187B1B724B3271600D580FB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/AssistantView.strings; sourceTree = ""; }; 6187B1B824B3271600D580FB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/AssistantViewScreens.strings; sourceTree = ""; }; - 6187B1B924B3271700D580FB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/CallIncomingView.strings; sourceTree = ""; }; - 6187B1BA24B3271700D580FB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/CallOutgoingView.strings; sourceTree = ""; }; - 6187B1BB24B3271700D580FB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/CallView.strings; sourceTree = ""; }; - 6187B1BC24B3271800D580FB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = "hu.lproj/CallView~ipad.strings"; sourceTree = ""; }; 6187B1BD24B3271800D580FB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/ChatConversationCreateView.strings; sourceTree = ""; }; 6187B1BE24B3271900D580FB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/ChatConversationImdnView.strings; sourceTree = ""; }; 6187B1BF24B3271900D580FB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/ChatConversationInfoView.strings; sourceTree = ""; }; @@ -1018,8 +1114,6 @@ 6187B1C824B3271D00D580FB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/HistoryDetailsView.strings; sourceTree = ""; }; 6187B1C924B3271D00D580FB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/HistoryListView.strings; sourceTree = ""; }; 6187B1CA24B3271E00D580FB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/ImageView.strings; sourceTree = ""; }; - 6187B1CB24B3271E00D580FB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/UICallConferenceCell.strings; sourceTree = ""; }; - 6187B1CC24B3271E00D580FB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/UICallPausedCell.strings; sourceTree = ""; }; 6187B1CD24B3271F00D580FB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/UIChatBubblePhotoCell.strings; sourceTree = ""; }; 6187B1CE24B3271F00D580FB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/UIChatBubbleTextCell.strings; sourceTree = ""; }; 6187B1CF24B3271F00D580FB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/UIChatCell.strings; sourceTree = ""; }; @@ -1519,14 +1613,8 @@ 63423C091C4501D000D9A050 /* Contact.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Contact.m; sourceTree = ""; }; 634610041B61330300548952 /* UILabel+Boldify.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UILabel+Boldify.h"; sourceTree = ""; }; 634610051B61330300548952 /* UILabel+Boldify.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UILabel+Boldify.m"; sourceTree = ""; }; - 6346100D1B61409800548952 /* CallOutgoingView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CallOutgoingView.h; sourceTree = ""; }; - 6346100E1B61409800548952 /* CallOutgoingView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CallOutgoingView.m; sourceTree = ""; }; - 634610111B6140A500548952 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/CallOutgoingView.xib; sourceTree = ""; }; 635173F71BA082A40095EB0A /* UIChatBubblePhotoCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIChatBubblePhotoCell.h; sourceTree = ""; }; 635173F81BA082A40095EB0A /* UIChatBubblePhotoCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIChatBubblePhotoCell.m; sourceTree = ""; }; - 6352A5721BE0D4B800594C1C /* CallSideMenuView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CallSideMenuView.h; sourceTree = ""; }; - 6352A5731BE0D4B800594C1C /* CallSideMenuView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CallSideMenuView.m; sourceTree = ""; }; - 6352A5741BE0D4B800594C1C /* CallSideMenuView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CallSideMenuView.xib; sourceTree = ""; }; 635775231B6673EC00C8B704 /* HistoryDetailsTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HistoryDetailsTableView.h; sourceTree = ""; }; 635775241B6673EC00C8B704 /* HistoryDetailsTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HistoryDetailsTableView.m; sourceTree = ""; }; 636316D21A1DEBCB0009B839 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/AboutView.xib; sourceTree = ""; }; @@ -1543,7 +1631,6 @@ 6381DA7B1C1AD5EA00DF3BBD /* UIBouncingView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIBouncingView.h; sourceTree = ""; }; 6381DA7C1C1AD5EA00DF3BBD /* UIBouncingView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIBouncingView.m; sourceTree = ""; }; 638F1A611C2021B2004B8E02 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Base.lproj/DialerView~ipad.xib"; sourceTree = ""; }; - 638F1A871C2167C2004B8E02 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Base.lproj/CallView~ipad.xib"; sourceTree = ""; }; 638F1A901C21993D004B8E02 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Base.lproj/UICompositeView~ipad.xib"; sourceTree = ""; }; 639CEAFE1A1DF4D9004DE38F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/StatusBarView.xib; sourceTree = ""; }; 639CEB011A1DF4E4004DE38F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/UIHistoryCell.xib; sourceTree = ""; }; @@ -1552,7 +1639,6 @@ 639E9C7E1C0DB13D00019A75 /* UICheckBoxTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UICheckBoxTableView.h; sourceTree = ""; }; 639E9C7F1C0DB13D00019A75 /* UICheckBoxTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UICheckBoxTableView.m; sourceTree = ""; }; 639E9C941C0DB7BE00019A75 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/FirstLoginView.xib; sourceTree = ""; }; - 639E9C9E1C0DB7DF00019A75 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/UICallPausedCell.xib; sourceTree = ""; }; 639E9CA11C0DB7E500019A75 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/UIChatBubblePhotoCell.xib; sourceTree = ""; }; 639E9CA41C0DB7EA00019A75 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/UIChatBubbleTextCell.xib; sourceTree = ""; }; 639E9CA71C0DB7F200019A75 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/UIChatCreateCell.xib; sourceTree = ""; }; @@ -1586,8 +1672,6 @@ 63B8D68D1BCBE65600C12B09 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/ChatConversationCreateView.xib; sourceTree = ""; }; 63B8D69F1BCBF43100C12B09 /* UIChatCreateCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIChatCreateCell.h; sourceTree = ""; }; 63B8D6A01BCBF43100C12B09 /* UIChatCreateCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIChatCreateCell.m; sourceTree = ""; }; - 63BC49E01BA2CDFC004EC273 /* UICallPausedCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UICallPausedCell.h; sourceTree = ""; }; - 63BC49E11BA2CDFC004EC273 /* UICallPausedCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UICallPausedCell.m; sourceTree = ""; }; 63BE7A761D75BDF6000990EF /* ShopTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShopTableView.h; sourceTree = ""; }; 63BE7A771D75BDF6000990EF /* ShopTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShopTableView.m; sourceTree = ""; }; 63C441C11BBC23ED0053DC5E /* UIAssistantTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIAssistantTextField.h; sourceTree = ""; }; @@ -1611,30 +1695,13 @@ 63EEE40D1BBA9B250087D3AF /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; }; 63F1DF421BCE618E00EDED90 /* UIAddressTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIAddressTextField.h; sourceTree = ""; }; 63F1DF431BCE618E00EDED90 /* UIAddressTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIAddressTextField.m; sourceTree = ""; }; - 63F1DF491BCE983100EDED90 /* CallConferenceTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CallConferenceTableView.h; sourceTree = ""; }; - 63F1DF4A1BCE983200EDED90 /* CallConferenceTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CallConferenceTableView.m; sourceTree = ""; }; - 63F1DF4C1BCE985F00EDED90 /* UICallConferenceCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UICallConferenceCell.h; sourceTree = ""; }; - 63F1DF4D1BCE985F00EDED90 /* UICallConferenceCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UICallConferenceCell.m; sourceTree = ""; }; - 63F1DF521BCE986A00EDED90 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/UICallConferenceCell.xib; sourceTree = ""; }; 63FB30331A680E73008CA393 /* UIRoundedImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIRoundedImageView.h; sourceTree = ""; }; 63FB30341A680E73008CA393 /* UIRoundedImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIRoundedImageView.m; sourceTree = ""; }; - 65CEDD144CABFAA70A29AF27 /* Pods_linphone.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_linphone.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 68D9EC27FCECD5DE2E19CD3C /* Pods-liblinphoneTester.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-liblinphoneTester.debug.xcconfig"; path = "Pods/Target Support Files/Pods-liblinphoneTester/Pods-liblinphoneTester.debug.xcconfig"; sourceTree = ""; }; - 6E1BC45342F5201DABD7FE55 /* Pods_latestCallsWidget.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_latestCallsWidget.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 6F30EA7BEA39DA427CE0754E /* Pods_msgNotificationService.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_msgNotificationService.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 70E542F213E147E3002BA2C0 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; }; 70E542F413E147EB002BA2C0 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; - 7513CBF7F2BA0A9F99977C2B /* Pods_richNotifications.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_richNotifications.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 791017662FE117B9B12E8938 /* Pods-messagesNotification.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-messagesNotification.debug.xcconfig"; path = "Pods/Target Support Files/Pods-messagesNotification/Pods-messagesNotification.debug.xcconfig"; sourceTree = ""; }; - 799BA1104845EB01ACE764D8 /* Pods-linphone.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-linphone.debug.xcconfig"; path = "Pods/Target Support Files/Pods-linphone/Pods-linphone.debug.xcconfig"; sourceTree = ""; }; - 7D8CCFE176C634813E3A2593 /* Pods-liblinphoneTesterTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-liblinphoneTesterTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-liblinphoneTesterTests/Pods-liblinphoneTesterTests.debug.xcconfig"; sourceTree = ""; }; - 82E9DEDA2A78C6DBBD1A54DB /* Pods_msgNotificationContent.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_msgNotificationContent.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 85FB19B6A8124D942C8471F1 /* Pods-linphoneTests.distribution.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-linphoneTests.distribution.xcconfig"; path = "Pods/Target Support Files/Pods-linphoneTests/Pods-linphoneTests.distribution.xcconfig"; sourceTree = ""; }; - 8B488C393394746F9D630789 /* Pods_latestChatroomsWidget.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_latestChatroomsWidget.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 8B4C43A28E90775F6FCA2CEE /* Pods-msgNotificationContent.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-msgNotificationContent.debug.xcconfig"; path = "Pods/Target Support Files/Pods-msgNotificationContent/Pods-msgNotificationContent.debug.xcconfig"; sourceTree = ""; }; + 72B39681098B0AF5B59B3A61 /* Pods_msgNotificationService.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_msgNotificationService.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 79B41078A602EFB886981917 /* Pods-linphone.distribution.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-linphone.distribution.xcconfig"; path = "Target Support Files/Pods-linphone/Pods-linphone.distribution.xcconfig"; sourceTree = ""; }; 8C1A1F7C1FA331D40064BE00 /* libsoci_sqlite3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libsoci_sqlite3.a; path = "liblinphone-sdk/apple-darwin/lib/libsoci_sqlite3.a"; sourceTree = ""; }; - 8C1B67051E671826001EA2FE /* AudioHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AudioHelper.m; sourceTree = ""; }; - 8C1B67081E6718BC001EA2FE /* AudioHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AudioHelper.h; path = Utils/AudioHelper.h; sourceTree = ""; }; 8C23BCB71D82AAC3005F19BB /* linphone.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = linphone.entitlements; sourceTree = ""; }; 8C2595DE1DEDCC8E007A6424 /* CallKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CallKit.framework; path = System/Library/Frameworks/CallKit.framework; sourceTree = SDKROOT; }; 8C2A81931F87B7FF0012A66B /* chat_group_avatar@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "chat_group_avatar@2x.png"; sourceTree = ""; }; @@ -1671,10 +1738,6 @@ 8CBD7BAD20B6B82F00E5DCC0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/UIChatCreateCollectionViewCell.xib; sourceTree = ""; }; 8CBD7BB120B6B86900E5DCC0 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/AssistantView.strings; sourceTree = ""; }; 8CBD7BB220B6B86A00E5DCC0 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/AssistantViewScreens.strings; sourceTree = ""; }; - 8CBD7BB320B6B86B00E5DCC0 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/CallIncomingView.strings; sourceTree = ""; }; - 8CBD7BB420B6B86B00E5DCC0 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/CallOutgoingView.strings; sourceTree = ""; }; - 8CBD7BB520B6B86C00E5DCC0 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/CallView.strings; sourceTree = ""; }; - 8CBD7BB620B6B86D00E5DCC0 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = "fr.lproj/CallView~ipad.strings"; sourceTree = ""; }; 8CBD7BB720B6B86E00E5DCC0 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/ChatConversationInfoView.strings; sourceTree = ""; }; 8CBD7BB820B6B86F00E5DCC0 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/ChatConversationImdnView.strings; sourceTree = ""; }; 8CBD7BB920B6B87000E5DCC0 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/ChatConversationCreateView.strings; sourceTree = ""; }; @@ -1689,8 +1752,6 @@ 8CBD7BC220B6B87600E5DCC0 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/HistoryDetailsView.strings; sourceTree = ""; }; 8CBD7BC320B6B87700E5DCC0 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/HistoryListView.strings; sourceTree = ""; }; 8CBD7BC420B6B87800E5DCC0 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/ImageView.strings; sourceTree = ""; }; - 8CBD7BC520B6B87800E5DCC0 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/UICallConferenceCell.strings; sourceTree = ""; }; - 8CBD7BC620B6B87900E5DCC0 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/UICallPausedCell.strings; sourceTree = ""; }; 8CBD7BC720B6B87A00E5DCC0 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/UIChatBubblePhotoCell.strings; sourceTree = ""; }; 8CBD7BC820B6B87B00E5DCC0 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/UIChatBubbleTextCell.strings; sourceTree = ""; }; 8CBD7BC920B6B87B00E5DCC0 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/UIChatCell.strings; sourceTree = ""; }; @@ -1728,13 +1789,14 @@ 8CF25D941F9F336100BEA0C1 /* check_unselected.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = check_unselected.png; sourceTree = ""; }; 8CF25D9B1F9F76BC00BEA0C1 /* chat_group_informations.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = chat_group_informations.png; sourceTree = ""; }; 8CF25D9C1F9F76BD00BEA0C1 /* chat_group_informations@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "chat_group_informations@2x.png"; sourceTree = ""; }; - 8FD0D10102F0A8922703B8A4 /* Pods-msgNotificationService.distributionadhoc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-msgNotificationService.distributionadhoc.xcconfig"; path = "Pods/Target Support Files/Pods-msgNotificationService/Pods-msgNotificationService.distributionadhoc.xcconfig"; sourceTree = ""; }; - 904C1EC75CB9E03374AAA802 /* Pods-msgNotificationService.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-msgNotificationService.release.xcconfig"; path = "Pods/Target Support Files/Pods-msgNotificationService/Pods-msgNotificationService.release.xcconfig"; sourceTree = ""; }; - 9CBD6E980619195CB7EE32AC /* Pods_liblinphoneTester.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_liblinphoneTester.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - ABB887316C42EE876A3051A9 /* Pods-linphoneTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-linphoneTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-linphoneTests/Pods-linphoneTests.debug.xcconfig"; sourceTree = ""; }; - ADCA571A7CF61077747BFE53 /* Pods-msgNotificationService.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-msgNotificationService.debug.xcconfig"; path = "Pods/Target Support Files/Pods-msgNotificationService/Pods-msgNotificationService.debug.xcconfig"; sourceTree = ""; }; - BAD0A9494E833034EB559687 /* Pods-msgNotificationContent.distributionadhoc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-msgNotificationContent.distributionadhoc.xcconfig"; path = "Pods/Target Support Files/Pods-msgNotificationContent/Pods-msgNotificationContent.distributionadhoc.xcconfig"; sourceTree = ""; }; - BE06BDE664323B2A53469696 /* Pods-liblinphoneTesterTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-liblinphoneTesterTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-liblinphoneTesterTests/Pods-liblinphoneTesterTests.release.xcconfig"; sourceTree = ""; }; + A164BAF39B3A5B9F905917A7 /* Pods_msgNotificationContent.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_msgNotificationContent.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B464E44A606CB50A65A96FE2 /* Pods_linphone.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_linphone.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B9F41097CE0124A05554DB9C /* Pods-msgNotificationService.distribution.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-msgNotificationService.distribution.xcconfig"; path = "Target Support Files/Pods-msgNotificationService/Pods-msgNotificationService.distribution.xcconfig"; sourceTree = ""; }; + C589627B9D9D2A4F9C816051 /* Pods-msgNotificationService.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-msgNotificationService.debug.xcconfig"; path = "Target Support Files/Pods-msgNotificationService/Pods-msgNotificationService.debug.xcconfig"; sourceTree = ""; }; + C60B66672721AFFA0026AC7D /* CallStatisticsData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallStatisticsData.swift; sourceTree = ""; }; + C60D265527299C94006238BB /* ControlsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlsViewModel.swift; sourceTree = ""; }; + C60D265727299F6F006238BB /* CoreExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreExtensions.swift; sourceTree = ""; }; + C60D265B272AA0BD006238BB /* UIImageExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImageExtensions.swift; sourceTree = ""; }; C61B1BF12667D075001A4E4A /* menu_security_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = menu_security_default.png; sourceTree = ""; }; C61B1BF32667D202001A4E4A /* more_menu_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = more_menu_default.png; sourceTree = ""; }; C61B1BF62667EC6B001A4E4A /* ephemeral_messages_color_A.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = ephemeral_messages_color_A.png; sourceTree = ""; }; @@ -1744,15 +1806,106 @@ C622E3EC26A8128F004F5434 /* vr_off.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = vr_off.png; sourceTree = ""; }; C622E3ED26A8128F004F5434 /* vr_pause.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = vr_pause.png; sourceTree = ""; }; C622E3EE26A81290004F5434 /* vr_play.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = vr_play.png; sourceTree = ""; }; + C6277DA6274BF1CD00406FB9 /* voip_radio_on.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_radio_on.png; sourceTree = ""; }; + C6277DA7274BF1CD00406FB9 /* voip_radio_off.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_radio_off.png; sourceTree = ""; }; + C6278496273C21E1002FAA29 /* LocalVideoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalVideoView.swift; sourceTree = ""; }; C64A854C2667B66900252AD2 /* EphemeralSettingsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EphemeralSettingsView.h; sourceTree = ""; }; C64A854D2667B67200252AD2 /* EphemeralSettingsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EphemeralSettingsView.m; sourceTree = ""; }; C64A854F2667B67A00252AD2 /* EphemeralSettingsView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = EphemeralSettingsView.xib; sourceTree = ""; }; C64A85512667B74100252AD2 /* ephemeral_messages_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = ephemeral_messages_default.png; sourceTree = ""; }; + C6586148273E595700A0DBFC /* VoipExtraButtonsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VoipExtraButtonsView.swift; sourceTree = ""; }; + C658614B273E5B5E00A0DBFC /* VoipExtraButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VoipExtraButton.swift; sourceTree = ""; }; + C65A5D2F27216B86005BA038 /* ActiveCallOrConferenceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveCallOrConferenceView.swift; sourceTree = ""; }; + C65A5D3727216CC0005BA038 /* MutableLiveData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MutableLiveData.swift; sourceTree = ""; }; + C65A5D3E27216E3A005BA038 /* CallData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallData.swift; sourceTree = ""; }; + C65A5D44272196AE005BA038 /* OptionalExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionalExtensions.swift; sourceTree = ""; }; C66B03BC26E8EB1A009B5EDC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/UIChatReplyBubbleView.xib; sourceTree = ""; }; C66B03C126E8EB82009B5EDC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/UIChatReplyBubbleView.strings; sourceTree = ""; }; C66B03C326E8EB87009B5EDC /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/UIChatReplyBubbleView.strings; sourceTree = ""; }; C66B040926EFDA54009B5EDC /* reply_cancel.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = reply_cancel.png; sourceTree = ""; }; C66B040D26F095CE009B5EDC /* cancel_forward.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = cancel_forward.png; sourceTree = ""; }; + C6710F4E2722903200ED888F /* RotatingSpinner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RotatingSpinner.swift; sourceTree = ""; }; + C6710F502722932600ED888F /* UIImageViewExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImageViewExtensions.swift; sourceTree = ""; }; + C6710F52272297C400ED888F /* VoipTexts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VoipTexts.swift; sourceTree = ""; }; + C6710F5427229D5900ED888F /* LightDarkColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightDarkColor.swift; sourceTree = ""; }; + C6710F5627229DEE00ED888F /* TextStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextStyle.swift; sourceTree = ""; }; + C6710F582722A9B800ED888F /* StyledLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StyledLabel.swift; sourceTree = ""; }; + C6710F5B2722AAED00ED888F /* UIDeviceExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIDeviceExtensions.swift; sourceTree = ""; }; + C6710F5E2722AECB00ED888F /* Roboto-Italic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Italic.ttf"; sourceTree = ""; }; + C6710F5F2722AECB00ED888F /* Roboto-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Bold.ttf"; sourceTree = ""; }; + C6710F602722AECB00ED888F /* Roboto-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Regular.ttf"; sourceTree = ""; }; + C6710F642722B13000ED888F /* voip_spinner.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_spinner.png; sourceTree = ""; }; + C6710F662722B1EF00ED888F /* voip_numpad_1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_numpad_1.png; sourceTree = ""; }; + C6710F672722B1EF00ED888F /* voip_numpad_0.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_numpad_0.png; sourceTree = ""; }; + C6710F682722B1EF00ED888F /* voip_copy.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_copy.png; sourceTree = ""; }; + C6710F692722B1EF00ED888F /* voip_merge_calls.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_merge_calls.png; sourceTree = ""; }; + C6710F6A2722B1EF00ED888F /* voip_call_numpad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_call_numpad.png; sourceTree = ""; }; + C6710F6B2722B1F000ED888F /* voip_single_contact_avatar.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_single_contact_avatar.png; sourceTree = ""; }; + C6710F6C2722B1F000ED888F /* voip_mandatory.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_mandatory.png; sourceTree = ""; }; + C6710F6D2722B1F000ED888F /* voip_bluetooth.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_bluetooth.png; sourceTree = ""; }; + C6710F6E2722B1F000ED888F /* voip_call_more.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_call_more.png; sourceTree = ""; }; + C6710F6F2722B1F100ED888F /* voip_multiple_contacts_avatar.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_multiple_contacts_avatar.png; sourceTree = ""; }; + C6710F702722B1F100ED888F /* voip_call_stats.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_call_stats.png; sourceTree = ""; }; + C6710F712722B1F100ED888F /* voip_conference_active_speaker.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_conference_active_speaker.png; sourceTree = ""; }; + C6710F722722B1F200ED888F /* voip_edit.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_edit.png; sourceTree = ""; }; + C6710F732722B1F200ED888F /* voip_call_chat.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_call_chat.png; sourceTree = ""; }; + C6710F742722B1F200ED888F /* voip_numpad_5.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_numpad_5.png; sourceTree = ""; }; + C6710F752722B1F300ED888F /* voip_hangup.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_hangup.png; sourceTree = ""; }; + C6710F762722B1F300ED888F /* voip_conference_new.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_conference_new.png; sourceTree = ""; }; + C6710F772722B1F300ED888F /* voip_chat_rooms_list.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_chat_rooms_list.png; sourceTree = ""; }; + C6710F782722B1F300ED888F /* voip_micro_on.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_micro_on.png; sourceTree = ""; }; + C6710F792722B1F400ED888F /* voip_calls_list.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_calls_list.png; sourceTree = ""; }; + C6710F7A2722B1F400ED888F /* voip_pause.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_pause.png; sourceTree = ""; }; + C6710F7B2722B1F500ED888F /* voip_conference_mosaic.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_conference_mosaic.png; sourceTree = ""; }; + C6710F7C2722B1F500ED888F /* voip_delete.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_delete.png; sourceTree = ""; }; + C6710F7D2722B1F500ED888F /* voip_audio_routes.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_audio_routes.png; sourceTree = ""; }; + C6710F7E2722B1F600ED888F /* voip_numpad_6.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_numpad_6.png; sourceTree = ""; }; + C6710F7F2722B1F600ED888F /* voip_numpad_3.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_numpad_3.png; sourceTree = ""; }; + C6710F802722B1F600ED888F /* voip_call_add.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_call_add.png; sourceTree = ""; }; + C6710F812722B1F700ED888F /* voip_numpad_9.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_numpad_9.png; sourceTree = ""; }; + C6710F822722B1F700ED888F /* voip_call.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_call.png; sourceTree = ""; }; + C6710F832722B1F700ED888F /* voip_change_camera.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_change_camera.png; sourceTree = ""; }; + C6710F842722B1F800ED888F /* voip_call_header_paused.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_call_header_paused.png; sourceTree = ""; }; + C6710F852722B1F800ED888F /* voip_export.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_export.png; sourceTree = ""; }; + C6710F862722B1F800ED888F /* voip_earpiece.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_earpiece.png; sourceTree = ""; }; + C6710F872722B1F800ED888F /* voip_numpad_hash.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_numpad_hash.png; sourceTree = ""; }; + C6710F882722B1F900ED888F /* voip_micro_off.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_micro_off.png; sourceTree = ""; }; + C6710F892722B1F900ED888F /* voip_numpad_2.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_numpad_2.png; sourceTree = ""; }; + C6710F8A2722B1F900ED888F /* voip_cancel.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_cancel.png; sourceTree = ""; }; + C6710F8B2722B1FA00ED888F /* voip_numpad_star.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_numpad_star.png; sourceTree = ""; }; + C6710F8C2722B1FA00ED888F /* voip_camera_off.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_camera_off.png; sourceTree = ""; }; + C6710F8D2722B1FA00ED888F /* voip_menu_more.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_menu_more.png; sourceTree = ""; }; + C6710F8E2722B1FB00ED888F /* voip_numpad_4.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_numpad_4.png; sourceTree = ""; }; + C6710F8F2722B1FB00ED888F /* voip_numpad_7.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_numpad_7.png; sourceTree = ""; }; + C6710F902722B1FB00ED888F /* voip_speaker_on.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_speaker_on.png; sourceTree = ""; }; + C6710F912722B1FC00ED888F /* voip_call_header_incoming.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_call_header_incoming.png; sourceTree = ""; }; + C6710F922722B1FC00ED888F /* voip_remote_recording.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_remote_recording.png; sourceTree = ""; }; + C6710F932722B1FC00ED888F /* voip_call_list_menu.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_call_list_menu.png; sourceTree = ""; }; + C6710F942722B1FD00ED888F /* voip_call_participants.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_call_participants.png; sourceTree = ""; }; + C6710F952722B1FD00ED888F /* voip_speaker_off.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_speaker_off.png; sourceTree = ""; }; + C6710F962722B1FD00ED888F /* voip_numpad_8.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_numpad_8.png; sourceTree = ""; }; + C6710F972722B1FE00ED888F /* voip_call_header_active.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_call_header_active.png; sourceTree = ""; }; + C6710F982722B1FE00ED888F /* voip_dropdown.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_dropdown.png; sourceTree = ""; }; + C6710F992722B1FE00ED888F /* voip_call_record.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_call_record.png; sourceTree = ""; }; + C6710F9A2722B1FE00ED888F /* voip_conference_paused_big.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_conference_paused_big.png; sourceTree = ""; }; + C6710F9B2722B1FF00ED888F /* voip_camera_on.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_camera_on.png; sourceTree = ""; }; + C6710F9C2722B1FF00ED888F /* voip_call_header_outgoing.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_call_header_outgoing.png; sourceTree = ""; }; + C6710F9D2722B20000ED888F /* voip_conference_play_big.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_conference_play_big.png; sourceTree = ""; }; + C6710F9E2722B20000ED888F /* voip_info.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_info.png; sourceTree = ""; }; + C6710FD82722BD0100ED888F /* UICallTimer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICallTimer.swift; sourceTree = ""; }; + C6710FDB2722C3BB00ED888F /* UIVIewExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIVIewExtensions.swift; sourceTree = ""; }; + C6710FDD2722D44A00ED888F /* IncomingOuntgoingCommonView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IncomingOuntgoingCommonView.swift; sourceTree = ""; }; + C6710FE02722F0E400ED888F /* Avatar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Avatar.swift; sourceTree = ""; }; + C6710FE427230B5800ED888F /* AddressExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddressExtensions.swift; sourceTree = ""; }; + C6710FE62723234400ED888F /* OutgoingCallView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutgoingCallView.swift; sourceTree = ""; }; + C6710FE82723DD7D00ED888F /* CallControlButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallControlButton.swift; sourceTree = ""; }; + C6710FEA2726874D00ED888F /* ButtonTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonTheme.swift; sourceTree = ""; }; + C67C97B0274FB4C10074A0D8 /* VoipConferenceDisplayModeSelectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VoipConferenceDisplayModeSelectionView.swift; sourceTree = ""; }; + C67C97B3274FC5EE0074A0D8 /* AudioRoutesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioRoutesView.swift; sourceTree = ""; }; + C67C97B7274FD76B0074A0D8 /* AudioRouteUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioRouteUtils.swift; sourceTree = ""; }; + C6824FB927219D890043D4FC /* IncomingCallView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncomingCallView.swift; sourceTree = ""; }; + C683B20D2722702300D4E15C /* VoipTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoipTheme.swift; sourceTree = ""; }; + C683B212272276CF00D4E15C /* UIColorExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColorExtensions.swift; sourceTree = ""; }; C6A1BB3126E8815300540D50 /* menu_info.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = menu_info.png; sourceTree = ""; }; C6A1BB3226E8815400540D50 /* menu_forward_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = menu_forward_default.png; sourceTree = ""; }; C6A1BB3326E8815400540D50 /* menu_copy_text_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = menu_copy_text_default.png; sourceTree = ""; }; @@ -1763,18 +1916,50 @@ C6A1BB4026E889AD00540D50 /* forward_message_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = forward_message_default.png; sourceTree = ""; }; C6A1BB4226E88F7C00540D50 /* menu_resend_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = menu_resend_default.png; sourceTree = ""; }; C6A1BB4426E890BD00540D50 /* file_voice_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = file_voice_default.png; sourceTree = ""; }; + C6B04D60274B954500F70559 /* ParticipantsListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParticipantsListView.swift; sourceTree = ""; }; + C6B04D62274B95D400F70559 /* VoipParticipantCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VoipParticipantCell.swift; sourceTree = ""; }; + C6B04D66274BD61200F70559 /* VoipConferenceActiveSpeakerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VoipConferenceActiveSpeakerView.swift; sourceTree = ""; }; + C6B04D68274BD6A100F70559 /* VoipActiveSpeakerParticipantCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VoipActiveSpeakerParticipantCell.swift; sourceTree = ""; }; C6B4443D26AAD0970076C517 /* file_video_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = file_video_default.png; sourceTree = ""; }; C6B4443E26AAD0970076C517 /* file_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = file_default.png; sourceTree = ""; }; C6B4443F26AAD0970076C517 /* file_picture_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = file_picture_default.png; sourceTree = ""; }; C6B4444026AAD0970076C517 /* file_audio_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = file_audio_default.png; sourceTree = ""; }; C6B4444126AAD0970076C517 /* file_pdf_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = file_pdf_default.png; sourceTree = ""; }; C6B4444726AADA530076C517 /* SwiftUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUtil.swift; sourceTree = ""; }; + C6C65E88272723DC00E48FC6 /* UIVIewControllerExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIVIewControllerExtensions.swift; sourceTree = ""; }; + C6C65E8A2727274A00E48FC6 /* NumpadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumpadView.swift; sourceTree = ""; }; + C6C98CCD27438A3F00059B55 /* DismissableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DismissableView.swift; sourceTree = ""; }; + C6C98CCF27439A7F00059B55 /* VoipCallCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VoipCallCell.swift; sourceTree = ""; }; + C6C98CD12743FD0B00059B55 /* VoipCallContextMenu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VoipCallContextMenu.swift; sourceTree = ""; }; + C6C98CD427453ED700059B55 /* ConferenceViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConferenceViewModel.swift; sourceTree = ""; }; + C6C98CD627453F9600059B55 /* ConferenceParticipantData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConferenceParticipantData.swift; sourceTree = ""; }; + C6C98CDA274541E400059B55 /* ConferenceParticipantDeviceData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConferenceParticipantDeviceData.swift; sourceTree = ""; }; + C6C98CDC274547C500059B55 /* ParticipantExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParticipantExtensions.swift; sourceTree = ""; }; + C6C98CDE2745590400059B55 /* ConferenceExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConferenceExtensions.swift; sourceTree = ""; }; + C6C98CE0274568F700059B55 /* VoipConferenceGridView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VoipConferenceGridView.swift; sourceTree = ""; }; + C6D09F3C273EE467003C2173 /* BouncingCounter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BouncingCounter.swift; sourceTree = ""; }; + C6D09F3E274273FB003C2173 /* CallStatsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallStatsView.swift; sourceTree = ""; }; + C6D09F4027428626003C2173 /* IceState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IceState.swift; sourceTree = ""; }; + C6D09F42274288D4003C2173 /* PayloadType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PayloadType.swift; sourceTree = ""; }; + C6D09F4A27438706003C2173 /* CallsListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallsListView.swift; sourceTree = ""; }; + C6D1EC48274D212A0091881C /* UICamSwitch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UICamSwitch.h; sourceTree = ""; }; + C6D1EC49274D212B0091881C /* UICamSwitch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UICamSwitch.m; sourceTree = ""; }; + C6D52B44274648E500904660 /* VoipGridParticipantCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VoipGridParticipantCell.swift; sourceTree = ""; }; C6DA657B261C950C0020CB43 /* VFSUtil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VFSUtil.swift; sourceTree = ""; }; + C6EA2F4727514D08008E60F8 /* voip_call_forward.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_call_forward.png; sourceTree = ""; }; + C6F2D4EE27392D960071BA52 /* CallsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallsViewModel.swift; sourceTree = ""; }; + C6F2D4F0273935860071BA52 /* CallExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallExtensions.swift; sourceTree = ""; }; + C6F2D4F22739475C0071BA52 /* ActiveCallView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActiveCallView.swift; sourceTree = ""; }; + C6F2D4F62739861F0071BA52 /* RemotelyRecording.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemotelyRecording.swift; sourceTree = ""; }; + C6F2D4F8273A4CD60071BA52 /* SharedLayoutConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedLayoutConstants.swift; sourceTree = ""; }; + C6F2D4FC273A85A90071BA52 /* PausedCallOrConferenceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PausedCallOrConferenceView.swift; sourceTree = ""; }; + C6F2D4FE273AF01A0071BA52 /* ControlsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlsView.swift; sourceTree = ""; }; + C6F2D500273B0EFC0071BA52 /* VoipDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoipDialog.swift; sourceTree = ""; }; + C6F2D502273BAC030071BA52 /* ButtonWithStateBackgrounds.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ButtonWithStateBackgrounds.swift; sourceTree = ""; }; + C6F2D504273BB3BB0071BA52 /* UIApplication+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIApplication+Extension.swift"; sourceTree = ""; }; C90FAA7615AF54E6002091CB /* HistoryDetailsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HistoryDetailsView.h; sourceTree = ""; }; C90FAA7715AF54E6002091CB /* HistoryDetailsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HistoryDetailsView.m; sourceTree = ""; }; C9B3A6FD15B485DB006F52EE /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Utils.h; path = Utils/Utils.h; sourceTree = ""; }; - CC6F924A8B1B1698914A3BB2 /* Pods-messagesNotification.distributionadhoc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-messagesNotification.distributionadhoc.xcconfig"; path = "Pods/Target Support Files/Pods-messagesNotification/Pods-messagesNotification.distributionadhoc.xcconfig"; sourceTree = ""; }; - CE119C214B6B8B2740622BBD /* Pods-linphoneTests.distributionadhoc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-linphoneTests.distributionadhoc.xcconfig"; path = "Pods/Target Support Files/Pods-linphoneTests/Pods-linphoneTests.distributionadhoc.xcconfig"; sourceTree = ""; }; CF15F21B20E4F9A3008B1DE6 /* UIImageViewDeletable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UIImageViewDeletable.h; sourceTree = ""; }; CF15F21C20E4F9A3008B1DE6 /* UIImageViewDeletable.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UIImageViewDeletable.m; sourceTree = ""; }; CF15F21D20E4F9A3008B1DE6 /* UIImageViewDeletable.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = UIImageViewDeletable.xib; sourceTree = ""; }; @@ -1793,13 +1978,10 @@ CF7602F3210898C600749F76 /* rec_off_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = rec_off_default.png; sourceTree = ""; }; CF7602F4210898C800749F76 /* rec_on_default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = rec_on_default.png; sourceTree = ""; }; CFBD7A2320E504AD007C5286 /* delete_img.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = delete_img.png; sourceTree = ""; }; - D252FEC7DD06DD5695A320A1 /* Pods_liblinphoneTesterTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_liblinphoneTesterTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D306459C1611EC2900BB571E /* UILoadingImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UILoadingImageView.h; sourceTree = ""; }; D306459D1611EC2900BB571E /* UILoadingImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UILoadingImageView.m; sourceTree = ""; }; D3128FDE15AABC7E00A2147A /* ContactDetailsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContactDetailsView.h; sourceTree = ""; }; D3128FDF15AABC7E00A2147A /* ContactDetailsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ContactDetailsView.m; sourceTree = ""; }; - D31AAF5C159B3919002C6B02 /* CallPausedTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CallPausedTableView.h; sourceTree = ""; }; - D31AAF5D159B3919002C6B02 /* CallPausedTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CallPausedTableView.m; sourceTree = ""; }; D31B4B1E159876C0002E6C72 /* UICompositeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UICompositeView.h; sourceTree = ""; }; D31B4B1F159876C0002E6C72 /* UICompositeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UICompositeView.m; sourceTree = ""; }; D31C9C96158A1CDE00756B45 /* UIHistoryCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIHistoryCell.h; sourceTree = ""; }; @@ -1827,8 +2009,6 @@ D35E7595159460560066B1C1 /* ChatsListView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ChatsListView.m; sourceTree = ""; }; D35E759C159460B50066B1C1 /* SettingsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsView.h; sourceTree = ""; }; D35E759D159460B50066B1C1 /* SettingsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SettingsView.m; sourceTree = ""; }; - D36FB2D31589EF7C0036F6F2 /* UIPauseButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIPauseButton.h; sourceTree = ""; }; - D36FB2D41589EF7C0036F6F2 /* UIPauseButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIPauseButton.m; sourceTree = ""; }; D378AB2815DCDB480098505D /* ImagePickerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImagePickerView.h; sourceTree = ""; }; D378AB2915DCDB490098505D /* ImagePickerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ImagePickerView.m; sourceTree = ""; }; D37C639915AADEF4009D0BAC /* ContactDetailsTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContactDetailsTableView.h; sourceTree = ""; }; @@ -1895,17 +2075,12 @@ D3ED3EA51587334C006C0DE4 /* HistoryListTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = HistoryListTableView.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; D3ED3EB515873928006C0DE4 /* HistoryListView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HistoryListView.h; sourceTree = ""; }; D3ED3EB615873929006C0DE4 /* HistoryListView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = HistoryListView.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; - D3F26BEE15986B71005F9CAB /* CallIncomingView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CallIncomingView.h; sourceTree = ""; }; - D3F26BEF15986B71005F9CAB /* CallIncomingView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CallIncomingView.m; sourceTree = ""; }; D3F795D315A582800077328B /* ChatConversationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ChatConversationView.h; sourceTree = ""; }; D3F795D415A582800077328B /* ChatConversationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ChatConversationView.m; sourceTree = ""; }; D3F7997F15BD32370018C273 /* TPMultiLayoutViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TPMultiLayoutViewController.h; path = Utils/TPMultiLayoutViewController/TPMultiLayoutViewController.h; sourceTree = ""; }; D3F7998015BD32370018C273 /* TPMultiLayoutViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TPMultiLayoutViewController.m; path = Utils/TPMultiLayoutViewController/TPMultiLayoutViewController.m; sourceTree = ""; }; - D3F83EE91582021700336684 /* CallView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CallView.h; sourceTree = ""; }; - D3F83EEA1582021700336684 /* CallView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CallView.m; sourceTree = ""; }; D3F83F8C158229C500336684 /* PhoneMainView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PhoneMainView.h; sourceTree = ""; }; D3F83F8D15822ABD00336684 /* PhoneMainView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PhoneMainView.m; sourceTree = ""; }; - E40C9A7D22675584396C0A3D /* Pods-msgNotificationContent.distribution.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-msgNotificationContent.distribution.xcconfig"; path = "Pods/Target Support Files/Pods-msgNotificationContent/Pods-msgNotificationContent.distribution.xcconfig"; sourceTree = ""; }; EA5F25D9232BD3E200475F2E /* msgNotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = msgNotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; }; EA5F25DB232BD3E200475F2E /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; EA5F25DD232BD3E200475F2E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -1916,7 +2091,6 @@ EA8CB83F239FD41400C330CC /* msgNotificationContent.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = msgNotificationContent.entitlements; sourceTree = ""; }; EAB783CF232F8E4D0076B1A0 /* msgNotificationService.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = msgNotificationService.entitlements; sourceTree = ""; }; EAE6C88323FABF690076A018 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Utils.swift; path = msgNotification/Utils.swift; sourceTree = SOURCE_ROOT; }; - EE807919DDB0B4B46AD1D439 /* Pods-messagesNotification.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-messagesNotification.release.xcconfig"; path = "Pods/Target Support Files/Pods-messagesNotification/Pods-messagesNotification.release.xcconfig"; sourceTree = ""; }; F0181B6B18BF7B1200A9A357 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; F03A9B9418C0DAE100C4D7FE /* libstdc++.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libstdc++.dylib"; path = "usr/lib/libstdc++.dylib"; sourceTree = SDKROOT; }; F03A9B9718C0DB6F00C4D7FE /* libc++.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libc++.dylib"; path = "usr/lib/libc++.dylib"; sourceTree = SDKROOT; }; @@ -1935,8 +2109,6 @@ F09548201883F15400E8A69B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/HistoryDetailsView.xib; sourceTree = ""; }; F09548211883F15400E8A69B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/HistoryListView.xib; sourceTree = ""; }; F09548221883F15400E8A69B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/ImageView.xib; sourceTree = ""; }; - F09548231883F15400E8A69B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/CallView.xib; sourceTree = ""; }; - F09548241883F15400E8A69B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/CallIncomingView.xib; sourceTree = ""; }; F095482C1883F15400E8A69B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/TabBarView.xib; sourceTree = ""; }; F095482E1883F15500E8A69B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/AssistantView.xib; sourceTree = ""; }; F09548301883F15500E8A69B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/AssistantViewScreens.xib; sourceTree = ""; }; @@ -1947,8 +2119,6 @@ F095483C1883F2E300E8A69B /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/DialerView.strings; sourceTree = ""; }; F09548421883F51B00E8A69B /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/HistoryListView.strings; sourceTree = ""; }; F09548441883F52900E8A69B /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/ImageView.strings; sourceTree = ""; }; - F09548461883F54200E8A69B /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/CallView.strings; sourceTree = ""; }; - F09548481883F55800E8A69B /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/CallIncomingView.strings; sourceTree = ""; }; F09548561883F61600E8A69B /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/TabBarView.strings; sourceTree = ""; }; F095485A1883F67B00E8A69B /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/AssistantView.strings; sourceTree = ""; }; F095485E1883F6EA00E8A69B /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/AssistantViewScreens.strings; sourceTree = ""; }; @@ -1960,8 +2130,6 @@ F0AF06F91A24BA760086C9C1 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/HistoryDetailsView.strings; sourceTree = ""; }; F0AF06FA1A24BA770086C9C1 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/HistoryListView.strings; sourceTree = ""; }; F0AF06FB1A24BA770086C9C1 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/ImageView.strings; sourceTree = ""; }; - F0AF06FC1A24BA770086C9C1 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/CallView.strings; sourceTree = ""; }; - F0AF06FD1A24BA770086C9C1 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/CallIncomingView.strings; sourceTree = ""; }; F0AF07021A24BA770086C9C1 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/UIChatCell.strings; sourceTree = ""; }; F0AF07041A24BA770086C9C1 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/UICompositeView.strings; sourceTree = ""; }; F0AF07061A24BA770086C9C1 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/UIContactCell.strings; sourceTree = ""; }; @@ -1977,12 +2145,6 @@ F0BB8C34193624C800974404 /* libresolv.9.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libresolv.9.dylib; path = usr/lib/libresolv.9.dylib; sourceTree = SDKROOT; }; F0BB8C4A193631B300974404 /* ImageIO.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ImageIO.framework; path = System/Library/Frameworks/ImageIO.framework; sourceTree = SDKROOT; }; F0FF66AA1ACAEEB0008A4486 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = ../../../../Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/IOKit.framework; sourceTree = ""; }; - F325590DD6CD7F6CC8F60C03 /* Pods_linphoneTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_linphoneTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - F4BE1A2318FC69D799C34F0A /* Pods-msgNotificationService.distribution.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-msgNotificationService.distribution.xcconfig"; path = "Pods/Target Support Files/Pods-msgNotificationService/Pods-msgNotificationService.distribution.xcconfig"; sourceTree = ""; }; - F6373F9231918DECD2B3004D /* Pods-linphoneTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-linphoneTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-linphoneTests/Pods-linphoneTests.release.xcconfig"; sourceTree = ""; }; - FD22CA9E3EFBFEFFF1B80BA2 /* Pods-liblinphoneTester.distributionadhoc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-liblinphoneTester.distributionadhoc.xcconfig"; path = "Pods/Target Support Files/Pods-liblinphoneTester/Pods-liblinphoneTester.distributionadhoc.xcconfig"; sourceTree = ""; }; - FE7D89A821FDC1BCA9BB9F8F /* Pods-linphone.distributionadhoc.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-linphone.distributionadhoc.xcconfig"; path = "Pods/Target Support Files/Pods-linphone/Pods-linphone.distributionadhoc.xcconfig"; sourceTree = ""; }; - FEAFB5AD0E3AA409BBD1136E /* Pods-linphone.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-linphone.release.xcconfig"; path = "Pods/Target Support Files/Pods-linphone/Pods-linphone.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1992,7 +2154,7 @@ files = ( EA88F3AC241BD05200E66528 /* UserNotificationsUI.framework in Frameworks */, EA88F3AB241BD05200E66528 /* UserNotifications.framework in Frameworks */, - 369CCF81C921CD7C4E49A637 /* Pods_msgNotificationContent.framework in Frameworks */, + 017AC1D70F142AE8EAC13BDB /* Pods_msgNotificationContent.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2002,7 +2164,6 @@ files = ( 61DD7E1F2372E88F001BDD01 /* CoreLocation.framework in Frameworks */, 6180D6FE21EE41A800AD9CB6 /* QuickLook.framework in Frameworks */, - 61AEBEA321906AFC00F35E7F /* BuildFile in Frameworks */, D37DC7181594AF3400B2A5EB /* MessageUI.framework in Frameworks */, 61F1997520C6B1D5006B069A /* AVKit.framework in Frameworks */, 249660951FD6A35F001D55AA /* Photos.framework in Frameworks */, @@ -2034,7 +2195,7 @@ F05BAA621A5D594E00411815 /* libz.dylib in Frameworks */, 344ABDF114850AE9007420B6 /* libc++.1.dylib in Frameworks */, 22D1B68112A3E0BE001AE361 /* libresolv.dylib in Frameworks */, - A634ABAFCB39B6AAE4CA991D /* Pods_linphone.framework in Frameworks */, + 9C0B30F54D61774AFD1473CE /* Pods_linphone.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2042,7 +2203,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 93566413F75DA69D2811A716 /* Pods_msgNotificationService.framework in Frameworks */, + E42B8736B8196388DDF2771A /* Pods_msgNotificationService.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2059,6 +2220,8 @@ 080E96DDFE201D6D7F000001 /* Classes */ = { isa = PBXGroup; children = ( + C65A5D41272184CE005BA038 /* SwiftUtil */, + C65A5D2D2721683B005BA038 /* Voip */, 22E0A81D111C44E100B04932 /* AboutView.h */, 22E0A81C111C44E100B04932 /* AboutView.m */, 636316D31A1DEBCB0009B839 /* AboutView.xib */, @@ -2069,23 +2232,6 @@ D350F20C15A43BB100149E54 /* AssistantView.m */, D38187E015FE348A00C3EDCA /* AssistantView.xib */, D3D5126A160B3A8E00946DF8 /* AssistantViewScreens.xib */, - 63F1DF491BCE983100EDED90 /* CallConferenceTableView.h */, - 63F1DF4A1BCE983200EDED90 /* CallConferenceTableView.m */, - D3F26BEE15986B71005F9CAB /* CallIncomingView.h */, - D3F26BEF15986B71005F9CAB /* CallIncomingView.m */, - D38187DC15FE347700C3EDCA /* CallIncomingView.xib */, - 6346100D1B61409800548952 /* CallOutgoingView.h */, - 6346100E1B61409800548952 /* CallOutgoingView.m */, - 634610101B6140A500548952 /* CallOutgoingView.xib */, - D31AAF5C159B3919002C6B02 /* CallPausedTableView.h */, - D31AAF5D159B3919002C6B02 /* CallPausedTableView.m */, - 6352A5721BE0D4B800594C1C /* CallSideMenuView.h */, - 6352A5731BE0D4B800594C1C /* CallSideMenuView.m */, - 6352A5741BE0D4B800594C1C /* CallSideMenuView.xib */, - D3F83EE91582021700336684 /* CallView.h */, - D3F83EEA1582021700336684 /* CallView.m */, - D381881C15FE3FCA00C3EDCA /* CallView.xib */, - 638F1A861C2167C2004B8E02 /* CallView~ipad.xib */, 8C9C5E0B1F83B2EF006987FA /* ChatConversationCreateCollectionViewController.h */, 8C9C5E0C1F83B2EF006987FA /* ChatConversationCreateCollectionViewController.m */, 6341807A1BBC103100F71761 /* ChatConversationCreateTableView.h */, @@ -2217,6 +2363,8 @@ 2214EB7012F84668002A5394 /* LinphoneUI */ = { isa = PBXGroup; children = ( + C6D1EC48274D212A0091881C /* UICamSwitch.h */, + C6D1EC49274D212B0091881C /* UICamSwitch.m */, C6A1BB3B26E882D000540D50 /* UIChatReplyBubbleView.h */, C6A1BB3C26E882D000540D50 /* UIChatReplyBubbleView.m */, C66B03BD26E8EB1A009B5EDC /* UIChatReplyBubbleView.xib */, @@ -2234,14 +2382,6 @@ 6381DA7C1C1AD5EA00DF3BBD /* UIBouncingView.m */, 2214EB7812F846B1002A5394 /* UICallButton.h */, 2214EB7912F846B1002A5394 /* UICallButton.m */, - 63F1DF4C1BCE985F00EDED90 /* UICallConferenceCell.h */, - 63F1DF4D1BCE985F00EDED90 /* UICallConferenceCell.m */, - 63F1DF531BCE986A00EDED90 /* UICallConferenceCell.xib */, - 63BC49E01BA2CDFC004EC273 /* UICallPausedCell.h */, - 63BC49E11BA2CDFC004EC273 /* UICallPausedCell.m */, - 639E9C9F1C0DB7DF00019A75 /* UICallPausedCell.xib */, - 22AA8AFF13D83F6300B30535 /* UICamSwitch.h */, - 22AA8B0013D83F6300B30535 /* UICamSwitch.m */, 635173F71BA082A40095EB0A /* UIChatBubblePhotoCell.h */, 635173F81BA082A40095EB0A /* UIChatBubblePhotoCell.m */, 639E9CA21C0DB7E500019A75 /* UIChatBubblePhotoCell.xib */, @@ -2288,8 +2428,6 @@ 61CCC3D721933B380060EDEA /* UIDeviceCell.h */, 61CCC3DE21933B580060EDEA /* UIDeviceCell.m */, 61CCC3E021933B660060EDEA /* UIDeviceCell.xib */, - 2214EB8712F84EBB002A5394 /* UIHangUpButton.h */, - 2214EB8812F84EBB002A5394 /* UIHangUpButton.m */, D31C9C96158A1CDE00756B45 /* UIHistoryCell.h */, D31C9C97158A1CDE00756B45 /* UIHistoryCell.m */, 639CEB021A1DF4E4004DE38F /* UIHistoryCell.xib */, @@ -2311,8 +2449,6 @@ D306459D1611EC2900BB571E /* UILoadingImageView.m */, 2214EBF112F86360002A5394 /* UIMutedMicroButton.h */, 2214EBF212F86360002A5394 /* UIMutedMicroButton.m */, - D36FB2D31589EF7C0036F6F2 /* UIPauseButton.h */, - D36FB2D41589EF7C0036F6F2 /* UIPauseButton.m */, CF7602E42108759A00749F76 /* UIRecordingCell.h */, CF7602E52108759A00749F76 /* UIRecordingCell.m */, CF7602E62108759A00749F76 /* UIRecordingCell.xib */, @@ -2323,23 +2459,19 @@ 8C5D1B991D9BC48100DC6539 /* UIShopTableCell.h */, 8C5D1B9B1D9BC48100DC6539 /* UIShopTableCell.xib */, 24A345A71D95799900881A5C /* UIShopTableCell.h */, - 22968A5D12F875C600588287 /* UISpeakerButton.h */, - 22968A5E12F875C600588287 /* UISpeakerButton.m */, 630CF5551AF7CE1500539F7A /* UITextField+DoneButton.h */, 630CF5561AF7CE1500539F7A /* UITextField+DoneButton.m */, F03CA84118C72F1A0008889D /* UITextViewNoDefine.h */, F03CA84218C72F1A0008889D /* UITextViewNoDefine.m */, D32648421588F6FA00930C67 /* UIToggleButton.h */, D32648431588F6FB00930C67 /* UIToggleButton.m */, - 340751E5150F38FC00B89C47 /* UIVideoButton.h */, - 340751E6150F38FD00B89C47 /* UIVideoButton.m */, 24A345A51D95798A00881A5C /* UIShopTableCell.m */, 24A3459D1D95797700881A5C /* UIShopTableCell.xib */, ); path = LinphoneUI; sourceTree = ""; }; - 29B97314FDCFA39411CA2CEA = { + 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { isa = PBXGroup; children = ( 8C23BCB71D82AAC3005F19BB /* linphone.entitlements */, @@ -2355,7 +2487,7 @@ 633E388219FFB0F400936D1C /* README.md */, 63AADBC31B6A0FF200AA16FD /* Resources */, D398D3031594B0FB00FD553C /* Settings */, - 561D0FD932DE3595BE512375 /* Pods */, + 5BCEB53D544D440FA098AEE3 /* Pods */, ); name = CustomTemplate; sourceTree = ""; @@ -2426,52 +2558,30 @@ 8C73477B1D9BA3A00022EE8C /* UserNotifications.framework */, 5E58962520DCE5700030868C /* UserNotificationsUI.framework */, 63CE583F1C85EBF400304800 /* VideoToolbox.framework */, - 6E1BC45342F5201DABD7FE55 /* Pods_latestCallsWidget.framework */, - 8B488C393394746F9D630789 /* Pods_latestChatroomsWidget.framework */, - 9CBD6E980619195CB7EE32AC /* Pods_liblinphoneTester.framework */, - D252FEC7DD06DD5695A320A1 /* Pods_liblinphoneTesterTests.framework */, - 65CEDD144CABFAA70A29AF27 /* Pods_linphone.framework */, - F325590DD6CD7F6CC8F60C03 /* Pods_linphoneTests.framework */, - 7513CBF7F2BA0A9F99977C2B /* Pods_richNotifications.framework */, - 82E9DEDA2A78C6DBBD1A54DB /* Pods_msgNotificationContent.framework */, - 6F30EA7BEA39DA427CE0754E /* Pods_msgNotificationService.framework */, + B464E44A606CB50A65A96FE2 /* Pods_linphone.framework */, + A164BAF39B3A5B9F905917A7 /* Pods_msgNotificationContent.framework */, + 72B39681098B0AF5B59B3A61 /* Pods_msgNotificationService.framework */, ); name = Frameworks; sourceTree = ""; }; - 561D0FD932DE3595BE512375 /* Pods */ = { + 5BCEB53D544D440FA098AEE3 /* Pods */ = { isa = PBXGroup; children = ( - 68D9EC27FCECD5DE2E19CD3C /* Pods-liblinphoneTester.debug.xcconfig */, - 38A3AE51B9E09ABF29222E5F /* Pods-liblinphoneTester.release.xcconfig */, - 24585CBE78DA4F005C7F9D71 /* Pods-liblinphoneTester.distribution.xcconfig */, - FD22CA9E3EFBFEFFF1B80BA2 /* Pods-liblinphoneTester.distributionadhoc.xcconfig */, - 7D8CCFE176C634813E3A2593 /* Pods-liblinphoneTesterTests.debug.xcconfig */, - BE06BDE664323B2A53469696 /* Pods-liblinphoneTesterTests.release.xcconfig */, - 248C326F4AD75E654C1CB37A /* Pods-liblinphoneTesterTests.distribution.xcconfig */, - 046DEFE77AD0675DA9932C4C /* Pods-liblinphoneTesterTests.distributionadhoc.xcconfig */, - 799BA1104845EB01ACE764D8 /* Pods-linphone.debug.xcconfig */, - FEAFB5AD0E3AA409BBD1136E /* Pods-linphone.release.xcconfig */, - 38DF35D11A7C0F45E990C83A /* Pods-linphone.distribution.xcconfig */, - FE7D89A821FDC1BCA9BB9F8F /* Pods-linphone.distributionadhoc.xcconfig */, - ABB887316C42EE876A3051A9 /* Pods-linphoneTests.debug.xcconfig */, - F6373F9231918DECD2B3004D /* Pods-linphoneTests.release.xcconfig */, - 85FB19B6A8124D942C8471F1 /* Pods-linphoneTests.distribution.xcconfig */, - CE119C214B6B8B2740622BBD /* Pods-linphoneTests.distributionadhoc.xcconfig */, - 791017662FE117B9B12E8938 /* Pods-messagesNotification.debug.xcconfig */, - EE807919DDB0B4B46AD1D439 /* Pods-messagesNotification.release.xcconfig */, - 13B1BD646346F33BF57412F2 /* Pods-messagesNotification.distribution.xcconfig */, - CC6F924A8B1B1698914A3BB2 /* Pods-messagesNotification.distributionadhoc.xcconfig */, - 8B4C43A28E90775F6FCA2CEE /* Pods-msgNotificationContent.debug.xcconfig */, - 34027665305514025971F85C /* Pods-msgNotificationContent.release.xcconfig */, - E40C9A7D22675584396C0A3D /* Pods-msgNotificationContent.distribution.xcconfig */, - BAD0A9494E833034EB559687 /* Pods-msgNotificationContent.distributionadhoc.xcconfig */, - ADCA571A7CF61077747BFE53 /* Pods-msgNotificationService.debug.xcconfig */, - 904C1EC75CB9E03374AAA802 /* Pods-msgNotificationService.release.xcconfig */, - F4BE1A2318FC69D799C34F0A /* Pods-msgNotificationService.distribution.xcconfig */, - 8FD0D10102F0A8922703B8A4 /* Pods-msgNotificationService.distributionadhoc.xcconfig */, + 53432234870660E9876CBCA8 /* Pods-linphone.debug.xcconfig */, + 0DF941C97B75E7BA39A90600 /* Pods-linphone.release.xcconfig */, + 79B41078A602EFB886981917 /* Pods-linphone.distribution.xcconfig */, + 22D3BF4C45858F6B07F4D2A4 /* Pods-linphone.distributionadhoc.xcconfig */, + 1060E68152C51FCE5ACBF779 /* Pods-msgNotificationContent.debug.xcconfig */, + 02DBDD5A09F46796AEC2485B /* Pods-msgNotificationContent.release.xcconfig */, + 507103607396F28FF4427108 /* Pods-msgNotificationContent.distribution.xcconfig */, + 6150F32455334A0A7B3D46C8 /* Pods-msgNotificationContent.distributionadhoc.xcconfig */, + C589627B9D9D2A4F9C816051 /* Pods-msgNotificationService.debug.xcconfig */, + 063D57B2E4769739DC5DA5C0 /* Pods-msgNotificationService.release.xcconfig */, + B9F41097CE0124A05554DB9C /* Pods-msgNotificationService.distribution.xcconfig */, + 3411568BE5527EB500F75EBB /* Pods-msgNotificationService.distributionadhoc.xcconfig */, ); - name = Pods; + path = Pods; sourceTree = ""; }; 61AE364C20C00B370089D9D3 /* linphoneExtension */ = { @@ -2501,6 +2611,67 @@ 633FEBE11D3CD5570014B822 /* images */ = { isa = PBXGroup; children = ( + C6EA2F4727514D08008E60F8 /* voip_call_forward.png */, + C6277DA7274BF1CD00406FB9 /* voip_radio_off.png */, + C6277DA6274BF1CD00406FB9 /* voip_radio_on.png */, + C6710F7D2722B1F500ED888F /* voip_audio_routes.png */, + C6710F6D2722B1F000ED888F /* voip_bluetooth.png */, + C6710F802722B1F600ED888F /* voip_call_add.png */, + C6710F732722B1F200ED888F /* voip_call_chat.png */, + C6710F972722B1FE00ED888F /* voip_call_header_active.png */, + C6710F912722B1FC00ED888F /* voip_call_header_incoming.png */, + C6710F9C2722B1FF00ED888F /* voip_call_header_outgoing.png */, + C6710F842722B1F800ED888F /* voip_call_header_paused.png */, + C6710F932722B1FC00ED888F /* voip_call_list_menu.png */, + C6710F6E2722B1F000ED888F /* voip_call_more.png */, + C6710F6A2722B1EF00ED888F /* voip_call_numpad.png */, + C6710F942722B1FD00ED888F /* voip_call_participants.png */, + C6710F992722B1FE00ED888F /* voip_call_record.png */, + C6710F702722B1F100ED888F /* voip_call_stats.png */, + C6710F822722B1F700ED888F /* voip_call.png */, + C6710F792722B1F400ED888F /* voip_calls_list.png */, + C6710F8C2722B1FA00ED888F /* voip_camera_off.png */, + C6710F9B2722B1FF00ED888F /* voip_camera_on.png */, + C6710F8A2722B1F900ED888F /* voip_cancel.png */, + C6710F832722B1F700ED888F /* voip_change_camera.png */, + C6710F772722B1F300ED888F /* voip_chat_rooms_list.png */, + C6710F712722B1F100ED888F /* voip_conference_active_speaker.png */, + C6710F7B2722B1F500ED888F /* voip_conference_mosaic.png */, + C6710F762722B1F300ED888F /* voip_conference_new.png */, + C6710F9A2722B1FE00ED888F /* voip_conference_paused_big.png */, + C6710F9D2722B20000ED888F /* voip_conference_play_big.png */, + C6710F682722B1EF00ED888F /* voip_copy.png */, + C6710F7C2722B1F500ED888F /* voip_delete.png */, + C6710F982722B1FE00ED888F /* voip_dropdown.png */, + C6710F862722B1F800ED888F /* voip_earpiece.png */, + C6710F722722B1F200ED888F /* voip_edit.png */, + C6710F852722B1F800ED888F /* voip_export.png */, + C6710F752722B1F300ED888F /* voip_hangup.png */, + C6710F9E2722B20000ED888F /* voip_info.png */, + C6710F6C2722B1F000ED888F /* voip_mandatory.png */, + C6710F8D2722B1FA00ED888F /* voip_menu_more.png */, + C6710F692722B1EF00ED888F /* voip_merge_calls.png */, + C6710F882722B1F900ED888F /* voip_micro_off.png */, + C6710F782722B1F300ED888F /* voip_micro_on.png */, + C6710F6F2722B1F100ED888F /* voip_multiple_contacts_avatar.png */, + C6710F672722B1EF00ED888F /* voip_numpad_0.png */, + C6710F662722B1EF00ED888F /* voip_numpad_1.png */, + C6710F892722B1F900ED888F /* voip_numpad_2.png */, + C6710F7F2722B1F600ED888F /* voip_numpad_3.png */, + C6710F8E2722B1FB00ED888F /* voip_numpad_4.png */, + C6710F742722B1F200ED888F /* voip_numpad_5.png */, + C6710F7E2722B1F600ED888F /* voip_numpad_6.png */, + C6710F8F2722B1FB00ED888F /* voip_numpad_7.png */, + C6710F962722B1FD00ED888F /* voip_numpad_8.png */, + C6710F812722B1F700ED888F /* voip_numpad_9.png */, + C6710F872722B1F800ED888F /* voip_numpad_hash.png */, + C6710F8B2722B1FA00ED888F /* voip_numpad_star.png */, + C6710F7A2722B1F400ED888F /* voip_pause.png */, + C6710F922722B1FC00ED888F /* voip_remote_recording.png */, + C6710F6B2722B1F000ED888F /* voip_single_contact_avatar.png */, + C6710F952722B1FD00ED888F /* voip_speaker_off.png */, + C6710F902722B1FB00ED888F /* voip_speaker_on.png */, + C6710F642722B13000ED888F /* voip_spinner.png */, C66B040D26F095CE009B5EDC /* cancel_forward.png */, C66B040926EFDA54009B5EDC /* reply_cancel.png */, C6A1BB4426E890BD00540D50 /* file_voice_default.png */, @@ -3038,6 +3209,7 @@ 63AADBC31B6A0FF200AA16FD /* Resources */ = { isa = PBXGroup; children = ( + C6710F5D2722AE8900ED888F /* fonts */, 63AADBE31B6A0FF200AA16FD /* assistant_external_sip.rc */, 63AADBE41B6A0FF200AA16FD /* assistant_linphone_create.rc */, 63AADBE51B6A0FF200AA16FD /* assistant_linphone_existing.rc */, @@ -3090,6 +3262,205 @@ path = ringtones; sourceTree = ""; }; + C658614A273E59A300A0DBFC /* CompositeViewControllers */ = { + isa = PBXGroup; + children = ( + C65A5D2F27216B86005BA038 /* ActiveCallOrConferenceView.swift */, + C6824FB927219D890043D4FC /* IncomingCallView.swift */, + C6710FE62723234400ED888F /* OutgoingCallView.swift */, + ); + path = CompositeViewControllers; + sourceTree = ""; + }; + C65A5D2D2721683B005BA038 /* Voip */ = { + isa = PBXGroup; + children = ( + C6F2D500273B0EFC0071BA52 /* VoipDialog.swift */, + C67C97B7274FD76B0074A0D8 /* AudioRouteUtils.swift */, + C6710F5A2722A9FB00ED888F /* Theme */, + C6710F4D2722900A00ED888F /* Widgets */, + C65A5D3127216C3E005BA038 /* Models */, + C65A5D2E27216B4C005BA038 /* Views */, + ); + path = Voip; + sourceTree = ""; + }; + C65A5D2E27216B4C005BA038 /* Views */ = { + isa = PBXGroup; + children = ( + C6F2D4F8273A4CD60071BA52 /* SharedLayoutConstants.swift */, + C6710FDF2722D47E00ED888F /* Fragments */, + C658614A273E59A300A0DBFC /* CompositeViewControllers */, + ); + path = Views; + sourceTree = ""; + }; + C65A5D3127216C3E005BA038 /* Models */ = { + isa = PBXGroup; + children = ( + C65A5D3E27216E3A005BA038 /* CallData.swift */, + C6C98CD627453F9600059B55 /* ConferenceParticipantData.swift */, + C6C98CDA274541E400059B55 /* ConferenceParticipantDeviceData.swift */, + C60B66672721AFFA0026AC7D /* CallStatisticsData.swift */, + C60D265527299C94006238BB /* ControlsViewModel.swift */, + C6F2D4EE27392D960071BA52 /* CallsViewModel.swift */, + C6C98CD427453ED700059B55 /* ConferenceViewModel.swift */, + ); + path = Models; + sourceTree = ""; + }; + C65A5D3427216CAB005BA038 /* ViewModel */ = { + isa = PBXGroup; + children = ( + C65A5D3727216CC0005BA038 /* MutableLiveData.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; + C65A5D41272184CE005BA038 /* SwiftUtil */ = { + isa = PBXGroup; + children = ( + C683B211272276C100D4E15C /* Extensions */, + C65A5D3427216CAB005BA038 /* ViewModel */, + ); + path = SwiftUtil; + sourceTree = ""; + }; + C6710F4D2722900A00ED888F /* Widgets */ = { + isa = PBXGroup; + children = ( + C6710F582722A9B800ED888F /* StyledLabel.swift */, + C6710FD82722BD0100ED888F /* UICallTimer.swift */, + C6710F4E2722903200ED888F /* RotatingSpinner.swift */, + C6710FE02722F0E400ED888F /* Avatar.swift */, + C6710FE82723DD7D00ED888F /* CallControlButton.swift */, + C658614B273E5B5E00A0DBFC /* VoipExtraButton.swift */, + C6D09F3C273EE467003C2173 /* BouncingCounter.swift */, + C6F2D502273BAC030071BA52 /* ButtonWithStateBackgrounds.swift */, + ); + path = Widgets; + sourceTree = ""; + }; + C6710F5A2722A9FB00ED888F /* Theme */ = { + isa = PBXGroup; + children = ( + C6710F5427229D5900ED888F /* LightDarkColor.swift */, + C6710F5627229DEE00ED888F /* TextStyle.swift */, + C683B20D2722702300D4E15C /* VoipTheme.swift */, + C6710F52272297C400ED888F /* VoipTexts.swift */, + C6710FEA2726874D00ED888F /* ButtonTheme.swift */, + ); + path = Theme; + sourceTree = ""; + }; + C6710F5D2722AE8900ED888F /* fonts */ = { + isa = PBXGroup; + children = ( + C6710F5F2722AECB00ED888F /* Roboto-Bold.ttf */, + C6710F5E2722AECB00ED888F /* Roboto-Italic.ttf */, + C6710F602722AECB00ED888F /* Roboto-Regular.ttf */, + ); + path = fonts; + sourceTree = ""; + }; + C6710FDF2722D47E00ED888F /* Fragments */ = { + isa = PBXGroup; + children = ( + C67C97AF274FB3620074A0D8 /* ParticipantsList */, + C67C97AE274FB3370074A0D8 /* CallsList */, + C67C97AC274FB2DA0074A0D8 /* ActiveCall */, + C6D52B4727481D3F00904660 /* Conference */, + C6F2D4FE273AF01A0071BA52 /* ControlsView.swift */, + C67C97B3274FC5EE0074A0D8 /* AudioRoutesView.swift */, + C6F2D4F62739861F0071BA52 /* RemotelyRecording.swift */, + C6278496273C21E1002FAA29 /* LocalVideoView.swift */, + C6586148273E595700A0DBFC /* VoipExtraButtonsView.swift */, + C6F2D4FC273A85A90071BA52 /* PausedCallOrConferenceView.swift */, + C6710FDD2722D44A00ED888F /* IncomingOuntgoingCommonView.swift */, + C6C65E8A2727274A00E48FC6 /* NumpadView.swift */, + C6D09F3E274273FB003C2173 /* CallStatsView.swift */, + C6C98CCD27438A3F00059B55 /* DismissableView.swift */, + ); + path = Fragments; + sourceTree = ""; + }; + C6710FE227230B2300ED888F /* IOS */ = { + isa = PBXGroup; + children = ( + C6F2D504273BB3BB0071BA52 /* UIApplication+Extension.swift */, + C6710F5B2722AAED00ED888F /* UIDeviceExtensions.swift */, + C65A5D44272196AE005BA038 /* OptionalExtensions.swift */, + C683B212272276CF00D4E15C /* UIColorExtensions.swift */, + C6710F502722932600ED888F /* UIImageViewExtensions.swift */, + C60D265B272AA0BD006238BB /* UIImageExtensions.swift */, + C6710FDB2722C3BB00ED888F /* UIVIewExtensions.swift */, + C6C65E88272723DC00E48FC6 /* UIVIewControllerExtensions.swift */, + ); + path = IOS; + sourceTree = ""; + }; + C6710FE327230B2D00ED888F /* LinphoneCore */ = { + isa = PBXGroup; + children = ( + C6710FE427230B5800ED888F /* AddressExtensions.swift */, + C60D265727299F6F006238BB /* CoreExtensions.swift */, + C6F2D4F0273935860071BA52 /* CallExtensions.swift */, + C6C98CDC274547C500059B55 /* ParticipantExtensions.swift */, + C6C98CDE2745590400059B55 /* ConferenceExtensions.swift */, + C6D09F4027428626003C2173 /* IceState.swift */, + C6D09F42274288D4003C2173 /* PayloadType.swift */, + ); + path = LinphoneCore; + sourceTree = ""; + }; + C67C97AC274FB2DA0074A0D8 /* ActiveCall */ = { + isa = PBXGroup; + children = ( + C6F2D4F22739475C0071BA52 /* ActiveCallView.swift */, + ); + path = ActiveCall; + sourceTree = ""; + }; + C67C97AE274FB3370074A0D8 /* CallsList */ = { + isa = PBXGroup; + children = ( + C6D09F4A27438706003C2173 /* CallsListView.swift */, + C6C98CCF27439A7F00059B55 /* VoipCallCell.swift */, + C6C98CD12743FD0B00059B55 /* VoipCallContextMenu.swift */, + ); + path = CallsList; + sourceTree = ""; + }; + C67C97AF274FB3620074A0D8 /* ParticipantsList */ = { + isa = PBXGroup; + children = ( + C6B04D62274B95D400F70559 /* VoipParticipantCell.swift */, + C6B04D60274B954500F70559 /* ParticipantsListView.swift */, + ); + path = ParticipantsList; + sourceTree = ""; + }; + C683B211272276C100D4E15C /* Extensions */ = { + isa = PBXGroup; + children = ( + C6710FE327230B2D00ED888F /* LinphoneCore */, + C6710FE227230B2300ED888F /* IOS */, + ); + path = Extensions; + sourceTree = ""; + }; + C6D52B4727481D3F00904660 /* Conference */ = { + isa = PBXGroup; + children = ( + C6C98CE0274568F700059B55 /* VoipConferenceGridView.swift */, + C6D52B44274648E500904660 /* VoipGridParticipantCell.swift */, + C6B04D66274BD61200F70559 /* VoipConferenceActiveSpeakerView.swift */, + C6B04D68274BD6A100F70559 /* VoipActiveSpeakerParticipantCell.swift */, + C67C97B0274FB4C10074A0D8 /* VoipConferenceDisplayModeSelectionView.swift */, + ); + path = Conference; + sourceTree = ""; + }; D326483415887D4400930C67 /* Utils */ = { isa = PBXGroup; children = ( @@ -3116,8 +3487,6 @@ 63D11C541C3D503A00E8FCEE /* Log.h */, 63423C081C4501D000D9A050 /* Contact.h */, 63423C091C4501D000D9A050 /* Contact.m */, - 8C1B67081E6718BC001EA2FE /* AudioHelper.h */, - 8C1B67051E671826001EA2FE /* AudioHelper.m */, C6B4444726AADA530076C517 /* SwiftUtil.swift */, ); name = Utils; @@ -3269,15 +3638,15 @@ isa = PBXNativeTarget; buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "linphone" */; buildPhases = ( - 20EEEF6D60A7B77C2EFF85DB /* [CP] Check Pods Manifest.lock */, + 068EB8CED53992A5D7A733DF /* [CP] Check Pods Manifest.lock */, 1D60588D0D05DD3D006BFB54 /* Resources */, 63DCC71D1A07B08E00916627 /* Run Script */, 1D60588E0D05DD3D006BFB54 /* Sources */, 1D60588F0D05DD3D006BFB54 /* Frameworks */, 8CDC89061EAF89A8006B5652 /* Embed Frameworks */, 5EF0C35020C806A5005081B0 /* Embed App Extensions */, - 4D22DCAAC0231865D78AC1E6 /* [CP] Embed Pods Frameworks */, 614D0A1821E77F5300C43EDF /* ShellScript */, + D7A9CDC06B7BB5D324EA10C9 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -3312,7 +3681,7 @@ isa = PBXNativeTarget; buildConfigurationList = EA5F25E1232BD3E300475F2E /* Build configuration list for PBXNativeTarget "msgNotificationService" */; buildPhases = ( - CD5CF0B3FE8B6052CD8219CE /* [CP] Check Pods Manifest.lock */, + 1E9EBAB02A694F2487E6099B /* [CP] Check Pods Manifest.lock */, EA5F25D5232BD3E200475F2E /* Sources */, 203E6292C3E84CD13778F720 /* Frameworks */, EA88A406242A6224007FEC61 /* Resources */, @@ -3331,7 +3700,7 @@ isa = PBXNativeTarget; buildConfigurationList = EA8CB834239F96CA00C330CC /* Build configuration list for PBXNativeTarget "msgNotificationContent" */; buildPhases = ( - B0A561F0290F0342EACE9005 /* [CP] Check Pods Manifest.lock */, + 2E68C0F1B7E1F6C3000F700C /* [CP] Check Pods Manifest.lock */, EA8CB823239F96CA00C330CC /* Sources */, 143EFEE2501CB14E6BB244EF /* Frameworks */, EA88F3AE241BD1ED00E66528 /* Resources */, @@ -3433,7 +3802,7 @@ fr, hu, ); - mainGroup = 29B97314FDCFA39411CA2CEA; + mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; productRefGroup = 19C28FACFE9D520D11CA2CBB /* Products */; projectDirPath = ""; projectRoot = ""; @@ -3465,6 +3834,7 @@ 633FEE2B1D3CD5590014B822 /* color_C.png in Resources */, 633FEE871D3CD5590014B822 /* led_inprogress@2x.png in Resources */, 633FEEC61D3CD55A0014B822 /* numpad_5_over@2x.png in Resources */, + C6710FC02722B20000ED888F /* voip_numpad_hash.png in Resources */, 633FEF021D3CD55A0014B822 /* options_disabled.png in Resources */, 633FEDC81D3CD5590014B822 /* call_missed.png in Resources */, 8CB2B8FA1F86229E0015CEE2 /* next_disabled.png in Resources */, @@ -3484,6 +3854,7 @@ 638F1A621C2021B2004B8E02 /* DialerView~ipad.xib in Resources */, 615A2817217F280C0060F920 /* chat_list_indicator.png in Resources */, 633FEEFF1D3CD55A0014B822 /* options_add_call_disabled@2x.png in Resources */, + C6710FB92722B20000ED888F /* voip_call_add.png in Resources */, 633FEF091D3CD55A0014B822 /* options_start_conference_disabled@2x.png in Resources */, C622E3F326A81290004F5434 /* vr_pause.png in Resources */, 633FEE051D3CD5590014B822 /* cancel_edit_disabled@2x.png in Resources */, @@ -3495,23 +3866,28 @@ 633FEE0B1D3CD5590014B822 /* chat_attachment_default@2x.png in Resources */, 633FEED41D3CD55A0014B822 /* numpad_7_default@2x.png in Resources */, 633FEEE01D3CD55A0014B822 /* numpad_8_over~ipad@2x.png in Resources */, + C6710FA42722B20000ED888F /* voip_single_contact_avatar.png in Resources */, 633FEDDC1D3CD5590014B822 /* call_start_body_disabled~ipad.png in Resources */, - 63E802DB1C625AEF000D5509 /* BuildFile in Resources */, + 63E802DB1C625AEF000D5509 /* (null) in Resources */, 633FEE2E1D3CD5590014B822 /* color_F.png in Resources */, + C6710FA72722B20000ED888F /* voip_call_more.png in Resources */, 633FEDC51D3CD5590014B822 /* call_hangup_disabled@2x.png in Resources */, 633FEEDF1D3CD55A0014B822 /* numpad_8_over~ipad.png in Resources */, + C6710FB62722B20000ED888F /* voip_audio_routes.png in Resources */, + C6710FB02722B20000ED888F /* voip_chat_rooms_list.png in Resources */, 8CD99A372090A824008A7CDA /* splashscreen@2x.png in Resources */, 24BFAAA9209B0630004F47A7 /* linphone_logo@2x.png in Resources */, 633FEE071D3CD5590014B822 /* chat_add_default@2x.png in Resources */, + C6710FBB2722B20000ED888F /* voip_call.png in Resources */, 633FEF551D3CD55A0014B822 /* waiting_time@2x.png in Resources */, 633FEEAC1D3CD55A0014B822 /* numpad_2_default@2x.png in Resources */, + C6710FD22722B20000ED888F /* voip_call_record.png in Resources */, 633FEE541D3CD5590014B822 /* dialer_back_default.png in Resources */, 633FEF0B1D3CD55A0014B822 /* options_transfer_call_default@2x.png in Resources */, 633FEDE81D3CD5590014B822 /* call_status_missed~ipad.png in Resources */, 63AADBFF1B6A0FF200AA16FD /* assistant_external_sip.rc in Resources */, 61CCC3E121933B660060EDEA /* UIDeviceCell.xib in Resources */, 633FEE9E1D3CD55A0014B822 /* numpad_0_over@2x.png in Resources */, - 634610121B6140A500548952 /* CallOutgoingView.xib in Resources */, 8CE24F581F8268850077AC0A /* conference_delete@2x.png in Resources */, 633FEDC21D3CD5590014B822 /* call_hangup_default.png in Resources */, 633FEEB41D3CD55A0014B822 /* numpad_3_default@2x.png in Resources */, @@ -3533,8 +3909,10 @@ 633FEE401D3CD5590014B822 /* contacts_all_selected.png in Resources */, 633FEE0C1D3CD5590014B822 /* chat_attachment_disabled.png in Resources */, C622E3EF26A81290004F5434 /* vr_stop.png in Resources */, + C6710FD12722B20000ED888F /* voip_dropdown.png in Resources */, 633FEF001D3CD55A0014B822 /* options_default.png in Resources */, CF15F21F20E4F9A3008B1DE6 /* UIImageViewDeletable.xib in Resources */, + C6710FBF2722B20000ED888F /* voip_earpiece.png in Resources */, 633FEE951D3CD55A0014B822 /* micro_default@2x.png in Resources */, 633FEE6A1D3CD5590014B822 /* footer_dialer_default.png in Resources */, 633FEEC91D3CD55A0014B822 /* numpad_5~ipad.png in Resources */, @@ -3558,18 +3936,20 @@ 633FEE7A1D3CD5590014B822 /* history_missed_default.png in Resources */, 633FEF121D3CD55A0014B822 /* pause_big_over_selected.png in Resources */, 633FED9D1D3CD5590014B822 /* add_field_default@2x.png in Resources */, + C6710FA32722B20000ED888F /* voip_call_numpad.png in Resources */, C622E3F426A81290004F5434 /* vr_play.png in Resources */, 639E9CB01C0DB83000019A75 /* SideMenuView.xib in Resources */, 633FEDBB1D3CD5590014B822 /* call_audio_start_default@2x.png in Resources */, 633FEF1A1D3CD55A0014B822 /* presence_away.png in Resources */, 639E9CB51C0DB88200019A75 /* PhoneMainView.xib in Resources */, + C6710F652722B13000ED888F /* voip_spinner.png in Resources */, 633FEDB61D3CD5590014B822 /* call_alt_start_default.png in Resources */, - 6352A5761BE0D4B800594C1C /* CallSideMenuView.xib in Resources */, 633FEF0D1D3CD55A0014B822 /* options_transfer_call_disabled@2x.png in Resources */, 633FEDEF1D3CD5590014B822 /* call_transfer_default@2x.png in Resources */, 633FEF391D3CD55A0014B822 /* routes_selected@2x.png in Resources */, 633FEE491D3CD5590014B822 /* delete_default@2x.png in Resources */, 633FEF291D3CD55A0014B822 /* route_earpiece_default@2x.png in Resources */, + C6710FAF2722B20000ED888F /* voip_conference_new.png in Resources */, 633FEE271D3CD5590014B822 /* checkbox_checked@2x.png in Resources */, 61586B85217A17070038AC45 /* menu_assistant.png in Resources */, 633FEDCC1D3CD5590014B822 /* call_quality_indicator_0.png in Resources */, @@ -3594,6 +3974,7 @@ 633FEEEF1D3CD55A0014B822 /* numpad_hash_over~ipad.png in Resources */, 633FEEE21D3CD55A0014B822 /* numpad_8~ipad@2x.png in Resources */, 633FEEED1D3CD55A0014B822 /* numpad_hash_over.png in Resources */, + C6710FC42722B20000ED888F /* voip_numpad_star.png in Resources */, 633FEE1F1D3CD5590014B822 /* chat_start_body_disabled@2x.png in Resources */, 633FEEF81D3CD55A0014B822 /* numpad_star_over~ipad.png in Resources */, C6A1BB3826E8815400540D50 /* menu_reply_default.png in Resources */, @@ -3601,6 +3982,7 @@ 639CEAFD1A1DF4D9004DE38F /* StatusBarView.xib in Resources */, 633FEDE91D3CD5590014B822 /* call_status_missed~ipad@2x.png in Resources */, 8CE24F4C1F8234A30077AC0A /* next_default@2x.png in Resources */, + C6710FB12722B20000ED888F /* voip_micro_on.png in Resources */, 244523B11E8266CC0037A187 /* chat_read.png in Resources */, 61AEBEC62191E47500F35E7F /* chevron_list_close.png in Resources */, 617B4A60260A2B7800A87337 /* RecordingsListView.xib in Resources */, @@ -3609,15 +3991,19 @@ 8CF25D951F9F336100BEA0C1 /* check_unselected@2x.png in Resources */, F088488A19FF8C41007FFCF3 /* UIContactCell.xib in Resources */, 633FEE381D3CD5590014B822 /* contact_add_default.png in Resources */, + C6710FC22722B20000ED888F /* voip_numpad_2.png in Resources */, 633FEE6F1D3CD5590014B822 /* footer_history_default@2x.png in Resources */, + C6710FD52722B20000ED888F /* voip_call_header_outgoing.png in Resources */, 633FEF201D3CD55A0014B822 /* presence_unregistered.png in Resources */, 61586B8D217A173F0038AC45 /* menu_options.png in Resources */, 61AEBEC82191E48400F35E7F /* chevron_list_close@2x.png in Resources */, 633FEF341D3CD55A0014B822 /* routes_default.png in Resources */, + C6710FC82722B20000ED888F /* voip_numpad_7.png in Resources */, 633FEE061D3CD5590014B822 /* chat_add_default.png in Resources */, 633FEDF21D3CD5590014B822 /* call_video_start_default.png in Resources */, 633FEF491D3CD55A0014B822 /* speaker_selected@2x.png in Resources */, 633FEF541D3CD55A0014B822 /* waiting_time.png in Resources */, + C6277DA8274BF1CE00406FB9 /* voip_radio_on.png in Resources */, 633FEE861D3CD5590014B822 /* led_inprogress.png in Resources */, 633FEDFC1D3CD5590014B822 /* camera_switch_default.png in Resources */, 24BFAA9F209B0630004F47A7 /* linphone_user.png in Resources */, @@ -3634,11 +4020,13 @@ 633FEE281D3CD5590014B822 /* checkbox_unchecked.png in Resources */, 633FEE9D1D3CD55A0014B822 /* numpad_0_over.png in Resources */, 633FEEC21D3CD55A0014B822 /* numpad_4~ipad@2x.png in Resources */, + C6710FCA2722B20000ED888F /* voip_call_header_incoming.png in Resources */, 633FEDFE1D3CD5590014B822 /* camera_switch_disabled.png in Resources */, 633FEE7F1D3CD5590014B822 /* history_missed_selected@2x.png in Resources */, 633FEDC01D3CD5590014B822 /* call_back_disabled.png in Resources */, 633FEED31D3CD55A0014B822 /* numpad_7_default.png in Resources */, 63130FB21C1ED06900371918 /* SideMenuView~ipad.xib in Resources */, + C6710FB82722B20000ED888F /* voip_numpad_3.png in Resources */, 633FEEE71D3CD55A0014B822 /* numpad_9_over~ipad.png in Resources */, 633FEEEA1D3CD55A0014B822 /* numpad_9~ipad@2x.png in Resources */, 633FEEC31D3CD55A0014B822 /* numpad_5_default.png in Resources */, @@ -3647,6 +4035,8 @@ 633FEEC01D3CD55A0014B822 /* numpad_4_over~ipad@2x.png in Resources */, 61586B8B217A17320038AC45 /* menu_link_account@2x.png in Resources */, 63CDC4661C3BDE370085F529 /* shortring.caf in Resources */, + C6710FB42722B20000ED888F /* voip_conference_mosaic.png in Resources */, + C6710FC92722B20000ED888F /* voip_speaker_on.png in Resources */, C6A1BB4126E889AD00540D50 /* forward_message_default.png in Resources */, 633FEDD51D3CD5590014B822 /* call_quality_indicator_4@2x.png in Resources */, 633FEDE71D3CD5590014B822 /* call_status_missed@2x.png in Resources */, @@ -3659,8 +4049,10 @@ 633FEEBB1D3CD55A0014B822 /* numpad_4_default.png in Resources */, 633FEF2B1D3CD55A0014B822 /* route_earpiece_disabled@2x.png in Resources */, 639E9CA31C0DB7EA00019A75 /* UIChatBubbleTextCell.xib in Resources */, + C6710FA62722B20000ED888F /* voip_bluetooth.png in Resources */, 633FEDD21D3CD5590014B822 /* call_quality_indicator_3.png in Resources */, 633FEDAF1D3CD5590014B822 /* call_add_default@2x.png in Resources */, + C6710FA82722B20000ED888F /* voip_multiple_contacts_avatar.png in Resources */, 633FEE931D3CD55A0014B822 /* menu@2x.png in Resources */, 633FEF3D1D3CD55A0014B822 /* security_ok@2x.png in Resources */, 633FEF161D3CD55A0014B822 /* pause_small_disabled.png in Resources */, @@ -3673,8 +4065,8 @@ 615A280F217F1FD50060F920 /* chat_add_group.png in Resources */, 633FEEC41D3CD55A0014B822 /* numpad_5_default@2x.png in Resources */, 633FEDAC1D3CD5590014B822 /* backspace_over.png in Resources */, - 639E9C9D1C0DB7DF00019A75 /* UICallPausedCell.xib in Resources */, 633FEE1B1D3CD5590014B822 /* chat_start_body_default@2x.png in Resources */, + C6710FB22722B20000ED888F /* voip_calls_list.png in Resources */, 633FEE021D3CD5590014B822 /* cancel_edit_default.png in Resources */, 633FEEE31D3CD55A0014B822 /* numpad_9_default.png in Resources */, 633FEE651D3CD5590014B822 /* footer_chat_disabled@2x.png in Resources */, @@ -3706,6 +4098,7 @@ 633FEDD11D3CD5590014B822 /* call_quality_indicator_2@2x.png in Resources */, 633FEDBC1D3CD5590014B822 /* call_audio_start_disabled.png in Resources */, 24BFAA9E209B0630004F47A7 /* dialer_background.png in Resources */, + C6710FD72722B20000ED888F /* voip_info.png in Resources */, 633FEE481D3CD5590014B822 /* delete_default.png in Resources */, 633FEEB01D3CD55A0014B822 /* numpad_2_over~ipad@2x.png in Resources */, 633FEE2A1D3CD5590014B822 /* color_A.png in Resources */, @@ -3713,6 +4106,8 @@ 63E27A321C4FECD000D332AE /* LaunchScreen.xib in Resources */, 633FEED11D3CD55A0014B822 /* numpad_6~ipad.png in Resources */, CF7602E82108759A00749F76 /* UIRecordingCell.xib in Resources */, + C6710FC62722B20000ED888F /* voip_menu_more.png in Resources */, + C6710FC12722B20000ED888F /* voip_micro_off.png in Resources */, C6B4444626AAD0980076C517 /* file_pdf_default.png in Resources */, 633FEED21D3CD55A0014B822 /* numpad_6~ipad@2x.png in Resources */, 633FEDCD1D3CD5590014B822 /* call_quality_indicator_0@2x.png in Resources */, @@ -3743,13 +4138,14 @@ D34F6F9E1594D3FB0095705B /* InAppSettings.bundle in Resources */, 633FEE4D1D3CD5590014B822 /* delete_field_default@2x.png in Resources */, 615A28362180720D0060F920 /* security_toogle_background_grey@2x.png in Resources */, + C6710F612722AECB00ED888F /* Roboto-Italic.ttf in Resources */, 639CEB091A1DF4FA004DE38F /* UIChatCell.xib in Resources */, 633FEE961D3CD55A0014B822 /* micro_disabled.png in Resources */, 63AADBF61B6A0FF200AA16FD /* linphonerc-factory in Resources */, + C6710FA92722B20000ED888F /* voip_call_stats.png in Resources */, 633FEE671D3CD5590014B822 /* footer_contacts_default@2x.png in Resources */, 615A2830218071E80060F920 /* security_toogle_background_green.png in Resources */, 63B8D68C1BCBE65600C12B09 /* ChatConversationCreateView.xib in Resources */, - D38187D915FE347700C3EDCA /* CallIncomingView.xib in Resources */, 63CDC45F1C3BDE370085F529 /* ringback.wav in Resources */, 8CD99A1C20908C27008A7CDA /* callkit_logo@2x.png in Resources */, 633FEE251D3CD5590014B822 /* chat_start_body_over~ipad@2x.png in Resources */, @@ -3782,13 +4178,16 @@ 633FEF3A1D3CD55A0014B822 /* security_ko.png in Resources */, 615A283A2180788E0060F920 /* security_toogle_button.png in Resources */, 633FEDA01D3CD5590014B822 /* avatar.png in Resources */, + C6710FA02722B20000ED888F /* voip_numpad_0.png in Resources */, 633FEEBC1D3CD55A0014B822 /* numpad_4_default@2x.png in Resources */, + C6710FAD2722B20000ED888F /* voip_numpad_5.png in Resources */, 633FEEA91D3CD55A0014B822 /* numpad_1~ipad.png in Resources */, 615A28402180A2620060F920 /* invite_linphone@2x.png in Resources */, C61B1BF42667D202001A4E4A /* more_menu_default.png in Resources */, 633FEDF71D3CD5590014B822 /* camera_default@2x.png in Resources */, C64A85522667B74100252AD2 /* ephemeral_messages_default.png in Resources */, 633FEDB31D3CD5590014B822 /* call_alt_back_default@2x.png in Resources */, + C6710FB52722B20000ED888F /* voip_delete.png in Resources */, 633FEDCF1D3CD5590014B822 /* call_quality_indicator_1@2x.png in Resources */, 633FEF131D3CD55A0014B822 /* pause_big_over_selected@2x.png in Resources */, 8CDC61951F84D89B0087CF7F /* check_selected.png in Resources */, @@ -3802,22 +4201,28 @@ 633FEDB21D3CD5590014B822 /* call_alt_back_default.png in Resources */, 633FEE3D1D3CD5590014B822 /* contacts_all_default@2x.png in Resources */, 633FEF251D3CD55A0014B822 /* route_bluetooth_disabled@2x.png in Resources */, + C6710FCF2722B20000ED888F /* voip_numpad_8.png in Resources */, 633FEDD81D3CD5590014B822 /* call_start_body_default~ipad.png in Resources */, 61586B83217A16FD0038AC45 /* menu_about@2x.png in Resources */, 633FEED81D3CD55A0014B822 /* numpad_7_over~ipad@2x.png in Resources */, + C6710FBE2722B20000ED888F /* voip_export.png in Resources */, 633FEDD71D3CD5590014B822 /* call_start_body_default@2x.png in Resources */, + C6710FC32722B20000ED888F /* voip_cancel.png in Resources */, 633FEE571D3CD5590014B822 /* dialer_back_disabled@2x.png in Resources */, 63CDC45E1C3BDE370085F529 /* msg.caf in Resources */, 633FEE6D1D3CD5590014B822 /* footer_dialer_disabled@2x.png in Resources */, 633FEF171D3CD55A0014B822 /* pause_small_disabled@2x.png in Resources */, + C6710FCE2722B20000ED888F /* voip_speaker_off.png in Resources */, C6A1BB3626E8815400540D50 /* menu_forward_default.png in Resources */, D38187DD15FE348A00C3EDCA /* AssistantView.xib in Resources */, 633FEDA61D3CD5590014B822 /* back_disabled.png in Resources */, 633FEED61D3CD55A0014B822 /* numpad_7_over@2x.png in Resources */, - 638F1A881C2167C2004B8E02 /* CallView~ipad.xib in Resources */, + C6710FD02722B20000ED888F /* voip_call_header_active.png in Resources */, + C6710FBA2722B20000ED888F /* voip_numpad_9.png in Resources */, 633FEDA11D3CD5590014B822 /* avatar@2x.png in Resources */, 633FED9E1D3CD5590014B822 /* add_field_over.png in Resources */, 633FEE0A1D3CD5590014B822 /* chat_attachment_default.png in Resources */, + C6710FC72722B20000ED888F /* voip_numpad_4.png in Resources */, 633FEE201D3CD5590014B822 /* chat_start_body_disabled~ipad.png in Resources */, 633FEE5B1D3CD5590014B822 /* edit_default@2x.png in Resources */, 633FEDE11D3CD5590014B822 /* call_start_body_over~ipad@2x.png in Resources */, @@ -3832,8 +4237,11 @@ C622E3F126A81290004F5434 /* vr_on.png in Resources */, 633FEF401D3CD55A0014B822 /* select_all_default.png in Resources */, C6B4444426AAD0980076C517 /* file_picture_default.png in Resources */, + C6EA2F4827514D09008E60F8 /* voip_call_forward.png in Resources */, 633FEDF01D3CD5590014B822 /* call_transfer_disabled.png in Resources */, 633FEE351D3CD5590014B822 /* conference_exit_default@2x.png in Resources */, + C6710FCB2722B20000ED888F /* voip_remote_recording.png in Resources */, + C6710FAA2722B20000ED888F /* voip_conference_active_speaker.png in Resources */, 633FEECF1D3CD55A0014B822 /* numpad_6_over~ipad.png in Resources */, 61586B87217A17160038AC45 /* menu_assistant@2x.png in Resources */, 633FEE9A1D3CD55A0014B822 /* nowebcamCIF.jpg in Resources */, @@ -3842,8 +4250,10 @@ 633FEEDB1D3CD55A0014B822 /* numpad_8_default.png in Resources */, 633FEE5C1D3CD5590014B822 /* edit_disabled.png in Resources */, 8CF25D9E1F9F76BD00BEA0C1 /* chat_group_informations@2x.png in Resources */, + C6710FD62722B20000ED888F /* voip_conference_play_big.png in Resources */, 633FEDCA1D3CD5590014B822 /* call_outgoing.png in Resources */, 633FEDF81D3CD5590014B822 /* camera_disabled.png in Resources */, + C6710FBD2722B20000ED888F /* voip_call_header_paused.png in Resources */, 8CB2B8FB1F86229E0015CEE2 /* next_disabled@2x.png in Resources */, 633FEEE91D3CD55A0014B822 /* numpad_9~ipad.png in Resources */, 633FEE331D3CD5590014B822 /* color_M.png in Resources */, @@ -3853,7 +4263,9 @@ 633FEF411D3CD55A0014B822 /* select_all_default@2x.png in Resources */, 633FEEFD1D3CD55A0014B822 /* options_add_call_default@2x.png in Resources */, 633FEEA81D3CD55A0014B822 /* numpad_1_over~ipad@2x.png in Resources */, + C6710FA12722B20000ED888F /* voip_copy.png in Resources */, D38187AD15FE340100C3EDCA /* ChatConversationView.xib in Resources */, + C6710FB72722B20000ED888F /* voip_numpad_6.png in Resources */, 633FEE7C1D3CD5590014B822 /* history_missed_disabled.png in Resources */, CF1DE92E210A0F5D00A0A97E /* UILinphoneAudioPlayer.xib in Resources */, 633FEDF11D3CD5590014B822 /* call_transfer_disabled@2x.png in Resources */, @@ -3877,6 +4289,7 @@ 633FEF471D3CD55A0014B822 /* speaker_disabled@2x.png in Resources */, 633FEEFE1D3CD55A0014B822 /* options_add_call_disabled.png in Resources */, 633FEE291D3CD5590014B822 /* checkbox_unchecked@2x.png in Resources */, + C6710FCD2722B20000ED888F /* voip_call_participants.png in Resources */, 63AADC001B6A0FF200AA16FD /* assistant_linphone_create.rc in Resources */, 633FEF1C1D3CD55A0014B822 /* presence_offline.png in Resources */, 633FEE901D3CD55A0014B822 /* list_details_over.png in Resources */, @@ -3886,12 +4299,14 @@ 63EC8D391D7438660066547B /* AssistantLinkView.xib in Resources */, 633FEE971D3CD55A0014B822 /* micro_disabled@2x.png in Resources */, D38187CD15FE346700C3EDCA /* HistoryDetailsView.xib in Resources */, + C6710FC52722B20000ED888F /* voip_camera_off.png in Resources */, 633FEEA21D3CD55A0014B822 /* numpad_0~ipad@2x.png in Resources */, 633FEE991D3CD55A0014B822 /* micro_selected@2x.png in Resources */, 633FEE621D3CD5590014B822 /* footer_chat_default.png in Resources */, 24BFAAA3209B0630004F47A7 /* contacts_sip_selected.png in Resources */, 633FEE0D1D3CD5590014B822 /* chat_attachment_disabled@2x.png in Resources */, 639E9CA61C0DB7F200019A75 /* UIChatCreateCell.xib in Resources */, + C6710FAE2722B20000ED888F /* voip_hangup.png in Resources */, 8CBD7BAC20B6B82F00E5DCC0 /* UIChatCreateCollectionViewCell.xib in Resources */, 633FEEA31D3CD55A0014B822 /* numpad_1_default.png in Resources */, 633FEEA51D3CD55A0014B822 /* numpad_1_over.png in Resources */, @@ -3908,6 +4323,7 @@ 633FEE691D3CD5590014B822 /* footer_contacts_disabled@2x.png in Resources */, 8CBD7BA020B6B7FD00E5DCC0 /* ChatConversationInfoView.xib in Resources */, 633FEF071D3CD55A0014B822 /* options_start_conference_default@2x.png in Resources */, + C6710FD32722B20000ED888F /* voip_conference_paused_big.png in Resources */, 633FEE151D3CD5590014B822 /* chat_send_default@2x.png in Resources */, 633FEDC31D3CD5590014B822 /* call_hangup_default@2x.png in Resources */, 633FEF061D3CD55A0014B822 /* options_start_conference_default.png in Resources */, @@ -3929,10 +4345,10 @@ C66B040E26F095D1009B5EDC /* cancel_forward.png in Resources */, CF7602F6210898CC00749F76 /* rec_on_default@2x.png in Resources */, 633FEF081D3CD55A0014B822 /* options_start_conference_disabled.png in Resources */, - 63F1DF511BCE986A00EDED90 /* UICallConferenceCell.xib in Resources */, 633FEE301D3CD5590014B822 /* color_H.png in Resources */, 633FEE7D1D3CD5590014B822 /* history_missed_disabled@2x.png in Resources */, 633FEE941D3CD55A0014B822 /* micro_default.png in Resources */, + C6710FB32722B20000ED888F /* voip_pause.png in Resources */, 633FEE611D3CD5590014B822 /* edit_list_disabled@2x.png in Resources */, 633FEE761D3CD5590014B822 /* history_all_selected.png in Resources */, 8C300D9B1E40E0CC00728EF3 /* lime_ko@2x.png in Resources */, @@ -3971,6 +4387,7 @@ 633FEF3B1D3CD55A0014B822 /* security_ko@2x.png in Resources */, 633FEE4A1D3CD5590014B822 /* delete_disabled.png in Resources */, 614D09CE21E74D5400C43EDF /* GoogleService-Info.plist in Resources */, + C6710F632722AECB00ED888F /* Roboto-Regular.ttf in Resources */, C6B4444526AAD0980076C517 /* file_audio_default.png in Resources */, C6A1BB3526E8815400540D50 /* menu_info.png in Resources */, C61B1BF72667EC6B001A4E4A /* ephemeral_messages_color_A.png in Resources */, @@ -3984,8 +4401,9 @@ 633FEE011D3CD5590014B822 /* camera_switch_over@2x.png in Resources */, 633FEEA01D3CD55A0014B822 /* numpad_0_over~ipad@2x.png in Resources */, 633FEF3E1D3CD55A0014B822 /* security_pending.png in Resources */, - D381881915FE3FCA00C3EDCA /* CallView.xib in Resources */, + C6710FBC2722B20000ED888F /* voip_change_camera.png in Resources */, 633FEE7E1D3CD5590014B822 /* history_missed_selected.png in Resources */, + C6277DA9274BF1CE00406FB9 /* voip_radio_off.png in Resources */, 633FEE261D3CD5590014B822 /* checkbox_checked.png in Resources */, 633FEF531D3CD55A0014B822 /* voicemail@2x.png in Resources */, 633FEF2C1D3CD55A0014B822 /* route_earpiece_selected.png in Resources */, @@ -3996,6 +4414,7 @@ 633FEECB1D3CD55A0014B822 /* numpad_6_default.png in Resources */, 633FEDC71D3CD5590014B822 /* call_incoming@2x.png in Resources */, 633FEDB81D3CD5590014B822 /* call_alt_start_disabled.png in Resources */, + C6710FCC2722B20000ED888F /* voip_call_list_menu.png in Resources */, 615A281D217F6FA80060F920 /* security_2_indicator@2x.png in Resources */, 633FEF3C1D3CD55A0014B822 /* security_ok.png in Resources */, 633FEEAF1D3CD55A0014B822 /* numpad_2_over~ipad.png in Resources */, @@ -4022,6 +4441,7 @@ 633FEDF31D3CD5590014B822 /* call_video_start_default@2x.png in Resources */, 24BFAAA2209B0630004F47A7 /* linphone_user~ipad@2x.png in Resources */, 633FEDBA1D3CD5590014B822 /* call_audio_start_default.png in Resources */, + C6710FA52722B20000ED888F /* voip_mandatory.png in Resources */, 61586B8F217A174F0038AC45 /* menu_options@2x.png in Resources */, 633FEE131D3CD5590014B822 /* chat_message_not_delivered@2x.png in Resources */, 615A282A21805B4C0060F920 /* security_toogle_icon_grey@2x.png in Resources */, @@ -4032,6 +4452,8 @@ 633FEDCE1D3CD5590014B822 /* call_quality_indicator_1.png in Resources */, 8CB2B8F91F86229E0015CEE2 /* chat_secure.png in Resources */, 633FEF4E1D3CD55A0014B822 /* valid_default.png in Resources */, + C6710FA22722B20000ED888F /* voip_merge_calls.png in Resources */, + C6710FAC2722B20000ED888F /* voip_call_chat.png in Resources */, 570742581D5A0691004B9C84 /* ShopView.xib in Resources */, 633FEE361D3CD5590014B822 /* conference_exit_over.png in Resources */, 633FEDAA1D3CD5590014B822 /* backspace_disabled.png in Resources */, @@ -4047,14 +4469,18 @@ 633FEDDD1D3CD5590014B822 /* call_start_body_disabled~ipad@2x.png in Resources */, 633FEEBD1D3CD55A0014B822 /* numpad_4_over.png in Resources */, 8CA70AD41F9E285C00A3D2EB /* chat_group_add@2x.png in Resources */, + C6710FAB2722B20000ED888F /* voip_edit.png in Resources */, C66B040A26EFDA55009B5EDC /* reply_cancel.png in Resources */, 633FEEF11D3CD55A0014B822 /* numpad_hash~ipad.png in Resources */, 633FEE781D3CD5590014B822 /* history_chat_indicator.png in Resources */, 633FEF431D3CD55A0014B822 /* select_all_disabled@2x.png in Resources */, + C6710F622722AECB00ED888F /* Roboto-Bold.ttf in Resources */, 633FEE771D3CD5590014B822 /* history_all_selected@2x.png in Resources */, + C6710F9F2722B20000ED888F /* voip_numpad_1.png in Resources */, 633FEF031D3CD55A0014B822 /* options_disabled@2x.png in Resources */, 633FEDA21D3CD5590014B822 /* avatar~ipad.png in Resources */, 24BFAAA1209B0630004F47A7 /* linphone_user@2x.png in Resources */, + C6710FD42722B20000ED888F /* voip_camera_on.png in Resources */, 633FEF211D3CD55A0014B822 /* presence_unregistered@2x.png in Resources */, 633FEEA71D3CD55A0014B822 /* numpad_1_over~ipad.png in Resources */, ); @@ -4090,16 +4516,20 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 20EEEF6D60A7B77C2EFF85DB /* [CP] Check Pods Manifest.lock */ = { + 068EB8CED53992A5D7A733DF /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( "${PODS_PODFILE_DIR_PATH}/Podfile.lock", "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); outputPaths = ( "$(DERIVED_FILE_DIR)/Pods-linphone-checkManifestLockResult.txt", ); @@ -4108,60 +4538,48 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 4D22DCAAC0231865D78AC1E6 /* [CP] Embed Pods Frameworks */ = { + 1E9EBAB02A694F2487E6099B /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-linphone/Pods-linphone-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/SVProgressHUD/SVProgressHUD.framework", - "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/bctoolbox-ios.framework", - "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/bctoolbox-tester.framework", - "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/bctoolbox.framework", - "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/belcard.framework", - "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/belle-sip.framework", - "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/belr.framework", - "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/lime.framework", - "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/limetester.framework", - "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/linphone.framework", - "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/linphonetester.framework", - "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/mediastreamer2.framework", - "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/msamr.framework", - "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/mscodec2.framework", - "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/msopenh264.framework", - "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/mssilk.framework", - "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/mswebrtc.framework", - "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/msx264.framework", - "${PODS_ROOT}/linphone-sdk/linphone-sdk/apple-darwin/Frameworks/ortp.framework", - "${BUILT_PRODUCTS_DIR}/linphone-sdk/linphonesw.framework", + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SVProgressHUD.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/bctoolbox-ios.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/bctoolbox-tester.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/bctoolbox.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/belcard.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/belle-sip.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/belr.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/lime.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/limetester.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/linphone.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/linphonetester.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/mediastreamer2.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/msamr.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/mscodec2.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/msopenh264.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/mssilk.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/mswebrtc.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/msx264.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ortp.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/linphonesw.framework", + "$(DERIVED_FILE_DIR)/Pods-msgNotificationService-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-linphone/Pods-linphone-frameworks.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 2E68C0F1B7E1F6C3000F700C /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-msgNotificationContent-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; 6112A019243B2C8400DBD5F5 /* ShellScript */ = { @@ -4236,48 +4654,58 @@ shellPath = /bin/sh; shellScript = "$SRCROOT/Tools/git_version.sh\n"; }; - B0A561F0290F0342EACE9005 /* [CP] Check Pods Manifest.lock */ = { + D7A9CDC06B7BB5D324EA10C9 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-linphone/Pods-linphone-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/SVProgressHUD/SVProgressHUD.framework", + "${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework", + "${PODS_ROOT}/../../../../../../../Volumes/dada/bc/linphone-sdk/ios/linphone-sdk/apple-darwin/Frameworks/bctoolbox-ios.framework", + "${PODS_ROOT}/../../../../../../../Volumes/dada/bc/linphone-sdk/ios/linphone-sdk/apple-darwin/Frameworks/bctoolbox.framework", + "${PODS_ROOT}/../../../../../../../Volumes/dada/bc/linphone-sdk/ios/linphone-sdk/apple-darwin/Frameworks/belcard.framework", + "${PODS_ROOT}/../../../../../../../Volumes/dada/bc/linphone-sdk/ios/linphone-sdk/apple-darwin/Frameworks/belle-sip.framework", + "${PODS_ROOT}/../../../../../../../Volumes/dada/bc/linphone-sdk/ios/linphone-sdk/apple-darwin/Frameworks/belr.framework", + "${PODS_ROOT}/../../../../../../../Volumes/dada/bc/linphone-sdk/ios/linphone-sdk/apple-darwin/Frameworks/lime.framework", + "${PODS_ROOT}/../../../../../../../Volumes/dada/bc/linphone-sdk/ios/linphone-sdk/apple-darwin/Frameworks/linphone.framework", + "${PODS_ROOT}/../../../../../../../Volumes/dada/bc/linphone-sdk/ios/linphone-sdk/apple-darwin/Frameworks/linphonetester.framework", + "${PODS_ROOT}/../../../../../../../Volumes/dada/bc/linphone-sdk/ios/linphone-sdk/apple-darwin/Frameworks/mediastreamer2.framework", + "${PODS_ROOT}/../../../../../../../Volumes/dada/bc/linphone-sdk/ios/linphone-sdk/apple-darwin/Frameworks/msamr.framework", + "${PODS_ROOT}/../../../../../../../Volumes/dada/bc/linphone-sdk/ios/linphone-sdk/apple-darwin/Frameworks/mscodec2.framework", + "${PODS_ROOT}/../../../../../../../Volumes/dada/bc/linphone-sdk/ios/linphone-sdk/apple-darwin/Frameworks/msopenh264.framework", + "${PODS_ROOT}/../../../../../../../Volumes/dada/bc/linphone-sdk/ios/linphone-sdk/apple-darwin/Frameworks/mssilk.framework", + "${PODS_ROOT}/../../../../../../../Volumes/dada/bc/linphone-sdk/ios/linphone-sdk/apple-darwin/Frameworks/mswebrtc.framework", + "${PODS_ROOT}/../../../../../../../Volumes/dada/bc/linphone-sdk/ios/linphone-sdk/apple-darwin/Frameworks/msx264.framework", + "${PODS_ROOT}/../../../../../../../Volumes/dada/bc/linphone-sdk/ios/linphone-sdk/apple-darwin/Frameworks/ortp.framework", + "${BUILT_PRODUCTS_DIR}/linphone-sdk/linphonesw.framework", ); + name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-msgNotificationContent-checkManifestLockResult.txt", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SVProgressHUD.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SnapKit.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/bctoolbox-ios.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/bctoolbox.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/belcard.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/belle-sip.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/belr.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/lime.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/linphone.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/linphonetester.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/mediastreamer2.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/msamr.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/mscodec2.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/msopenh264.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/mssilk.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/mswebrtc.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/msx264.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ortp.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/linphonesw.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - CD5CF0B3FE8B6052CD8219CE /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-msgNotificationService-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-linphone/Pods-linphone-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -4289,15 +4717,27 @@ files = ( 63B81A0F1B57DA33009604A6 /* TPKeyboardAvoidingTableView.m in Sources */, CF1DE92D210A0F5D00A0A97E /* UILinphoneAudioPlayer.m in Sources */, + C6D1EC4A274D212B0091881C /* UICamSwitch.m in Sources */, 1D60589B0D05DD56006BFB54 /* main.m in Sources */, 6135761C240E81BB005304D4 /* UIInterfaceStyleButton.m in Sources */, 8CD99A3C2090B9FA008A7CDA /* ChatConversationImdnView.m in Sources */, 1D3623260D0F684500981E51 /* LinphoneAppDelegate.m in Sources */, + C6F2D503273BAC030071BA52 /* ButtonWithStateBackgrounds.swift in Sources */, + C6C98CD027439A7F00059B55 /* VoipCallCell.swift in Sources */, + C60B66682721AFFA0026AC7D /* CallStatisticsData.swift in Sources */, C6B4444826AADA530076C517 /* SwiftUtil.swift in Sources */, 614C087A23D1A37400217F80 /* CallManager.swift in Sources */, CF15F21E20E4F9A3008B1DE6 /* UIImageViewDeletable.m in Sources */, + C6F2D4FD273A85A90071BA52 /* PausedCallOrConferenceView.swift in Sources */, + C6F2D4FF273AF01A0071BA52 /* ControlsView.swift in Sources */, + C6710F5527229D5900ED888F /* LightDarkColor.swift in Sources */, + C6F2D4F9273A4CD70071BA52 /* SharedLayoutConstants.swift in Sources */, + C6D09F3F274273FB003C2173 /* CallStatsView.swift in Sources */, 22F2508E107141E100AC9B3F /* DialerView.m in Sources */, 633756451B67D2B200E21BAD /* SideMenuView.m in Sources */, + C6F2D4F1273935860071BA52 /* CallExtensions.swift in Sources */, + C6710F5C2722AAED00ED888F /* UIDeviceExtensions.swift in Sources */, + C65A5D3027216B86005BA038 /* ActiveCallOrConferenceView.swift in Sources */, 8CD99A422090CE6F008A7CDA /* UIChatConversationImdnTableViewCell.m in Sources */, 22E0A822111C44E100B04932 /* AboutView.m in Sources */, 633671611BCBAAD200BFCBDE /* ChatConversationCreateView.m in Sources */, @@ -4305,37 +4745,38 @@ 2248E90E12F7E4CF00220D9C /* UIDigitButton.m in Sources */, 6134812F2407B35200695B41 /* AppManager.swift in Sources */, 633756391B67BAF400E21BAD /* SideMenuTableView.m in Sources */, + C6B04D63274B95D500F70559 /* VoipParticipantCell.swift in Sources */, 2214EB7A12F846B1002A5394 /* UICallButton.m in Sources */, - 6352A5751BE0D4B800594C1C /* CallSideMenuView.m in Sources */, - 2214EB8912F84EBB002A5394 /* UIHangUpButton.m in Sources */, + C683B213272276CF00D4E15C /* UIColorExtensions.swift in Sources */, + C6710F512722932600ED888F /* UIImageViewExtensions.swift in Sources */, 630CF5571AF7CE1500539F7A /* UITextField+DoneButton.m in Sources */, 2214EBF312F86360002A5394 /* UIMutedMicroButton.m in Sources */, + C6D09F4127428626003C2173 /* IceState.swift in Sources */, + C658614C273E5B5E00A0DBFC /* VoipExtraButton.swift in Sources */, 8C9C5E111F83BD97006987FA /* UIChatCreateCollectionViewCell.m in Sources */, - 22968A5F12F875C600588287 /* UISpeakerButton.m in Sources */, 63701DDF1BA32039006A9AE3 /* UIConfirmationDialog.m in Sources */, 6134812D2406CECC00695B41 /* ConfigManager.swift in Sources */, 22C755601317E59C007BC101 /* UIBluetoothButton.m in Sources */, - 22AA8B0113D83F6300B30535 /* UICamSwitch.m in Sources */, 63B8D6A21BCBF43100C12B09 /* UIChatCreateCell.m in Sources */, 636BC9971B5F921B00C754CE /* UIIconButton.m in Sources */, 63423C0A1C4501D000D9A050 /* Contact.m in Sources */, - 340751E7150F38FD00B89C47 /* UIVideoButton.m in Sources */, + C6710FE12722F0E400ED888F /* Avatar.swift in Sources */, 34216F401547EBCD00EA9777 /* VideoZoomHandler.m in Sources */, 614C087823D1A35F00217F80 /* ProviderDelegate.swift in Sources */, - D3F83EEC1582021700336684 /* CallView.m in Sources */, CF7602D7210867E800749F76 /* RecordingsListView.m in Sources */, - 63F1DF4B1BCE983200EDED90 /* CallConferenceTableView.m in Sources */, D3F83F8E15822ABE00336684 /* PhoneMainView.m in Sources */, C6A1BB3E26E882D000540D50 /* UIChatReplyBubbleView.m in Sources */, 6377AC801BDE4069007F7625 /* UIBackToCallButton.m in Sources */, + C6D09F43274288D4003C2173 /* PayloadType.swift in Sources */, 6308F9C51BF0DD6600D1234B /* XMLRPCHelper.m in Sources */, + C6F2D4F72739861F0071BA52 /* RemotelyRecording.swift in Sources */, D3ED3E871586291E006C0DE4 /* TabBarView.m in Sources */, 617C242A263022690042FB4A /* UIChatContentView.m in Sources */, D3ED3EA71587334E006C0DE4 /* HistoryListTableView.m in Sources */, 61AEBEBD2191990A00F35E7F /* DevicesListView.m in Sources */, D3ED3EB81587392C006C0DE4 /* HistoryListView.m in Sources */, + C6710FE72723234400ED888F /* OutgoingCallView.swift in Sources */, 24A345A61D95798A00881A5C /* UIShopTableCell.m in Sources */, - 8C1B67061E671826001EA2FE /* AudioHelper.m in Sources */, D35497FE15875372000081D8 /* ContactsListView.m in Sources */, 635173F91BA082A40095EB0A /* UIChatBubblePhotoCell.m in Sources */, D3549816158761D0000081D8 /* ContactsListTableView.m in Sources */, @@ -4345,71 +4786,99 @@ 6341807C1BBC103100F71761 /* ChatConversationCreateTableView.m in Sources */, 63BE7A781D75BDF6000990EF /* ShopTableView.m in Sources */, D326483815887D5200930C67 /* OrderedDictionary.m in Sources */, + C6710F4F2722903200ED888F /* RotatingSpinner.swift in Sources */, D32648441588F6FC00930C67 /* UIToggleButton.m in Sources */, - D36FB2D51589EF7C0036F6F2 /* UIPauseButton.m in Sources */, + C6C98CDF2745590500059B55 /* ConferenceExtensions.swift in Sources */, + C67C97B4274FC5EF0074A0D8 /* AudioRoutesView.swift in Sources */, + C6824FBA27219D890043D4FC /* IncomingCallView.swift in Sources */, + C6C98CD527453ED900059B55 /* ConferenceViewModel.swift in Sources */, D31C9C98158A1CDF00756B45 /* UIHistoryCell.m in Sources */, D35E7597159460580066B1C1 /* ChatsListView.m in Sources */, D35E759F159460B70066B1C1 /* SettingsView.m in Sources */, 63B81A101B57DA33009604A6 /* UIScrollView+TPKeyboardAvoidingAdditions.m in Sources */, F03CA84318C72F1A0008889D /* UITextViewNoDefine.m in Sources */, 63B81A0D1B57DA33009604A6 /* TPKeyboardAvoidingCollectionView.m in Sources */, + C6710FD92722BD0100ED888F /* UICallTimer.swift in Sources */, 570742611D5A09B8004B9C84 /* ShopView.m in Sources */, - 63F1DF4F1BCE985F00EDED90 /* UICallConferenceCell.m in Sources */, + C65A5D3B27216CC0005BA038 /* MutableLiveData.swift in Sources */, + C6C98CCE27438A3F00059B55 /* DismissableView.swift in Sources */, D37DC6C11594AE1800B2A5EB /* LinphoneCoreSettingsStore.m in Sources */, 63CD4B4F1A5AAC8C00B84282 /* DTAlertView.m in Sources */, D3EA53FD159850E80037DC6B /* LinphoneManager.m in Sources */, + C60D265C272AA0BD006238BB /* UIImageExtensions.swift in Sources */, + C6D09F3D273EE467003C2173 /* BouncingCounter.swift in Sources */, C6DA657C261C950C0020CB43 /* VFSUtil.swift in Sources */, 63B81A0E1B57DA33009604A6 /* TPKeyboardAvoidingScrollView.m in Sources */, 633888451BFB2C49001D5E7B /* HPGrowingTextView.m in Sources */, 63F1DF441BCE618E00EDED90 /* UIAddressTextField.m in Sources */, D3EA540D1598528B0037DC6B /* ChatsListTableView.m in Sources */, D3EA5411159853750037DC6B /* UIChatCell.m in Sources */, - D3F26BF115986B73005F9CAB /* CallIncomingView.m in Sources */, D31B4B21159876C0002E6C72 /* UICompositeView.m in Sources */, - D31AAF5E159B3919002C6B02 /* CallPausedTableView.m in Sources */, 8C9C5E0D1F83B2EF006987FA /* ChatConversationCreateCollectionViewController.m in Sources */, 631098491D4660580041F2B3 /* CountryListView.m in Sources */, D32B9DFC15A2F131000B6DEC /* FastAddressBook.m in Sources */, D350F20E15A43BB100149E54 /* AssistantView.m in Sources */, D3F795D615A582810077328B /* ChatConversationView.m in Sources */, + C60D265627299C94006238BB /* ControlsViewModel.swift in Sources */, D32B6E2915A5BC440033019F /* ChatConversationTableView.m in Sources */, + C6D09F4B27438707003C2173 /* CallsListView.swift in Sources */, D3A8BB7015A6C7D500F96BE5 /* UIChatBubbleTextCell.m in Sources */, + C683B20E2722702300D4E15C /* VoipTheme.swift in Sources */, + C6278497273C21E1002FAA29 /* LocalVideoView.swift in Sources */, 63D11C531C3D501200E8FCEE /* Log.m in Sources */, D3128FE115AABC7E00A2147A /* ContactDetailsView.m in Sources */, 6135761F240E81D0005304D4 /* UIInterfaceStyleToggleButton.m in Sources */, + C6B04D67274BD61300F70559 /* VoipConferenceActiveSpeakerView.swift in Sources */, D37C639B15AADEF6009D0BAC /* ContactDetailsTableView.m in Sources */, 63E59A3F1ADE70D900646FB3 /* InAppProductsManager.m in Sources */, + C67C97B8274FD76B0074A0D8 /* AudioRouteUtils.swift in Sources */, + C6C98CE1274568F800059B55 /* VoipConferenceGridView.swift in Sources */, D3C6526715AC1A8F0092A874 /* UIContactDetailsCell.m in Sources */, 631348301B6F7B6600C6BDCB /* UIRoundBorderedButton.m in Sources */, C90FAA7915AF54E6002091CB /* HistoryDetailsView.m in Sources */, 63FB30351A680E73008CA393 /* UIRoundedImageView.m in Sources */, 635775251B6673EC00C8B704 /* HistoryDetailsTableView.m in Sources */, + C6C98CD727453F9600059B55 /* ConferenceParticipantData.swift in Sources */, 63C441C31BBC23ED0053DC5E /* UIAssistantTextField.m in Sources */, - 6346100F1B61409800548952 /* CallOutgoingView.m in Sources */, + C6B04D69274BD6A100F70559 /* VoipActiveSpeakerParticipantCell.swift in Sources */, + C65A5D45272196AE005BA038 /* OptionalExtensions.swift in Sources */, D35860D615B549B500513429 /* Utils.m in Sources */, D3F7998115BD32370018C273 /* TPMultiLayoutViewController.m in Sources */, D3807FBF15C28940005BE9BC /* DCRoundSwitch.m in Sources */, D3807FC115C28940005BE9BC /* DCRoundSwitchKnobLayer.m in Sources */, + C6B04D61274B954500F70559 /* ParticipantsListView.swift in Sources */, 61CCC3DF21933B580060EDEA /* UIDeviceCell.m in Sources */, 6306440E1BECB08500134C72 /* FirstLoginView.m in Sources */, D3807FC315C28940005BE9BC /* DCRoundSwitchOutlineLayer.m in Sources */, CF7602E221086EB200749F76 /* RecordingsListTableView.m in Sources */, D3807FC515C28940005BE9BC /* DCRoundSwitchToggleLayer.m in Sources */, + C6586149273E595700A0DBFC /* VoipExtraButtonsView.swift in Sources */, 633E41821D74259000320475 /* AssistantLinkView.m in Sources */, + C6710F5727229DEE00ED888F /* TextStyle.swift in Sources */, D3807FE815C2894A005BE9BC /* IASKAppSettingsViewController.m in Sources */, D3807FEC15C2894A005BE9BC /* IASKSpecifierValuesViewController.m in Sources */, 8CA70AE41F9E39E400A3D2EB /* UIChatConversationInfoTableViewCell.m in Sources */, D3807FEE15C2894A005BE9BC /* IASKSettingsReader.m in Sources */, + C6C65E8B2727274A00E48FC6 /* NumpadView.swift in Sources */, + C6F2D501273B0EFC0071BA52 /* VoipDialog.swift in Sources */, + C6710F53272297C400ED888F /* VoipTexts.swift in Sources */, D3807FF015C2894A005BE9BC /* IASKSettingsStore.m in Sources */, + C6710FEB2726874D00ED888F /* ButtonTheme.swift in Sources */, 8CA70AD11F9E0AE100A3D2EB /* ChatConversationInfoView.m in Sources */, D3807FF215C2894A005BE9BC /* IASKSettingsStoreFile.m in Sources */, D3807FF415C2894A005BE9BC /* IASKSettingsStoreUserDefaults.m in Sources */, + C6710FDC2722C3BB00ED888F /* UIVIewExtensions.swift in Sources */, + C6F2D4F32739475C0071BA52 /* ActiveCallView.swift in Sources */, 639E9C801C0DB13D00019A75 /* UICheckBoxTableView.m in Sources */, CF7602E72108759A00749F76 /* UIRecordingCell.m in Sources */, + C6C98CD22743FD0B00059B55 /* VoipCallContextMenu.swift in Sources */, D3807FF615C2894A005BE9BC /* IASKSpecifier.m in Sources */, + C6710FE92723DD7D00ED888F /* CallControlButton.swift in Sources */, D3807FF815C2894A005BE9BC /* IASKPSSliderSpecifierViewCell.m in Sources */, D3807FFA15C2894A005BE9BC /* IASKPSTextFieldSpecifierViewCell.m in Sources */, D3807FFC15C2894A005BE9BC /* IASKPSTitleValueSpecifierViewCell.m in Sources */, + C6F2D4EF27392D970071BA52 /* CallsViewModel.swift in Sources */, + C6C65E89272723DC00E48FC6 /* UIVIewControllerExtensions.swift in Sources */, D3807FFE15C2894A005BE9BC /* IASKSlider.m in Sources */, D380800015C2894A005BE9BC /* IASKSwitch.m in Sources */, D380800215C2894A005BE9BC /* IASKTextField.m in Sources */, @@ -4417,14 +4886,23 @@ C64A854E2667B67200252AD2 /* EphemeralSettingsView.m in Sources */, 8C92ABF31FA773E50006FB5D /* UIChatNotifiedEventCell.m in Sources */, 633FEF581D3CD5E00014B822 /* UIAvatarPresence.m in Sources */, + C6C98CDB274541E400059B55 /* ConferenceParticipantDeviceData.swift in Sources */, 637157A11B283FE200C91677 /* FileTransferDelegate.m in Sources */, + C6710FDE2722D44B00ED888F /* IncomingOuntgoingCommonView.swift in Sources */, D378AB2A15DCDB4A0098505D /* ImagePickerView.m in Sources */, + C6710F592722A9B800ED888F /* StyledLabel.swift in Sources */, 22405F001601C19200B92522 /* ImageView.m in Sources */, - 63BC49E21BA2CDFC004EC273 /* UICallPausedCell.m in Sources */, + C60D265827299F70006238BB /* CoreExtensions.swift in Sources */, + C6710FE527230B5800ED888F /* AddressExtensions.swift in Sources */, D37EE162160377D7003608A6 /* DTActionSheet.m in Sources */, + C67C97B1274FB4C10074A0D8 /* VoipConferenceDisplayModeSelectionView.swift in Sources */, + C6D52B45274648E500904660 /* VoipGridParticipantCell.swift in Sources */, D306459E1611EC2A00BB571E /* UILoadingImageView.m in Sources */, 6381DA7D1C1AD5EA00DF3BBD /* UIBouncingView.m in Sources */, + C6C98CDD274547C500059B55 /* ParticipantExtensions.swift in Sources */, D37E3ECD1619C27A0087659A /* CAAnimation+Blocks.m in Sources */, + C6F2D505273BB3BB0071BA52 /* UIApplication+Extension.swift in Sources */, + C65A5D3F27216E3A005BA038 /* CallData.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4525,16 +5003,6 @@ name = "SideMenuView~ipad.xib"; sourceTree = ""; }; - 634610101B6140A500548952 /* CallOutgoingView.xib */ = { - isa = PBXVariantGroup; - children = ( - 634610111B6140A500548952 /* Base */, - 8CBD7BB420B6B86B00E5DCC0 /* fr */, - 6187B1BA24B3271700D580FB /* hu */, - ); - name = CallOutgoingView.xib; - sourceTree = ""; - }; 636316D31A1DEBCB0009B839 /* AboutView.xib */ = { isa = PBXVariantGroup; children = ( @@ -4567,16 +5035,6 @@ name = "DialerView~ipad.xib"; sourceTree = ""; }; - 638F1A861C2167C2004B8E02 /* CallView~ipad.xib */ = { - isa = PBXVariantGroup; - children = ( - 638F1A871C2167C2004B8E02 /* Base */, - 8CBD7BB620B6B86D00E5DCC0 /* fr */, - 6187B1BC24B3271800D580FB /* hu */, - ); - name = "CallView~ipad.xib"; - sourceTree = ""; - }; 638F1A8F1C21993D004B8E02 /* UICompositeView~ipad.xib */ = { isa = PBXVariantGroup; children = ( @@ -4642,16 +5100,6 @@ name = FirstLoginView.xib; sourceTree = ""; }; - 639E9C9F1C0DB7DF00019A75 /* UICallPausedCell.xib */ = { - isa = PBXVariantGroup; - children = ( - 639E9C9E1C0DB7DF00019A75 /* Base */, - 8CBD7BC620B6B87900E5DCC0 /* fr */, - 6187B1CC24B3271E00D580FB /* hu */, - ); - name = UICallPausedCell.xib; - sourceTree = ""; - }; 639E9CA21C0DB7E500019A75 /* UIChatBubblePhotoCell.xib */ = { isa = PBXVariantGroup; children = ( @@ -4756,16 +5204,6 @@ name = AssistantLinkView.xib; sourceTree = ""; }; - 63F1DF531BCE986A00EDED90 /* UICallConferenceCell.xib */ = { - isa = PBXVariantGroup; - children = ( - 63F1DF521BCE986A00EDED90 /* Base */, - 8CBD7BC520B6B87800E5DCC0 /* fr */, - 6187B1CB24B3271E00D580FB /* hu */, - ); - name = UICallConferenceCell.xib; - sourceTree = ""; - }; 8CBD7BA220B6B7FD00E5DCC0 /* ChatConversationInfoView.xib */ = { isa = PBXVariantGroup; children = ( @@ -4922,18 +5360,6 @@ name = HistoryListView.xib; sourceTree = ""; }; - D38187DC15FE347700C3EDCA /* CallIncomingView.xib */ = { - isa = PBXVariantGroup; - children = ( - F09548241883F15400E8A69B /* Base */, - F09548481883F55800E8A69B /* ru */, - F0AF06FD1A24BA770086C9C1 /* ar */, - 8CBD7BB320B6B86B00E5DCC0 /* fr */, - 6187B1B924B3271700D580FB /* hu */, - ); - name = CallIncomingView.xib; - sourceTree = ""; - }; D38187E015FE348A00C3EDCA /* AssistantView.xib */ = { isa = PBXVariantGroup; children = ( @@ -4959,18 +5385,6 @@ path = LinphoneUI; sourceTree = ""; }; - D381881C15FE3FCA00C3EDCA /* CallView.xib */ = { - isa = PBXVariantGroup; - children = ( - F09548231883F15400E8A69B /* Base */, - F09548461883F54200E8A69B /* ru */, - F0AF06FC1A24BA770086C9C1 /* ar */, - 8CBD7BB520B6B86C00E5DCC0 /* fr */, - 6187B1BB24B3271700D580FB /* hu */, - ); - name = CallView.xib; - sourceTree = ""; - }; D3D5126A160B3A8E00946DF8 /* AssistantViewScreens.xib */ = { isa = PBXVariantGroup; children = ( @@ -5007,7 +5421,7 @@ /* Begin XCBuildConfiguration section */ 1D6058940D05DD3E006BFB54 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 799BA1104845EB01ACE764D8 /* Pods-linphone.debug.xcconfig */; + baseConfigurationReference = 53432234870660E9876CBCA8 /* Pods-linphone.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; ALWAYS_SEARCH_USER_PATHS = NO; @@ -5050,7 +5464,10 @@ INFOPLIST_FILE = "linphone-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)"; + LIBRARY_SEARCH_PATHS = ( + "$(BUILT_PRODUCTS_DIR)", + "$(inherited)", + ); LINK_WITH_STANDARD_LIBRARIES = YES; MARKETING_VERSION = 4.4.0; OTHER_CFLAGS = ( @@ -5059,7 +5476,7 @@ "-DENABLE_QRCODE=TRUE", "-DENABLE_SMS_INVITE=TRUE", "$(inherited)", - "-DLINPHONE_SDK_VERSION=\\\"5.1.0-alpha.75+d4a0bd2\\\"", + "-DLINPHONE_SDK_VERSION=\\\"5.2.0-alpha.20+3cffbc5\\\"", ); OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone; @@ -5072,6 +5489,7 @@ SWIFT_OBJC_BRIDGING_HEADER = "Classes/linphone-Bridging-Header.h"; SWIFT_OBJC_INTERFACE_HEADER_NAME = "linphoneapp-Swift.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_PRECOMPILE_BRIDGING_HEADER = YES; SWIFT_VERSION = 5.0; WARNING_CFLAGS = ( "-Werror=objc-method-access", @@ -5133,7 +5551,7 @@ }; 228B19A71302902F00F154D3 /* DistributionAdhoc */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FE7D89A821FDC1BCA9BB9F8F /* Pods-linphone.distributionadhoc.xcconfig */; + baseConfigurationReference = 22D3BF4C45858F6B07F4D2A4 /* Pods-linphone.distributionadhoc.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; ALWAYS_SEARCH_USER_PATHS = NO; @@ -5173,7 +5591,10 @@ INFOPLIST_FILE = "linphone-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)"; + LIBRARY_SEARCH_PATHS = ( + "$(BUILT_PRODUCTS_DIR)", + "$(inherited)", + ); LINK_WITH_STANDARD_LIBRARIES = YES; MARKETING_VERSION = 4.4.0; OTHER_CFLAGS = ( @@ -5182,7 +5603,7 @@ "-DENABLE_QRCODE=TRUE", "-DENABLE_SMS_INVITE=TRUE", "$(inherited)", - "-DLINPHONE_SDK_VERSION=\\\"5.1.0-alpha.75+d4a0bd2\\\"", + "-DLINPHONE_SDK_VERSION=\\\"5.2.0-alpha.20+3cffbc5\\\"", ); OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone; @@ -5194,6 +5615,7 @@ STRIP_SWIFT_SYMBOLS = NO; SWIFT_OBJC_BRIDGING_HEADER = "Classes/linphone-Bridging-Header.h"; SWIFT_OBJC_INTERFACE_HEADER_NAME = "linphoneapp-Swift.h"; + SWIFT_PRECOMPILE_BRIDGING_HEADER = YES; SWIFT_VERSION = 5.0; WARNING_CFLAGS = ( "-Werror=objc-method-access", @@ -5255,7 +5677,7 @@ }; 22F3D55613CC3C9100A0DA02 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FEAFB5AD0E3AA409BBD1136E /* Pods-linphone.release.xcconfig */; + baseConfigurationReference = 0DF941C97B75E7BA39A90600 /* Pods-linphone.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; ALWAYS_SEARCH_USER_PATHS = NO; @@ -5295,7 +5717,10 @@ INFOPLIST_FILE = "linphone-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)"; + LIBRARY_SEARCH_PATHS = ( + "$(BUILT_PRODUCTS_DIR)", + "$(inherited)", + ); LINK_WITH_STANDARD_LIBRARIES = YES; MARKETING_VERSION = 4.4.0; OTHER_CFLAGS = ( @@ -5304,7 +5729,7 @@ "-DENABLE_QRCODE=TRUE", "-DENABLE_SMS_INVITE=TRUE", "$(inherited)", - "-DLINPHONE_SDK_VERSION=\\\"5.1.0-alpha.75+d4a0bd2\\\"", + "-DLINPHONE_SDK_VERSION=\\\"5.2.0-alpha.20+3cffbc5\\\"", ); OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone; @@ -5316,6 +5741,7 @@ STRIP_SWIFT_SYMBOLS = NO; SWIFT_OBJC_BRIDGING_HEADER = "Classes/linphone-Bridging-Header.h"; SWIFT_OBJC_INTERFACE_HEADER_NAME = "linphoneapp-Swift.h"; + SWIFT_PRECOMPILE_BRIDGING_HEADER = YES; SWIFT_VERSION = 5.0; WARNING_CFLAGS = ( "-Werror=objc-method-access", @@ -5376,7 +5802,7 @@ }; 22F51EE8107FA53D00F98953 /* Distribution */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 38DF35D11A7C0F45E990C83A /* Pods-linphone.distribution.xcconfig */; + baseConfigurationReference = 79B41078A602EFB886981917 /* Pods-linphone.distribution.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; ALWAYS_SEARCH_USER_PATHS = NO; @@ -5416,7 +5842,10 @@ INFOPLIST_FILE = "linphone-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)"; + LIBRARY_SEARCH_PATHS = ( + "$(BUILT_PRODUCTS_DIR)", + "$(inherited)", + ); LINK_WITH_STANDARD_LIBRARIES = YES; MARKETING_VERSION = 4.4.0; OTHER_CFLAGS = ( @@ -5425,7 +5854,7 @@ "-DENABLE_QRCODE=TRUE", "-DENABLE_SMS_INVITE=TRUE", "$(inherited)", - "-DLINPHONE_SDK_VERSION=\\\"5.1.0-alpha.75+d4a0bd2\\\"", + "-DLINPHONE_SDK_VERSION=\\\"5.2.0-alpha.20+3cffbc5\\\"", ); OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone; @@ -5437,6 +5866,7 @@ STRIP_SWIFT_SYMBOLS = NO; SWIFT_OBJC_BRIDGING_HEADER = "Classes/linphone-Bridging-Header.h"; SWIFT_OBJC_INTERFACE_HEADER_NAME = "linphoneapp-Swift.h"; + SWIFT_PRECOMPILE_BRIDGING_HEADER = YES; SWIFT_VERSION = 5.0; WARNING_CFLAGS = ( "-Werror=objc-method-access", @@ -5662,7 +6092,7 @@ }; EA5F25E2232BD3E300475F2E /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = ADCA571A7CF61077747BFE53 /* Pods-msgNotificationService.debug.xcconfig */; + baseConfigurationReference = C589627B9D9D2A4F9C816051 /* Pods-msgNotificationService.debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; APPLICATION_EXTENSION_API_ONLY = YES; @@ -5718,7 +6148,7 @@ }; EA5F25E3232BD3E300475F2E /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 904C1EC75CB9E03374AAA802 /* Pods-msgNotificationService.release.xcconfig */; + baseConfigurationReference = 063D57B2E4769739DC5DA5C0 /* Pods-msgNotificationService.release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; APPLICATION_EXTENSION_API_ONLY = YES; @@ -5770,7 +6200,7 @@ }; EA5F25E4232BD3E300475F2E /* Distribution */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F4BE1A2318FC69D799C34F0A /* Pods-msgNotificationService.distribution.xcconfig */; + baseConfigurationReference = B9F41097CE0124A05554DB9C /* Pods-msgNotificationService.distribution.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; APPLICATION_EXTENSION_API_ONLY = YES; @@ -5822,7 +6252,7 @@ }; EA5F25E5232BD3E300475F2E /* DistributionAdhoc */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 8FD0D10102F0A8922703B8A4 /* Pods-msgNotificationService.distributionadhoc.xcconfig */; + baseConfigurationReference = 3411568BE5527EB500F75EBB /* Pods-msgNotificationService.distributionadhoc.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; APPLICATION_EXTENSION_API_ONLY = YES; @@ -5874,7 +6304,7 @@ }; EA8CB835239F96CA00C330CC /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 8B4C43A28E90775F6FCA2CEE /* Pods-msgNotificationContent.debug.xcconfig */; + baseConfigurationReference = 1060E68152C51FCE5ACBF779 /* Pods-msgNotificationContent.debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -5929,7 +6359,7 @@ }; EA8CB836239F96CA00C330CC /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 34027665305514025971F85C /* Pods-msgNotificationContent.release.xcconfig */; + baseConfigurationReference = 02DBDD5A09F46796AEC2485B /* Pods-msgNotificationContent.release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -5980,7 +6410,7 @@ }; EA8CB837239F96CA00C330CC /* Distribution */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E40C9A7D22675584396C0A3D /* Pods-msgNotificationContent.distribution.xcconfig */; + baseConfigurationReference = 507103607396F28FF4427108 /* Pods-msgNotificationContent.distribution.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -6031,7 +6461,7 @@ }; EA8CB838239F96CA00C330CC /* DistributionAdhoc */ = { isa = XCBuildConfiguration; - baseConfigurationReference = BAD0A9494E833034EB559687 /* Pods-msgNotificationContent.distributionadhoc.xcconfig */; + baseConfigurationReference = 6150F32455334A0A7B3D46C8 /* Pods-msgNotificationContent.distributionadhoc.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; diff --git a/linphone_Prefix.pch b/linphone_Prefix.pch index 6f87a986f..389c6e7d3 100644 --- a/linphone_Prefix.pch +++ b/linphone_Prefix.pch @@ -12,7 +12,6 @@ #import "Contact.h" #import "UIToggleButton.h" -#import "UISpeakerButton.h" #import "UIBluetoothButton.h" #import "UIMutedMicroButton.h"