diff --git a/Classes/AudioHelper.m b/Classes/AudioHelper.m
new file mode 100644
index 000000000..7298e65b3
--- /dev/null
+++ b/Classes/AudioHelper.m
@@ -0,0 +1,52 @@
+/*
+ * 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
new file mode 100644
index 000000000..ae5bd9336
--- /dev/null
+++ b/Classes/Base.lproj/CallIncomingView.xib
@@ -0,0 +1,317 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Classes/Base.lproj/CallOutgoingView.xib b/Classes/Base.lproj/CallOutgoingView.xib
new file mode 100644
index 000000000..890553e00
--- /dev/null
+++ b/Classes/Base.lproj/CallOutgoingView.xib
@@ -0,0 +1,623 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Classes/Base.lproj/CallView.xib b/Classes/Base.lproj/CallView.xib
new file mode 100644
index 000000000..57a17a2c7
--- /dev/null
+++ b/Classes/Base.lproj/CallView.xib
@@ -0,0 +1,1857 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+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
new file mode 100644
index 000000000..684d42dc6
--- /dev/null
+++ b/Classes/Base.lproj/CallView~ipad.xib
@@ -0,0 +1,1397 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Classes/SwiftUtil/Extensions/IOS/OptionalExtensions.swift b/Classes/CallConferenceTableView.h
similarity index 79%
rename from Classes/SwiftUtil/Extensions/IOS/OptionalExtensions.swift
rename to Classes/CallConferenceTableView.h
index 58f10f627..41698c257 100644
--- a/Classes/SwiftUtil/Extensions/IOS/OptionalExtensions.swift
+++ b/Classes/CallConferenceTableView.h
@@ -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,16 +17,10 @@
* along with this program. If not, see .
*/
+#import
-extension Optional {
- var logable: Any {
- switch self {
- case .none:
- return "|⭕️"
- case let .some(value):
- return value
- }
- }
-
-
-}
+@interface CallConferenceTableView : UITableViewController
+
+- (void)update;
+
+@end
diff --git a/Classes/CallConferenceTableView.m b/Classes/CallConferenceTableView.m
new file mode 100644
index 000000000..1abf56af9
--- /dev/null
+++ b/Classes/CallConferenceTableView.m
@@ -0,0 +1,65 @@
+/*
+ * 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
new file mode 100644
index 000000000..8031a7b2e
--- /dev/null
+++ b/Classes/CallIncomingView.h
@@ -0,0 +1,54 @@
+/*
+ * 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
new file mode 100644
index 000000000..ccf22052b
--- /dev/null
+++ b/Classes/CallIncomingView.m
@@ -0,0 +1,150 @@
+/*
+ * 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 cc2e380e2..6ea0686fb 100644
--- a/Classes/CallManager.swift
+++ b/Classes/CallManager.swift
@@ -37,13 +37,15 @@ import AVFoundation
static var theCallManager: CallManager?
let providerDelegate: ProviderDelegate! // to support callkit
let callController: CXCallController! // to support callkit
- var core: Core?
+ var lc: Core?
+ @objc var speakerBeforePause : Bool = false
@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?
@@ -64,8 +66,8 @@ import AVFoundation
@objc func setCore(core: OpaquePointer) {
- self.core = Core.getSwiftObject(cObject: core)
- self.core?.addDelegate(delegate: self)
+ lc = Core.getSwiftObject(cObject: core)
+ lc?.addDelegate(delegate: self)
}
@objc static func getAppData(call: OpaquePointer) -> CallAppData? {
@@ -105,7 +107,7 @@ import AVFoundation
if (callId == nil) {
return nil
}
- let calls = core?.calls
+ let calls = lc?.calls
if let callTmp = calls?.first(where: { $0.callLog?.callId == callId }) {
return callTmp
}
@@ -113,8 +115,8 @@ import AVFoundation
}
@objc func stopLinphoneCore() {
- if (core?.callsNb == 0) {
- core?.stopAsync()
+ if (lc?.callsNb == 0) {
+ lc?.stopAsync()
}
}
@@ -138,6 +140,60 @@ 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
@@ -183,7 +239,7 @@ import AVFoundation
func acceptCall(call: Call, hasVideo:Bool) {
do {
- let callParams = try core!.createCallParams(call: call)
+ let callParams = try lc!.createCallParams(call: call)
callParams.videoEnabled = hasVideo
if (ConfigManager.instance().lpConfigBoolForKey(key: "edge_opt_preference")) {
let low_bandwidth = (AppManager.network() == .network_2g)
@@ -214,7 +270,7 @@ import AVFoundation
}
let sAddr = Address.getSwiftObject(cObject: addr!)
- if (CallManager.callKitEnabled() && !CallManager.instance().nextCallIsTransfer && core?.conference?.isIn != true) {
+ if (CallManager.callKitEnabled() && !CallManager.instance().nextCallIsTransfer && !isInConference()) {
let uuid = UUID()
let name = FastAddressBook.displayName(for: addr) ?? "unknow"
let handle = CXHandle(type: .generic, value: sAddr.asStringUriOnly())
@@ -235,7 +291,7 @@ import AVFoundation
func doCall(addr: Address, isSas: Bool) throws {
let displayName = FastAddressBook.displayName(for: addr.getCobject)
- let lcallParams = try CallManager.instance().core!.createCallParams(call: nil)
+ let lcallParams = try CallManager.instance().lc!.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
@@ -250,7 +306,7 @@ import AVFoundation
}
if (CallManager.instance().nextCallIsTransfer) {
- let call = CallManager.instance().core!.currentCall
+ let call = CallManager.instance().lc!.currentCall
try call?.transferTo(referTo: addr)
CallManager.instance().nextCallIsTransfer = false
} else {
@@ -261,7 +317,7 @@ import AVFoundation
if (isSas) {
lcallParams.mediaEncryption = .ZRTP
}
- let call = CallManager.instance().core!.inviteAddressWithParams(addr: addr, params: lcallParams)
+ let call = CallManager.instance().lc!.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
@@ -278,6 +334,32 @@ 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()
@@ -337,7 +419,7 @@ import AVFoundation
}
@objc func setHeldOtherCalls(exceptCallid: String) {
- for call in CallManager.instance().core!.calls {
+ for call in CallManager.instance().lc!.calls {
if (call.callLog?.callId != exceptCallid && call.state != .Paused && call.state != .Pausing && call.state != .PausedByRemote) {
setHeld(call: call, hold: true)
}
@@ -345,7 +427,7 @@ import AVFoundation
}
func setResumeCalls() {
- for call in CallManager.instance().core!.calls {
+ for call in CallManager.instance().lc!.calls {
if (call.state == .Paused || call.state == .Pausing || call.state == .PausedByRemote) {
setHeld(call: call, hold: false)
}
@@ -362,7 +444,7 @@ import AVFoundation
@objc func acceptVideo(call: OpaquePointer, confirm: Bool) {
let sCall = Call.getSwiftObject(cObject: call)
- let params = try? core?.createCallParams(call: sCall)
+ let params = try? lc?.createCallParams(call: sCall)
params?.videoEnabled = confirm
try? sCall.acceptUpdate(params: params)
}
@@ -383,7 +465,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().core?.getCallByCallid(callId: callId!)
+ let call = CallManager.instance().lc?.getCallByCallid(callId: callId!)
if (call != nil && call?.state != .PushIncomingReceived) {
// sometimes (for example) due to network, registration failed, in this case, keep the call
continue
@@ -397,6 +479,12 @@ 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
@@ -448,6 +536,11 @@ import AVFoundation
}
}
}
+
+ if (CallManager.instance().speakerBeforePause) {
+ CallManager.instance().speakerBeforePause = false
+ CallManager.instance().changeRouteToSpeaker()
+ }
break
case .OutgoingInit,
.OutgoingProgress,
@@ -476,6 +569,14 @@ 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.
@@ -534,6 +635,12 @@ 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: [
@@ -546,13 +653,13 @@ import AVFoundation
// Audio messages
@objc func activateAudioSession() {
- core?.activateAudioSession(actived: true)
+ lc?.activateAudioSession(actived: true)
}
@objc func getSpeakerSoundCard() -> String? {
var speakerCard: String? = nil
var earpieceCard: String? = nil
- core?.audioDevices.forEach { device in
+ lc?.audioDevices.forEach { device in
if (device.hasCapability(capability: .CapabilityPlay)) {
if (device.type == .Speaker) {
speakerCard = device.id
@@ -564,46 +671,116 @@ import AVFoundation
return speakerCard != nil ? speakerCard : earpieceCard
}
- // Local Conference
+
+
+ // Conference
+
+ @objc func hostConference() -> Bool {
+ return conference != nil
+ }
- @objc func startLocalConference() {
- if (CallManager.callKitEnabled()) {
- let calls = core?.calls
- if (calls == nil || calls!.isEmpty) {
+ func addAllToConference() {
+ if (conference == nil) {
+ guard let cp = try?lc?.createConferenceParams() else {
+ Log.directLog(BCTBX_LOG_ERROR, text: "Unable to create conference parameters")
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
+ 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)
}
-
- 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()
}
}
- 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)")
+ @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
+ return
+ }
+ }
+ 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
+ }
+ }
+ }
+ 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
new file mode 100644
index 000000000..1d4430efe
--- /dev/null
+++ b/Classes/CallOutgoingView.h
@@ -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
+
+#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
new file mode 100644
index 000000000..c5057377d
--- /dev/null
+++ b/Classes/CallOutgoingView.m
@@ -0,0 +1,267 @@
+/*
+ * 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/SwiftUtil/Extensions/IOS/UIImageViewExtensions.swift b/Classes/CallPausedTableView.h
similarity index 78%
rename from Classes/SwiftUtil/Extensions/IOS/UIImageViewExtensions.swift
rename to Classes/CallPausedTableView.h
index 9f926344a..622f19084 100644
--- a/Classes/SwiftUtil/Extensions/IOS/UIImageViewExtensions.swift
+++ b/Classes/CallPausedTableView.h
@@ -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,11 +17,10 @@
* along with this program. If not, see .
*/
-import Foundation
+#import
-extension UIImageView {
- func tint(_ color:UIColor) {
- self.image = self.image?.withRenderingMode(.alwaysTemplate)
- tintColor = color
- }
-}
+@interface CallPausedTableView : UITableViewController
+
+- (void)update;
+
+@end
diff --git a/Classes/CallPausedTableView.m b/Classes/CallPausedTableView.m
new file mode 100644
index 000000000..2dc9b6c29
--- /dev/null
+++ b/Classes/CallPausedTableView.m
@@ -0,0 +1,104 @@
+/*
+ * 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/SwiftUtil/Extensions/IOS/UIVIewControllerExtensions.swift b/Classes/CallSideMenuView.h
similarity index 72%
rename from Classes/SwiftUtil/Extensions/IOS/UIVIewControllerExtensions.swift
rename to Classes/CallSideMenuView.h
index 8c27c7dec..2a97f78d5 100644
--- a/Classes/SwiftUtil/Extensions/IOS/UIVIewControllerExtensions.swift
+++ b/Classes/CallSideMenuView.h
@@ -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,15 @@
* along with this program. If not, see .
*/
-import Foundation
-import SnapKit
-import UIKit
+#import
-extension UIViewController {
- func VIEW( _ desc: UICompositeViewDescription) -> T{
- return PhoneMainView.instance().mainViewController.getCachedController(desc.name) as! T
- }
-
-}
+#import "SideMenuTableView.h"
+#import "PhoneMainView.h"
+
+@interface CallSideMenuView : UIViewController
+
+@property(weak, nonatomic) IBOutlet UILabel *statsLabel;
+
+- (IBAction)onLateralSwipe:(id)sender;
+
+@end
diff --git a/Classes/CallSideMenuView.m b/Classes/CallSideMenuView.m
new file mode 100644
index 000000000..27ee4de48
--- /dev/null
+++ b/Classes/CallSideMenuView.m
@@ -0,0 +1,230 @@
+/*
+ * 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
new file mode 100644
index 000000000..a0208f810
--- /dev/null
+++ b/Classes/CallSideMenuView.xib
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Classes/CallView.h b/Classes/CallView.h
new file mode 100644
index 000000000..fa188895d
--- /dev/null
+++ b/Classes/CallView.h
@@ -0,0 +1,117 @@
+/*
+ * 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
new file mode 100644
index 000000000..2ae10de87
--- /dev/null
+++ b/Classes/CallView.m
@@ -0,0 +1,1006 @@
+/*
+ * 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 26607f58d..78a9042b6 100644
--- a/Classes/ChatConversationImdnView.h
+++ b/Classes/ChatConversationImdnView.h
@@ -24,7 +24,6 @@
#import "UICompositeView.h"
#import "UIRoundBorderedButton.h"
-#import "UIChatBubbleTextCell.h"
@interface ChatConversationImdnView : UIViewController
{
diff --git a/Classes/ChatConversationView.h b/Classes/ChatConversationView.h
index 7d8fd00dc..7c76cb286 100644
--- a/Classes/ChatConversationView.h
+++ b/Classes/ChatConversationView.h
@@ -32,6 +32,7 @@
#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 6ae1f25b4..40e86856f 100644
--- a/Classes/ChatConversationView.m
+++ b/Classes/ChatConversationView.m
@@ -28,8 +28,6 @@
#import "SVProgressHUD.h"
#import "EphemeralSettingsView.h"
#import "Utils.h"
-#import "linphoneapp-Swift.h"
-
@implementation FileContext
@@ -1855,7 +1853,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];
}
- [AudioRouteUtils routeAudioToSpeaker];
+ [CallManager.instance changeRouteToSpeaker];
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 03a7e4174..fd27e9078 100644
--- a/Classes/Contact.m
+++ b/Classes/Contact.m
@@ -120,7 +120,6 @@
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 60e005dc9..4913b6173 100644
--- a/Classes/DevicesListView.m
+++ b/Classes/DevicesListView.m
@@ -20,8 +20,6 @@
#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 d24f91ee2..df1a5d70f 100644
--- a/Classes/DialerView.m
+++ b/Classes/DialerView.m
@@ -21,7 +21,6 @@
#import "LinphoneManager.h"
#import "PhoneMainView.h"
-#import "linphoneapp-Swift.h"
@implementation DialerView
@@ -398,7 +397,7 @@ static UICompositeViewDescription *compositeDescription = nil;
}
- (IBAction)onBackClick:(id)event {
- [PhoneMainView.instance popToView:ActiveCallOrConferenceView.compositeViewDescription];
+ [PhoneMainView.instance popToView:CallView.compositeViewDescription];
}
- (IBAction)onAddressChange:(id)sender {
diff --git a/Classes/ImagePickerView.m b/Classes/ImagePickerView.m
index d4e4cebb7..ab1850f74 100644
--- a/Classes/ImagePickerView.m
+++ b/Classes/ImagePickerView.m
@@ -301,83 +301,96 @@ 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];
- }
+ 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];
+ }
};
- 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];
+ 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 {
+ [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];
+ }
+ });
+ }];
+ }
}
+(void) pickDocumentForDelegate:(id)documentMenuDelegate {
diff --git a/Classes/LinphoneAppDelegate.h b/Classes/LinphoneAppDelegate.h
index 9a4768d11..d3820267b 100644
--- a/Classes/LinphoneAppDelegate.h
+++ b/Classes/LinphoneAppDelegate.h
@@ -23,6 +23,7 @@
#import
#import
#import
+#import "linphoneapp-Swift.h"
@interface LinphoneAppDelegate : NSObject {
diff --git a/Classes/LinphoneAppDelegate.m b/Classes/LinphoneAppDelegate.m
index 1d4c152c3..be99479de 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:ActiveCallOrConferenceView.compositeViewDescription];
+ [PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription];
} else {
[PhoneMainView.instance displayIncomingCall:call];
}
@@ -321,8 +321,6 @@
return NO;
}
- VIEW(ActiveCallOrConferenceView); // to get created and all observers added
-
return YES;
}
@@ -411,9 +409,16 @@
}
// 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:@"INStartAudioCallIntent"]||[userActivity.activityType isEqualToString:@"INStartVideoCallIntent"]) { // tel URI handler.
+
+ if ([userActivity.activityType isEqualToString:@"INStartVideoCallIntent"]) {
+ LOGI(@"CallKit: satrt video.");
+ CallView *view = VIEW(CallView);
+ [view.videoButton setOn];
+ }
+ if ([userActivity.activityType isEqualToString:@"INStartAudioCallIntent"]) { // tel URI handler.
INInteraction *interaction = userActivity.interaction;
INStartAudioCallIntent *startAudioCallIntent = (INStartAudioCallIntent *)interaction.intent;
INPerson *contact = startAudioCallIntent.contacts[0];
@@ -532,7 +537,8 @@
if ([response.actionIdentifier isEqual:@"Answer"]) {
// use the standard handler
- [CallManager.instance acceptCallWithCall:call hasVideo:NO];
+ [PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription];
+ linphone_call_accept(call);
} else if ([response.actionIdentifier isEqual:@"Decline"]) {
linphone_call_decline(call, LinphoneReasonDeclined);
} else if ([response.actionIdentifier isEqual:@"Reply"]) {
@@ -569,6 +575,7 @@
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)
@@ -601,7 +608,7 @@
}
} else if ([response.notification.request.content.categoryIdentifier isEqual:@"video_request"]) {
if (!call) return;
- [PhoneMainView.instance changeCurrentView:ActiveCallOrConferenceView.compositeViewDescription];
+ [PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription];
NSTimer *videoDismissTimer = nil;
UIConfirmationDialog *sheet = [UIConfirmationDialog ShowWithMessage:response.notification.request.content.body
cancelMessage:nil
@@ -685,7 +692,8 @@
if ([notification.category isEqualToString:@"incoming_call"]) {
if ([identifier isEqualToString:@"answer"]) {
// use the standard handler
- [CallManager.instance acceptCallWithCall:call hasVideo:NO];
+ [PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription];
+ linphone_call_accept(call);
} else if ([identifier isEqualToString:@"decline"]) {
LinphoneCall *call = linphone_core_get_current_call(LC);
if (call)
@@ -722,7 +730,8 @@
if ([notification.category isEqualToString:@"incoming_call"]) {
if ([identifier isEqualToString:@"answer"]) {
// use the standard handler
- [CallManager.instance acceptCallWithCall:call hasVideo:NO];
+ [PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription];
+ linphone_call_accept(call);
} 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 d9a59ac84..a48f5f294 100644
--- a/Classes/LinphoneCoreSettingsStore.m
+++ b/Classes/LinphoneCoreSettingsStore.m
@@ -25,8 +25,6 @@
#include "linphone/lpconfig.h"
#include
#include
-#import "linphoneapp-Swift.h"
-
@implementation LinphoneCoreSettingsStore
diff --git a/Classes/LinphoneManager.h b/Classes/LinphoneManager.h
index f0027fb6f..53be84967 100644
--- a/Classes/LinphoneManager.h
+++ b/Classes/LinphoneManager.h
@@ -35,6 +35,7 @@
#include "bctoolbox/list.h"
#import "OrderedDictionary.h"
+#import "linphoneapp-Swift.h"
extern NSString *const LINPHONERC_APPLICATION_KEY;
@@ -48,6 +49,7 @@ 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;
@@ -202,6 +204,7 @@ 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 8cb934663..c980e5644 100644
--- a/Classes/LinphoneManager.m
+++ b/Classes/LinphoneManager.m
@@ -31,6 +31,7 @@
#import "LinphoneCoreSettingsStore.h"
#import "LinphoneAppDelegate.h"
#import "LinphoneManager.h"
+#import "Utils/AudioHelper.h"
#import "Utils/FileTransferDelegate.h"
#include "linphone/factory.h"
@@ -45,7 +46,6 @@
#import "ChatsListView.h"
#import "ChatConversationView.h"
#import
-#import "linphoneapp-Swift.h"
#define LINPHONE_LOGS_MAX_ENTRY 5000
@@ -233,7 +233,11 @@ 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];
@@ -1763,7 +1767,20 @@ 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 {
@@ -2145,6 +2162,7 @@ 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 341bcf9a8..0830037cb 100644
--- a/Classes/LinphoneUI/Base.lproj/StatusBarView.xib
+++ b/Classes/LinphoneUI/Base.lproj/StatusBarView.xib
@@ -1,10 +1,8 @@
-
-
-
+
+
-
-
+
@@ -23,15 +21,15 @@
-
-
+
+
-
+
-
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -106,15 +104,15 @@
-
+
-
+
-
-
-
-
+
+
+
+
diff --git a/Classes/LinphoneUI/Base.lproj/UICallConferenceCell.xib b/Classes/LinphoneUI/Base.lproj/UICallConferenceCell.xib
new file mode 100644
index 000000000..a64c1fc9a
--- /dev/null
+++ b/Classes/LinphoneUI/Base.lproj/UICallConferenceCell.xib
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Classes/LinphoneUI/Base.lproj/UICallPausedCell.xib b/Classes/LinphoneUI/Base.lproj/UICallPausedCell.xib
new file mode 100644
index 000000000..423378a9e
--- /dev/null
+++ b/Classes/LinphoneUI/Base.lproj/UICallPausedCell.xib
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Classes/LinphoneUI/UIBackToCallButton.m b/Classes/LinphoneUI/UIBackToCallButton.m
index 3a272f5a8..60c9f4df4 100644
--- a/Classes/LinphoneUI/UIBackToCallButton.m
+++ b/Classes/LinphoneUI/UIBackToCallButton.m
@@ -20,7 +20,6 @@
#import "UIBackToCallButton.h"
#import "LinphoneManager.h"
#import "PhoneMainView.h"
-#import "linphoneapp-Swift.h"
@implementation UIBackToCallButton
@@ -47,7 +46,7 @@
}
- (IBAction)onBackToCallClick:(id)sender {
- [PhoneMainView.instance popToView:ActiveCallOrConferenceView.compositeViewDescription];
+ [PhoneMainView.instance popToView:CallView.compositeViewDescription];
}
@end
diff --git a/Classes/LinphoneUI/UIBluetoothButton.m b/Classes/LinphoneUI/UIBluetoothButton.m
index f14af83b3..53abc1818 100644
--- a/Classes/LinphoneUI/UIBluetoothButton.m
+++ b/Classes/LinphoneUI/UIBluetoothButton.m
@@ -18,8 +18,10 @@
*/
#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 aa925fc22..c84a7dafd 100644
--- a/Classes/LinphoneUI/UICallButton.m
+++ b/Classes/LinphoneUI/UICallButton.m
@@ -21,8 +21,6 @@
#import "LinphoneManager.h"
#import
-#import "linphoneapp-Swift.h"
-
@implementation UICallButton
diff --git a/Classes/LinphoneUI/UICallConferenceCell.h b/Classes/LinphoneUI/UICallConferenceCell.h
new file mode 100644
index 000000000..cf59dfe3c
--- /dev/null
+++ b/Classes/LinphoneUI/UICallConferenceCell.h
@@ -0,0 +1,37 @@
+/*
+ * 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
new file mode 100644
index 000000000..0e64ca4b1
--- /dev/null
+++ b/Classes/LinphoneUI/UICallConferenceCell.m
@@ -0,0 +1,69 @@
+/*
+ * 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/SwiftUtil/Extensions/LinphoneCore/ConferenceExtensions.swift b/Classes/LinphoneUI/UICallPausedCell.h
similarity index 58%
rename from Classes/SwiftUtil/Extensions/LinphoneCore/ConferenceExtensions.swift
rename to Classes/LinphoneUI/UICallPausedCell.h
index 483f3bc16..77bdedd71 100644
--- a/Classes/SwiftUtil/Extensions/LinphoneCore/ConferenceExtensions.swift
+++ b/Classes/LinphoneUI/UICallPausedCell.h
@@ -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,17 +17,18 @@
* along with this program. If not, see .
*/
-import Foundation
-import linphonesw
+#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;
-extension Conference : CustomStringConvertible {
- public var description: String {
- if let username = conferenceAddress?.username {
- return "<\(username)>"
- }
- return ""
- }
-}
+- (id)initWithIdentifier:(NSString *)identifier;
+- (void)setCall:(LinphoneCall *)call;
+@end
diff --git a/Classes/LinphoneUI/UICallPausedCell.m b/Classes/LinphoneUI/UICallPausedCell.m
new file mode 100644
index 000000000..fe51d9c5d
--- /dev/null
+++ b/Classes/LinphoneUI/UICallPausedCell.m
@@ -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 "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 01b13c93b..ccf47d7aa 100644
--- a/Classes/LinphoneUI/UIChatBubblePhotoCell.m
+++ b/Classes/LinphoneUI/UIChatBubblePhotoCell.m
@@ -25,8 +25,6 @@
#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 079891029..4038ac799 100644
--- a/Classes/LinphoneUI/UIChatBubbleTextCell.m
+++ b/Classes/LinphoneUI/UIChatBubbleTextCell.m
@@ -25,8 +25,6 @@
#import
#import
#import
-#import "linphoneapp-Swift.h"
-
@implementation UIChatBubbleTextCell
diff --git a/Classes/Voip/Views/SharedLayoutConstants.swift b/Classes/LinphoneUI/UIHangUpButton.h
similarity index 79%
rename from Classes/Voip/Views/SharedLayoutConstants.swift
rename to Classes/LinphoneUI/UIHangUpButton.h
index 163d8ba07..784369828 100644
--- a/Classes/Voip/Views/SharedLayoutConstants.swift
+++ b/Classes/LinphoneUI/UIHangUpButton.h
@@ -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,11 +17,13 @@
* along with this program. If not, see .
*/
+#import
-import Foundation
-
-class SharedLayoutConstants {
- static let buttons_bottom_margin = 15
- static let margin_call_view_side_controls_buttons = 12
+#import "UIIconButton.h"
+@interface UIHangUpButton : UIIconButton {
}
+
+- (void)update;
+
+@end
diff --git a/Classes/LinphoneUI/UIHangUpButton.m b/Classes/LinphoneUI/UIHangUpButton.m
new file mode 100644
index 000000000..6ab96297d
--- /dev/null
+++ b/Classes/LinphoneUI/UIHangUpButton.m
@@ -0,0 +1,111 @@
+/*
+ * 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/SwiftUtil/Extensions/LinphoneCore/ParticipantExtensions.swift b/Classes/LinphoneUI/UIPauseButton.h
similarity index 61%
rename from Classes/SwiftUtil/Extensions/LinphoneCore/ParticipantExtensions.swift
rename to Classes/LinphoneUI/UIPauseButton.h
index 9b7b07652..0146c3174 100644
--- a/Classes/SwiftUtil/Extensions/LinphoneCore/ParticipantExtensions.swift
+++ b/Classes/LinphoneUI/UIPauseButton.h
@@ -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,17 +17,22 @@
* along with this program. If not, see .
*/
-import Foundation
-import linphonesw
+#import "UIToggleButton.h"
+#include "linphone/linphonecore.h"
+typedef enum _UIPauseButtonType {
+ UIPauseButtonType_CurrentCall,
+ UIPauseButtonType_Call,
+ UIPauseButtonType_Conference
+} UIPauseButtonType;
-extension Participant : CustomStringConvertible {
- public var description: String {
- if let address = address?.asStringUriOnly() {
- return ""
- }
- return ""
- }
+@interface UIPauseButton : UIToggleButton {
+ @private
+ UIPauseButtonType type;
+ LinphoneCall* call;
}
+- (void)setType:(UIPauseButtonType) type call:(LinphoneCall*)call;
+
+@end
diff --git a/Classes/LinphoneUI/UIPauseButton.m b/Classes/LinphoneUI/UIPauseButton.m
new file mode 100644
index 000000000..797e88675
--- /dev/null
+++ b/Classes/LinphoneUI/UIPauseButton.m
@@ -0,0 +1,180 @@
+/*
+ * 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/SwiftUtil/Extensions/LinphoneCore/PayloadType.swift b/Classes/LinphoneUI/UISpeakerButton.h
similarity index 85%
rename from Classes/SwiftUtil/Extensions/LinphoneCore/PayloadType.swift
rename to Classes/LinphoneUI/UISpeakerButton.h
index 197a8d733..34361634e 100644
--- a/Classes/SwiftUtil/Extensions/LinphoneCore/PayloadType.swift
+++ b/Classes/LinphoneUI/UISpeakerButton.h
@@ -17,11 +17,12 @@
* along with this program. If not, see .
*/
-import Foundation
-import linphonesw
-import linphone
+#import
+
+#import "UIToggleButton.h"
+
+@interface UISpeakerButton : UIToggleButton {
-extension linphonesw.PayloadType {
-
-
}
+
+@end
diff --git a/Classes/LinphoneUI/UISpeakerButton.m b/Classes/LinphoneUI/UISpeakerButton.m
new file mode 100644
index 000000000..64c12d5b9
--- /dev/null
+++ b/Classes/LinphoneUI/UISpeakerButton.m
@@ -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
+#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.h b/Classes/LinphoneUI/UIVideoButton.h
new file mode 100644
index 000000000..79def1378
--- /dev/null
+++ b/Classes/LinphoneUI/UIVideoButton.h
@@ -0,0 +1,29 @@
+/*
+ * 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 "UIToggleButton.h"
+
+@interface UIVideoButton : UIToggleButton {
+}
+
+@property(nonatomic, strong) IBOutlet UIActivityIndicatorView *waitView;
+
+@end
diff --git a/Classes/LinphoneUI/UIVideoButton.m b/Classes/LinphoneUI/UIVideoButton.m
new file mode 100644
index 000000000..af97c4295
--- /dev/null
+++ b/Classes/LinphoneUI/UIVideoButton.m
@@ -0,0 +1,114 @@
+/*
+ * 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
new file mode 100644
index 000000000..629130fa2
Binary files /dev/null and b/Classes/LinphoneUI/fr.lproj/UICallConferenceCell.strings differ
diff --git a/Classes/LinphoneUI/fr.lproj/UICallPausedCell.strings b/Classes/LinphoneUI/fr.lproj/UICallPausedCell.strings
new file mode 100644
index 000000000..4d0eb9bb9
Binary files /dev/null and b/Classes/LinphoneUI/fr.lproj/UICallPausedCell.strings differ
diff --git a/Classes/LinphoneUI/hu.lproj/UICallConferenceCell.strings b/Classes/LinphoneUI/hu.lproj/UICallConferenceCell.strings
new file mode 100644
index 000000000..c90ef96ac
Binary files /dev/null and b/Classes/LinphoneUI/hu.lproj/UICallConferenceCell.strings differ
diff --git a/Classes/LinphoneUI/hu.lproj/UICallPausedCell.strings b/Classes/LinphoneUI/hu.lproj/UICallPausedCell.strings
new file mode 100644
index 000000000..7e626165f
Binary files /dev/null and b/Classes/LinphoneUI/hu.lproj/UICallPausedCell.strings differ
diff --git a/Classes/Log.h b/Classes/Log.h
index c838afa12..c2ee3d897 100644
--- a/Classes/Log.h
+++ b/Classes/Log.h
@@ -32,11 +32,6 @@
+ (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 672030668..adfe87b5a 100644
--- a/Classes/PhoneMainView.h
+++ b/Classes/PhoneMainView.h
@@ -27,6 +27,10 @@
#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"
@@ -74,7 +78,7 @@
@end
-@interface PhoneMainView : UIViewController {
+@interface PhoneMainView : UIViewController {
@private
NSMutableArray *inhibitedEvents;
}
@@ -92,7 +96,6 @@
- (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 a45b51376..3d0ad4962 100644
--- a/Classes/PhoneMainView.m
+++ b/Classes/PhoneMainView.m
@@ -22,8 +22,6 @@
#import "LinphoneAppDelegate.h"
#import "Log.h"
#import "PhoneMainView.h"
-#import "linphoneapp-Swift.h"
-
static RootViewManager *rootViewManagerInstance = nil;
@@ -375,9 +373,7 @@ static RootViewManager *rootViewManagerInstance = nil;
break;
}
case LinphoneCallOutgoingInit: {
- OutgoingCallView *v = VIEW(OutgoingCallView);
- [self changeCurrentView:OutgoingCallView.compositeViewDescription];
- [v setCallWithCall:call];
+ [self changeCurrentView:CallOutgoingView.compositeViewDescription];
break;
}
case LinphoneCallPausedByRemote:
@@ -385,12 +381,39 @@ 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:
@@ -610,15 +633,6 @@ 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];
}
@@ -750,10 +764,10 @@ static RootViewManager *rootViewManagerInstance = nil;
[CallManager.instance acceptCallWithCall:call hasVideo:YES];
} else {
AudioServicesPlaySystemSound(lm.sounds.vibrate);
- IncomingCallView *view = VIEW(IncomingCallView);
+ CallIncomingView *view = VIEW(CallIncomingView);
[self changeCurrentView:view.compositeViewDescription];
- [view setCallWithCall:call];
- //CDFIX [view setDelegate:self];
+ [view setCall:call];
+ [view setDelegate:self];
}
}
}
diff --git a/Classes/ProviderDelegate.swift b/Classes/ProviderDelegate.swift
index 5f4ecd512..c9eb1dbb1 100644
--- a/Classes/ProviderDelegate.swift
+++ b/Classes/ProviderDelegate.swift
@@ -99,7 +99,7 @@ class ProviderDelegate: NSObject {
provider.reportNewIncomingCall(with: uuid, update: update) { error in
if error == nil {
if CallManager.instance().endCallkit {
- let call = CallManager.instance().core?.getCallByCallid(callId: callId!)
+ let call = CallManager.instance().lc?.getCallByCallid(callId: callId!)
if (call?.state == .PushIncomingReceived) {
try? call?.terminate()
}
@@ -191,7 +191,7 @@ extension ProviderDelegate: CXProviderDelegate {
CallManager.instance().backgroundContextCameraIsEnabled = call!.params?.videoEnabled ?? false
call?.cameraEnabled = false // Disable camera while app is not on foreground
}
- CallManager.instance().core?.configureAudioSession()
+ CallManager.instance().lc?.configureAudioSession()
CallManager.instance().acceptCall(call: call!, hasVideo: call!.params?.videoEnabled ?? false)
action.fulfill()
}
@@ -206,8 +206,8 @@ extension ProviderDelegate: CXProviderDelegate {
}
do {
- if (CallManager.instance().core?.isInConference ?? false && action.isOnHold) {
- try CallManager.instance().core?.leaveConference()
+ if (CallManager.instance().lc?.isInConference ?? false && action.isOnHold) {
+ try CallManager.instance().lc?.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
@@ -219,10 +219,11 @@ extension ProviderDelegate: CXProviderDelegate {
if (call!.params?.localConferenceMode ?? false) {
return
}
+ CallManager.instance().speakerBeforePause = CallManager.instance().isSpeakerEnabled()
try call!.pause()
} else {
- if (CallManager.instance().core?.conference != nil && CallManager.instance().core?.callsNb ?? 0 > 1) {
- try CallManager.instance().core?.enterConference()
+ if (CallManager.instance().lc?.conference != nil && CallManager.instance().lc?.callsNb ?? 0 > 1) {
+ try CallManager.instance().lc?.enterConference()
NotificationCenter.default.post(name: Notification.Name("LinphoneCallUpdate"), object: self)
} else {
try call!.resume()
@@ -251,7 +252,7 @@ extension ProviderDelegate: CXProviderDelegate {
action.fail()
}
- CallManager.instance().core?.configureAudioSession()
+ CallManager.instance().lc?.configureAudioSession()
try CallManager.instance().doCall(addr: addr!, isSas: callInfo?.sasEnabled ?? false)
} catch {
Log.directLog(BCTBX_LOG_ERROR, text: "CallKit: Call started failed because \(error)")
@@ -262,7 +263,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().addAllToLocalConference()
+ CallManager.instance().addAllToConference()
action.fulfill()
}
@@ -270,7 +271,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().core!.micEnabled = !CallManager.instance().core!.micEnabled
+ CallManager.instance().lc!.micEnabled = !CallManager.instance().lc!.micEnabled
action.fulfill()
}
@@ -303,12 +304,12 @@ extension ProviderDelegate: CXProviderDelegate {
func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: audio session activated.")
- CallManager.instance().core?.activateAudioSession(actived: true)
+ CallManager.instance().lc?.activateAudioSession(actived: true)
}
func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: audio session deactivated.")
- CallManager.instance().core?.activateAudioSession(actived: false)
+ CallManager.instance().lc?.activateAudioSession(actived: false)
}
}
diff --git a/Classes/SwiftUtil/Extensions/IOS/UIApplication+Extension.swift b/Classes/SwiftUtil/Extensions/IOS/UIApplication+Extension.swift
deleted file mode 100644
index 50f5e9353..000000000
--- a/Classes/SwiftUtil/Extensions/IOS/UIApplication+Extension.swift
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
-* 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
deleted file mode 100644
index 2d4b8efc9..000000000
--- a/Classes/SwiftUtil/Extensions/IOS/UIColorExtensions.swift
+++ /dev/null
@@ -1,40 +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 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
deleted file mode 100644
index fa00d6236..000000000
--- a/Classes/SwiftUtil/Extensions/IOS/UIDeviceExtensions.swift
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
-* 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
deleted file mode 100644
index a409d782a..000000000
--- a/Classes/SwiftUtil/Extensions/IOS/UIImageExtensions.swift
+++ /dev/null
@@ -1,50 +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 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/SwiftUtil/Extensions/IOS/UIVIewExtensions.swift b/Classes/SwiftUtil/Extensions/IOS/UIVIewExtensions.swift
deleted file mode 100644
index 7e99f532f..000000000
--- a/Classes/SwiftUtil/Extensions/IOS/UIVIewExtensions.swift
+++ /dev/null
@@ -1,326 +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 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
deleted file mode 100644
index 39bd741f8..000000000
--- a/Classes/SwiftUtil/Extensions/LinphoneCore/AddressExtensions.swift
+++ /dev/null
@@ -1,55 +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 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
deleted file mode 100644
index 197f48fbc..000000000
--- a/Classes/SwiftUtil/Extensions/LinphoneCore/CallExtensions.swift
+++ /dev/null
@@ -1,48 +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 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/CoreExtensions.swift b/Classes/SwiftUtil/Extensions/LinphoneCore/CoreExtensions.swift
deleted file mode 100644
index ee0c99edb..000000000
--- a/Classes/SwiftUtil/Extensions/LinphoneCore/CoreExtensions.swift
+++ /dev/null
@@ -1,48 +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 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
deleted file mode 100644
index 7274e2f7e..000000000
--- a/Classes/SwiftUtil/Extensions/LinphoneCore/IceState.swift
+++ /dev/null
@@ -1,35 +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 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/ViewModel/MutableLiveData.swift b/Classes/SwiftUtil/ViewModel/MutableLiveData.swift
deleted file mode 100644
index 125939785..000000000
--- a/Classes/SwiftUtil/ViewModel/MutableLiveData.swift
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
-* 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/Voip/Theme/ButtonTheme.swift b/Classes/Utils/AudioHelper.h
similarity index 60%
rename from Classes/Voip/Theme/ButtonTheme.swift
rename to Classes/Utils/AudioHelper.h
index 88b8306bb..fdf2845b1 100644
--- a/Classes/Voip/Theme/ButtonTheme.swift
+++ b/Classes/Utils/AudioHelper.h
@@ -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,17 +17,20 @@
* along with this program. If not, see .
*/
+#ifndef AudioHelper_h
+#define AudioHelper_h
-import Foundation
-import UIKit
+#import
+@import AVFoundation;
-struct ButtonTheme {
- var tintableStateIcons: [UInt: TintableIcon] // State indexed
- var backgroundStateColors: [UInt: LightDarkColor] // State indexed
-}
+@interface AudioHelper : NSObject
-struct TintableIcon {
- var name:String
- var tintColor: LightDarkColor? = nil
-}
++ (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 413c6c0b5..0738c54b4 100644
--- a/Classes/Utils/FileTransferDelegate.m
+++ b/Classes/Utils/FileTransferDelegate.m
@@ -21,8 +21,6 @@
#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 9c0b29c96..c7fe474c8 100644
--- a/Classes/Utils/Log.m
+++ b/Classes/Utils/Log.m
@@ -67,24 +67,6 @@
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
deleted file mode 100644
index 94a26f71e..000000000
--- a/Classes/Voip/AudioRouteUtils.swift
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 49f5fa2a5..000000000
--- a/Classes/Voip/Models/CallData.swift
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 5d075d063..000000000
--- a/Classes/Voip/Models/CallStatisticsData.swift
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 2caa75232..000000000
--- a/Classes/Voip/Models/CallsViewModel.swift
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 2ccdf95d9..000000000
--- a/Classes/Voip/Models/ConferenceParticipantData.swift
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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
deleted file mode 100644
index b10dd635f..000000000
--- a/Classes/Voip/Models/ConferenceParticipantDeviceData.swift
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 26206c306..000000000
--- a/Classes/Voip/Models/ConferenceViewModel.swift
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 51109541e..000000000
--- a/Classes/Voip/Models/ControlsViewModel.swift
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * 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/Voip/Theme/LightDarkColor.swift b/Classes/Voip/Theme/LightDarkColor.swift
deleted file mode 100644
index a01e375b2..000000000
--- a/Classes/Voip/Theme/LightDarkColor.swift
+++ /dev/null
@@ -1,42 +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 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
deleted file mode 100644
index ceff5a5eb..000000000
--- a/Classes/Voip/Theme/TextStyle.swift
+++ /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 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
deleted file mode 100644
index 4df37ec4d..000000000
--- a/Classes/Voip/Theme/VoipTexts.swift
+++ /dev/null
@@ -1,137 +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 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
deleted file mode 100644
index 1a5d77d9d..000000000
--- a/Classes/Voip/Theme/VoipTheme.swift
+++ /dev/null
@@ -1,342 +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 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
deleted file mode 100644
index f85be7062..000000000
--- a/Classes/Voip/Views/CompositeViewControllers/ActiveCallOrConferenceView.swift
+++ /dev/null
@@ -1,348 +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 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
deleted file mode 100644
index 6009bf0a5..000000000
--- a/Classes/Voip/Views/CompositeViewControllers/IncomingCallView.swift
+++ /dev/null
@@ -1,81 +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 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
deleted file mode 100644
index d2898a148..000000000
--- a/Classes/Voip/Views/CompositeViewControllers/OutgoingCallView.swift
+++ /dev/null
@@ -1,102 +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 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
deleted file mode 100644
index 2b8194b70..000000000
--- a/Classes/Voip/Views/Fragments/ActiveCall/ActiveCallView.swift
+++ /dev/null
@@ -1,231 +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 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
deleted file mode 100644
index 1618e3727..000000000
--- a/Classes/Voip/Views/Fragments/AudioRoutesView.swift
+++ /dev/null
@@ -1,80 +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 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
deleted file mode 100644
index 142cc03d6..000000000
--- a/Classes/Voip/Views/Fragments/CallStatsView.swift
+++ /dev/null
@@ -1,105 +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 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
deleted file mode 100644
index 6fad29f47..000000000
--- a/Classes/Voip/Views/Fragments/CallsList/CallsListView.swift
+++ /dev/null
@@ -1,139 +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 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
deleted file mode 100644
index 22c92def4..000000000
--- a/Classes/Voip/Views/Fragments/CallsList/VoipCallCell.swift
+++ /dev/null
@@ -1,106 +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 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
deleted file mode 100644
index 79cdaa063..000000000
--- a/Classes/Voip/Views/Fragments/CallsList/VoipCallContextMenu.swift
+++ /dev/null
@@ -1,160 +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 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
deleted file mode 100644
index 02c2f384f..000000000
--- a/Classes/Voip/Views/Fragments/Conference/VoipActiveSpeakerParticipantCell.swift
+++ /dev/null
@@ -1,121 +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 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
deleted file mode 100644
index e0bc2d937..000000000
--- a/Classes/Voip/Views/Fragments/Conference/VoipConferenceActiveSpeakerView.swift
+++ /dev/null
@@ -1,260 +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 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
deleted file mode 100644
index 42a694bbd..000000000
--- a/Classes/Voip/Views/Fragments/Conference/VoipConferenceDisplayModeSelectionView.swift
+++ /dev/null
@@ -1,140 +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 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
deleted file mode 100644
index 1b6509c03..000000000
--- a/Classes/Voip/Views/Fragments/Conference/VoipConferenceGridView.swift
+++ /dev/null
@@ -1,242 +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 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
deleted file mode 100644
index 6339a0d15..000000000
--- a/Classes/Voip/Views/Fragments/Conference/VoipGridParticipantCell.swift
+++ /dev/null
@@ -1,129 +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 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
deleted file mode 100644
index 58c285aa5..000000000
--- a/Classes/Voip/Views/Fragments/ControlsView.swift
+++ /dev/null
@@ -1,109 +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 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
deleted file mode 100644
index 9bb1f7d9b..000000000
--- a/Classes/Voip/Views/Fragments/DismissableView.swift
+++ /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 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
deleted file mode 100644
index 40a21da83..000000000
--- a/Classes/Voip/Views/Fragments/IncomingOuntgoingCommonView.swift
+++ /dev/null
@@ -1,98 +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 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
deleted file mode 100644
index dba74002d..000000000
--- a/Classes/Voip/Views/Fragments/LocalVideoView.swift
+++ /dev/null
@@ -1,87 +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 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
deleted file mode 100644
index e949e82f8..000000000
--- a/Classes/Voip/Views/Fragments/NumpadView.swift
+++ /dev/null
@@ -1,123 +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 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
deleted file mode 100644
index b81357bb7..000000000
--- a/Classes/Voip/Views/Fragments/ParticipantsList/ParticipantsListView.swift
+++ /dev/null
@@ -1,97 +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 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
deleted file mode 100644
index c57a4e7a0..000000000
--- a/Classes/Voip/Views/Fragments/ParticipantsList/VoipParticipantCell.swift
+++ /dev/null
@@ -1,113 +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 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
deleted file mode 100644
index 45a1a9a98..000000000
--- a/Classes/Voip/Views/Fragments/PausedCallOrConferenceView.swift
+++ /dev/null
@@ -1,70 +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 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
deleted file mode 100644
index a3e957865..000000000
--- a/Classes/Voip/Views/Fragments/RemotelyRecording.swift
+++ /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 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
deleted file mode 100644
index 2a7deca61..000000000
--- a/Classes/Voip/Views/Fragments/VoipExtraButtonsView.swift
+++ /dev/null
@@ -1,133 +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 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/Voip/VoipDialog.swift b/Classes/Voip/VoipDialog.swift
deleted file mode 100644
index 8488e8764..000000000
--- a/Classes/Voip/VoipDialog.swift
+++ /dev/null
@@ -1,107 +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 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
deleted file mode 100644
index 8e4084fa5..000000000
--- a/Classes/Voip/Widgets/Avatar.swift
+++ /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 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
deleted file mode 100644
index a07f5124b..000000000
--- a/Classes/Voip/Widgets/BouncingCounter.swift
+++ /dev/null
@@ -1,75 +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 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
deleted file mode 100644
index fa437dc8f..000000000
--- a/Classes/Voip/Widgets/ButtonWithStateBackgrounds.swift
+++ /dev/null
@@ -1,49 +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 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
deleted file mode 100644
index 03a8a4835..000000000
--- a/Classes/Voip/Widgets/CallControlButton.swift
+++ /dev/null
@@ -1,92 +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 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/Voip/Widgets/RotatingSpinner.swift b/Classes/Voip/Widgets/RotatingSpinner.swift
deleted file mode 100644
index a03f12671..000000000
--- a/Classes/Voip/Widgets/RotatingSpinner.swift
+++ /dev/null
@@ -1,48 +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 Foundation
-
-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()
- }
-}
-
diff --git a/Classes/Voip/Widgets/StyledLabel.swift b/Classes/Voip/Widgets/StyledLabel.swift
deleted file mode 100644
index 29c15da3e..000000000
--- a/Classes/Voip/Widgets/StyledLabel.swift
+++ /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 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
deleted file mode 100644
index 1eaf13ef9..000000000
--- a/Classes/Voip/Widgets/UICallTimer.swift
+++ /dev/null
@@ -1,70 +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 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
deleted file mode 100644
index 67fce96c1..000000000
--- a/Classes/Voip/Widgets/VoipExtraButton.swift
+++ /dev/null
@@ -1,75 +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 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
new file mode 100644
index 000000000..c5adc5123
Binary files /dev/null and b/Classes/ar.lproj/CallIncomingView.strings differ
diff --git a/Classes/ar.lproj/CallView.strings b/Classes/ar.lproj/CallView.strings
new file mode 100644
index 000000000..6544e6570
Binary files /dev/null and b/Classes/ar.lproj/CallView.strings differ
diff --git a/Classes/fr.lproj/CallIncomingView.strings b/Classes/fr.lproj/CallIncomingView.strings
new file mode 100644
index 000000000..b59db541e
Binary files /dev/null and b/Classes/fr.lproj/CallIncomingView.strings differ
diff --git a/Classes/fr.lproj/CallOutgoingView.strings b/Classes/fr.lproj/CallOutgoingView.strings
new file mode 100644
index 000000000..f507fda06
Binary files /dev/null and b/Classes/fr.lproj/CallOutgoingView.strings differ
diff --git a/Classes/fr.lproj/CallView.strings b/Classes/fr.lproj/CallView.strings
new file mode 100644
index 000000000..8303fcfed
Binary files /dev/null and b/Classes/fr.lproj/CallView.strings differ
diff --git a/Classes/fr.lproj/CallView~ipad.strings b/Classes/fr.lproj/CallView~ipad.strings
new file mode 100644
index 000000000..b4c093b13
Binary files /dev/null and b/Classes/fr.lproj/CallView~ipad.strings differ
diff --git a/Classes/hu.lproj/CallIncomingView.strings b/Classes/hu.lproj/CallIncomingView.strings
new file mode 100644
index 000000000..de84a65e8
Binary files /dev/null and b/Classes/hu.lproj/CallIncomingView.strings differ
diff --git a/Classes/hu.lproj/CallOutgoingView.strings b/Classes/hu.lproj/CallOutgoingView.strings
new file mode 100644
index 000000000..912463c42
Binary files /dev/null and b/Classes/hu.lproj/CallOutgoingView.strings differ
diff --git a/Classes/hu.lproj/CallView.strings b/Classes/hu.lproj/CallView.strings
new file mode 100644
index 000000000..95effba3d
Binary files /dev/null and b/Classes/hu.lproj/CallView.strings differ
diff --git a/Classes/hu.lproj/CallView~ipad.strings b/Classes/hu.lproj/CallView~ipad.strings
new file mode 100644
index 000000000..3bd40d4d4
Binary files /dev/null and b/Classes/hu.lproj/CallView~ipad.strings differ
diff --git a/Classes/linphone-Bridging-Header.h b/Classes/linphone-Bridging-Header.h
index c09603899..a520acc8e 100644
--- a/Classes/linphone-Bridging-Header.h
+++ b/Classes/linphone-Bridging-Header.h
@@ -7,8 +7,5 @@
#import
#import "FastAddressBook.h"
#import "Log.h"
-#import "LinphoneUI/UICompositeView.h"
-#import "Contact.h"
-#import "StatusBarView.h"
-#import "LinphoneUI/UIBouncingView.h"
-#import "PhoneMainView.h"
+#import "AudioHelper.h"
+
diff --git a/Classes/ru.lproj/CallIncomingView.strings b/Classes/ru.lproj/CallIncomingView.strings
new file mode 100644
index 000000000..f433ea682
Binary files /dev/null and b/Classes/ru.lproj/CallIncomingView.strings differ
diff --git a/Classes/ru.lproj/CallView.strings b/Classes/ru.lproj/CallView.strings
new file mode 100644
index 000000000..c3217f4ee
Binary files /dev/null and b/Classes/ru.lproj/CallView.strings differ
diff --git a/Podfile b/Podfile
index 4bdf972fb..d6a747f18 100644
--- a/Podfile
+++ b/Podfile
@@ -26,7 +26,6 @@ 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
deleted file mode 100644
index 8d6cf0551..000000000
Binary files a/Resources/fonts/Roboto-Bold.ttf and /dev/null differ
diff --git a/Resources/fonts/Roboto-Italic.ttf b/Resources/fonts/Roboto-Italic.ttf
deleted file mode 100644
index 737244bc0..000000000
Binary files a/Resources/fonts/Roboto-Italic.ttf and /dev/null differ
diff --git a/Resources/fonts/Roboto-Regular.ttf b/Resources/fonts/Roboto-Regular.ttf
deleted file mode 100644
index 3a4973c78..000000000
Binary files a/Resources/fonts/Roboto-Regular.ttf and /dev/null differ
diff --git a/Resources/images/voip_audio_routes.png b/Resources/images/voip_audio_routes.png
deleted file mode 100644
index fae4e84e3..000000000
Binary files a/Resources/images/voip_audio_routes.png and /dev/null differ
diff --git a/Resources/images/voip_bluetooth.png b/Resources/images/voip_bluetooth.png
deleted file mode 100644
index 3010fb418..000000000
Binary files a/Resources/images/voip_bluetooth.png and /dev/null differ
diff --git a/Resources/images/voip_call.png b/Resources/images/voip_call.png
deleted file mode 100644
index 1a95c052e..000000000
Binary files a/Resources/images/voip_call.png and /dev/null differ
diff --git a/Resources/images/voip_call_add.png b/Resources/images/voip_call_add.png
deleted file mode 100644
index 8237f3305..000000000
Binary files a/Resources/images/voip_call_add.png and /dev/null differ
diff --git a/Resources/images/voip_call_chat.png b/Resources/images/voip_call_chat.png
deleted file mode 100644
index 4ff01de6f..000000000
Binary files a/Resources/images/voip_call_chat.png and /dev/null differ
diff --git a/Resources/images/voip_call_forward.png b/Resources/images/voip_call_forward.png
deleted file mode 100644
index cc61de482..000000000
Binary files a/Resources/images/voip_call_forward.png and /dev/null differ
diff --git a/Resources/images/voip_call_header_active.png b/Resources/images/voip_call_header_active.png
deleted file mode 100644
index 3ef0106bc..000000000
Binary files a/Resources/images/voip_call_header_active.png and /dev/null differ
diff --git a/Resources/images/voip_call_header_incoming.png b/Resources/images/voip_call_header_incoming.png
deleted file mode 100644
index 7a8d458ad..000000000
Binary files a/Resources/images/voip_call_header_incoming.png and /dev/null differ
diff --git a/Resources/images/voip_call_header_outgoing.png b/Resources/images/voip_call_header_outgoing.png
deleted file mode 100644
index 474abe754..000000000
Binary files a/Resources/images/voip_call_header_outgoing.png and /dev/null differ
diff --git a/Resources/images/voip_call_header_paused.png b/Resources/images/voip_call_header_paused.png
deleted file mode 100644
index fdcfaf5d2..000000000
Binary files a/Resources/images/voip_call_header_paused.png and /dev/null differ
diff --git a/Resources/images/voip_call_list_menu.png b/Resources/images/voip_call_list_menu.png
deleted file mode 100644
index fdd82bea6..000000000
Binary files a/Resources/images/voip_call_list_menu.png and /dev/null differ
diff --git a/Resources/images/voip_call_more.png b/Resources/images/voip_call_more.png
deleted file mode 100644
index 73bb13b36..000000000
Binary files a/Resources/images/voip_call_more.png and /dev/null differ
diff --git a/Resources/images/voip_call_numpad.png b/Resources/images/voip_call_numpad.png
deleted file mode 100644
index a0fd33835..000000000
Binary files a/Resources/images/voip_call_numpad.png and /dev/null differ
diff --git a/Resources/images/voip_call_participants.png b/Resources/images/voip_call_participants.png
deleted file mode 100644
index b2b766b14..000000000
Binary files a/Resources/images/voip_call_participants.png and /dev/null differ
diff --git a/Resources/images/voip_call_record.png b/Resources/images/voip_call_record.png
deleted file mode 100644
index 9c392dc47..000000000
Binary files a/Resources/images/voip_call_record.png and /dev/null differ
diff --git a/Resources/images/voip_call_stats.png b/Resources/images/voip_call_stats.png
deleted file mode 100644
index 3dd39d43b..000000000
Binary files a/Resources/images/voip_call_stats.png and /dev/null differ
diff --git a/Resources/images/voip_calls_list.png b/Resources/images/voip_calls_list.png
deleted file mode 100644
index a3d69b84e..000000000
Binary files a/Resources/images/voip_calls_list.png and /dev/null differ
diff --git a/Resources/images/voip_camera_off.png b/Resources/images/voip_camera_off.png
deleted file mode 100644
index dd41c338b..000000000
Binary files a/Resources/images/voip_camera_off.png and /dev/null differ
diff --git a/Resources/images/voip_camera_on.png b/Resources/images/voip_camera_on.png
deleted file mode 100644
index 7109c577d..000000000
Binary files a/Resources/images/voip_camera_on.png and /dev/null differ
diff --git a/Resources/images/voip_cancel.png b/Resources/images/voip_cancel.png
deleted file mode 100644
index 493b35e79..000000000
Binary files a/Resources/images/voip_cancel.png and /dev/null differ
diff --git a/Resources/images/voip_change_camera.png b/Resources/images/voip_change_camera.png
deleted file mode 100644
index d6dc15cb8..000000000
Binary files a/Resources/images/voip_change_camera.png and /dev/null differ
diff --git a/Resources/images/voip_chat_rooms_list.png b/Resources/images/voip_chat_rooms_list.png
deleted file mode 100644
index edf722c7e..000000000
Binary files a/Resources/images/voip_chat_rooms_list.png and /dev/null differ
diff --git a/Resources/images/voip_conference_active_speaker.png b/Resources/images/voip_conference_active_speaker.png
deleted file mode 100644
index 18f21106a..000000000
Binary files a/Resources/images/voip_conference_active_speaker.png and /dev/null differ
diff --git a/Resources/images/voip_conference_mosaic.png b/Resources/images/voip_conference_mosaic.png
deleted file mode 100644
index 8fa0137b7..000000000
Binary files a/Resources/images/voip_conference_mosaic.png and /dev/null differ
diff --git a/Resources/images/voip_conference_new.png b/Resources/images/voip_conference_new.png
deleted file mode 100644
index 8985782ae..000000000
Binary files a/Resources/images/voip_conference_new.png and /dev/null differ
diff --git a/Resources/images/voip_conference_paused_big.png b/Resources/images/voip_conference_paused_big.png
deleted file mode 100644
index 745f17220..000000000
Binary files a/Resources/images/voip_conference_paused_big.png and /dev/null differ
diff --git a/Resources/images/voip_conference_play_big.png b/Resources/images/voip_conference_play_big.png
deleted file mode 100644
index 303d05faa..000000000
Binary files a/Resources/images/voip_conference_play_big.png and /dev/null differ
diff --git a/Resources/images/voip_copy.png b/Resources/images/voip_copy.png
deleted file mode 100644
index 43639693e..000000000
Binary files a/Resources/images/voip_copy.png and /dev/null differ
diff --git a/Resources/images/voip_delete.png b/Resources/images/voip_delete.png
deleted file mode 100644
index 3022d156d..000000000
Binary files a/Resources/images/voip_delete.png and /dev/null differ
diff --git a/Resources/images/voip_dropdown.png b/Resources/images/voip_dropdown.png
deleted file mode 100644
index d9fccac91..000000000
Binary files a/Resources/images/voip_dropdown.png and /dev/null differ
diff --git a/Resources/images/voip_earpiece.png b/Resources/images/voip_earpiece.png
deleted file mode 100644
index e95a30801..000000000
Binary files a/Resources/images/voip_earpiece.png and /dev/null differ
diff --git a/Resources/images/voip_edit.png b/Resources/images/voip_edit.png
deleted file mode 100644
index c24930212..000000000
Binary files a/Resources/images/voip_edit.png and /dev/null differ
diff --git a/Resources/images/voip_export.png b/Resources/images/voip_export.png
deleted file mode 100644
index 3fdfa078a..000000000
Binary files a/Resources/images/voip_export.png and /dev/null differ
diff --git a/Resources/images/voip_hangup.png b/Resources/images/voip_hangup.png
deleted file mode 100644
index a2ceab5d8..000000000
Binary files a/Resources/images/voip_hangup.png and /dev/null differ
diff --git a/Resources/images/voip_info.png b/Resources/images/voip_info.png
deleted file mode 100644
index ae8dd86f8..000000000
Binary files a/Resources/images/voip_info.png and /dev/null differ
diff --git a/Resources/images/voip_mandatory.png b/Resources/images/voip_mandatory.png
deleted file mode 100644
index 4be37f7e4..000000000
Binary files a/Resources/images/voip_mandatory.png and /dev/null differ
diff --git a/Resources/images/voip_menu_more.png b/Resources/images/voip_menu_more.png
deleted file mode 100644
index 7f7f7d8fd..000000000
Binary files a/Resources/images/voip_menu_more.png and /dev/null differ
diff --git a/Resources/images/voip_merge_calls.png b/Resources/images/voip_merge_calls.png
deleted file mode 100644
index 6c4da5988..000000000
Binary files a/Resources/images/voip_merge_calls.png and /dev/null differ
diff --git a/Resources/images/voip_micro_off.png b/Resources/images/voip_micro_off.png
deleted file mode 100644
index 57569b4f2..000000000
Binary files a/Resources/images/voip_micro_off.png and /dev/null differ
diff --git a/Resources/images/voip_micro_on.png b/Resources/images/voip_micro_on.png
deleted file mode 100644
index 6552d35d5..000000000
Binary files a/Resources/images/voip_micro_on.png and /dev/null differ
diff --git a/Resources/images/voip_multiple_contacts_avatar.png b/Resources/images/voip_multiple_contacts_avatar.png
deleted file mode 100644
index 78b10f11b..000000000
Binary files a/Resources/images/voip_multiple_contacts_avatar.png and /dev/null differ
diff --git a/Resources/images/voip_numpad_0.png b/Resources/images/voip_numpad_0.png
deleted file mode 100644
index 115bdb17d..000000000
Binary files a/Resources/images/voip_numpad_0.png and /dev/null differ
diff --git a/Resources/images/voip_numpad_1.png b/Resources/images/voip_numpad_1.png
deleted file mode 100644
index 4d8b7f5cc..000000000
Binary files a/Resources/images/voip_numpad_1.png and /dev/null differ
diff --git a/Resources/images/voip_numpad_2.png b/Resources/images/voip_numpad_2.png
deleted file mode 100644
index 6b561c468..000000000
Binary files a/Resources/images/voip_numpad_2.png and /dev/null differ
diff --git a/Resources/images/voip_numpad_3.png b/Resources/images/voip_numpad_3.png
deleted file mode 100644
index 386715586..000000000
Binary files a/Resources/images/voip_numpad_3.png and /dev/null differ
diff --git a/Resources/images/voip_numpad_4.png b/Resources/images/voip_numpad_4.png
deleted file mode 100644
index e3dfdcc51..000000000
Binary files a/Resources/images/voip_numpad_4.png and /dev/null differ
diff --git a/Resources/images/voip_numpad_5.png b/Resources/images/voip_numpad_5.png
deleted file mode 100644
index a18af28e5..000000000
Binary files a/Resources/images/voip_numpad_5.png and /dev/null differ
diff --git a/Resources/images/voip_numpad_6.png b/Resources/images/voip_numpad_6.png
deleted file mode 100644
index 79279cb99..000000000
Binary files a/Resources/images/voip_numpad_6.png and /dev/null differ
diff --git a/Resources/images/voip_numpad_7.png b/Resources/images/voip_numpad_7.png
deleted file mode 100644
index c68656fd3..000000000
Binary files a/Resources/images/voip_numpad_7.png and /dev/null differ
diff --git a/Resources/images/voip_numpad_8.png b/Resources/images/voip_numpad_8.png
deleted file mode 100644
index 8d84c96ba..000000000
Binary files a/Resources/images/voip_numpad_8.png and /dev/null differ
diff --git a/Resources/images/voip_numpad_9.png b/Resources/images/voip_numpad_9.png
deleted file mode 100644
index af3e0e0bf..000000000
Binary files a/Resources/images/voip_numpad_9.png and /dev/null differ
diff --git a/Resources/images/voip_numpad_hash.png b/Resources/images/voip_numpad_hash.png
deleted file mode 100644
index 790e7d12e..000000000
Binary files a/Resources/images/voip_numpad_hash.png and /dev/null differ
diff --git a/Resources/images/voip_numpad_star.png b/Resources/images/voip_numpad_star.png
deleted file mode 100644
index 5a2649de4..000000000
Binary files a/Resources/images/voip_numpad_star.png and /dev/null differ
diff --git a/Resources/images/voip_pause.png b/Resources/images/voip_pause.png
deleted file mode 100644
index e888da937..000000000
Binary files a/Resources/images/voip_pause.png and /dev/null differ
diff --git a/Resources/images/voip_radio_off.png b/Resources/images/voip_radio_off.png
deleted file mode 100644
index b703dea80..000000000
Binary files a/Resources/images/voip_radio_off.png and /dev/null differ
diff --git a/Resources/images/voip_radio_on.png b/Resources/images/voip_radio_on.png
deleted file mode 100644
index feaf00e11..000000000
Binary files a/Resources/images/voip_radio_on.png and /dev/null differ
diff --git a/Resources/images/voip_remote_recording.png b/Resources/images/voip_remote_recording.png
deleted file mode 100644
index 3e4e94009..000000000
Binary files a/Resources/images/voip_remote_recording.png and /dev/null differ
diff --git a/Resources/images/voip_single_contact_avatar.png b/Resources/images/voip_single_contact_avatar.png
deleted file mode 100644
index 5d158fa49..000000000
Binary files a/Resources/images/voip_single_contact_avatar.png and /dev/null differ
diff --git a/Resources/images/voip_speaker_off.png b/Resources/images/voip_speaker_off.png
deleted file mode 100644
index c018c9899..000000000
Binary files a/Resources/images/voip_speaker_off.png and /dev/null differ
diff --git a/Resources/images/voip_speaker_on.png b/Resources/images/voip_speaker_on.png
deleted file mode 100644
index 07f13c9b6..000000000
Binary files a/Resources/images/voip_speaker_on.png and /dev/null differ
diff --git a/Resources/images/voip_spinner.png b/Resources/images/voip_spinner.png
deleted file mode 100644
index 8238de56b..000000000
Binary files a/Resources/images/voip_spinner.png and /dev/null differ
diff --git a/linphone-Info.plist b/linphone-Info.plist
index b9d24c1fb..8bd93f866 100644
--- a/linphone-Info.plist
+++ b/linphone-Info.plist
@@ -102,8 +102,6 @@
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
@@ -112,6 +110,8 @@
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,12 +126,6 @@
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 907c0c136..fbe3126e2 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,6 +25,8 @@
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 */; };
@@ -51,8 +53,10 @@
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 */; };
@@ -105,6 +109,7 @@
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 */; };
@@ -558,7 +563,11 @@
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 */; };
@@ -568,6 +577,7 @@
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 */; };
@@ -575,6 +585,7 @@
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 */; };
@@ -598,6 +609,7 @@
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 */; };
@@ -610,12 +622,16 @@
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 /* (null) in Resources */ = {isa = PBXBuildFile; };
+ 63E802DB1C625AEF000D5509 /* BuildFile 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 */; };
@@ -654,11 +670,8 @@
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 */; };
- 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 */; };
+ 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 */; };
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 */; };
@@ -668,105 +681,14 @@
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 */; };
@@ -776,46 +698,13 @@
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 */; };
@@ -832,6 +721,7 @@
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 */; };
@@ -846,6 +736,7 @@
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 */; };
@@ -878,8 +769,10 @@
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 */; };
@@ -890,10 +783,11 @@
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 */; };
@@ -965,10 +859,8 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
- 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 = ""; };
+ 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 = ""; };
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 = ""; };
@@ -977,6 +869,8 @@
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; };
@@ -994,12 +888,15 @@
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 = ""; };
@@ -1008,6 +905,8 @@
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 = ""; };
@@ -1028,14 +927,16 @@
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; };
- 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 = ""; };
+ 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 = ""; };
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; };
- 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 = ""; };
+ 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 = ""; };
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 = ""; };
@@ -1055,7 +956,6 @@
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 = ""; };
@@ -1100,6 +1000,10 @@
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 = ""; };
@@ -1114,6 +1018,8 @@
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 = ""; };
@@ -1613,8 +1519,14 @@
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 = ""; };
@@ -1631,6 +1543,7 @@
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 = ""; };
@@ -1639,6 +1552,7 @@
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 = ""; };
@@ -1672,6 +1586,8 @@
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 = ""; };
@@ -1695,13 +1611,30 @@
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; };
- 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 = ""; };
+ 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 = ""; };
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 = ""; };
@@ -1738,6 +1671,10 @@
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 = "