diff --git a/.gitignore b/.gitignore index 1dc8b16..99358f2 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,7 @@ java/.idea/modules.xml java/.idea/workspace.xml java/.idea/navEditor.xml java/.idea/assetWizardSettings.xml -java/.DS_Store +.DS_Store java/build java/captures java/.externalNativeBuild @@ -26,9 +26,10 @@ AppPackages [Oo]bj/ [Bb]in/ -swift/HelloLinphone/Podfile.lock -swift/HelloLinphone/Pods/ -swift/HelloLinphone/HelloLinphone.xcodeproj/project.xcworkspace/xcuserdata/ -swift/HelloLinphone/HelloLinphone.xcodeproj/xcuserdata/ -swift/HelloLinphone/HelloLinphone.xcworkspace/xcuserdata/ +ios/swift/*/Podfile.lock +ios/swift/*/Pods/ +ios/swift/*/*.xcodeproj/project.xcworkspace/xcuserdata/ +ios/swift/*/*.xcodeproj/xcuserdata/ +ios/swift/*/*.xcworkspace/xcuserdata/ +ios/swift/*/build diff --git a/ios/swift/0-Helloworld/Helloworld.xcodeproj/project.pbxproj b/ios/swift/0-Helloworld/Helloworld.xcodeproj/project.pbxproj new file mode 100644 index 0000000..e48ebdb --- /dev/null +++ b/ios/swift/0-Helloworld/Helloworld.xcodeproj/project.pbxproj @@ -0,0 +1,424 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 663D8CDF26E8E35400EE487F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663D8CDE26E8E35400EE487F /* AppDelegate.swift */; }; + 663D8CE126E8E35400EE487F /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663D8CE026E8E35400EE487F /* SceneDelegate.swift */; }; + 663D8CE326E8E35400EE487F /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663D8CE226E8E35400EE487F /* ContentView.swift */; }; + 663D8CE526E8E35500EE487F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 663D8CE426E8E35500EE487F /* Assets.xcassets */; }; + 663D8CE826E8E35500EE487F /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 663D8CE726E8E35500EE487F /* Preview Assets.xcassets */; }; + 663D8CEB26E8E35500EE487F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 663D8CE926E8E35500EE487F /* LaunchScreen.storyboard */; }; + 663D8CF326E8E73700EE487F /* HelloworldTutorial.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663D8CF226E8E73700EE487F /* HelloworldTutorial.swift */; }; + AE5DCE54FB07B00CDB39CF8C /* Pods_Helloworld.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 658B93FC56A6311767BE2F24 /* Pods_Helloworld.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 1D8A919C73E1D36DB079968D /* Pods-Helloworld.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Helloworld.debug.xcconfig"; path = "Target Support Files/Pods-Helloworld/Pods-Helloworld.debug.xcconfig"; sourceTree = ""; }; + 658B93FC56A6311767BE2F24 /* Pods_Helloworld.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Helloworld.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 663D8CDB26E8E35400EE487F /* Helloworld.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Helloworld.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 663D8CDE26E8E35400EE487F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 663D8CE026E8E35400EE487F /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 663D8CE226E8E35400EE487F /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 663D8CE426E8E35500EE487F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 663D8CE726E8E35500EE487F /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 663D8CEA26E8E35500EE487F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 663D8CEC26E8E35500EE487F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 663D8CF226E8E73700EE487F /* HelloworldTutorial.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = HelloworldTutorial.swift; path = ../../../../Downloads/HelloworldTutorial.swift; sourceTree = ""; }; + B14B29CDA5B52FB0218CB545 /* Pods-Helloworld.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Helloworld.release.xcconfig"; path = "Target Support Files/Pods-Helloworld/Pods-Helloworld.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 663D8CD826E8E35400EE487F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + AE5DCE54FB07B00CDB39CF8C /* Pods_Helloworld.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 663D8CD226E8E35400EE487F = { + isa = PBXGroup; + children = ( + 663D8CDD26E8E35400EE487F /* Helloworld */, + 663D8CDC26E8E35400EE487F /* Products */, + FBA8E9BB676F4A0224467983 /* Pods */, + 930DDB069926306D8F86A1DF /* Frameworks */, + ); + sourceTree = ""; + }; + 663D8CDC26E8E35400EE487F /* Products */ = { + isa = PBXGroup; + children = ( + 663D8CDB26E8E35400EE487F /* Helloworld.app */, + ); + name = Products; + sourceTree = ""; + }; + 663D8CDD26E8E35400EE487F /* Helloworld */ = { + isa = PBXGroup; + children = ( + 663D8CDE26E8E35400EE487F /* AppDelegate.swift */, + 663D8CE026E8E35400EE487F /* SceneDelegate.swift */, + 663D8CE226E8E35400EE487F /* ContentView.swift */, + 663D8CE426E8E35500EE487F /* Assets.xcassets */, + 663D8CE926E8E35500EE487F /* LaunchScreen.storyboard */, + 663D8CEC26E8E35500EE487F /* Info.plist */, + 663D8CE626E8E35500EE487F /* Preview Content */, + 663D8CF226E8E73700EE487F /* HelloworldTutorial.swift */, + ); + path = Helloworld; + sourceTree = ""; + }; + 663D8CE626E8E35500EE487F /* Preview Content */ = { + isa = PBXGroup; + children = ( + 663D8CE726E8E35500EE487F /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 930DDB069926306D8F86A1DF /* Frameworks */ = { + isa = PBXGroup; + children = ( + 658B93FC56A6311767BE2F24 /* Pods_Helloworld.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + FBA8E9BB676F4A0224467983 /* Pods */ = { + isa = PBXGroup; + children = ( + 1D8A919C73E1D36DB079968D /* Pods-Helloworld.debug.xcconfig */, + B14B29CDA5B52FB0218CB545 /* Pods-Helloworld.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 663D8CDA26E8E35400EE487F /* Helloworld */ = { + isa = PBXNativeTarget; + buildConfigurationList = 663D8CEF26E8E35500EE487F /* Build configuration list for PBXNativeTarget "Helloworld" */; + buildPhases = ( + 65BF27D12FE9B5322F627C5F /* [CP] Check Pods Manifest.lock */, + 663D8CD726E8E35400EE487F /* Sources */, + 663D8CD826E8E35400EE487F /* Frameworks */, + 663D8CD926E8E35400EE487F /* Resources */, + 6D3F26BE93D3EC1C45D2BDAE /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Helloworld; + productName = Helloworld; + productReference = 663D8CDB26E8E35400EE487F /* Helloworld.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 663D8CD326E8E35400EE487F /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1250; + LastUpgradeCheck = 1250; + TargetAttributes = { + 663D8CDA26E8E35400EE487F = { + CreatedOnToolsVersion = 12.5.1; + }; + }; + }; + buildConfigurationList = 663D8CD626E8E35400EE487F /* Build configuration list for PBXProject "Helloworld" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 663D8CD226E8E35400EE487F; + productRefGroup = 663D8CDC26E8E35400EE487F /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 663D8CDA26E8E35400EE487F /* Helloworld */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 663D8CD926E8E35400EE487F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 663D8CEB26E8E35500EE487F /* LaunchScreen.storyboard in Resources */, + 663D8CE826E8E35500EE487F /* Preview Assets.xcassets in Resources */, + 663D8CE526E8E35500EE487F /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 65BF27D12FE9B5322F627C5F /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Helloworld-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 6D3F26BE93D3EC1C45D2BDAE /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Helloworld/Pods-Helloworld-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Helloworld/Pods-Helloworld-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Helloworld/Pods-Helloworld-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 663D8CD726E8E35400EE487F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 663D8CDF26E8E35400EE487F /* AppDelegate.swift in Sources */, + 663D8CE126E8E35400EE487F /* SceneDelegate.swift in Sources */, + 663D8CE326E8E35400EE487F /* ContentView.swift in Sources */, + 663D8CF326E8E73700EE487F /* HelloworldTutorial.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 663D8CE926E8E35500EE487F /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 663D8CEA26E8E35500EE487F /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 663D8CED26E8E35500EE487F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 663D8CEE26E8E35500EE487F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 663D8CF026E8E35500EE487F /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 1D8A919C73E1D36DB079968D /* Pods-Helloworld.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"Helloworld/Preview Content\""; + DEVELOPMENT_TEAM = Z2V957B3D6; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = Helloworld/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = BC.Helloworld; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 663D8CF126E8E35500EE487F /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B14B29CDA5B52FB0218CB545 /* Pods-Helloworld.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"Helloworld/Preview Content\""; + DEVELOPMENT_TEAM = Z2V957B3D6; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = Helloworld/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = BC.Helloworld; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 663D8CD626E8E35400EE487F /* Build configuration list for PBXProject "Helloworld" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 663D8CED26E8E35500EE487F /* Debug */, + 663D8CEE26E8E35500EE487F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 663D8CEF26E8E35500EE487F /* Build configuration list for PBXNativeTarget "Helloworld" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 663D8CF026E8E35500EE487F /* Debug */, + 663D8CF126E8E35500EE487F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 663D8CD326E8E35400EE487F /* Project object */; +} diff --git a/swift/HelloLinphone/HelloLinphone.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/swift/0-Helloworld/Helloworld.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 68% rename from swift/HelloLinphone/HelloLinphone.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to ios/swift/0-Helloworld/Helloworld.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 275987e..919434a 100644 --- a/swift/HelloLinphone/HelloLinphone.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/ios/swift/0-Helloworld/Helloworld.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/swift/HelloLinphone/HelloLinphone.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/swift/0-Helloworld/Helloworld.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from swift/HelloLinphone/HelloLinphone.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to ios/swift/0-Helloworld/Helloworld.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/swift/HelloLinphone/HelloLinphone/AppDelegate.swift b/ios/swift/0-Helloworld/Helloworld/AppDelegate.swift similarity index 88% rename from swift/HelloLinphone/HelloLinphone/AppDelegate.swift rename to ios/swift/0-Helloworld/Helloworld/AppDelegate.swift index 9f1f5d7..fde9805 100644 --- a/swift/HelloLinphone/HelloLinphone/AppDelegate.swift +++ b/ios/swift/0-Helloworld/Helloworld/AppDelegate.swift @@ -1,17 +1,17 @@ // // AppDelegate.swift -// HelloLinphone +// Helloworld // -// Created by Danmei Chen on 23/06/2020. -// Copyright © 2020 belledonne. All rights reserved. +// Created by QuentinArguillere on 08/09/2021. // import UIKit +import SwiftUI -@UIApplicationMain +@main class AppDelegate: UIResponder, UIApplicationDelegate { - - + + @ObservedObject var tutorialContext = HelloworldTutorialContext() func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. diff --git a/ios/swift/0-Helloworld/Helloworld/Assets.xcassets/AccentColor.colorset/Contents.json b/ios/swift/0-Helloworld/Helloworld/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/ios/swift/0-Helloworld/Helloworld/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/swift/HelloLinphone/HelloLinphone/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/swift/0-Helloworld/Helloworld/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from swift/HelloLinphone/HelloLinphone/Assets.xcassets/AppIcon.appiconset/Contents.json rename to ios/swift/0-Helloworld/Helloworld/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/swift/HelloLinphone/HelloLinphone/Assets.xcassets/Contents.json b/ios/swift/0-Helloworld/Helloworld/Assets.xcassets/Contents.json similarity index 100% rename from swift/HelloLinphone/HelloLinphone/Assets.xcassets/Contents.json rename to ios/swift/0-Helloworld/Helloworld/Assets.xcassets/Contents.json diff --git a/swift/HelloLinphone/HelloLinphone/Base.lproj/LaunchScreen.storyboard b/ios/swift/0-Helloworld/Helloworld/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from swift/HelloLinphone/HelloLinphone/Base.lproj/LaunchScreen.storyboard rename to ios/swift/0-Helloworld/Helloworld/Base.lproj/LaunchScreen.storyboard diff --git a/ios/swift/0-Helloworld/Helloworld/ContentView.swift b/ios/swift/0-Helloworld/Helloworld/ContentView.swift new file mode 100644 index 0000000..d09995c --- /dev/null +++ b/ios/swift/0-Helloworld/Helloworld/ContentView.swift @@ -0,0 +1,32 @@ +// +// ContentView.swift +// HelloworldTutorial +// +// Created by QuentinArguillere on 08/09/2021. +// Copyright © 2021 BelledonneCommunications. All rights reserved. +// + +import SwiftUI + +struct ContentView: View { + + @ObservedObject var tutorialContext : HelloworldTutorialContext + + var body: some View { + + VStack { + Group { + Spacer() + Text("Hello World ! \nCore Version is \(tutorialContext.coreVersion)") + Spacer() + } + } + .padding() + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView(tutorialContext: HelloworldTutorialContext()) + } +} diff --git a/ios/swift/0-Helloworld/Helloworld/Info.plist b/ios/swift/0-Helloworld/Helloworld/Info.plist new file mode 100644 index 0000000..2688b32 --- /dev/null +++ b/ios/swift/0-Helloworld/Helloworld/Info.plist @@ -0,0 +1,62 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/swift/HelloLinphone/HelloLinphone/Preview Content/Preview Assets.xcassets/Contents.json b/ios/swift/0-Helloworld/Helloworld/Preview Content/Preview Assets.xcassets/Contents.json similarity index 100% rename from swift/HelloLinphone/HelloLinphone/Preview Content/Preview Assets.xcassets/Contents.json rename to ios/swift/0-Helloworld/Helloworld/Preview Content/Preview Assets.xcassets/Contents.json diff --git a/swift/HelloLinphone/HelloLinphone/SceneDelegate.swift b/ios/swift/0-Helloworld/Helloworld/SceneDelegate.swift similarity index 68% rename from swift/HelloLinphone/HelloLinphone/SceneDelegate.swift rename to ios/swift/0-Helloworld/Helloworld/SceneDelegate.swift index 240a757..5699f23 100644 --- a/swift/HelloLinphone/HelloLinphone/SceneDelegate.swift +++ b/ios/swift/0-Helloworld/Helloworld/SceneDelegate.swift @@ -1,19 +1,16 @@ // // SceneDelegate.swift -// HelloLinphone +// Helloworld // -// Created by Danmei Chen on 23/06/2020. -// Copyright © 2020 belledonne. All rights reserved. +// Created by QuentinArguillere on 08/09/2021. // import UIKit import SwiftUI -import linphonesw class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? - var mCore: Core! func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { @@ -21,23 +18,17 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). - // Core is the main object of the SDK. You can't do much without it. - // To create a Core, we need the instance of the Factory. - let factory = Factory.Instance - - // Your Core can use up to 2 configuration files, but that isn't mandatory. - // On ios the Core doesn't need to have the application context to work. - try? mCore = factory.createCore(configPath: "", factoryConfigPath: "", systemContext: nil) + let delegate = UIApplication.shared.delegate as! AppDelegate // Create the SwiftUI view that provides the window contents. - let contentView = ContentView(coreVersion: Core.getVersion) + let contentView = ContentView(tutorialContext: delegate.tutorialContext) // Use a UIHostingController as window root view controller. if let windowScene = scene as? UIWindowScene { - let window = UIWindow(windowScene: windowScene) - window.rootViewController = UIHostingController(rootView: contentView) - self.window = window - window.makeKeyAndVisible() + let window = UIWindow(windowScene: windowScene) + window.rootViewController = UIHostingController(rootView: contentView) + self.window = window + window.makeKeyAndVisible() } } @@ -45,7 +36,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // Called as the scene is being released by the system. // This occurs shortly after the scene enters the background, or when its session is discarded. // Release any resources associated with this scene that can be re-created the next time the scene connects. - // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). + // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). } func sceneDidBecomeActive(_ scene: UIScene) { diff --git a/ios/swift/0-Helloworld/Podfile b/ios/swift/0-Helloworld/Podfile new file mode 100644 index 0000000..14032bf --- /dev/null +++ b/ios/swift/0-Helloworld/Podfile @@ -0,0 +1,24 @@ +# Uncomment the next line to define a global platform for your project +platform :ios, '11.0' +source "https://gitlab.linphone.org/BC/public/podspec.git" +source "https://github.com/CocoaPods/Specs.git" + +def basic_pods + if ENV['PODFILE_PATH'].nil? + pod 'linphone-sdk', '~> 5.0.0' + else + pod 'linphone-sdk', :path => ENV['PODFILE_PATH'] # local sdk + end + +end + + + +target 'Helloworld' do + # Comment the next line if you don't want to use dynamic frameworks + use_frameworks! + + # Pods for Helloworld + basic_pods + +end diff --git a/ios/swift/0-Helloworld/README.md b/ios/swift/0-Helloworld/README.md new file mode 100644 index 0000000..8767981 --- /dev/null +++ b/ios/swift/0-Helloworld/README.md @@ -0,0 +1,8 @@ +Hello World tutorial +==================== + +The purpose of this tutorial is to explain how to add our SDK as a dependency of an Swift project and how to create the `Core` object that all our APIs depends on. + +Start by taking a look at the `Podfile` file, and use the "pod install" command to create Helloworld.xcworkspace and build the app + +The user interface will only display the `Core`'s version, but in the next tutorial you will learn how to use it to login your SIP account. diff --git a/swift/HelloLinphone/HelloLinphone.xcodeproj/project.pbxproj b/ios/swift/1-LoginTutorial/LoginTutorial.xcodeproj/project.pbxproj similarity index 59% rename from swift/HelloLinphone/HelloLinphone.xcodeproj/project.pbxproj rename to ios/swift/1-LoginTutorial/LoginTutorial.xcodeproj/project.pbxproj index 7398130..2801467 100644 --- a/swift/HelloLinphone/HelloLinphone.xcodeproj/project.pbxproj +++ b/ios/swift/1-LoginTutorial/LoginTutorial.xcodeproj/project.pbxproj @@ -3,141 +3,143 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 51; objects = { /* Begin PBXBuildFile section */ - 6109A53324A1FB600080B339 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6109A53224A1FB600080B339 /* AppDelegate.swift */; }; - 6109A53524A1FB600080B339 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6109A53424A1FB600080B339 /* SceneDelegate.swift */; }; - 6109A53724A1FB600080B339 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6109A53624A1FB600080B339 /* ContentView.swift */; }; - 6109A53924A1FB610080B339 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6109A53824A1FB610080B339 /* Assets.xcassets */; }; - 6109A53C24A1FB610080B339 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6109A53B24A1FB610080B339 /* Preview Assets.xcassets */; }; - 6109A53F24A1FB610080B339 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6109A53D24A1FB610080B339 /* LaunchScreen.storyboard */; }; - 77A49EE3C36F606277338500 /* Pods_HelloLinphone.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C78D4B854746B756F138EC /* Pods_HelloLinphone.framework */; }; + 43EDF804909F295D8FD16E72 /* Pods_LoginTutorial.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52A14DA7DC9DFF94D4A572CE /* Pods_LoginTutorial.framework */; }; + 6604167924D4606A0064FC6C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6604167824D4606A0064FC6C /* AppDelegate.swift */; }; + 6604167B24D4606A0064FC6C /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6604167A24D4606A0064FC6C /* SceneDelegate.swift */; }; + 6604167D24D4606A0064FC6C /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6604167C24D4606A0064FC6C /* ContentView.swift */; }; + 6604167F24D4606B0064FC6C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6604167E24D4606B0064FC6C /* Assets.xcassets */; }; + 6604168224D4606B0064FC6C /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6604168124D4606B0064FC6C /* Preview Assets.xcassets */; }; + 6604168524D4606B0064FC6C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6604168324D4606B0064FC6C /* LaunchScreen.storyboard */; }; + 6604168D24D4607A0064FC6C /* LoginTutorial.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6604168C24D4607A0064FC6C /* LoginTutorial.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 58D449155D749B90DECEF33F /* Pods-HelloLinphone.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloLinphone.debug.xcconfig"; path = "Target Support Files/Pods-HelloLinphone/Pods-HelloLinphone.debug.xcconfig"; sourceTree = ""; }; - 6109A52F24A1FB600080B339 /* HelloLinphone.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloLinphone.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 6109A53224A1FB600080B339 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 6109A53424A1FB600080B339 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; - 6109A53624A1FB600080B339 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; - 6109A53824A1FB610080B339 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 6109A53B24A1FB610080B339 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; - 6109A53E24A1FB610080B339 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 6109A54024A1FB610080B339 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 77C78D4B854746B756F138EC /* Pods_HelloLinphone.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_HelloLinphone.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - CCABD171CB8B5F0B32B7C819 /* Pods-HelloLinphone.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloLinphone.release.xcconfig"; path = "Target Support Files/Pods-HelloLinphone/Pods-HelloLinphone.release.xcconfig"; sourceTree = ""; }; + 52A14DA7DC9DFF94D4A572CE /* Pods_LoginTutorial.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LoginTutorial.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 6604167524D4606A0064FC6C /* LoginTutorial.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LoginTutorial.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 6604167824D4606A0064FC6C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 6604167A24D4606A0064FC6C /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 6604167C24D4606A0064FC6C /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 6604167E24D4606B0064FC6C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 6604168124D4606B0064FC6C /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 6604168424D4606B0064FC6C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 6604168624D4606B0064FC6C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 6604168C24D4607A0064FC6C /* LoginTutorial.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginTutorial.swift; sourceTree = ""; }; + 86EBE739D84760246FCA17F1 /* Pods-LoginTutorial.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LoginTutorial.debug.xcconfig"; path = "Target Support Files/Pods-LoginTutorial/Pods-LoginTutorial.debug.xcconfig"; sourceTree = ""; }; + A3C6682B4C89F9C91835CCC0 /* Pods-LoginTutorial.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LoginTutorial.release.xcconfig"; path = "Target Support Files/Pods-LoginTutorial/Pods-LoginTutorial.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 6109A52C24A1FB600080B339 /* Frameworks */ = { + 6604167224D4606A0064FC6C /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 77A49EE3C36F606277338500 /* Pods_HelloLinphone.framework in Frameworks */, + 43EDF804909F295D8FD16E72 /* Pods_LoginTutorial.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 12F6903ABA1B6D98B920C503 /* Frameworks */ = { + 0A22ADFA8AED8923D454874F /* Pods */ = { isa = PBXGroup; children = ( - 77C78D4B854746B756F138EC /* Pods_HelloLinphone.framework */, + 86EBE739D84760246FCA17F1 /* Pods-LoginTutorial.debug.xcconfig */, + A3C6682B4C89F9C91835CCC0 /* Pods-LoginTutorial.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + 3200FE58EDFCC48A3C66678D /* Frameworks */ = { + isa = PBXGroup; + children = ( + 52A14DA7DC9DFF94D4A572CE /* Pods_LoginTutorial.framework */, ); name = Frameworks; sourceTree = ""; }; - 6109A52624A1FB600080B339 = { + 6604166C24D4606A0064FC6C = { isa = PBXGroup; children = ( - 6109A53124A1FB600080B339 /* HelloLinphone */, - 6109A53024A1FB600080B339 /* Products */, - 7DEB177E8F89B4C059EAD432 /* Pods */, - 12F6903ABA1B6D98B920C503 /* Frameworks */, + 6604167724D4606A0064FC6C /* LoginTutorial */, + 6604167624D4606A0064FC6C /* Products */, + 0A22ADFA8AED8923D454874F /* Pods */, + 3200FE58EDFCC48A3C66678D /* Frameworks */, ); sourceTree = ""; }; - 6109A53024A1FB600080B339 /* Products */ = { + 6604167624D4606A0064FC6C /* Products */ = { isa = PBXGroup; children = ( - 6109A52F24A1FB600080B339 /* HelloLinphone.app */, + 6604167524D4606A0064FC6C /* LoginTutorial.app */, ); name = Products; sourceTree = ""; }; - 6109A53124A1FB600080B339 /* HelloLinphone */ = { + 6604167724D4606A0064FC6C /* LoginTutorial */ = { isa = PBXGroup; children = ( - 6109A53224A1FB600080B339 /* AppDelegate.swift */, - 6109A53424A1FB600080B339 /* SceneDelegate.swift */, - 6109A53624A1FB600080B339 /* ContentView.swift */, - 6109A53824A1FB610080B339 /* Assets.xcassets */, - 6109A53D24A1FB610080B339 /* LaunchScreen.storyboard */, - 6109A54024A1FB610080B339 /* Info.plist */, - 6109A53A24A1FB610080B339 /* Preview Content */, + 6604167824D4606A0064FC6C /* AppDelegate.swift */, + 6604167A24D4606A0064FC6C /* SceneDelegate.swift */, + 6604168C24D4607A0064FC6C /* LoginTutorial.swift */, + 6604167C24D4606A0064FC6C /* ContentView.swift */, + 6604167E24D4606B0064FC6C /* Assets.xcassets */, + 6604168324D4606B0064FC6C /* LaunchScreen.storyboard */, + 6604168624D4606B0064FC6C /* Info.plist */, + 6604168024D4606B0064FC6C /* Preview Content */, ); - path = HelloLinphone; + path = LoginTutorial; sourceTree = ""; }; - 6109A53A24A1FB610080B339 /* Preview Content */ = { + 6604168024D4606B0064FC6C /* Preview Content */ = { isa = PBXGroup; children = ( - 6109A53B24A1FB610080B339 /* Preview Assets.xcassets */, + 6604168124D4606B0064FC6C /* Preview Assets.xcassets */, ); path = "Preview Content"; sourceTree = ""; }; - 7DEB177E8F89B4C059EAD432 /* Pods */ = { - isa = PBXGroup; - children = ( - 58D449155D749B90DECEF33F /* Pods-HelloLinphone.debug.xcconfig */, - CCABD171CB8B5F0B32B7C819 /* Pods-HelloLinphone.release.xcconfig */, - ); - name = Pods; - path = Pods; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 6109A52E24A1FB600080B339 /* HelloLinphone */ = { + 6604167424D4606A0064FC6C /* LoginTutorial */ = { isa = PBXNativeTarget; - buildConfigurationList = 6109A54324A1FB610080B339 /* Build configuration list for PBXNativeTarget "HelloLinphone" */; + buildConfigurationList = 6604168924D4606B0064FC6C /* Build configuration list for PBXNativeTarget "LoginTutorial" */; buildPhases = ( - F68B6F6ACD48C202C8DC4322 /* [CP] Check Pods Manifest.lock */, - 6109A52B24A1FB600080B339 /* Sources */, - 6109A52C24A1FB600080B339 /* Frameworks */, - 6109A52D24A1FB600080B339 /* Resources */, - 724EF46BD0CBE3A1971D77D0 /* [CP] Embed Pods Frameworks */, + 4EF45A68E9E85663FD1A4EC6 /* [CP] Check Pods Manifest.lock */, + 6604167124D4606A0064FC6C /* Sources */, + 6604167224D4606A0064FC6C /* Frameworks */, + 6604167324D4606A0064FC6C /* Resources */, + 0131188DB5E696F536B36946 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( ); - name = HelloLinphone; - productName = HelloLinphone; - productReference = 6109A52F24A1FB600080B339 /* HelloLinphone.app */; + name = LoginTutorial; + productName = LoginTutorial; + productReference = 6604167524D4606A0064FC6C /* LoginTutorial.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ - 6109A52724A1FB600080B339 /* Project object */ = { + 6604166D24D4606A0064FC6C /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1150; LastUpgradeCheck = 1150; - ORGANIZATIONNAME = belledonne; + ORGANIZATIONNAME = BelledonneCommunications; TargetAttributes = { - 6109A52E24A1FB600080B339 = { + 6604167424D4606A0064FC6C = { CreatedOnToolsVersion = 11.5; }; }; }; - buildConfigurationList = 6109A52A24A1FB600080B339 /* Build configuration list for PBXProject "HelloLinphone" */; + buildConfigurationList = 6604167024D4606A0064FC6C /* Build configuration list for PBXProject "LoginTutorial" */; compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; @@ -145,48 +147,48 @@ en, Base, ); - mainGroup = 6109A52624A1FB600080B339; - productRefGroup = 6109A53024A1FB600080B339 /* Products */; + mainGroup = 6604166C24D4606A0064FC6C; + productRefGroup = 6604167624D4606A0064FC6C /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( - 6109A52E24A1FB600080B339 /* HelloLinphone */, + 6604167424D4606A0064FC6C /* LoginTutorial */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 6109A52D24A1FB600080B339 /* Resources */ = { + 6604167324D4606A0064FC6C /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 6109A53F24A1FB610080B339 /* LaunchScreen.storyboard in Resources */, - 6109A53C24A1FB610080B339 /* Preview Assets.xcassets in Resources */, - 6109A53924A1FB610080B339 /* Assets.xcassets in Resources */, + 6604168524D4606B0064FC6C /* LaunchScreen.storyboard in Resources */, + 6604168224D4606B0064FC6C /* Preview Assets.xcassets in Resources */, + 6604167F24D4606B0064FC6C /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 724EF46BD0CBE3A1971D77D0 /* [CP] Embed Pods Frameworks */ = { + 0131188DB5E696F536B36946 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-HelloLinphone/Pods-HelloLinphone-frameworks-${CONFIGURATION}-input-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-LoginTutorial/Pods-LoginTutorial-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-HelloLinphone/Pods-HelloLinphone-frameworks-${CONFIGURATION}-output-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-LoginTutorial/Pods-LoginTutorial-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-HelloLinphone/Pods-HelloLinphone-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-LoginTutorial/Pods-LoginTutorial-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - F68B6F6ACD48C202C8DC4322 /* [CP] Check Pods Manifest.lock */ = { + 4EF45A68E9E85663FD1A4EC6 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -201,7 +203,7 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-HelloLinphone-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-LoginTutorial-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -211,23 +213,24 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 6109A52B24A1FB600080B339 /* Sources */ = { + 6604167124D4606A0064FC6C /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 6109A53324A1FB600080B339 /* AppDelegate.swift in Sources */, - 6109A53524A1FB600080B339 /* SceneDelegate.swift in Sources */, - 6109A53724A1FB600080B339 /* ContentView.swift in Sources */, + 6604167924D4606A0064FC6C /* AppDelegate.swift in Sources */, + 6604167B24D4606A0064FC6C /* SceneDelegate.swift in Sources */, + 6604168D24D4607A0064FC6C /* LoginTutorial.swift in Sources */, + 6604167D24D4606A0064FC6C /* ContentView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ - 6109A53D24A1FB610080B339 /* LaunchScreen.storyboard */ = { + 6604168324D4606B0064FC6C /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( - 6109A53E24A1FB610080B339 /* Base */, + 6604168424D4606B0064FC6C /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; @@ -235,7 +238,7 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ - 6109A54124A1FB610080B339 /* Debug */ = { + 6604168724D4606B0064FC6C /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; @@ -295,7 +298,7 @@ }; name = Debug; }; - 6109A54224A1FB610080B339 /* Release */ = { + 6604168824D4606B0064FC6C /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; @@ -349,42 +352,42 @@ }; name = Release; }; - 6109A54424A1FB610080B339 /* Debug */ = { + 6604168A24D4606B0064FC6C /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 58D449155D749B90DECEF33F /* Pods-HelloLinphone.debug.xcconfig */; + baseConfigurationReference = 86EBE739D84760246FCA17F1 /* Pods-LoginTutorial.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_ASSET_PATHS = "\"HelloLinphone/Preview Content\""; + DEVELOPMENT_ASSET_PATHS = "\"LoginTutorial/Preview Content\""; DEVELOPMENT_TEAM = Z2V957B3D6; ENABLE_PREVIEWS = YES; - INFOPLIST_FILE = HelloLinphone/Info.plist; + INFOPLIST_FILE = LoginTutorial/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = belledonne.HelloLinphone; + PRODUCT_BUNDLE_IDENTIFIER = BC.LoginTutorial; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; - 6109A54524A1FB610080B339 /* Release */ = { + 6604168B24D4606B0064FC6C /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = CCABD171CB8B5F0B32B7C819 /* Pods-HelloLinphone.release.xcconfig */; + baseConfigurationReference = A3C6682B4C89F9C91835CCC0 /* Pods-LoginTutorial.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_ASSET_PATHS = "\"HelloLinphone/Preview Content\""; + DEVELOPMENT_ASSET_PATHS = "\"LoginTutorial/Preview Content\""; DEVELOPMENT_TEAM = Z2V957B3D6; ENABLE_PREVIEWS = YES; - INFOPLIST_FILE = HelloLinphone/Info.plist; + INFOPLIST_FILE = LoginTutorial/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = belledonne.HelloLinphone; + PRODUCT_BUNDLE_IDENTIFIER = BC.LoginTutorial; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -394,25 +397,25 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 6109A52A24A1FB600080B339 /* Build configuration list for PBXProject "HelloLinphone" */ = { + 6604167024D4606A0064FC6C /* Build configuration list for PBXProject "LoginTutorial" */ = { isa = XCConfigurationList; buildConfigurations = ( - 6109A54124A1FB610080B339 /* Debug */, - 6109A54224A1FB610080B339 /* Release */, + 6604168724D4606B0064FC6C /* Debug */, + 6604168824D4606B0064FC6C /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 6109A54324A1FB610080B339 /* Build configuration list for PBXNativeTarget "HelloLinphone" */ = { + 6604168924D4606B0064FC6C /* Build configuration list for PBXNativeTarget "LoginTutorial" */ = { isa = XCConfigurationList; buildConfigurations = ( - 6109A54424A1FB610080B339 /* Debug */, - 6109A54524A1FB610080B339 /* Release */, + 6604168A24D4606B0064FC6C /* Debug */, + 6604168B24D4606B0064FC6C /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; - rootObject = 6109A52724A1FB600080B339 /* Project object */; + rootObject = 6604166D24D4606A0064FC6C /* Project object */; } diff --git a/ios/swift/1-LoginTutorial/LoginTutorial.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/swift/1-LoginTutorial/LoginTutorial.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..850b263 --- /dev/null +++ b/ios/swift/1-LoginTutorial/LoginTutorial.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/swift/HelloLinphone/HelloLinphone.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/swift/1-LoginTutorial/LoginTutorial.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from swift/HelloLinphone/HelloLinphone.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to ios/swift/1-LoginTutorial/LoginTutorial.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/ios/swift/1-LoginTutorial/LoginTutorial/AppDelegate.swift b/ios/swift/1-LoginTutorial/LoginTutorial/AppDelegate.swift new file mode 100644 index 0000000..920836e --- /dev/null +++ b/ios/swift/1-LoginTutorial/LoginTutorial/AppDelegate.swift @@ -0,0 +1,38 @@ +// +// AppDelegate.swift +// LoginTutorial +// +// Created by QuentinArguillere on 31/07/2020. +// Copyright © 2020 BelledonneCommunications. All rights reserved. +// + +import UIKit +import SwiftUI + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + @ObservedObject var tutorialContext = LoginTutorialContext() + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + +} + diff --git a/ios/swift/1-LoginTutorial/LoginTutorial/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/swift/1-LoginTutorial/LoginTutorial/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9221b9b --- /dev/null +++ b/ios/swift/1-LoginTutorial/LoginTutorial/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/swift/1-LoginTutorial/LoginTutorial/Assets.xcassets/Contents.json b/ios/swift/1-LoginTutorial/LoginTutorial/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/ios/swift/1-LoginTutorial/LoginTutorial/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/swift/1-LoginTutorial/LoginTutorial/Base.lproj/LaunchScreen.storyboard b/ios/swift/1-LoginTutorial/LoginTutorial/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..865e932 --- /dev/null +++ b/ios/swift/1-LoginTutorial/LoginTutorial/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/swift/1-LoginTutorial/LoginTutorial/ContentView.swift b/ios/swift/1-LoginTutorial/LoginTutorial/ContentView.swift new file mode 100644 index 0000000..8985de4 --- /dev/null +++ b/ios/swift/1-LoginTutorial/LoginTutorial/ContentView.swift @@ -0,0 +1,87 @@ +// +// ContentView.swift +// LoginTutorial +// +// Created by QuentinArguillere on 31/07/2020. +// Copyright © 2020 BelledonneCommunications. All rights reserved. +// + +import SwiftUI + +struct ContentView: View { + + @ObservedObject var tutorialContext : LoginTutorialContext + + var body: some View { + + VStack { + Group { + HStack { + Text("Username:") + .font(.title) + TextField("", text : $tutorialContext.username) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .disabled(tutorialContext.loggedIn) + } + HStack { + Text("Password:") + .font(.title) + TextField("", text : $tutorialContext.passwd) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .disabled(tutorialContext.loggedIn) + } + HStack { + Text("Domain:") + .font(.title) + TextField("", text : $tutorialContext.domain) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .disabled(tutorialContext.loggedIn) + } + Picker(selection: $tutorialContext.transportType, label: Text("Transport:")) { + Text("TLS").tag("TLS") + Text("TCP").tag("TCP") + Text("UDP").tag("UDP") + }.pickerStyle(SegmentedPickerStyle()).padding() + VStack { + HStack { + Button(action: { + if (self.tutorialContext.loggedIn) + { + self.tutorialContext.unregister() + self.tutorialContext.delete() + } else { + self.tutorialContext.login() + } + }) + { + Text(tutorialContext.loggedIn ? "Log out & \ndelete account" : "Create & \nlog in account") + .font(.largeTitle) + .foregroundColor(Color.white) + .frame(width: 220.0, height: 90) + .background(Color.gray) + } + + } + HStack { + Text("Login State : ") + .font(.footnote) + Text(tutorialContext.loggedIn ? "Looged in" : "Unregistered") + .font(.footnote) + .foregroundColor(tutorialContext.loggedIn ? Color.green : Color.black) + }.padding(.top, 10.0) + } + } + Group { + Spacer() + Text("Core Version is \(tutorialContext.coreVersion)") + } + } + .padding() + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView(tutorialContext: LoginTutorialContext()) + } +} diff --git a/swift/HelloLinphone/HelloLinphone/Info.plist b/ios/swift/1-LoginTutorial/LoginTutorial/Info.plist similarity index 100% rename from swift/HelloLinphone/HelloLinphone/Info.plist rename to ios/swift/1-LoginTutorial/LoginTutorial/Info.plist diff --git a/ios/swift/1-LoginTutorial/LoginTutorial/LoginTutorial.swift b/ios/swift/1-LoginTutorial/LoginTutorial/LoginTutorial.swift new file mode 100644 index 0000000..8590751 --- /dev/null +++ b/ios/swift/1-LoginTutorial/LoginTutorial/LoginTutorial.swift @@ -0,0 +1,128 @@ +// +// LoginExample.swift +// LoginTutorial +// +// Created by QuentinArguillere on 08/09/2021. +// Copyright © 2021 BelledonneCommunications. All rights reserved. +// + +import linphonesw + +class LoginTutorialContext : ObservableObject +{ + var mCore: Core! + @Published var coreVersion: String = Core.getVersion + + /*------------ Login tutorial related variables -------*/ + var mRegistrationDelegate : CoreDelegate! + @Published var username : String = "user" + @Published var passwd : String = "pwd" + @Published var domain : String = "sip.example.org" + @Published var loggedIn: Bool = false + @Published var transportType : String = "TLS" + + init() + { + + LoggingService.Instance.logLevel = LogLevel.Debug + + try? mCore = Factory.Instance.createCore(configPath: "", factoryConfigPath: "", systemContext: nil) + try? mCore.start() + + // Create a Core listener to listen for the callback we need + // In this case, we want to know about the account registration status + mRegistrationDelegate = CoreDelegateStub(onAccountRegistrationStateChanged: { (core: Core, account: Account, state: RegistrationState, message: String) in + + // If account has been configured correctly, we will go through Progress and Ok states + // Otherwise, we will be Failed. + NSLog("New registration state is \(state) for user id \( String(describing: account.params?.identityAddress?.asString()))\n") + if (state == .Ok) { + self.loggedIn = true + } else if (state == .Cleared) { + self.loggedIn = false + } + }) + mCore.addDelegate(delegate: mRegistrationDelegate) + } + + func login() { + + do { + // Get the transport protocol to use. + // TLS is strongly recommended + // Only use UDP if you don't have the choice + var transport : TransportType + if (transportType == "TLS") { transport = TransportType.Tls } + else if (transportType == "TCP") { transport = TransportType.Tcp } + else { transport = TransportType.Udp } + + // To configure a SIP account, we need an Account object and an AuthInfo object + // The first one is how to connect to the proxy server, the second one stores the credentials + + // The auth info can be created from the Factory as it's only a data class + // userID is set to null as it's the same as the username in our case + // ha1 is set to null as we are using the clear text password. Upon first register, the hash will be computed automatically. + // The realm will be determined automatically from the first register, as well as the algorithm + let authInfo = try Factory.Instance.createAuthInfo(username: username, userid: "", passwd: passwd, ha1: "", realm: "", domain: domain) + + // Account object replaces deprecated ProxyConfig object + // Account object is configured through an AccountParams object that we can obtain from the Core + let accountParams = try mCore.createAccountParams() + + // A SIP account is identified by an identity address that we can construct from the username and domain + let identity = try Factory.Instance.createAddress(addr: String("sip:" + username + "@" + domain)) + try! accountParams.setIdentityaddress(newValue: identity) + + // We also need to configure where the proxy server is located + let address = try Factory.Instance.createAddress(addr: String("sip:" + domain)) + + // We use the Address object to easily set the transport protocol + try address.setTransport(newValue: transport) + try accountParams.setServeraddress(newValue: address) + // And we ensure the account will start the registration process + accountParams.registerEnabled = true + + // Now that our AccountParams is configured, we can create the Account object + let account = try mCore.createAccount(params: accountParams) + + // Now let's add our objects to the Core + mCore.addAuthInfo(info: authInfo) + try mCore.addAccount(account: account) + + // Also set the newly added account as default + mCore.defaultAccount = account + + } catch { NSLog(error.localizedDescription) } + } + + func unregister() + { + // Here we will disable the registration of our Account + if let account = mCore.defaultAccount { + + let params = account.params + // Returned params object is const, so to make changes we first need to clone it + let clonedParams = params?.clone() + + // Now let's make our changes + clonedParams?.registerEnabled = false + + // And apply them + account.params = clonedParams + } + } + func delete() { + // To completely remove an Account + if let account = mCore.defaultAccount { + mCore.removeAccount(account: account) + + // To remove all accounts use + mCore.clearAccounts() + + // Same for auth info + mCore.clearAllAuthInfo() + } + } + + +} diff --git a/ios/swift/1-LoginTutorial/LoginTutorial/Preview Content/Preview Assets.xcassets/Contents.json b/ios/swift/1-LoginTutorial/LoginTutorial/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/ios/swift/1-LoginTutorial/LoginTutorial/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/swift/1-LoginTutorial/LoginTutorial/SceneDelegate.swift b/ios/swift/1-LoginTutorial/LoginTutorial/SceneDelegate.swift new file mode 100644 index 0000000..e90febe --- /dev/null +++ b/ios/swift/1-LoginTutorial/LoginTutorial/SceneDelegate.swift @@ -0,0 +1,65 @@ +// +// SceneDelegate.swift +// LoginTutorial +// +// Created by QuentinArguillere on 31/07/2020. +// Copyright © 2020 BelledonneCommunications. All rights reserved. +// + +import UIKit +import SwiftUI + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + + let delegate = UIApplication.shared.delegate as! AppDelegate + + // Create the SwiftUI view that provides the window contents. + let contentView = ContentView(tutorialContext: delegate.tutorialContext) + + // Use a UIHostingController as window root view controller. + if let windowScene = scene as? UIWindowScene { + let window = UIWindow(windowScene: windowScene) + window.rootViewController = UIHostingController(rootView: contentView) + self.window = window + window.makeKeyAndVisible() + } + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } + + +} + diff --git a/ios/swift/1-LoginTutorial/Podfile b/ios/swift/1-LoginTutorial/Podfile new file mode 100644 index 0000000..d33476d --- /dev/null +++ b/ios/swift/1-LoginTutorial/Podfile @@ -0,0 +1,24 @@ +# Uncomment the next line to define a global platform for your project +platform :ios, '11.0' +source "https://gitlab.linphone.org/BC/public/podspec.git" +source "https://github.com/CocoaPods/Specs.git" + +def basic_pods + if ENV['PODFILE_PATH'].nil? + pod 'linphone-sdk', '~> 5.0.0' + else + pod 'linphone-sdk', :path => ENV['PODFILE_PATH'] # local sdk + end + +end + + + +target 'LoginTutorial' do + # Comment the next line if you don't want to use dynamic frameworks + use_frameworks! + + # Pods for LoginTutorial + basic_pods + +end diff --git a/ios/swift/1-LoginTutorial/README.md b/ios/swift/1-LoginTutorial/README.md new file mode 100644 index 0000000..b03ef03 --- /dev/null +++ b/ios/swift/1-LoginTutorial/README.md @@ -0,0 +1,10 @@ +Account login tutorial +==================== + +Now that you have set up Linphone-SDK in a Swift project, let's start using it. + +We will see how to login on a SIP server using the `Core` object instanciated in the previous tutorial. As always, you will have to start with a "Pod install" to generate the xcworkspace for this app. + +If you don't have a SIP server yet, you can create an account for free using our [free SIP service](https://subscribe.linphone.org/). + +Once you'll be logged-in, you'll be able to continue to the next tutorials to make calls and send messages. diff --git a/ios/swift/2-IncomingCall/IncomingCall.xcodeproj/project.pbxproj b/ios/swift/2-IncomingCall/IncomingCall.xcodeproj/project.pbxproj new file mode 100644 index 0000000..62dcc46 --- /dev/null +++ b/ios/swift/2-IncomingCall/IncomingCall.xcodeproj/project.pbxproj @@ -0,0 +1,424 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 203CAD3D220FB2AEDD1C2463 /* Pods_IncomingCall.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F200A240C9F5BF773AF9024 /* Pods_IncomingCall.framework */; }; + 66BF265826E9FCC90048D176 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66BF265726E9FCC90048D176 /* AppDelegate.swift */; }; + 66BF265A26E9FCC90048D176 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66BF265926E9FCC90048D176 /* SceneDelegate.swift */; }; + 66BF265C26E9FCC90048D176 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66BF265B26E9FCC90048D176 /* ContentView.swift */; }; + 66BF265E26E9FCD00048D176 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 66BF265D26E9FCD00048D176 /* Assets.xcassets */; }; + 66BF266126E9FCD00048D176 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 66BF266026E9FCD00048D176 /* Preview Assets.xcassets */; }; + 66BF266426E9FCD00048D176 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 66BF266226E9FCD00048D176 /* LaunchScreen.storyboard */; }; + 66BF266C26EA003C0048D176 /* IncomingCall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66BF266B26EA003C0048D176 /* IncomingCall.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 1F200A240C9F5BF773AF9024 /* Pods_IncomingCall.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_IncomingCall.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 4B0753133024A1C67E84CE1C /* Pods-IncomingCall.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-IncomingCall.debug.xcconfig"; path = "Target Support Files/Pods-IncomingCall/Pods-IncomingCall.debug.xcconfig"; sourceTree = ""; }; + 4B835D0562384FC441266A58 /* Pods-IncomingCall.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-IncomingCall.release.xcconfig"; path = "Target Support Files/Pods-IncomingCall/Pods-IncomingCall.release.xcconfig"; sourceTree = ""; }; + 66BF265426E9FCC90048D176 /* IncomingCall.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = IncomingCall.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 66BF265726E9FCC90048D176 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 66BF265926E9FCC90048D176 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 66BF265B26E9FCC90048D176 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 66BF265D26E9FCD00048D176 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 66BF266026E9FCD00048D176 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 66BF266326E9FCD00048D176 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 66BF266526E9FCD00048D176 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 66BF266B26EA003C0048D176 /* IncomingCall.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IncomingCall.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 66BF265126E9FCC90048D176 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 203CAD3D220FB2AEDD1C2463 /* Pods_IncomingCall.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 521BEF3D6EE06929A69119A3 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1F200A240C9F5BF773AF9024 /* Pods_IncomingCall.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 66BF264B26E9FCC90048D176 = { + isa = PBXGroup; + children = ( + 66BF265626E9FCC90048D176 /* IncomingCall */, + 66BF265526E9FCC90048D176 /* Products */, + 8ED5175BFA4F710BD04F20C6 /* Pods */, + 521BEF3D6EE06929A69119A3 /* Frameworks */, + ); + sourceTree = ""; + }; + 66BF265526E9FCC90048D176 /* Products */ = { + isa = PBXGroup; + children = ( + 66BF265426E9FCC90048D176 /* IncomingCall.app */, + ); + name = Products; + sourceTree = ""; + }; + 66BF265626E9FCC90048D176 /* IncomingCall */ = { + isa = PBXGroup; + children = ( + 66BF265726E9FCC90048D176 /* AppDelegate.swift */, + 66BF265926E9FCC90048D176 /* SceneDelegate.swift */, + 66BF265B26E9FCC90048D176 /* ContentView.swift */, + 66BF265D26E9FCD00048D176 /* Assets.xcassets */, + 66BF266226E9FCD00048D176 /* LaunchScreen.storyboard */, + 66BF266526E9FCD00048D176 /* Info.plist */, + 66BF266B26EA003C0048D176 /* IncomingCall.swift */, + 66BF265F26E9FCD00048D176 /* Preview Content */, + ); + path = IncomingCall; + sourceTree = ""; + }; + 66BF265F26E9FCD00048D176 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 66BF266026E9FCD00048D176 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 8ED5175BFA4F710BD04F20C6 /* Pods */ = { + isa = PBXGroup; + children = ( + 4B0753133024A1C67E84CE1C /* Pods-IncomingCall.debug.xcconfig */, + 4B835D0562384FC441266A58 /* Pods-IncomingCall.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 66BF265326E9FCC90048D176 /* IncomingCall */ = { + isa = PBXNativeTarget; + buildConfigurationList = 66BF266826E9FCD00048D176 /* Build configuration list for PBXNativeTarget "IncomingCall" */; + buildPhases = ( + 1A16B6EAABD03BDFB6DDFABD /* [CP] Check Pods Manifest.lock */, + 66BF265026E9FCC90048D176 /* Sources */, + 66BF265126E9FCC90048D176 /* Frameworks */, + 66BF265226E9FCC90048D176 /* Resources */, + 37BA1CF9C1962947CFE3234B /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = IncomingCall; + productName = IncomingCall; + productReference = 66BF265426E9FCC90048D176 /* IncomingCall.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 66BF264C26E9FCC90048D176 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1250; + LastUpgradeCheck = 1250; + TargetAttributes = { + 66BF265326E9FCC90048D176 = { + CreatedOnToolsVersion = 12.5.1; + }; + }; + }; + buildConfigurationList = 66BF264F26E9FCC90048D176 /* Build configuration list for PBXProject "IncomingCall" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 66BF264B26E9FCC90048D176; + productRefGroup = 66BF265526E9FCC90048D176 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 66BF265326E9FCC90048D176 /* IncomingCall */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 66BF265226E9FCC90048D176 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 66BF266426E9FCD00048D176 /* LaunchScreen.storyboard in Resources */, + 66BF266126E9FCD00048D176 /* Preview Assets.xcassets in Resources */, + 66BF265E26E9FCD00048D176 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 1A16B6EAABD03BDFB6DDFABD /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-IncomingCall-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 37BA1CF9C1962947CFE3234B /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-IncomingCall/Pods-IncomingCall-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-IncomingCall/Pods-IncomingCall-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-IncomingCall/Pods-IncomingCall-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 66BF265026E9FCC90048D176 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 66BF265826E9FCC90048D176 /* AppDelegate.swift in Sources */, + 66BF266C26EA003C0048D176 /* IncomingCall.swift in Sources */, + 66BF265A26E9FCC90048D176 /* SceneDelegate.swift in Sources */, + 66BF265C26E9FCC90048D176 /* ContentView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 66BF266226E9FCD00048D176 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 66BF266326E9FCD00048D176 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 66BF266626E9FCD00048D176 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 66BF266726E9FCD00048D176 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 66BF266926E9FCD00048D176 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 4B0753133024A1C67E84CE1C /* Pods-IncomingCall.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"IncomingCall/Preview Content\""; + DEVELOPMENT_TEAM = Z2V957B3D6; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = IncomingCall/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = BC.IncomingCall; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 66BF266A26E9FCD00048D176 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 4B835D0562384FC441266A58 /* Pods-IncomingCall.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"IncomingCall/Preview Content\""; + DEVELOPMENT_TEAM = Z2V957B3D6; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = IncomingCall/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = BC.IncomingCall; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 66BF264F26E9FCC90048D176 /* Build configuration list for PBXProject "IncomingCall" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 66BF266626E9FCD00048D176 /* Debug */, + 66BF266726E9FCD00048D176 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 66BF266826E9FCD00048D176 /* Build configuration list for PBXNativeTarget "IncomingCall" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 66BF266926E9FCD00048D176 /* Debug */, + 66BF266A26E9FCD00048D176 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 66BF264C26E9FCC90048D176 /* Project object */; +} diff --git a/ios/swift/2-IncomingCall/IncomingCall.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/swift/2-IncomingCall/IncomingCall.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/ios/swift/2-IncomingCall/IncomingCall.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/swift/2-IncomingCall/IncomingCall.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/swift/2-IncomingCall/IncomingCall.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/swift/2-IncomingCall/IncomingCall.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/swift/2-IncomingCall/IncomingCall/AppDelegate.swift b/ios/swift/2-IncomingCall/IncomingCall/AppDelegate.swift new file mode 100644 index 0000000..49abbd8 --- /dev/null +++ b/ios/swift/2-IncomingCall/IncomingCall/AppDelegate.swift @@ -0,0 +1,38 @@ +// +// AppDelegate.swift +// LoginTutorial +// +// Created by QuentinArguillere on 31/07/2020. +// Copyright © 2020 BelledonneCommunications. All rights reserved. +// + +import UIKit +import SwiftUI + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + @ObservedObject var tutorialContext = IncomingCallTutorialContext() + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + +} + diff --git a/ios/swift/2-IncomingCall/IncomingCall/Assets.xcassets/AccentColor.colorset/Contents.json b/ios/swift/2-IncomingCall/IncomingCall/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/ios/swift/2-IncomingCall/IncomingCall/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/swift/2-IncomingCall/IncomingCall/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/swift/2-IncomingCall/IncomingCall/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9221b9b --- /dev/null +++ b/ios/swift/2-IncomingCall/IncomingCall/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/swift/2-IncomingCall/IncomingCall/Assets.xcassets/Contents.json b/ios/swift/2-IncomingCall/IncomingCall/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/ios/swift/2-IncomingCall/IncomingCall/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/swift/2-IncomingCall/IncomingCall/Base.lproj/LaunchScreen.storyboard b/ios/swift/2-IncomingCall/IncomingCall/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..865e932 --- /dev/null +++ b/ios/swift/2-IncomingCall/IncomingCall/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/swift/2-IncomingCall/IncomingCall/ContentView.swift b/ios/swift/2-IncomingCall/IncomingCall/ContentView.swift new file mode 100644 index 0000000..c2571c1 --- /dev/null +++ b/ios/swift/2-IncomingCall/IncomingCall/ContentView.swift @@ -0,0 +1,149 @@ +// +// ContentView.swift +// IncomingCall tutorial +// +// Created by QuentinArguillere on 09/09/2021. +// Copyright © 2021 BelledonneCommunications. All rights reserved. +// + +import SwiftUI + +struct ContentView: View { + + @ObservedObject var tutorialContext : IncomingCallTutorialContext + + func callStateString() -> String { + if (tutorialContext.isCallRunning) { + return "Call running" + } else if (tutorialContext.isCallIncoming) { + return "Incoming call" + } else { + return "No Call" + } + } + + var body: some View { + + VStack { + Group { + HStack { + Text("Username:") + .font(.title) + TextField("", text : $tutorialContext.username) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .disabled(tutorialContext.loggedIn) + } + HStack { + Text("Password:") + .font(.title) + TextField("", text : $tutorialContext.passwd) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .disabled(tutorialContext.loggedIn) + } + HStack { + Text("Domain:") + .font(.title) + TextField("", text : $tutorialContext.domain) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .disabled(tutorialContext.loggedIn) + } + Picker(selection: $tutorialContext.transportType, label: Text("Transport:")) { + Text("TLS").tag("TLS") + Text("TCP").tag("TCP") + Text("UDP").tag("UDP") + }.pickerStyle(SegmentedPickerStyle()).padding() + VStack { + HStack { + Button(action: { + if (self.tutorialContext.loggedIn) + { + self.tutorialContext.unregister() + self.tutorialContext.delete() + } else { + self.tutorialContext.login() + } + }) + { + Text(tutorialContext.loggedIn ? "Log out & \ndelete account" : "Create & \nlog in account") + .font(.largeTitle) + .foregroundColor(Color.white) + .frame(width: 220.0, height: 90) + .background(Color.gray) + } + } + HStack { + Text("Login State : ") + .font(.footnote) + Text(tutorialContext.loggedIn ? "Looged in" : "Unregistered") + .font(.footnote) + .foregroundColor(tutorialContext.loggedIn ? Color.green : Color.black) + }.padding(.top, 10.0) + } + VStack { + HStack { + Button(action: { + if (self.tutorialContext.isCallIncoming) { + self.tutorialContext.acceptCall() + } else if (self.tutorialContext.isCallRunning){ + self.tutorialContext.terminateCall() + } + }) + { + Text( (tutorialContext.isCallRunning) ? "Terminate" : "Accept") + .font(.largeTitle) + .foregroundColor(Color.white) + .frame(width: 180.0, height: 42.0) + .background(Color.gray) + } + .disabled(!tutorialContext.isCallIncoming && !tutorialContext.isCallRunning) + HStack { + Text(callStateString()).italic() + Spacer() + } + } + HStack { + Text("Caller:").font(.title).underline() + Text(tutorialContext.remoteAddress) + Spacer() + }.padding(.top, 5) + HStack { + Text("Call msg:").font(.title3).underline() + Text(tutorialContext.callMsg) + Spacer() + }.padding(.top, 5) + HStack { + Button(action: tutorialContext.toggleSpeaker) + { + Text((tutorialContext.isSpeakerEnabled) ? "Speaker OFF" : "Speaker ON") + .font(.title3) + .foregroundColor(Color.white) + .frame(width: 140.0, height: 42.0) + .background(Color.gray) + } + .disabled(!tutorialContext.isCallRunning) + Button(action: tutorialContext.muteMicrophone) + { + Text((tutorialContext.isMicrophoneEnabled) ? "Microphone OFF" : "Microphone ON") + .font(.title3) + .foregroundColor(Color.white) + .frame(width: 160.0, height: 42.0) + .background(Color.gray) + } + .disabled(!tutorialContext.isCallRunning) + }.padding(.top, 10) + }.padding(.top, 30) + } + Group { + Spacer() + Text("Core Version is \(tutorialContext.coreVersion)") + } + } + .padding() + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView(tutorialContext: IncomingCallTutorialContext()) + } +} diff --git a/ios/swift/2-IncomingCall/IncomingCall/IncomingCall.swift b/ios/swift/2-IncomingCall/IncomingCall/IncomingCall.swift new file mode 100644 index 0000000..8d0ea5d --- /dev/null +++ b/ios/swift/2-IncomingCall/IncomingCall/IncomingCall.swift @@ -0,0 +1,162 @@ +// +// IncomingCall.swift +// IncomingCall tutorial +// +// Created by QuentinArguillere on 08/09/2021. +// Copyright © 2021 BelledonneCommunications. All rights reserved. +// + +import linphonesw + +class IncomingCallTutorialContext : ObservableObject +{ + var mCore: Core! + @Published var coreVersion: String = Core.getVersion + + var mAccount: Account? + var mCoreDelegate : CoreDelegate! + @Published var username : String = "user" + @Published var passwd : String = "pwd" + @Published var domain : String = "sip.example.org" + @Published var loggedIn: Bool = false + @Published var transportType : String = "TLS" + + // Incoming call related variables + @Published var callMsg : String = "" + @Published var isCallIncoming : Bool = false + @Published var isCallRunning : Bool = false + @Published var remoteAddress : String = "Nobody yet" + @Published var isSpeakerEnabled : Bool = false + @Published var isMicrophoneEnabled : Bool = false + + init() + { + LoggingService.Instance.logLevel = LogLevel.Debug + + try? mCore = Factory.Instance.createCore(configPath: "", factoryConfigPath: "", systemContext: nil) + try? mCore.start() + + mCoreDelegate = CoreDelegateStub( onCallStateChanged: { (core: Core, call: Call, state: Call.State, message: String) in + self.callMsg = message + if (state == .IncomingReceived) { // When a call is received + self.isCallIncoming = true + self.isCallRunning = false + self.remoteAddress = call.remoteAddress!.asStringUriOnly() + } else if (state == .Connected) { // When a call is over + self.isCallIncoming = false + self.isCallRunning = true + } else if (state == .Released) { // When a call is over + self.isCallIncoming = false + self.isCallRunning = false + self.remoteAddress = "Nobody yet" + } + }, onAudioDeviceChanged: { (core: Core, device: AudioDevice) in + // This callback will be triggered when a successful audio device has been changed + }, onAudioDevicesListUpdated: { (core: Core) in + // This callback will be triggered when the available devices list has changed, + // for example after a bluetooth headset has been connected/disconnected. + }, onAccountRegistrationStateChanged: { (core: Core, account: Account, state: RegistrationState, message: String) in + NSLog("New registration state is \(state) for user id \( String(describing: account.params?.identityAddress?.asString()))\n") + if (state == .Ok) { + self.loggedIn = true + } else if (state == .Cleared) { + self.loggedIn = false + } + }) + mCore.addDelegate(delegate: mCoreDelegate) + } + + func login() { + + do { + var transport : TransportType + if (transportType == "TLS") { transport = TransportType.Tls } + else if (transportType == "TCP") { transport = TransportType.Tcp } + else { transport = TransportType.Udp } + + let authInfo = try Factory.Instance.createAuthInfo(username: username, userid: "", passwd: passwd, ha1: "", realm: "", domain: domain) + let accountParams = try mCore.createAccountParams() + let identity = try Factory.Instance.createAddress(addr: String("sip:" + username + "@" + domain)) + try! accountParams.setIdentityaddress(newValue: identity) + let address = try Factory.Instance.createAddress(addr: String("sip:" + domain)) + try address.setTransport(newValue: transport) + try accountParams.setServeraddress(newValue: address) + accountParams.registerEnabled = true + mAccount = try mCore.createAccount(params: accountParams) + mCore.addAuthInfo(info: authInfo) + try mCore.addAccount(account: mAccount!) + mCore.defaultAccount = mAccount + + } catch { NSLog(error.localizedDescription) } + } + + func unregister() + { + if let account = mCore.defaultAccount { + let params = account.params + let clonedParams = params?.clone() + clonedParams?.registerEnabled = false + account.params = clonedParams + } + } + func delete() { + if let account = mCore.defaultAccount { + mCore.removeAccount(account: account) + mCore.clearAccounts() + mCore.clearAllAuthInfo() + } + } + + func terminateCall() { + do { + // Terminates the call, whether it is ringing or running + try mCore.currentCall?.terminate() + } catch { NSLog(error.localizedDescription) } + } + + func acceptCall() { + // IMPORTANT : Make sure you allowed the use of the microphone (see key "Privacy - Microphone usage description" in Info.plist) ! + do { + // if we wanted, we could create a CallParams object + // and answer using this object to make changes to the call configuration + // (see OutgoingCall tutorial) + try mCore.currentCall?.accept() + } catch { NSLog(error.localizedDescription) } + } + + func muteMicrophone() { + // The following toggles the microphone, disabling completely / enabling the sound capture + // from the device microphone + mCore.micEnabled = !mCore.micEnabled + isMicrophoneEnabled = !isMicrophoneEnabled + } + + func toggleSpeaker() { + // Get the currently used audio device + let currentAudioDevice = mCore.currentCall?.outputAudioDevice + let speakerEnabled = currentAudioDevice?.type == AudioDeviceType.Speaker + + let test = currentAudioDevice?.deviceName + // We can get a list of all available audio devices using + // Note that on tablets for example, there may be no Earpiece device + for audioDevice in mCore.audioDevices { + + // For IOS, the Speaker is an exception, Linphone cannot differentiate Input and Output. + // This means that the default output device, the earpiece, is paired with the default phone microphone. + // Setting the output audio device to the microphone will redirect the sound to the earpiece. + if (speakerEnabled && audioDevice.type == AudioDeviceType.Microphone) { + mCore.currentCall?.outputAudioDevice = audioDevice + isSpeakerEnabled = false + return + } else if (!speakerEnabled && audioDevice.type == AudioDeviceType.Speaker) { + mCore.currentCall?.outputAudioDevice = audioDevice + isSpeakerEnabled = true + return + } + /* If we wanted to route the audio to a bluetooth headset + else if (audioDevice.type == AudioDevice.Type.Bluetooth) { + core.currentCall?.outputAudioDevice = audioDevice + }*/ + } + } +} diff --git a/ios/swift/2-IncomingCall/IncomingCall/Info.plist b/ios/swift/2-IncomingCall/IncomingCall/Info.plist new file mode 100644 index 0000000..c1065bd --- /dev/null +++ b/ios/swift/2-IncomingCall/IncomingCall/Info.plist @@ -0,0 +1,64 @@ + + + + + NSMicrophoneUsageDescription + Microphone access + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/ios/swift/2-IncomingCall/IncomingCall/Preview Content/Preview Assets.xcassets/Contents.json b/ios/swift/2-IncomingCall/IncomingCall/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/ios/swift/2-IncomingCall/IncomingCall/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/swift/2-IncomingCall/IncomingCall/SceneDelegate.swift b/ios/swift/2-IncomingCall/IncomingCall/SceneDelegate.swift new file mode 100644 index 0000000..e90febe --- /dev/null +++ b/ios/swift/2-IncomingCall/IncomingCall/SceneDelegate.swift @@ -0,0 +1,65 @@ +// +// SceneDelegate.swift +// LoginTutorial +// +// Created by QuentinArguillere on 31/07/2020. +// Copyright © 2020 BelledonneCommunications. All rights reserved. +// + +import UIKit +import SwiftUI + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + + let delegate = UIApplication.shared.delegate as! AppDelegate + + // Create the SwiftUI view that provides the window contents. + let contentView = ContentView(tutorialContext: delegate.tutorialContext) + + // Use a UIHostingController as window root view controller. + if let windowScene = scene as? UIWindowScene { + let window = UIWindow(windowScene: windowScene) + window.rootViewController = UIHostingController(rootView: contentView) + self.window = window + window.makeKeyAndVisible() + } + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } + + +} + diff --git a/ios/swift/2-IncomingCall/Podfile b/ios/swift/2-IncomingCall/Podfile new file mode 100644 index 0000000..5d8a7f4 --- /dev/null +++ b/ios/swift/2-IncomingCall/Podfile @@ -0,0 +1,24 @@ +# Uncomment the next line to define a global platform for your project +platform :ios, '11.0' +source "https://gitlab.linphone.org/BC/public/podspec.git" +source "https://github.com/CocoaPods/Specs.git" + +def basic_pods + if ENV['PODFILE_PATH'].nil? + pod 'linphone-sdk', '~> 5.0.0' + else + pod 'linphone-sdk', :path => ENV['PODFILE_PATH'] # local sdk + end + +end + + + +target 'IncomingCall' do + # Comment the next line if you don't want to use dynamic frameworks + use_frameworks! + + # Pods for IncomingCall + basic_pods + +end diff --git a/ios/swift/2-IncomingCall/README.md b/ios/swift/2-IncomingCall/README.md new file mode 100644 index 0000000..a6a2649 --- /dev/null +++ b/ios/swift/2-IncomingCall/README.md @@ -0,0 +1,10 @@ +Incoming call tutorial +==================== + +This tutorial will focus on how the app will be notified when a call is being received and how to either accept it or terminate it. + +We'll also cover how to toggle the microphone and the speakerphone during an active call. + +If you want to test it on either a device or an emulator, you'll need another SIP client to make the call. If you don't, you can use the [outgoing call tutorial](https://gitlab.linphone.org/BC/public/tutorials/-/tree/master/ios/swift/3-OutgoingCall) to do it. + +Note that changes to the "info.plist" file were made to enable the iphone microphone diff --git a/ios/swift/3-OutgoingCall/OutgoingCall.xcodeproj/project.pbxproj b/ios/swift/3-OutgoingCall/OutgoingCall.xcodeproj/project.pbxproj new file mode 100644 index 0000000..39fc00a --- /dev/null +++ b/ios/swift/3-OutgoingCall/OutgoingCall.xcodeproj/project.pbxproj @@ -0,0 +1,424 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 664BC0C126EA4E6D007EE298 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BC0C026EA4E6D007EE298 /* AppDelegate.swift */; }; + 664BC0C326EA4E6D007EE298 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BC0C226EA4E6D007EE298 /* SceneDelegate.swift */; }; + 664BC0C526EA4E6D007EE298 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BC0C426EA4E6D007EE298 /* ContentView.swift */; }; + 664BC0C726EA4E6E007EE298 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 664BC0C626EA4E6E007EE298 /* Assets.xcassets */; }; + 664BC0CA26EA4E6E007EE298 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 664BC0C926EA4E6E007EE298 /* Preview Assets.xcassets */; }; + 664BC0CD26EA4E6E007EE298 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 664BC0CB26EA4E6E007EE298 /* LaunchScreen.storyboard */; }; + 664BC0D526EA5B2E007EE298 /* OutgoingCall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BC0D426EA5B2E007EE298 /* OutgoingCall.swift */; }; + CCE49B664A0761B2077AA237 /* Pods_OutgoingCall.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E3E7DFC01F4CC7F8D82420D /* Pods_OutgoingCall.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 0E3E7DFC01F4CC7F8D82420D /* Pods_OutgoingCall.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_OutgoingCall.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 4F69E3A4EB1062C38B03A5BE /* Pods-OutgoingCall.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OutgoingCall.debug.xcconfig"; path = "Target Support Files/Pods-OutgoingCall/Pods-OutgoingCall.debug.xcconfig"; sourceTree = ""; }; + 664BC0BD26EA4E6D007EE298 /* OutgoingCall.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OutgoingCall.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 664BC0C026EA4E6D007EE298 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 664BC0C226EA4E6D007EE298 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 664BC0C426EA4E6D007EE298 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 664BC0C626EA4E6E007EE298 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 664BC0C926EA4E6E007EE298 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 664BC0CC26EA4E6E007EE298 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 664BC0CE26EA4E6E007EE298 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 664BC0D426EA5B2E007EE298 /* OutgoingCall.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutgoingCall.swift; sourceTree = ""; }; + 673A75DA326F185877FA9965 /* Pods-OutgoingCall.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OutgoingCall.release.xcconfig"; path = "Target Support Files/Pods-OutgoingCall/Pods-OutgoingCall.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 664BC0BA26EA4E6D007EE298 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + CCE49B664A0761B2077AA237 /* Pods_OutgoingCall.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 011FA7818C38DEF3B6BB2C74 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 0E3E7DFC01F4CC7F8D82420D /* Pods_OutgoingCall.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 664BC0B426EA4E6D007EE298 = { + isa = PBXGroup; + children = ( + 664BC0BF26EA4E6D007EE298 /* OutgoingCall */, + 664BC0BE26EA4E6D007EE298 /* Products */, + 740448E44E890F2FA8682B0B /* Pods */, + 011FA7818C38DEF3B6BB2C74 /* Frameworks */, + ); + sourceTree = ""; + }; + 664BC0BE26EA4E6D007EE298 /* Products */ = { + isa = PBXGroup; + children = ( + 664BC0BD26EA4E6D007EE298 /* OutgoingCall.app */, + ); + name = Products; + sourceTree = ""; + }; + 664BC0BF26EA4E6D007EE298 /* OutgoingCall */ = { + isa = PBXGroup; + children = ( + 664BC0C026EA4E6D007EE298 /* AppDelegate.swift */, + 664BC0C226EA4E6D007EE298 /* SceneDelegate.swift */, + 664BC0C426EA4E6D007EE298 /* ContentView.swift */, + 664BC0C626EA4E6E007EE298 /* Assets.xcassets */, + 664BC0CB26EA4E6E007EE298 /* LaunchScreen.storyboard */, + 664BC0CE26EA4E6E007EE298 /* Info.plist */, + 664BC0D426EA5B2E007EE298 /* OutgoingCall.swift */, + 664BC0C826EA4E6E007EE298 /* Preview Content */, + ); + path = OutgoingCall; + sourceTree = ""; + }; + 664BC0C826EA4E6E007EE298 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 664BC0C926EA4E6E007EE298 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 740448E44E890F2FA8682B0B /* Pods */ = { + isa = PBXGroup; + children = ( + 4F69E3A4EB1062C38B03A5BE /* Pods-OutgoingCall.debug.xcconfig */, + 673A75DA326F185877FA9965 /* Pods-OutgoingCall.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 664BC0BC26EA4E6D007EE298 /* OutgoingCall */ = { + isa = PBXNativeTarget; + buildConfigurationList = 664BC0D126EA4E6E007EE298 /* Build configuration list for PBXNativeTarget "OutgoingCall" */; + buildPhases = ( + A67E5FDCF316693C4106D5EC /* [CP] Check Pods Manifest.lock */, + 664BC0B926EA4E6D007EE298 /* Sources */, + 664BC0BA26EA4E6D007EE298 /* Frameworks */, + 664BC0BB26EA4E6D007EE298 /* Resources */, + 71824FDC693517F29C1C5E13 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = OutgoingCall; + productName = OutgoingCall; + productReference = 664BC0BD26EA4E6D007EE298 /* OutgoingCall.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 664BC0B526EA4E6D007EE298 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1250; + LastUpgradeCheck = 1250; + TargetAttributes = { + 664BC0BC26EA4E6D007EE298 = { + CreatedOnToolsVersion = 12.5.1; + }; + }; + }; + buildConfigurationList = 664BC0B826EA4E6D007EE298 /* Build configuration list for PBXProject "OutgoingCall" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 664BC0B426EA4E6D007EE298; + productRefGroup = 664BC0BE26EA4E6D007EE298 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 664BC0BC26EA4E6D007EE298 /* OutgoingCall */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 664BC0BB26EA4E6D007EE298 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 664BC0CD26EA4E6E007EE298 /* LaunchScreen.storyboard in Resources */, + 664BC0CA26EA4E6E007EE298 /* Preview Assets.xcassets in Resources */, + 664BC0C726EA4E6E007EE298 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 71824FDC693517F29C1C5E13 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-OutgoingCall/Pods-OutgoingCall-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-OutgoingCall/Pods-OutgoingCall-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-OutgoingCall/Pods-OutgoingCall-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + A67E5FDCF316693C4106D5EC /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-OutgoingCall-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 664BC0B926EA4E6D007EE298 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 664BC0C126EA4E6D007EE298 /* AppDelegate.swift in Sources */, + 664BC0D526EA5B2E007EE298 /* OutgoingCall.swift in Sources */, + 664BC0C326EA4E6D007EE298 /* SceneDelegate.swift in Sources */, + 664BC0C526EA4E6D007EE298 /* ContentView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 664BC0CB26EA4E6E007EE298 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 664BC0CC26EA4E6E007EE298 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 664BC0CF26EA4E6E007EE298 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 664BC0D026EA4E6E007EE298 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 664BC0D226EA4E6E007EE298 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 4F69E3A4EB1062C38B03A5BE /* Pods-OutgoingCall.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"OutgoingCall/Preview Content\""; + DEVELOPMENT_TEAM = Z2V957B3D6; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = OutgoingCall/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = BC.OutgoingCall; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 664BC0D326EA4E6E007EE298 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 673A75DA326F185877FA9965 /* Pods-OutgoingCall.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"OutgoingCall/Preview Content\""; + DEVELOPMENT_TEAM = Z2V957B3D6; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = OutgoingCall/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = BC.OutgoingCall; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 664BC0B826EA4E6D007EE298 /* Build configuration list for PBXProject "OutgoingCall" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 664BC0CF26EA4E6E007EE298 /* Debug */, + 664BC0D026EA4E6E007EE298 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 664BC0D126EA4E6E007EE298 /* Build configuration list for PBXNativeTarget "OutgoingCall" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 664BC0D226EA4E6E007EE298 /* Debug */, + 664BC0D326EA4E6E007EE298 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 664BC0B526EA4E6D007EE298 /* Project object */; +} diff --git a/ios/swift/3-OutgoingCall/OutgoingCall.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/swift/3-OutgoingCall/OutgoingCall.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/ios/swift/3-OutgoingCall/OutgoingCall.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/swift/3-OutgoingCall/OutgoingCall.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/swift/3-OutgoingCall/OutgoingCall.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/swift/3-OutgoingCall/OutgoingCall.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/swift/3-OutgoingCall/OutgoingCall/AppDelegate.swift b/ios/swift/3-OutgoingCall/OutgoingCall/AppDelegate.swift new file mode 100644 index 0000000..9258782 --- /dev/null +++ b/ios/swift/3-OutgoingCall/OutgoingCall/AppDelegate.swift @@ -0,0 +1,38 @@ +// +// AppDelegate.swift +// LoginTutorial +// +// Created by QuentinArguillere on 31/07/2020. +// Copyright © 2020 BelledonneCommunications. All rights reserved. +// + +import UIKit +import SwiftUI + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + @ObservedObject var tutorialContext = OutgoingCallTutorialContext() + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + +} + diff --git a/ios/swift/3-OutgoingCall/OutgoingCall/Assets.xcassets/AccentColor.colorset/Contents.json b/ios/swift/3-OutgoingCall/OutgoingCall/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/ios/swift/3-OutgoingCall/OutgoingCall/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/swift/3-OutgoingCall/OutgoingCall/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/swift/3-OutgoingCall/OutgoingCall/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9221b9b --- /dev/null +++ b/ios/swift/3-OutgoingCall/OutgoingCall/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/swift/3-OutgoingCall/OutgoingCall/Assets.xcassets/Contents.json b/ios/swift/3-OutgoingCall/OutgoingCall/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/ios/swift/3-OutgoingCall/OutgoingCall/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/swift/3-OutgoingCall/OutgoingCall/Base.lproj/LaunchScreen.storyboard b/ios/swift/3-OutgoingCall/OutgoingCall/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..865e932 --- /dev/null +++ b/ios/swift/3-OutgoingCall/OutgoingCall/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/swift/3-OutgoingCall/OutgoingCall/ContentView.swift b/ios/swift/3-OutgoingCall/OutgoingCall/ContentView.swift new file mode 100644 index 0000000..68e1c20 --- /dev/null +++ b/ios/swift/3-OutgoingCall/OutgoingCall/ContentView.swift @@ -0,0 +1,171 @@ +// +// ContentView.swift +// IncomingCall tutorial +// +// Created by QuentinArguillere on 09/09/2021. +// Copyright © 2021 BelledonneCommunications. All rights reserved. +// + +import SwiftUI +import linphonesw + +struct ContentView: View { + + @ObservedObject var tutorialContext : OutgoingCallTutorialContext + + func callStateString() -> String { + if (tutorialContext.isCallRunning) { + return "Call running" + } else { + return "No Call" + } + } + + var body: some View { + + VStack { + Group { + HStack { + Text("Username:") + .font(.title) + TextField("", text : $tutorialContext.username) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .disabled(tutorialContext.loggedIn) + } + HStack { + Text("Password:") + .font(.title) + TextField("", text : $tutorialContext.passwd) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .disabled(tutorialContext.loggedIn) + } + HStack { + Text("Domain:") + .font(.title) + TextField("", text : $tutorialContext.domain) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .disabled(tutorialContext.loggedIn) + } + Picker(selection: $tutorialContext.transportType, label: Text("Transport:")) { + Text("TLS").tag("TLS") + Text("TCP").tag("TCP") + Text("UDP").tag("UDP") + }.pickerStyle(SegmentedPickerStyle()).padding() + VStack { + HStack { + Button(action: { + if (self.tutorialContext.loggedIn) + { + self.tutorialContext.unregister() + self.tutorialContext.delete() + } else { + self.tutorialContext.login() + } + }) + { + Text(tutorialContext.loggedIn ? "Log out & \ndelete account" : "Create & \nlog in account") + .font(.largeTitle) + .foregroundColor(Color.white) + .frame(width: 220.0, height: 90) + .background(Color.gray) + } + } + HStack { + Text("Login State : ") + .font(.footnote) + Text(tutorialContext.loggedIn ? "Looged in" : "Unregistered") + .font(.footnote) + .foregroundColor(tutorialContext.loggedIn ? Color.green : Color.black) + }.padding(.top, 10.0) + } + VStack { + HStack { + Text("Call dest:") + .font(.title) + TextField("", text : $tutorialContext.remoteAddress) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .disabled(!tutorialContext.loggedIn) + } + HStack { + Button(action: { + if (self.tutorialContext.isCallRunning) { + self.tutorialContext.terminateCall() + } else { + self.tutorialContext.outgoingCall() + } + }) + { + Text( (tutorialContext.isCallRunning) ? "End" : "Call") + .font(.largeTitle) + .foregroundColor(Color.white) + .frame(width: 180.0, height: 42.0) + .background(Color.gray) + } + HStack { + Text(tutorialContext.isCallRunning ? "Running" : "") + .italic().foregroundColor(.green) + Spacer() + } + } + HStack { + Text("Call msg:").font(.title3).underline() + Text(tutorialContext.callMsg) + Spacer() + }.padding(.top, 5) + HStack { + Button(action: tutorialContext.toggleVideo) + { + Text((tutorialContext.isVideoEnabled) ? "Video OFF" : "Video ON") + .font(.title3) + .foregroundColor(Color.white) + .frame(width: 130.0, height: 42.0) + .background(Color.gray) + } + .disabled(!tutorialContext.isCallRunning) + Button(action: tutorialContext.toggleCamera) + { + Text("Change camera") + .font(.title3) + .foregroundColor(Color.white) + .frame(width: 160.0, height: 42.0) + .background(Color.gray) + } + .disabled(!tutorialContext.canChangeCamera || !tutorialContext.isVideoEnabled) + }.padding(.top, 5) + HStack { + VStack { + LinphoneVideoViewHolder() { view in + self.tutorialContext.mCore.nativeVideoWindow = view + } + .frame(width: 120, height: 160.0) + .border(Color.gray) + .padding(.leading) + Text("What I receive") + } + Spacer() + VStack { + LinphoneVideoViewHolder() { view in + self.tutorialContext.mCore.nativePreviewWindow = view + } + .frame(width: 120, height: 160.0) + .border(Color.gray) + .padding(.leading) + Text("What I send") + } + } + }.padding(.top, 30) + } + Group { + Spacer() + Text("Core Version is \(tutorialContext.coreVersion)") + } + } + .padding() + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView(tutorialContext: OutgoingCallTutorialContext()) + } +} diff --git a/ios/swift/3-OutgoingCall/OutgoingCall/Info.plist b/ios/swift/3-OutgoingCall/OutgoingCall/Info.plist new file mode 100644 index 0000000..51a76df --- /dev/null +++ b/ios/swift/3-OutgoingCall/OutgoingCall/Info.plist @@ -0,0 +1,66 @@ + + + + + NSCameraUsageDescription + Cameras access + NSMicrophoneUsageDescription + Microphone access + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/ios/swift/3-OutgoingCall/OutgoingCall/OutgoingCall.swift b/ios/swift/3-OutgoingCall/OutgoingCall/OutgoingCall.swift new file mode 100644 index 0000000..a5c221f --- /dev/null +++ b/ios/swift/3-OutgoingCall/OutgoingCall/OutgoingCall.swift @@ -0,0 +1,238 @@ +// +// OutgoingCall.swift +// OutgoingCall tutorial +// +// Created by QuentinArguillere on 08/09/2021. +// Copyright © 2021 BelledonneCommunications. All rights reserved. +// + +import linphonesw + +class OutgoingCallTutorialContext : ObservableObject +{ + var mCore: Core! + @Published var coreVersion: String = Core.getVersion + + var mAccount: Account? + var mCoreDelegate : CoreDelegate! + @Published var username : String = "user" + @Published var passwd : String = "pwd" + @Published var domain : String = "sip.example.org" + @Published var loggedIn: Bool = false + @Published var transportType : String = "TLS" + + // Outgoing call related variables + @Published var callMsg : String = "" + @Published var isCallRunning : Bool = false + @Published var isVideoEnabled : Bool = false + @Published var canChangeCamera : Bool = false + @Published var remoteAddress : String = "sip:arguillq@sip.linphone.org" + + init() + { + LoggingService.Instance.logLevel = LogLevel.Debug + + try? mCore = Factory.Instance.createCore(configPath: "", factoryConfigPath: "", systemContext: nil) + // Here we enable the video capture & display at Core level + // It doesn't mean calls will be made with video automatically, + // But it allows to use it later + mCore.videoCaptureEnabled = true + mCore.videoDisplayEnabled = true + + // When enabling the video, the remote will either automatically answer the update request + // or it will ask it's user depending on it's policy. + // Here we have configured the policy to always automatically accept video requests + mCore.videoActivationPolicy!.automaticallyAccept = true + // If you don't want to automatically accept, + // you'll have to use a code similar to the one in toggleVideo to answer a received request + + + // If the following property is enabled, it will automatically configure created call params with video enabled + //core.videoActivationPolicy.automaticallyInitiate = true + + try? mCore.start() + + mCoreDelegate = CoreDelegateStub( onCallStateChanged: { (core: Core, call: Call, state: Call.State, message: String) in + // This function will be called each time a call state changes, + // which includes new incoming/outgoing calls + self.callMsg = message + + if (state == .OutgoingInit) { + // First state an outgoing call will go through + } else if (state == .OutgoingProgress) { + // Right after outgoing init + } else if (state == .OutgoingRinging) { + // This state will be reached upon reception of the 180 RINGING + } else if (state == .Connected) { + // When the 200 OK has been received + } else if (state == .StreamsRunning) { + // This state indicates the call is active. + // You may reach this state multiple times, for example after a pause/resume + // or after the ICE negotiation completes + // Wait for the call to be connected before allowing a call update + self.isCallRunning = true + + // Only enable toggle camera button if there is more than 1 camera + // We check if core.videoDevicesList.size > 2 because of the fake camera with static image created by our SDK (see below) + self.canChangeCamera = core.videoDevicesList.count > 2 + } else if (state == .Paused) { + // When you put a call in pause, it will became Paused + self.canChangeCamera = false + } else if (state == .PausedByRemote) { + // When the remote end of the call pauses it, it will be PausedByRemote + } else if (state == .Updating) { + // When we request a call update, for example when toggling video + } else if (state == .UpdatedByRemote) { + // When the remote requests a call update + } else if (state == .Released) { + // Call state will be released shortly after the End state + self.isCallRunning = false + self.canChangeCamera = false + } else if (state == .Error) { + + } + }, onAccountRegistrationStateChanged: { (core: Core, account: Account, state: RegistrationState, message: String) in + NSLog("New registration state is \(state) for user id \( String(describing: account.params?.identityAddress?.asString()))\n") + if (state == .Ok) { + self.loggedIn = true + } else if (state == .Cleared) { + self.loggedIn = false + } + }) + mCore.addDelegate(delegate: mCoreDelegate) + } + + func login() { + + do { + var transport : TransportType + if (transportType == "TLS") { transport = TransportType.Tls } + else if (transportType == "TCP") { transport = TransportType.Tcp } + else { transport = TransportType.Udp } + + let authInfo = try Factory.Instance.createAuthInfo(username: username, userid: "", passwd: passwd, ha1: "", realm: "", domain: domain) + let accountParams = try mCore.createAccountParams() + let identity = try Factory.Instance.createAddress(addr: String("sip:" + username + "@" + domain)) + try! accountParams.setIdentityaddress(newValue: identity) + let address = try Factory.Instance.createAddress(addr: String("sip:" + domain)) + try address.setTransport(newValue: transport) + try accountParams.setServeraddress(newValue: address) + accountParams.registerEnabled = true + mAccount = try mCore.createAccount(params: accountParams) + mCore.addAuthInfo(info: authInfo) + try mCore.addAccount(account: mAccount!) + mCore.defaultAccount = mAccount + + } catch { NSLog(error.localizedDescription) } + } + + func unregister() + { + if let account = mCore.defaultAccount { + let params = account.params + let clonedParams = params?.clone() + clonedParams?.registerEnabled = false + account.params = clonedParams + } + } + func delete() { + if let account = mCore.defaultAccount { + mCore.removeAccount(account: account) + mCore.clearAccounts() + mCore.clearAllAuthInfo() + } + } + + + func outgoingCall() { + do { + // As for everything we need to get the SIP URI of the remote and convert it to an Address + let remoteAddress = try Factory.Instance.createAddress(addr: remoteAddress) + + // We also need a CallParams object + // Create call params expects a Call object for incoming calls, but for outgoing we must use null safely + let params = try mCore.createCallParams(call: nil) + + // We can now configure it + // Here we ask for no encryption but we could ask for ZRTP/SRTP/DTLS + params.mediaEncryption = MediaEncryption.None + // If we wanted to start the call with video directly + //params.videoEnabled = true + + // Finally we start the call + let _ = mCore.inviteAddressWithParams(addr: remoteAddress, params: params) + // Call process can be followed in onCallStateChanged callback from core listener + } catch { NSLog(error.localizedDescription) } + + } + + func terminateCall() { + do { + if (mCore.callsNb == 0) { return } + + // If the call state isn't paused, we can get it using core.currentCall + let coreCall = (mCore.currentCall != nil) ? mCore.currentCall : mCore.calls[0] + + // Terminating a call is quite simple + if let call = coreCall { + try call.terminate() + } + } catch { NSLog(error.localizedDescription) } + } + + func toggleVideo() { + do { + if (mCore.callsNb == 0) { return } + let coreCall = (mCore.currentCall != nil) ? mCore.currentCall : mCore.calls[0] + // We will need the CAMERA permission for video call + + if let call = coreCall { + // To update the call, we need to create a new call params, from the call object this time + let params = try mCore.createCallParams(call: call) + // Here we toggle the video state (disable it if enabled, enable it if disabled) + // Note that we are using currentParams and not params or remoteParams + // params is the object you configured when the call was started + // remote params is the same but for the remote + // current params is the real params of the call, resulting of the mix of local & remote params + params.videoEnabled = !(call.currentParams!.videoEnabled) + isVideoEnabled = params.videoEnabled + // Finally we request the call update + try call.update(params: params) + // Note that when toggling off the video, TextureViews will keep showing the latest frame displayed + } + } catch { NSLog(error.localizedDescription) } + } + + func toggleCamera() { + do { + // Currently used camera + let currentDevice = mCore.videoDevice + + // Let's iterate over all camera available and choose another one + for camera in mCore.videoDevicesList { + // All devices will have a "Static picture" fake camera, and we don't want to use it + if (camera != currentDevice && camera != "StaticImage: Static picture") { + try mCore.setVideodevice(newValue: camera) + break + } + } + } catch { NSLog(error.localizedDescription) } + } + + func pauseOrResume() { + do { + if (mCore.callsNb == 0) { return } + let coreCall = (mCore.currentCall != nil) ? mCore.currentCall : mCore.calls[0] + + if let call = coreCall { + if (call.state != Call.State.Paused && call.state != Call.State.Pausing) { + // If our call isn't paused, let's pause it + try call.pause() + } else if (call.state != Call.State.Resuming) { + // Otherwise let's resume it + try call.resume() + } + } + } catch { NSLog(error.localizedDescription) } + } +} diff --git a/ios/swift/3-OutgoingCall/OutgoingCall/Preview Content/Preview Assets.xcassets/Contents.json b/ios/swift/3-OutgoingCall/OutgoingCall/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/ios/swift/3-OutgoingCall/OutgoingCall/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/swift/3-OutgoingCall/OutgoingCall/SceneDelegate.swift b/ios/swift/3-OutgoingCall/OutgoingCall/SceneDelegate.swift new file mode 100644 index 0000000..e90febe --- /dev/null +++ b/ios/swift/3-OutgoingCall/OutgoingCall/SceneDelegate.swift @@ -0,0 +1,65 @@ +// +// SceneDelegate.swift +// LoginTutorial +// +// Created by QuentinArguillere on 31/07/2020. +// Copyright © 2020 BelledonneCommunications. All rights reserved. +// + +import UIKit +import SwiftUI + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + + let delegate = UIApplication.shared.delegate as! AppDelegate + + // Create the SwiftUI view that provides the window contents. + let contentView = ContentView(tutorialContext: delegate.tutorialContext) + + // Use a UIHostingController as window root view controller. + if let windowScene = scene as? UIWindowScene { + let window = UIWindow(windowScene: windowScene) + window.rootViewController = UIHostingController(rootView: contentView) + self.window = window + window.makeKeyAndVisible() + } + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } + + +} + diff --git a/ios/swift/3-OutgoingCall/Podfile b/ios/swift/3-OutgoingCall/Podfile new file mode 100644 index 0000000..a3243e2 --- /dev/null +++ b/ios/swift/3-OutgoingCall/Podfile @@ -0,0 +1,24 @@ +# Uncomment the next line to define a global platform for your project +platform :ios, '11.0' +source "https://gitlab.linphone.org/BC/public/podspec.git" +source "https://github.com/CocoaPods/Specs.git" + +def basic_pods + if ENV['PODFILE_PATH'].nil? + pod 'linphone-sdk', '~> 5.0.0' + else + pod 'linphone-sdk', :path => ENV['PODFILE_PATH'] # local sdk + end + +end + + + +target 'OutgoingCall' do + # Comment the next line if you don't want to use dynamic frameworks + use_frameworks! + + # Pods for OutgoingCall + basic_pods + +end diff --git a/ios/swift/3-OutgoingCall/README.md b/ios/swift/3-OutgoingCall/README.md new file mode 100644 index 0000000..f679d8b --- /dev/null +++ b/ios/swift/3-OutgoingCall/README.md @@ -0,0 +1,8 @@ +Outgoing call tutorial +==================== + +In the previous tutorial we saw how to handle an incoming call, now let's start one. + +We'll also see how to enable video during a call, switch between the front and back cameras if available and display our own preview. + +Note that changes to the "info.plist" file were made to enable iphone cameras. diff --git a/ios/swift/4-CallKitTutorial/CallKitTutorial.xcodeproj/project.pbxproj b/ios/swift/4-CallKitTutorial/CallKitTutorial.xcodeproj/project.pbxproj new file mode 100644 index 0000000..2c2e466 --- /dev/null +++ b/ios/swift/4-CallKitTutorial/CallKitTutorial.xcodeproj/project.pbxproj @@ -0,0 +1,431 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 577005D03F6821591475FBF9 /* Pods_CallKitTutorial.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 454830E9C41DBBF20AF2BD05 /* Pods_CallKitTutorial.framework */; }; + 6608A96624E197D5006E6C68 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6608A96524E197D5006E6C68 /* AppDelegate.swift */; }; + 6608A96824E197D5006E6C68 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6608A96724E197D5006E6C68 /* SceneDelegate.swift */; }; + 6608A96A24E197D5006E6C68 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6608A96924E197D5006E6C68 /* ContentView.swift */; }; + 6608A96C24E197D5006E6C68 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6608A96B24E197D5006E6C68 /* Assets.xcassets */; }; + 6608A96F24E197D5006E6C68 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6608A96E24E197D5006E6C68 /* Preview Assets.xcassets */; }; + 6608A97224E197D5006E6C68 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6608A97024E197D5006E6C68 /* LaunchScreen.storyboard */; }; + 6608A97A24E19817006E6C68 /* CallKitTutorial.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6608A97924E19817006E6C68 /* CallKitTutorial.swift */; }; + 6608A97C24E1981E006E6C68 /* CallKitProviderDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6608A97B24E1981E006E6C68 /* CallKitProviderDelegate.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 018936DA8FE1500B9E181610 /* Pods-CallKitTutorial.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CallKitTutorial.debug.xcconfig"; path = "Target Support Files/Pods-CallKitTutorial/Pods-CallKitTutorial.debug.xcconfig"; sourceTree = ""; }; + 454830E9C41DBBF20AF2BD05 /* Pods_CallKitTutorial.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CallKitTutorial.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 6608A96224E197D5006E6C68 /* CallKitTutorial.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CallKitTutorial.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 6608A96524E197D5006E6C68 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 6608A96724E197D5006E6C68 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 6608A96924E197D5006E6C68 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 6608A96B24E197D5006E6C68 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 6608A96E24E197D5006E6C68 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 6608A97124E197D5006E6C68 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 6608A97324E197D5006E6C68 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 6608A97924E19817006E6C68 /* CallKitTutorial.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallKitTutorial.swift; sourceTree = ""; }; + 6608A97B24E1981E006E6C68 /* CallKitProviderDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallKitProviderDelegate.swift; sourceTree = ""; }; + 6608A97D24E19852006E6C68 /* CallKitTutorial.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = CallKitTutorial.entitlements; sourceTree = ""; }; + C87B170DFD4D3072825B25EF /* Pods-CallKitTutorial.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CallKitTutorial.release.xcconfig"; path = "Target Support Files/Pods-CallKitTutorial/Pods-CallKitTutorial.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 6608A95F24E197D5006E6C68 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 577005D03F6821591475FBF9 /* Pods_CallKitTutorial.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 319770E3ECD19EBD7557F82B /* Frameworks */ = { + isa = PBXGroup; + children = ( + 454830E9C41DBBF20AF2BD05 /* Pods_CallKitTutorial.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 38284A71627D413A7ACC2F07 /* Pods */ = { + isa = PBXGroup; + children = ( + 018936DA8FE1500B9E181610 /* Pods-CallKitTutorial.debug.xcconfig */, + C87B170DFD4D3072825B25EF /* Pods-CallKitTutorial.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + 6608A95924E197D5006E6C68 = { + isa = PBXGroup; + children = ( + 6608A96424E197D5006E6C68 /* CallKitTutorial */, + 6608A96324E197D5006E6C68 /* Products */, + 38284A71627D413A7ACC2F07 /* Pods */, + 319770E3ECD19EBD7557F82B /* Frameworks */, + ); + sourceTree = ""; + }; + 6608A96324E197D5006E6C68 /* Products */ = { + isa = PBXGroup; + children = ( + 6608A96224E197D5006E6C68 /* CallKitTutorial.app */, + ); + name = Products; + sourceTree = ""; + }; + 6608A96424E197D5006E6C68 /* CallKitTutorial */ = { + isa = PBXGroup; + children = ( + 6608A97D24E19852006E6C68 /* CallKitTutorial.entitlements */, + 6608A96524E197D5006E6C68 /* AppDelegate.swift */, + 6608A96724E197D5006E6C68 /* SceneDelegate.swift */, + 6608A97B24E1981E006E6C68 /* CallKitProviderDelegate.swift */, + 6608A97924E19817006E6C68 /* CallKitTutorial.swift */, + 6608A96924E197D5006E6C68 /* ContentView.swift */, + 6608A96B24E197D5006E6C68 /* Assets.xcassets */, + 6608A97024E197D5006E6C68 /* LaunchScreen.storyboard */, + 6608A97324E197D5006E6C68 /* Info.plist */, + 6608A96D24E197D5006E6C68 /* Preview Content */, + ); + path = CallKitTutorial; + sourceTree = ""; + }; + 6608A96D24E197D5006E6C68 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 6608A96E24E197D5006E6C68 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 6608A96124E197D5006E6C68 /* CallKitTutorial */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6608A97624E197D5006E6C68 /* Build configuration list for PBXNativeTarget "CallKitTutorial" */; + buildPhases = ( + D642DC4C3C74BB17FF5A3E9F /* [CP] Check Pods Manifest.lock */, + 6608A95E24E197D5006E6C68 /* Sources */, + 6608A95F24E197D5006E6C68 /* Frameworks */, + 6608A96024E197D5006E6C68 /* Resources */, + 640A5744B5ABBA6A3EDBFDB1 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = CallKitTutorial; + productName = CallKitTutorial; + productReference = 6608A96224E197D5006E6C68 /* CallKitTutorial.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 6608A95A24E197D5006E6C68 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1160; + LastUpgradeCheck = 1160; + ORGANIZATIONNAME = BelledonneCommunications; + TargetAttributes = { + 6608A96124E197D5006E6C68 = { + CreatedOnToolsVersion = 11.6; + }; + }; + }; + buildConfigurationList = 6608A95D24E197D5006E6C68 /* Build configuration list for PBXProject "CallKitTutorial" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 6608A95924E197D5006E6C68; + productRefGroup = 6608A96324E197D5006E6C68 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 6608A96124E197D5006E6C68 /* CallKitTutorial */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 6608A96024E197D5006E6C68 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6608A97224E197D5006E6C68 /* LaunchScreen.storyboard in Resources */, + 6608A96F24E197D5006E6C68 /* Preview Assets.xcassets in Resources */, + 6608A96C24E197D5006E6C68 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 640A5744B5ABBA6A3EDBFDB1 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-CallKitTutorial/Pods-CallKitTutorial-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-CallKitTutorial/Pods-CallKitTutorial-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-CallKitTutorial/Pods-CallKitTutorial-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + D642DC4C3C74BB17FF5A3E9F /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-CallKitTutorial-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 6608A95E24E197D5006E6C68 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6608A97A24E19817006E6C68 /* CallKitTutorial.swift in Sources */, + 6608A96624E197D5006E6C68 /* AppDelegate.swift in Sources */, + 6608A96824E197D5006E6C68 /* SceneDelegate.swift in Sources */, + 6608A96A24E197D5006E6C68 /* ContentView.swift in Sources */, + 6608A97C24E1981E006E6C68 /* CallKitProviderDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 6608A97024E197D5006E6C68 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 6608A97124E197D5006E6C68 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 6608A97424E197D5006E6C68 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.6; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 6608A97524E197D5006E6C68 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.6; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 6608A97724E197D5006E6C68 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 018936DA8FE1500B9E181610 /* Pods-CallKitTutorial.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = CallKitTutorial/CallKitTutorial.entitlements; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"CallKitTutorial/Preview Content\""; + DEVELOPMENT_TEAM = Z2V957B3D6; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = CallKitTutorial/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.linphone.tutorials.callkit; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 6608A97824E197D5006E6C68 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C87B170DFD4D3072825B25EF /* Pods-CallKitTutorial.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = CallKitTutorial/CallKitTutorial.entitlements; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"CallKitTutorial/Preview Content\""; + DEVELOPMENT_TEAM = Z2V957B3D6; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = CallKitTutorial/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.linphone.tutorials.callkit; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 6608A95D24E197D5006E6C68 /* Build configuration list for PBXProject "CallKitTutorial" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6608A97424E197D5006E6C68 /* Debug */, + 6608A97524E197D5006E6C68 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6608A97624E197D5006E6C68 /* Build configuration list for PBXNativeTarget "CallKitTutorial" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6608A97724E197D5006E6C68 /* Debug */, + 6608A97824E197D5006E6C68 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 6608A95A24E197D5006E6C68 /* Project object */; +} diff --git a/ios/swift/4-CallKitTutorial/CallKitTutorial.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/swift/4-CallKitTutorial/CallKitTutorial.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..ddcdb94 --- /dev/null +++ b/ios/swift/4-CallKitTutorial/CallKitTutorial.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/swift/4-CallKitTutorial/CallKitTutorial.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/swift/4-CallKitTutorial/CallKitTutorial.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/swift/4-CallKitTutorial/CallKitTutorial.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/swift/4-CallKitTutorial/CallKitTutorial/AppDelegate.swift b/ios/swift/4-CallKitTutorial/CallKitTutorial/AppDelegate.swift new file mode 100644 index 0000000..de98621 --- /dev/null +++ b/ios/swift/4-CallKitTutorial/CallKitTutorial/AppDelegate.swift @@ -0,0 +1,38 @@ +// +// AppDelegate.swift +// CallKitTutorial +// +// Created by QuentinArguillere on 10/08/2020. +// Copyright © 2020 BelledonneCommunications. All rights reserved. +// + +import UIKit +import SwiftUI + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + @ObservedObject var tutorialContext = CallKitExampleContext() + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + +} + diff --git a/ios/swift/4-CallKitTutorial/CallKitTutorial/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/swift/4-CallKitTutorial/CallKitTutorial/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9221b9b --- /dev/null +++ b/ios/swift/4-CallKitTutorial/CallKitTutorial/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/swift/4-CallKitTutorial/CallKitTutorial/Assets.xcassets/Contents.json b/ios/swift/4-CallKitTutorial/CallKitTutorial/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/ios/swift/4-CallKitTutorial/CallKitTutorial/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/swift/4-CallKitTutorial/CallKitTutorial/Base.lproj/LaunchScreen.storyboard b/ios/swift/4-CallKitTutorial/CallKitTutorial/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..865e932 --- /dev/null +++ b/ios/swift/4-CallKitTutorial/CallKitTutorial/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/swift/4-CallKitTutorial/CallKitTutorial/CallKitProviderDelegate.swift b/ios/swift/4-CallKitTutorial/CallKitTutorial/CallKitProviderDelegate.swift new file mode 100644 index 0000000..34e4a23 --- /dev/null +++ b/ios/swift/4-CallKitTutorial/CallKitTutorial/CallKitProviderDelegate.swift @@ -0,0 +1,99 @@ +// +// ProviderDelegate.swift +// CallTutorial +// +// Created by QuentinArguillere on 05/08/2020. +// Copyright © 2020 BelledonneCommunications. All rights reserved. +// + +import Foundation +import CallKit +import linphonesw +import AVFoundation + + +class CallKitProviderDelegate : NSObject +{ + private let provider: CXProvider + let mCallController = CXCallController() + var tutorialContext : CallKitExampleContext! + + var incomingCallUUID : UUID! + + init(context: CallKitExampleContext) + { + tutorialContext = context + let providerConfiguration = CXProviderConfiguration(localizedName: Bundle.main.infoDictionary!["CFBundleName"] as! String) + providerConfiguration.supportsVideo = true + providerConfiguration.supportedHandleTypes = [.generic] + + providerConfiguration.maximumCallsPerCallGroup = 1 + providerConfiguration.maximumCallGroups = 1 + + provider = CXProvider(configuration: providerConfiguration) + super.init() + provider.setDelegate(self, queue: nil) // The CXProvider delegate will trigger CallKit related callbacks + + } + + func incomingCall() + { + incomingCallUUID = UUID() + let update = CXCallUpdate() + update.remoteHandle = CXHandle(type:.generic, value: tutorialContext.incomingCallName) + + provider.reportNewIncomingCall(with: incomingCallUUID, update: update, completion: { error in }) // Report to CallKit a call is incoming + } + + func stopCall() + { + let endCallAction = CXEndCallAction(call: incomingCallUUID) + let transaction = CXTransaction(action: endCallAction) + + mCallController.request(transaction, completion: { error in }) // Report to CallKit a call must end + } + +} + + +// In this extension, we implement the action we want to be done when CallKit is notified of something. +// This can happen through the CallKit GUI in the app, or directly in the code (see, incomingCall(), stopCall() functions above) +extension CallKitProviderDelegate: CXProviderDelegate { + + func provider(_ provider: CXProvider, perform action: CXEndCallAction) { + do { + if (tutorialContext.mCall?.state != .End && tutorialContext.mCall?.state != .Released) { + try tutorialContext.mCall?.terminate() + } + } catch { NSLog(error.localizedDescription) } + + tutorialContext.isCallRunning = false + tutorialContext.isCallIncoming = false + action.fulfill() + } + + func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) { + do { + try tutorialContext.mCall?.accept() + tutorialContext.isCallRunning = true + } catch { + print(error) + } + action.fulfill() + } + + func provider(_ provider: CXProvider, perform action: CXSetHeldCallAction) {} + func provider(_ provider: CXProvider, perform action: CXStartCallAction) {} + func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {} + func provider(_ provider: CXProvider, perform action: CXPlayDTMFCallAction) {} + func provider(_ provider: CXProvider, timedOutPerforming action: CXAction) {} + func providerDidReset(_ provider: CXProvider) {} + + func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) { + tutorialContext.mCore.activateAudioSession(actived: true) + } + + func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) { + tutorialContext.mCore.activateAudioSession(actived: false) + } +} diff --git a/ios/swift/4-CallKitTutorial/CallKitTutorial/CallKitTutorial.entitlements b/ios/swift/4-CallKitTutorial/CallKitTutorial/CallKitTutorial.entitlements new file mode 100644 index 0000000..903def2 --- /dev/null +++ b/ios/swift/4-CallKitTutorial/CallKitTutorial/CallKitTutorial.entitlements @@ -0,0 +1,8 @@ + + + + + aps-environment + development + + diff --git a/ios/swift/4-CallKitTutorial/CallKitTutorial/CallKitTutorial.swift b/ios/swift/4-CallKitTutorial/CallKitTutorial/CallKitTutorial.swift new file mode 100644 index 0000000..f7b36cf --- /dev/null +++ b/ios/swift/4-CallKitTutorial/CallKitTutorial/CallKitTutorial.swift @@ -0,0 +1,141 @@ +// +// CallExample.swift +// CallTutorial +// +// Created by QuentinArguillere on 31/07/2020. +// Copyright © 2020 BelledonneCommunications. All rights reserved. +// + +import linphonesw +import AVFoundation + +class CallKitExampleContext : ObservableObject +{ + var mCore: Core! + @Published var coreVersion: String = Core.getVersion + + var mAccount: Account? + var mCoreDelegate : CoreDelegate! + @Published var username : String = "quentindev" + @Published var passwd : String = "dev" + @Published var domain : String = "sip.linphone.org" + @Published var loggedIn: Bool = false + @Published var transportType : String = "TLS" + + @Published var callMsg : String = "" + @Published var isCallIncoming : Bool = false + @Published var isCallRunning : Bool = false + @Published var remoteAddress : String = "Nobody yet" + @Published var isSpeakerEnabled : Bool = false + @Published var isMicrophoneEnabled : Bool = false + + /*------------ Callkit tutorial related variables ---------------*/ + let incomingCallName = "Incoming call example" + var mCall : Call? + var mProviderDelegate : CallKitProviderDelegate! + var mCallAlreadyStopped : Bool = false; + + init() + { + LoggingService.Instance.logLevel = LogLevel.Debug + + let factory = Factory.Instance + // IMPORTANT : In this tutorial, we require the use of a core configuration file. + // This way, once the registration is done, and until it is cleared, it will return to the LoggedIn state on launch. + // This allows us to have a functional call when the app was closed and is started by a VOIP push notification (incoming call + // We also need to enable "Push Notitifications" and "Background Mode - Voice Over IP" + let configDir = factory.getConfigDir(context: nil) + try? mCore = factory.createCore(configPath: "\(configDir)/MyConfig", factoryConfigPath: "", systemContext: nil) + mProviderDelegate = CallKitProviderDelegate(context: self) + // enabling push notifications management in the core + mCore.callkitEnabled = true + mCore.pushNotificationEnabled = true + try? mCore.start() + + mCoreDelegate = CoreDelegateStub( onCallStateChanged: { (core: Core, call: Call, state: Call.State, message: String) in + self.callMsg = message + + if (state == .PushIncomingReceived){ + // We're being called by someone (and app is in background) + self.mCall = call + self.isCallIncoming = true + self.mProviderDelegate.incomingCall() + } else if (state == .IncomingReceived) { + // If app is in foreground, it's likely that we will receive the SIP invite before the Push notification + if (!self.isCallIncoming) { + self.mCall = call + self.isCallIncoming = true + self.mProviderDelegate.incomingCall() + } + self.remoteAddress = call.remoteAddress!.asStringUriOnly() + } else if (state == .Connected) { + self.isCallIncoming = false + self.isCallRunning = true + } else if (state == .Released || state == .End || state == .Error) { + // Call has been terminated by any side + + // Report to CallKit that the call is over, if the terminate action was initiated by other end of the call + if (self.isCallRunning) { + self.mProviderDelegate.stopCall() + } + self.remoteAddress = "Nobody yet" + } + }, onAccountRegistrationStateChanged: { (core: Core, account: Account, state: RegistrationState, message: String) in + NSLog("New registration state is \(state) for user id \( String(describing: account.params?.identityAddress?.asString()))\n") + if (state == .Ok) { + self.loggedIn = true + // Since core has "Push Enabled", the reception and setting of the push notification token is done automatically + // It should have been set and used when we log in, you can check here or in the liblinphone logs + NSLog("Account registered Push voip token: \(account.params?.pushNotificationConfig?.voipToken)") + } else if (state == .Cleared) { + self.loggedIn = false + } + }) + mCore.addDelegate(delegate: mCoreDelegate) + } + + func login() { + + do { + var transport : TransportType + if (transportType == "TLS") { transport = TransportType.Tls } + else if (transportType == "TCP") { transport = TransportType.Tcp } + else { transport = TransportType.Udp } + + let authInfo = try Factory.Instance.createAuthInfo(username: username, userid: "", passwd: passwd, ha1: "", realm: "", domain: domain) + let accountParams = try mCore.createAccountParams() + let identity = try Factory.Instance.createAddress(addr: String("sip:" + username + "@" + domain)) + try! accountParams.setIdentityaddress(newValue: identity) + let address = try Factory.Instance.createAddress(addr: String("sip:" + domain)) + try address.setTransport(newValue: transport) + try accountParams.setServeraddress(newValue: address) + accountParams.registerEnabled = true + // Enable push notifications on this account + accountParams.pushNotificationAllowed = true + // We're in a sandbox application, so we must set the provider to "apns.dev" since it will be "apns" by default, which is used only for production apps + accountParams.pushNotificationConfig?.provider = "apns.dev" + mAccount = try mCore.createAccount(params: accountParams) + mCore.addAuthInfo(info: authInfo) + try mCore.addAccount(account: mAccount!) + mCore.defaultAccount = mAccount + + } catch { NSLog(error.localizedDescription) } + } + + func unregister() + { + if let account = mCore.defaultAccount { + let params = account.params + let clonedParams = params?.clone() + clonedParams?.registerEnabled = false + account.params = clonedParams + } + } + func delete() { + if let account = mCore.defaultAccount { + mCore.removeAccount(account: account) + mCore.clearAccounts() + mCore.clearAllAuthInfo() + } + } +} diff --git a/ios/swift/4-CallKitTutorial/CallKitTutorial/ContentView.swift b/ios/swift/4-CallKitTutorial/CallKitTutorial/ContentView.swift new file mode 100644 index 0000000..ac8166d --- /dev/null +++ b/ios/swift/4-CallKitTutorial/CallKitTutorial/ContentView.swift @@ -0,0 +1,116 @@ +// +// ContentView.swift +// CallKit tutorial +// +// Created by QuentinArguillere on 09/09/2021. +// Copyright © 2021 BelledonneCommunications. All rights reserved. +// + +import SwiftUI + +struct ContentView: View { + + @ObservedObject var tutorialContext : CallKitExampleContext + + func callStateString() -> String { + if (tutorialContext.isCallRunning) { + return "Call running" + } else if (tutorialContext.isCallIncoming) { + return "Incoming call" + } else { + return "No Call" + } + } + + var body: some View { + + VStack { + Group { + HStack { + Text("Username:") + .font(.title) + TextField("", text : $tutorialContext.username) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .disabled(tutorialContext.loggedIn) + } + HStack { + Text("Password:") + .font(.title) + TextField("", text : $tutorialContext.passwd) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .disabled(tutorialContext.loggedIn) + } + HStack { + Text("Domain:") + .font(.title) + TextField("", text : $tutorialContext.domain) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .disabled(tutorialContext.loggedIn) + } + Picker(selection: $tutorialContext.transportType, label: Text("Transport:")) { + Text("TLS").tag("TLS") + Text("TCP").tag("TCP") + Text("UDP").tag("UDP") + }.pickerStyle(SegmentedPickerStyle()).padding() + VStack { + HStack { + Button(action: { + if (self.tutorialContext.loggedIn) + { + self.tutorialContext.unregister() + self.tutorialContext.delete() + } else { + self.tutorialContext.login() + } + }) + { + Text(tutorialContext.loggedIn ? "Log out & \ndelete account" : "Create & \nlog in account") + .font(.largeTitle) + .foregroundColor(Color.white) + .frame(width: 220.0, height: 90) + .background(Color.gray) + } + } + HStack { + Text("Login State : ") + .font(.footnote) + Text(tutorialContext.loggedIn ? "Looged in" : "Unregistered") + .font(.footnote) + .foregroundColor(tutorialContext.loggedIn ? Color.green : Color.black) + }.padding(.top, 10.0) + } + VStack { + HStack { + Text("Caller:").font(.title).underline() + Text(tutorialContext.remoteAddress) + Spacer() + }.padding(.top, 5) + HStack { + Text("Call msg:").font(.title3).underline() + Text(tutorialContext.callMsg) + Spacer() + }.padding(.top, 5) + }.padding(.top, 30) + Button(action: tutorialContext.mProviderDelegate.stopCall) + { + Text("End call").font(.largeTitle) + .foregroundColor(Color.white) + .frame(width: 120.0, height: 42.0) + .background(Color.gray) + }.disabled(!tutorialContext.isCallRunning) + .padding(.top, 10) + } + Group { + Spacer() + Text("Core Version is \(tutorialContext.coreVersion)") + } + } + .padding() + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView(tutorialContext: CallKitExampleContext()) + } +} diff --git a/ios/swift/4-CallKitTutorial/CallKitTutorial/Info.plist b/ios/swift/4-CallKitTutorial/CallKitTutorial/Info.plist new file mode 100644 index 0000000..84b1af3 --- /dev/null +++ b/ios/swift/4-CallKitTutorial/CallKitTutorial/Info.plist @@ -0,0 +1,70 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSApplicationCategoryType + + LSRequiresIPhoneOS + + NSCameraUsageDescription + Camera access + NSMicrophoneUsageDescription + Microphone access + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + + UIBackgroundModes + + voip + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/ios/swift/4-CallKitTutorial/CallKitTutorial/Preview Content/Preview Assets.xcassets/Contents.json b/ios/swift/4-CallKitTutorial/CallKitTutorial/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/ios/swift/4-CallKitTutorial/CallKitTutorial/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/swift/4-CallKitTutorial/CallKitTutorial/SceneDelegate.swift b/ios/swift/4-CallKitTutorial/CallKitTutorial/SceneDelegate.swift new file mode 100644 index 0000000..cc62bec --- /dev/null +++ b/ios/swift/4-CallKitTutorial/CallKitTutorial/SceneDelegate.swift @@ -0,0 +1,66 @@ +// +// SceneDelegate.swift +// CallKitTutorial +// +// Created by QuentinArguillere on 10/08/2020. +// Copyright © 2020 BelledonneCommunications. All rights reserved. +// + +import UIKit +import SwiftUI + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + + // Create the SwiftUI view that provides the window contents. + let delegate = UIApplication.shared.delegate as! AppDelegate + + let contentView = ContentView(tutorialContext: delegate.tutorialContext) + + // Use a UIHostingController as window root view controller. + if let windowScene = scene as? UIWindowScene { + let window = UIWindow(windowScene: windowScene) + window.rootViewController = UIHostingController(rootView: contentView) + self.window = window + window.makeKeyAndVisible() + } + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } + + +} + diff --git a/swift/HelloLinphone/Podfile b/ios/swift/4-CallKitTutorial/Podfile similarity index 73% rename from swift/HelloLinphone/Podfile rename to ios/swift/4-CallKitTutorial/Podfile index 5f136bc..add88a2 100644 --- a/swift/HelloLinphone/Podfile +++ b/ios/swift/4-CallKitTutorial/Podfile @@ -1,11 +1,11 @@ # Uncomment the next line to define a global platform for your project platform :ios, '11.0' source "https://gitlab.linphone.org/BC/public/podspec.git" -#source "https://github.com/CocoaPods/Specs.git" +source "https://github.com/CocoaPods/Specs.git" def basic_pods if ENV['PODFILE_PATH'].nil? - pod 'linphone-sdk', '~> 4.4.0' + pod 'linphone-sdk', '~> 5.0.0' else pod 'linphone-sdk', :path => ENV['PODFILE_PATH'] # local sdk end @@ -14,11 +14,11 @@ end -target 'HelloLinphone' do +target 'CallKitTutorial' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! - # Pods for HelloLinphone + # Pods for CallKitTutorial basic_pods end diff --git a/ios/swift/4-CallKitTutorial/README.md b/ios/swift/4-CallKitTutorial/README.md new file mode 100644 index 0000000..61f58d6 --- /dev/null +++ b/ios/swift/4-CallKitTutorial/README.md @@ -0,0 +1,10 @@ +Push notifications tutorial +==================== + +On mobile devices (Android & iOS), you probably want your app to be reachable even if it's not in the foreground. + +To do that you need it to be able to receive push notifications from your SIP proxy, and in this tutorial, using [Apple Push Notification Service](https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html), you'll learn how to simply send the device push information to your server. + +Apple will also require you to use their CallKit API when you receive a push notification, you must always notify CallKit when you receive a VOIP Push or your app will be terminated. If you use the "Core.pushEnabled=true" settings, as is done in this app, most of this work is done in the sdk and you only require to notify an incoming call when your Core Delegate notifies you of a new call. + +Compared to the previous tutorials, some changes have been required in `CallKitTutorial.xcodeproj` in order to enable `Push Notifications` and `BackGround Modes (Voice Over IP)` in the capabilities of your project. diff --git a/ios/swift/5-BasicChat/BasicChat.xcodeproj/project.pbxproj b/ios/swift/5-BasicChat/BasicChat.xcodeproj/project.pbxproj new file mode 100644 index 0000000..ad4a301 --- /dev/null +++ b/ios/swift/5-BasicChat/BasicChat.xcodeproj/project.pbxproj @@ -0,0 +1,424 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 34397F2DCE0A26771A291122 /* Pods_BasicChat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17BDC97FF3DCFB7B82244F46 /* Pods_BasicChat.framework */; }; + 664BC0E326EF459B007EE298 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BC0E226EF459B007EE298 /* AppDelegate.swift */; }; + 664BC0E526EF459B007EE298 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BC0E426EF459B007EE298 /* SceneDelegate.swift */; }; + 664BC0E726EF459B007EE298 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BC0E626EF459B007EE298 /* ContentView.swift */; }; + 664BC0E926EF459D007EE298 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 664BC0E826EF459D007EE298 /* Assets.xcassets */; }; + 664BC0EC26EF459D007EE298 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 664BC0EB26EF459D007EE298 /* Preview Assets.xcassets */; }; + 664BC0EF26EF459D007EE298 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 664BC0ED26EF459D007EE298 /* LaunchScreen.storyboard */; }; + 664BC0F726EF481A007EE298 /* BasicChat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BC0F626EF481A007EE298 /* BasicChat.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 17BDC97FF3DCFB7B82244F46 /* Pods_BasicChat.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BasicChat.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 664BC0DF26EF459B007EE298 /* BasicChat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BasicChat.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 664BC0E226EF459B007EE298 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 664BC0E426EF459B007EE298 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 664BC0E626EF459B007EE298 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 664BC0E826EF459D007EE298 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 664BC0EB26EF459D007EE298 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 664BC0EE26EF459D007EE298 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 664BC0F026EF459D007EE298 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 664BC0F626EF481A007EE298 /* BasicChat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicChat.swift; sourceTree = ""; }; + C9B5BB74C5F76BED02B82031 /* Pods-BasicChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BasicChat.release.xcconfig"; path = "Target Support Files/Pods-BasicChat/Pods-BasicChat.release.xcconfig"; sourceTree = ""; }; + F46732417EF2391C68D697D6 /* Pods-BasicChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BasicChat.debug.xcconfig"; path = "Target Support Files/Pods-BasicChat/Pods-BasicChat.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 664BC0DC26EF459B007EE298 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 34397F2DCE0A26771A291122 /* Pods_BasicChat.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 05AF3043CF55B85A1A53217A /* Frameworks */ = { + isa = PBXGroup; + children = ( + 17BDC97FF3DCFB7B82244F46 /* Pods_BasicChat.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 664BC0D626EF459B007EE298 = { + isa = PBXGroup; + children = ( + 664BC0E126EF459B007EE298 /* BasicChat */, + 664BC0E026EF459B007EE298 /* Products */, + 9DDE73864BE97E2EE2764318 /* Pods */, + 05AF3043CF55B85A1A53217A /* Frameworks */, + ); + sourceTree = ""; + }; + 664BC0E026EF459B007EE298 /* Products */ = { + isa = PBXGroup; + children = ( + 664BC0DF26EF459B007EE298 /* BasicChat.app */, + ); + name = Products; + sourceTree = ""; + }; + 664BC0E126EF459B007EE298 /* BasicChat */ = { + isa = PBXGroup; + children = ( + 664BC0E226EF459B007EE298 /* AppDelegate.swift */, + 664BC0E426EF459B007EE298 /* SceneDelegate.swift */, + 664BC0E626EF459B007EE298 /* ContentView.swift */, + 664BC0E826EF459D007EE298 /* Assets.xcassets */, + 664BC0ED26EF459D007EE298 /* LaunchScreen.storyboard */, + 664BC0F026EF459D007EE298 /* Info.plist */, + 664BC0F626EF481A007EE298 /* BasicChat.swift */, + 664BC0EA26EF459D007EE298 /* Preview Content */, + ); + path = BasicChat; + sourceTree = ""; + }; + 664BC0EA26EF459D007EE298 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 664BC0EB26EF459D007EE298 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 9DDE73864BE97E2EE2764318 /* Pods */ = { + isa = PBXGroup; + children = ( + F46732417EF2391C68D697D6 /* Pods-BasicChat.debug.xcconfig */, + C9B5BB74C5F76BED02B82031 /* Pods-BasicChat.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 664BC0DE26EF459B007EE298 /* BasicChat */ = { + isa = PBXNativeTarget; + buildConfigurationList = 664BC0F326EF459D007EE298 /* Build configuration list for PBXNativeTarget "BasicChat" */; + buildPhases = ( + 24E9CA4BD04B47A0932B80AF /* [CP] Check Pods Manifest.lock */, + 664BC0DB26EF459B007EE298 /* Sources */, + 664BC0DC26EF459B007EE298 /* Frameworks */, + 664BC0DD26EF459B007EE298 /* Resources */, + 3288DCA5B7DA5A1E061B4A8E /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = BasicChat; + productName = BasicChat; + productReference = 664BC0DF26EF459B007EE298 /* BasicChat.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 664BC0D726EF459B007EE298 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1250; + LastUpgradeCheck = 1250; + TargetAttributes = { + 664BC0DE26EF459B007EE298 = { + CreatedOnToolsVersion = 12.5.1; + }; + }; + }; + buildConfigurationList = 664BC0DA26EF459B007EE298 /* Build configuration list for PBXProject "BasicChat" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 664BC0D626EF459B007EE298; + productRefGroup = 664BC0E026EF459B007EE298 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 664BC0DE26EF459B007EE298 /* BasicChat */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 664BC0DD26EF459B007EE298 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 664BC0EF26EF459D007EE298 /* LaunchScreen.storyboard in Resources */, + 664BC0EC26EF459D007EE298 /* Preview Assets.xcassets in Resources */, + 664BC0E926EF459D007EE298 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 24E9CA4BD04B47A0932B80AF /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-BasicChat-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 3288DCA5B7DA5A1E061B4A8E /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-BasicChat/Pods-BasicChat-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-BasicChat/Pods-BasicChat-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-BasicChat/Pods-BasicChat-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 664BC0DB26EF459B007EE298 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 664BC0F726EF481A007EE298 /* BasicChat.swift in Sources */, + 664BC0E326EF459B007EE298 /* AppDelegate.swift in Sources */, + 664BC0E526EF459B007EE298 /* SceneDelegate.swift in Sources */, + 664BC0E726EF459B007EE298 /* ContentView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 664BC0ED26EF459D007EE298 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 664BC0EE26EF459D007EE298 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 664BC0F126EF459D007EE298 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 664BC0F226EF459D007EE298 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 664BC0F426EF459D007EE298 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F46732417EF2391C68D697D6 /* Pods-BasicChat.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"BasicChat/Preview Content\""; + DEVELOPMENT_TEAM = Z2V957B3D6; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = BasicChat/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = BC.BasicChat; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 664BC0F526EF459D007EE298 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C9B5BB74C5F76BED02B82031 /* Pods-BasicChat.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"BasicChat/Preview Content\""; + DEVELOPMENT_TEAM = Z2V957B3D6; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = BasicChat/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = BC.BasicChat; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 664BC0DA26EF459B007EE298 /* Build configuration list for PBXProject "BasicChat" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 664BC0F126EF459D007EE298 /* Debug */, + 664BC0F226EF459D007EE298 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 664BC0F326EF459D007EE298 /* Build configuration list for PBXNativeTarget "BasicChat" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 664BC0F426EF459D007EE298 /* Debug */, + 664BC0F526EF459D007EE298 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 664BC0D726EF459B007EE298 /* Project object */; +} diff --git a/ios/swift/5-BasicChat/BasicChat.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/swift/5-BasicChat/BasicChat.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/ios/swift/5-BasicChat/BasicChat.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/swift/5-BasicChat/BasicChat.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/swift/5-BasicChat/BasicChat.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/swift/5-BasicChat/BasicChat.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/swift/5-BasicChat/BasicChat/AppDelegate.swift b/ios/swift/5-BasicChat/BasicChat/AppDelegate.swift new file mode 100644 index 0000000..dbcbd61 --- /dev/null +++ b/ios/swift/5-BasicChat/BasicChat/AppDelegate.swift @@ -0,0 +1,38 @@ +// +// AppDelegate.swift +// BasicChat +// +// Created by QuentinArguillere on 31/07/2020. +// Copyright © 2020 BelledonneCommunications. All rights reserved. +// + +import UIKit +import SwiftUI + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + @ObservedObject var tutorialContext = BasicChatTutorialContext() + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + +} + diff --git a/ios/swift/5-BasicChat/BasicChat/Assets.xcassets/AccentColor.colorset/Contents.json b/ios/swift/5-BasicChat/BasicChat/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/ios/swift/5-BasicChat/BasicChat/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/swift/5-BasicChat/BasicChat/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/swift/5-BasicChat/BasicChat/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9221b9b --- /dev/null +++ b/ios/swift/5-BasicChat/BasicChat/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/swift/5-BasicChat/BasicChat/Assets.xcassets/Contents.json b/ios/swift/5-BasicChat/BasicChat/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/ios/swift/5-BasicChat/BasicChat/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/swift/5-BasicChat/BasicChat/Base.lproj/LaunchScreen.storyboard b/ios/swift/5-BasicChat/BasicChat/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..865e932 --- /dev/null +++ b/ios/swift/5-BasicChat/BasicChat/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/swift/5-BasicChat/BasicChat/BasicChat.swift b/ios/swift/5-BasicChat/BasicChat/BasicChat.swift new file mode 100644 index 0000000..e199411 --- /dev/null +++ b/ios/swift/5-BasicChat/BasicChat/BasicChat.swift @@ -0,0 +1,241 @@ +// +// BasicChat.swift +// BasicChat +// +// Created by QuentinArguillere on 08/09/2021. +// Copyright © 2021 BelledonneCommunications. All rights reserved. +// + +import linphonesw + + + +class BasicChatTutorialContext : ObservableObject +{ + var mCore: Core! + @Published var coreVersion: String = Core.getVersion + + var mRegistrationDelegate : CoreDelegate! + @Published var username : String = "user" + @Published var passwd : String = "pwd" + @Published var domain : String = "sip.example.org" + @Published var loggedIn: Bool = false + @Published var transportType : String = "TLS" + + /*------------ Basic chat tutorial related variables -------*/ + var mChatroom : ChatRoom? + var mChatMessageDelegate : ChatMessageDelegate! + var mChatMessage : ChatMessage? + var mLastFileMessageReceived : ChatMessage? + @Published var msgToSend : String = "msg" + @Published var remoteAddress : String = "sip:remote@sip.example.org" + @Published var canEditAddress : Bool = true + @Published var isDownloading : Bool = false + @Published var messagesReceived : String = "" + var fileFolderUrl : URL! + var fileUrl : URL! + + init() + { + LoggingService.Instance.logLevel = LogLevel.Debug + + try? mCore = Factory.Instance.createCore(configPath: "", factoryConfigPath: "", systemContext: nil) + try? mCore.start() + + mRegistrationDelegate = CoreDelegateStub(onMessageReceived : { (core: Core, chatRoom: ChatRoom, message: ChatMessage) in + // We will be called in this when a message is received + // If the chat room wasn't existing, it is automatically created by the library + // If we already sent a chat message, the chatRoom variable will be the same as the one we already have + if (self.mChatroom == nil) { + if (chatRoom.hasCapability(mask: ChatRoomCapabilities.Basic.rawValue)) { + // Keep the chatRoom object to use it to send messages if it hasn't been created yet + self.mChatroom = chatRoom + if let remoteAddress = chatRoom.peerAddress?.asStringUriOnly() { + self.remoteAddress = remoteAddress + } + self.canEditAddress = false + } + } + // We will notify the sender the message has been read by us + chatRoom.markAsRead() + + for content in message.contents { + if (content.isFileTransfer) { + self.mLastFileMessageReceived = message + self.messagesReceived += "\n--File available for download--" + } else if (content.isText) { + self.messagesReceived += "\nThem: \(message.utf8Text)" + } + } + + }, onAccountRegistrationStateChanged: { (core: Core, account: Account, state: RegistrationState, message: String) in + NSLog("New registration state is \(state) for user id \( String(describing: account.params?.identityAddress?.asString()))\n") + if (state == .Ok) { + self.loggedIn = true + } else if (state == .Cleared) { + self.loggedIn = false + } + }) + mCore.addDelegate(delegate: mRegistrationDelegate) + + // This delegate has to be attached to each specific chat message we want to monitor, before sending it + mChatMessageDelegate = ChatMessageDelegateStub(onMsgStateChanged : { (message: ChatMessage, state: ChatMessage.State) in + print("MessageTrace - msg state changed: \(state)\n") + if (state == ChatMessage.State.FileTransferDone && self.isDownloading == true) { + self.isDownloading = false + } else if (state == .Delivered) { + self.messagesReceived += "\nMe: \(message.utf8Text)" + } + }) + + // example file to send + let documentsPath = NSURL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]) + fileFolderUrl = documentsPath.appendingPathComponent("TutorialFiles") + fileUrl = fileFolderUrl?.appendingPathComponent("file_to_transfer.txt") + do{ + try FileManager.default.createDirectory(atPath: fileFolderUrl!.path, withIntermediateDirectories: true, attributes: nil) + try String("My file content").write(to: fileUrl!, atomically: false, encoding: .utf8) + }catch let error as NSError{ + print("Unable to create d)irectory",error) + } + } + + func login() { + + do { + var transport : TransportType + if (transportType == "TLS") { transport = TransportType.Tls } + else if (transportType == "TCP") { transport = TransportType.Tcp } + else { transport = TransportType.Udp } + + let authInfo = try Factory.Instance.createAuthInfo(username: username, userid: "", passwd: passwd, ha1: "", realm: "", domain: domain) + let accountParams = try mCore.createAccountParams() + let identity = try Factory.Instance.createAddress(addr: String("sip:" + username + "@" + domain)) + try! accountParams.setIdentityaddress(newValue: identity) + let address = try Factory.Instance.createAddress(addr: String("sip:" + domain)) + try address.setTransport(newValue: transport) + try accountParams.setServeraddress(newValue: address) + accountParams.registerEnabled = true + let account = try mCore.createAccount(params: accountParams) + + mCore.addAuthInfo(info: authInfo) + try mCore.addAccount(account: account) + + mCore.defaultAccount = account + + } catch { NSLog(error.localizedDescription) } + } + + func unregister() + { + if let account = mCore.defaultAccount { + let params = account.params + let clonedParams = params?.clone() + clonedParams?.registerEnabled = false + account.params = clonedParams + } + } + func delete() { + if let account = mCore.defaultAccount { + mCore.removeAccount(account: account) + mCore.clearAccounts() + mCore.clearAllAuthInfo() + } + } + + func createBasicChatRoom() { + do { + // In this tutorial we will create a Basic chat room + // It doesn't include advanced features such as end-to-end encryption or groups + // But it is interoperable with any SIP service as it's relying on SIP SIMPLE messages + // If you try to enable a feature not supported by the basic backend, isValid() will return false + let params = try mCore.createDefaultChatRoomParams() + params.backend = ChatRoomBackend.Basic + params.encryptionEnabled = false + params.groupEnabled = false + + if (params.isValid) { + // We also need the SIP address of the person we will chat with + let remote = try Factory.Instance.createAddress(addr: remoteAddress) + // And finally we will need our local SIP address + let localAddress = mCore.defaultAccount?.params?.identityAddress + mChatroom = try mCore.createChatRoom(params: params, localAddr: localAddress, participants: [remote]) + if (mChatroom != nil) { + canEditAddress = false + } + } + } catch { NSLog(error.localizedDescription) } + } + + func sendMessage() { + do { + if (mChatroom == nil) { + // We need a ChatRoom object to send chat messages in it, so let's create it if it hasn't been done yet + createBasicChatRoom() + } + mChatMessage = nil + // We need to create a ChatMessage object using the ChatRoom + mChatMessage = try mChatroom!.createMessageFromUtf8(message: msgToSend) + + // Then we can send it, progress will be notified using the onMsgStateChanged callback + mChatMessage!.addDelegate(delegate: mChatMessageDelegate) + + // Send the message + mChatMessage!.send() + + // Clear the message input field + msgToSend.removeAll() + } catch { NSLog(error.localizedDescription) } + } + + func sendFile() { + do { + if (mChatroom == nil) { + // We need a ChatRoom object to send chat messages in it, so let's create it if it hasn't been done yet + createBasicChatRoom() + } + + // We need to create a Content for our file transfer + let content = try Factory.Instance.createContent() + // Every content needs a content type & subtype + content.name = "file_to_transfer.txt" + content.type = "text" + content.subtype = "plain" + + // The simplest way to upload a file is to provide it's path + content.filePath = fileUrl.path + + // We need to create a ChatMessage object using the ChatRoom + let chatMessage = try mChatroom!.createFileTransferMessage(initialContent: content) + + // Then we can send it, progress will be notified using the onMsgStateChanged callback + chatMessage.addDelegate(delegate: mChatMessageDelegate) + + // Ensure a file sharing server URL is correctly set in the Core + mCore.fileTransferServer = "https://www.linphone.org:444/lft.php" + + // Send the message + chatMessage.send() + + } catch { NSLog(error.localizedDescription) } + } + + + func downloadLastFileMessage() { + if let message = mLastFileMessageReceived { + for content in message.contents { + if (content.isFileTransfer && content.filePath.isEmpty) { + let contentName = content.name + if (!contentName.isEmpty) { + content.filePath = fileFolderUrl!.appendingPathComponent(contentName).path + print("Start downloading \(content.name) into \(content.filePath)") + isDownloading = true + if (!message.downloadContent(content: content)) { + print ("Download of \(contentName) failed") + } + } + } + } + } + } +} diff --git a/ios/swift/5-BasicChat/BasicChat/ContentView.swift b/ios/swift/5-BasicChat/BasicChat/ContentView.swift new file mode 100644 index 0000000..41dc65c --- /dev/null +++ b/ios/swift/5-BasicChat/BasicChat/ContentView.swift @@ -0,0 +1,144 @@ +// +// ContentView.swift +// BasicChat +// +// Created by QuentinArguillere on 31/07/2020. +// Copyright © 2020 BelledonneCommunications. All rights reserved. +// + +import SwiftUI + +struct ActivityIndicator: UIViewRepresentable { + func makeUIView(context: UIViewRepresentableContext) -> UIActivityIndicatorView + { + return UIActivityIndicatorView(style: .medium) + } + + func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext) + { + uiView.startAnimating() + } +} + +struct ContentView: View { + + @ObservedObject var tutorialContext : BasicChatTutorialContext + + var body: some View { + + VStack { + Group { + HStack { + Text("Username:") + .font(.title) + TextField("", text : $tutorialContext.username) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .disabled(tutorialContext.loggedIn) + } + HStack { + Text("Password:") + .font(.title) + TextField("", text : $tutorialContext.passwd) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .disabled(tutorialContext.loggedIn) + } + HStack { + Text("Domain:") + .font(.title) + TextField("", text : $tutorialContext.domain) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .disabled(tutorialContext.loggedIn) + } + Picker(selection: $tutorialContext.transportType, label: Text("Transport:")) { + Text("TLS").tag("TLS") + Text("TCP").tag("TCP") + Text("UDP").tag("UDP") + }.pickerStyle(SegmentedPickerStyle()).padding() + VStack { + HStack { + Button(action: { + if (self.tutorialContext.loggedIn) + { + self.tutorialContext.unregister() + self.tutorialContext.delete() + } else { + self.tutorialContext.login() + } + }) + { + Text(tutorialContext.loggedIn ? "Log out & \ndelete account" : "Create & \nlog in account") + .font(.largeTitle) + .foregroundColor(Color.white) + .frame(width: 220.0, height: 90) + .background(Color.gray) + } + + } + HStack { + Text("Login State : ") + .font(.footnote) + Text(tutorialContext.loggedIn ? "Looged in" : "Unregistered") + .font(.footnote) + .foregroundColor(tutorialContext.loggedIn ? Color.green : Color.black) + }.padding(.top, 10.0) + HStack { + Text("Chat with:") + TextField("", text : $tutorialContext.remoteAddress) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .disabled(!tutorialContext.canEditAddress) + } + Text("Chat received").bold() + ScrollView { + Text(tutorialContext.messagesReceived) + .font(.footnote) + .frame(width: 330, height: 400) + }.border(Color.gray) + HStack { + TextField("Sent text", text : $tutorialContext.msgToSend) + .textFieldStyle(RoundedBorderTextFieldStyle()) + Button(action: tutorialContext.sendMessage) + { + Text("Send") + .font(.callout) + .foregroundColor(Color.white) + .frame(width: 50.0, height: 30.0) + .background(Color.gray) + } + } + HStack { + Button(action: tutorialContext.sendFile) + { + Text("Send example \n file") + .foregroundColor(Color.white) + .multilineTextAlignment(.center) + .frame(width: 120.0, height: 50.0) + .background(Color.gray) + } + Button(action: tutorialContext.downloadLastFileMessage) + { + Text("Download last files \n received") + .foregroundColor(Color.white) + .multilineTextAlignment(.center) + .frame(width: 150.0, height: 50.0) + .background(Color.gray) + }.disabled(tutorialContext.mLastFileMessageReceived == nil) + if (tutorialContext.isDownloading) { + ActivityIndicator() + } + } + } + } + Group { + Spacer() + Text("Core Version is \(tutorialContext.coreVersion)") + } + } + .padding() + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView(tutorialContext: BasicChatTutorialContext()) + } +} diff --git a/ios/swift/5-BasicChat/BasicChat/Info.plist b/ios/swift/5-BasicChat/BasicChat/Info.plist new file mode 100644 index 0000000..2688b32 --- /dev/null +++ b/ios/swift/5-BasicChat/BasicChat/Info.plist @@ -0,0 +1,62 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/ios/swift/5-BasicChat/BasicChat/Preview Content/Preview Assets.xcassets/Contents.json b/ios/swift/5-BasicChat/BasicChat/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/ios/swift/5-BasicChat/BasicChat/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/swift/5-BasicChat/BasicChat/SceneDelegate.swift b/ios/swift/5-BasicChat/BasicChat/SceneDelegate.swift new file mode 100644 index 0000000..87c3c91 --- /dev/null +++ b/ios/swift/5-BasicChat/BasicChat/SceneDelegate.swift @@ -0,0 +1,65 @@ +// +// SceneDelegate.swift +// BasicChat +// +// Created by QuentinArguillere on 31/07/2020. +// Copyright © 2020 BelledonneCommunications. All rights reserved. +// + +import UIKit +import SwiftUI + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + + let delegate = UIApplication.shared.delegate as! AppDelegate + + // Create the SwiftUI view that provides the window contents. + let contentView = ContentView(tutorialContext: delegate.tutorialContext) + + // Use a UIHostingController as window root view controller. + if let windowScene = scene as? UIWindowScene { + let window = UIWindow(windowScene: windowScene) + window.rootViewController = UIHostingController(rootView: contentView) + self.window = window + window.makeKeyAndVisible() + } + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } + + +} + diff --git a/ios/swift/5-BasicChat/Podfile b/ios/swift/5-BasicChat/Podfile new file mode 100644 index 0000000..80a7c57 --- /dev/null +++ b/ios/swift/5-BasicChat/Podfile @@ -0,0 +1,24 @@ +# Uncomment the next line to define a global platform for your project +platform :ios, '11.0' +source "https://gitlab.linphone.org/BC/public/podspec.git" +source "https://github.com/CocoaPods/Specs.git" + +def basic_pods + if ENV['PODFILE_PATH'].nil? + pod 'linphone-sdk', '~> 5.0.0' + else + pod 'linphone-sdk', :path => ENV['PODFILE_PATH'] # local sdk + end + +end + + + +target 'BasicChat' do + # Comment the next line if you don't want to use dynamic frameworks + use_frameworks! + + # Pods for BasicChat + basic_pods + +end diff --git a/ios/swift/5-BasicChat/README.md b/ios/swift/5-BasicChat/README.md new file mode 100644 index 0000000..c8e125b --- /dev/null +++ b/ios/swift/5-BasicChat/README.md @@ -0,0 +1,8 @@ +Basic chat tutorial +==================== + +This tutorial will demonstrate how to send and display a simple SIP message containing either text or an image (but it works the same for any kind of file). + +Note that for file transfer, a file transfer server is required. In this tutorial we'll use the one at `https://www.linphone.org:444/lft.php` that we use in our own linphone-android and linphone-iphone apps, but you can get its [source code](https://gitlab.linphone.org/BC/public/flexisip-http-file-transfer-server) and deploy your own. + +Messages sent in this tutorial are standard SIP messages, so no matter the SIP proxy server you are using it should work, unlike the next [advanced chat tutorial](https://gitlab.linphone.org/BC/public/tutorials/-/tree/master/ios/swift/6-AdvancedChat). diff --git a/ios/swift/6-AdvancedChat/AdvancedChat.xcodeproj/project.pbxproj b/ios/swift/6-AdvancedChat/AdvancedChat.xcodeproj/project.pbxproj new file mode 100644 index 0000000..f782718 --- /dev/null +++ b/ios/swift/6-AdvancedChat/AdvancedChat.xcodeproj/project.pbxproj @@ -0,0 +1,424 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 5F916A81CD75D10483A4DAC8 /* Pods_AdvancedChat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E72FBDC953F141F267B03540 /* Pods_AdvancedChat.framework */; }; + 664BC12726F0CA1F007EE298 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BC12626F0CA1F007EE298 /* AppDelegate.swift */; }; + 664BC12926F0CA1F007EE298 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BC12826F0CA1F007EE298 /* SceneDelegate.swift */; }; + 664BC12B26F0CA1F007EE298 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BC12A26F0CA1F007EE298 /* ContentView.swift */; }; + 664BC12D26F0CA20007EE298 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 664BC12C26F0CA20007EE298 /* Assets.xcassets */; }; + 664BC13026F0CA20007EE298 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 664BC12F26F0CA20007EE298 /* Preview Assets.xcassets */; }; + 664BC13326F0CA20007EE298 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 664BC13126F0CA20007EE298 /* LaunchScreen.storyboard */; }; + 664BC13B26F0CA94007EE298 /* AdvancedChat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BC13A26F0CA94007EE298 /* AdvancedChat.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 664BC12326F0CA1F007EE298 /* AdvancedChat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AdvancedChat.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 664BC12626F0CA1F007EE298 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 664BC12826F0CA1F007EE298 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 664BC12A26F0CA1F007EE298 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 664BC12C26F0CA20007EE298 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 664BC12F26F0CA20007EE298 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 664BC13226F0CA20007EE298 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 664BC13426F0CA20007EE298 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 664BC13A26F0CA94007EE298 /* AdvancedChat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdvancedChat.swift; sourceTree = ""; }; + 8FCAA86A89E245EB7D78FAED /* Pods-AdvancedChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AdvancedChat.debug.xcconfig"; path = "Target Support Files/Pods-AdvancedChat/Pods-AdvancedChat.debug.xcconfig"; sourceTree = ""; }; + 9B4A3E0699D8A5E8F930E207 /* Pods-AdvancedChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AdvancedChat.release.xcconfig"; path = "Target Support Files/Pods-AdvancedChat/Pods-AdvancedChat.release.xcconfig"; sourceTree = ""; }; + E72FBDC953F141F267B03540 /* Pods_AdvancedChat.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AdvancedChat.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 664BC12026F0CA1F007EE298 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 5F916A81CD75D10483A4DAC8 /* Pods_AdvancedChat.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 47B1E7CE04184A06D7C8479E /* Frameworks */ = { + isa = PBXGroup; + children = ( + E72FBDC953F141F267B03540 /* Pods_AdvancedChat.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 664BC11A26F0CA1F007EE298 = { + isa = PBXGroup; + children = ( + 664BC12526F0CA1F007EE298 /* AdvancedChat */, + 664BC12426F0CA1F007EE298 /* Products */, + D66D20255E37FA73AC508469 /* Pods */, + 47B1E7CE04184A06D7C8479E /* Frameworks */, + ); + sourceTree = ""; + }; + 664BC12426F0CA1F007EE298 /* Products */ = { + isa = PBXGroup; + children = ( + 664BC12326F0CA1F007EE298 /* AdvancedChat.app */, + ); + name = Products; + sourceTree = ""; + }; + 664BC12526F0CA1F007EE298 /* AdvancedChat */ = { + isa = PBXGroup; + children = ( + 664BC12626F0CA1F007EE298 /* AppDelegate.swift */, + 664BC12826F0CA1F007EE298 /* SceneDelegate.swift */, + 664BC12A26F0CA1F007EE298 /* ContentView.swift */, + 664BC12C26F0CA20007EE298 /* Assets.xcassets */, + 664BC13126F0CA20007EE298 /* LaunchScreen.storyboard */, + 664BC13A26F0CA94007EE298 /* AdvancedChat.swift */, + 664BC13426F0CA20007EE298 /* Info.plist */, + 664BC12E26F0CA20007EE298 /* Preview Content */, + ); + path = AdvancedChat; + sourceTree = ""; + }; + 664BC12E26F0CA20007EE298 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 664BC12F26F0CA20007EE298 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + D66D20255E37FA73AC508469 /* Pods */ = { + isa = PBXGroup; + children = ( + 8FCAA86A89E245EB7D78FAED /* Pods-AdvancedChat.debug.xcconfig */, + 9B4A3E0699D8A5E8F930E207 /* Pods-AdvancedChat.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 664BC12226F0CA1F007EE298 /* AdvancedChat */ = { + isa = PBXNativeTarget; + buildConfigurationList = 664BC13726F0CA20007EE298 /* Build configuration list for PBXNativeTarget "AdvancedChat" */; + buildPhases = ( + 70209B2F1633EA190EA8E09D /* [CP] Check Pods Manifest.lock */, + 664BC11F26F0CA1F007EE298 /* Sources */, + 664BC12026F0CA1F007EE298 /* Frameworks */, + 664BC12126F0CA1F007EE298 /* Resources */, + 16DC7246B49FC8754CAB12B5 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AdvancedChat; + productName = AdvancedChat; + productReference = 664BC12326F0CA1F007EE298 /* AdvancedChat.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 664BC11B26F0CA1F007EE298 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1250; + LastUpgradeCheck = 1250; + TargetAttributes = { + 664BC12226F0CA1F007EE298 = { + CreatedOnToolsVersion = 12.5.1; + }; + }; + }; + buildConfigurationList = 664BC11E26F0CA1F007EE298 /* Build configuration list for PBXProject "AdvancedChat" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 664BC11A26F0CA1F007EE298; + productRefGroup = 664BC12426F0CA1F007EE298 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 664BC12226F0CA1F007EE298 /* AdvancedChat */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 664BC12126F0CA1F007EE298 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 664BC13326F0CA20007EE298 /* LaunchScreen.storyboard in Resources */, + 664BC13026F0CA20007EE298 /* Preview Assets.xcassets in Resources */, + 664BC12D26F0CA20007EE298 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 16DC7246B49FC8754CAB12B5 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-AdvancedChat/Pods-AdvancedChat-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-AdvancedChat/Pods-AdvancedChat-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AdvancedChat/Pods-AdvancedChat-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 70209B2F1633EA190EA8E09D /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-AdvancedChat-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 664BC11F26F0CA1F007EE298 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 664BC12726F0CA1F007EE298 /* AppDelegate.swift in Sources */, + 664BC12926F0CA1F007EE298 /* SceneDelegate.swift in Sources */, + 664BC12B26F0CA1F007EE298 /* ContentView.swift in Sources */, + 664BC13B26F0CA94007EE298 /* AdvancedChat.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 664BC13126F0CA20007EE298 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 664BC13226F0CA20007EE298 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 664BC13526F0CA20007EE298 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 664BC13626F0CA20007EE298 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 664BC13826F0CA20007EE298 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8FCAA86A89E245EB7D78FAED /* Pods-AdvancedChat.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"AdvancedChat/Preview Content\""; + DEVELOPMENT_TEAM = Z2V957B3D6; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = AdvancedChat/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = BC.AdvancedChat; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 664BC13926F0CA20007EE298 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9B4A3E0699D8A5E8F930E207 /* Pods-AdvancedChat.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"AdvancedChat/Preview Content\""; + DEVELOPMENT_TEAM = Z2V957B3D6; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = AdvancedChat/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = BC.AdvancedChat; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 664BC11E26F0CA1F007EE298 /* Build configuration list for PBXProject "AdvancedChat" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 664BC13526F0CA20007EE298 /* Debug */, + 664BC13626F0CA20007EE298 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 664BC13726F0CA20007EE298 /* Build configuration list for PBXNativeTarget "AdvancedChat" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 664BC13826F0CA20007EE298 /* Debug */, + 664BC13926F0CA20007EE298 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 664BC11B26F0CA1F007EE298 /* Project object */; +} diff --git a/ios/swift/6-AdvancedChat/AdvancedChat.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/swift/6-AdvancedChat/AdvancedChat.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/ios/swift/6-AdvancedChat/AdvancedChat.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/swift/6-AdvancedChat/AdvancedChat.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/swift/6-AdvancedChat/AdvancedChat.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/swift/6-AdvancedChat/AdvancedChat.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/swift/6-AdvancedChat/AdvancedChat/AdvancedChat.swift b/ios/swift/6-AdvancedChat/AdvancedChat/AdvancedChat.swift new file mode 100644 index 0000000..b987e0d --- /dev/null +++ b/ios/swift/6-AdvancedChat/AdvancedChat/AdvancedChat.swift @@ -0,0 +1,276 @@ +// +// GroupChat.swift +// GroupChat +// +// Created by QuentinArguillere on 08/09/2021. +// Copyright © 2021 BelledonneCommunications. All rights reserved. +// + +import linphonesw + + + +class AdvancedChatTutorialContext : ObservableObject +{ + var mCore: Core! + @Published var coreVersion: String = Core.getVersion + + var mRegistrationDelegate : CoreDelegate! + @Published var username : String = "user" + @Published var passwd : String = "pwd" + @Published var domain : String = "sip.example.org" + @Published var loggedIn: Bool = false + @Published var transportType : String = "TLS" + + /*------------ Advanced chat tutorial related variables -------*/ + var mChatroom : ChatRoom? + var mChatroomDelegate : ChatRoomDelegate! + var mChatMessageDelegate : ChatMessageDelegate! + var mChatMessage : ChatMessage? + var mLastFileMessageReceived : ChatMessage? + @Published var msgToSend : String = "msg" + @Published var remoteAddress : String = "sip:remote@sip.example.org" + @Published var canEditAddress : Bool = true + @Published var isDownloading : Bool = false + @Published var messagesReceived : String = "" + var fileFolderUrl : URL! + var fileUrl : URL! + + init() + { + LoggingService.Instance.logLevel = LogLevel.Debug + + try? mCore = Factory.Instance.createCore(configPath: "", factoryConfigPath: "", systemContext: nil) + try? mCore.start() + + mRegistrationDelegate = CoreDelegateStub(onMessageReceived : { (core: Core, chatRoom: ChatRoom, message: ChatMessage) in + if (self.mChatroom == nil) { + // Check it is an one-to-one encrypted chat room + if (chatRoom.hasCapability(mask: ChatRoomCapabilities.OneToOne.rawValue) && + chatRoom.hasCapability(mask: ChatRoomCapabilities.Encrypted.rawValue)) { + // Keep the chatRoom object to use it to send messages if it hasn't been created yet + self.mChatroom = chatRoom + self.mChatroom!.addDelegate(delegate: self.mChatroomDelegate) + self.enableEphemeral() + if let remoteAddress = chatRoom.peerAddress?.asStringUriOnly() { + self.remoteAddress = remoteAddress + } + self.canEditAddress = false + } + } + + chatRoom.markAsRead() + + for content in message.contents { + if (content.isFileTransfer) { + self.mLastFileMessageReceived = message + self.messagesReceived += "\n--File available for download--" + } else if (content.isText) { + self.messagesReceived += "\nThem: \(message.utf8Text)" + } + } + + }, onAccountRegistrationStateChanged: { (core: Core, account: Account, state: RegistrationState, message: String) in + NSLog("New registration state is \(state) for user id \( String(describing: account.params?.identityAddress?.asString()))\n") + if (state == .Ok) { + self.loggedIn = true + } else if (state == .Cleared) { + self.loggedIn = false + } + }) + mCore.addDelegate(delegate: mRegistrationDelegate) + + // This delegate has to be attached to each specific chat message we want to monitor, before sending it + mChatMessageDelegate = ChatMessageDelegateStub(onMsgStateChanged : { (message: ChatMessage, state: ChatMessage.State) in + print("MessageTrace - msg state changed: \(state)\n") + if (state == .InProgress) { + + } else if (state == .Delivered) { + // The proxy server has acknowledged the message with a 200 OK + self.messagesReceived += "\nMe: \(message.utf8Text)" + } else if (state == .DeliveredToUser) { + // User has received it + } else if (state == .Displayed) { + // User has read it (client called chatRoom.markAsRead() + } else if (state == .NotDelivered) { + // User might be invalid or not registered + } else if (state == .FileTransferDone && self.isDownloading == true) { + // We finished uploading/downloading the file + self.isDownloading = false + } + }) + + + mChatroomDelegate = ChatRoomDelegateStub ( onStateChanged: { (chatRoom: ChatRoom, newState: ChatRoom.State?) in + if (newState == ChatRoom.State.Created) { + self.enableEphemeral() + } + }, onEphemeralEvent: { (chatRoom: ChatRoom, eventLog: EventLog) in + // This event is generated when the chat room ephemeral settings are being changed + }, onEphemeralMessageTimerStarted: { (chatRoom: ChatRoom, eventLog: EventLog) in + // This is called when a message has been read by all recipient, so the timer has started + }, onEphemeralMessageDeleted: { (chatRoom: ChatRoom, eventLog: EventLog) in + // This is called when a message has expired and we should remove it from the view + }) + + + // example file to send + let documentsPath = NSURL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]) + fileFolderUrl = documentsPath.appendingPathComponent("TutorialFiles") + fileUrl = fileFolderUrl?.appendingPathComponent("file_to_transfer.txt") + do{ + try FileManager.default.createDirectory(atPath: fileFolderUrl!.path, withIntermediateDirectories: true, attributes: nil) + try String("My file content").write(to: fileUrl!, atomically: false, encoding: .utf8) + }catch let error as NSError{ + print("Unable to create d)irectory",error) + } + } + + func login() { + + do { + var transport : TransportType + if (transportType == "TLS") { transport = TransportType.Tls } + else if (transportType == "TCP") { transport = TransportType.Tcp } + else { transport = TransportType.Udp } + + let authInfo = try Factory.Instance.createAuthInfo(username: username, userid: "", passwd: passwd, ha1: "", realm: "", domain: domain) + let accountParams = try mCore.createAccountParams() + let identity = try Factory.Instance.createAddress(addr: String("sip:" + username + "@" + domain)) + try! accountParams.setIdentityaddress(newValue: identity) + let address = try Factory.Instance.createAddress(addr: String("sip:" + domain)) + try address.setTransport(newValue: transport) + try accountParams.setServeraddress(newValue: address) + accountParams.registerEnabled = true + + // We need a conference factory URI set on the Account to be able to create chat rooms with flexisip backend + accountParams.conferenceFactoryUri = "sip:conference-factory@sip.linphone.org" + + mCore.addAuthInfo(info: authInfo) + + let account = try mCore.createAccount(params: accountParams) + try mCore.addAccount(account: account) + mCore.defaultAccount = account + + // We also need a LIME X3DH server URL configured for end to end encryption + mCore.limeX3DhServerUrl = "https://lime.linphone.org/lime-server/lime-server.php" + } catch { NSLog(error.localizedDescription) } + } + + func unregister() + { + if let account = mCore.defaultAccount { + let params = account.params + let clonedParams = params?.clone() + clonedParams?.registerEnabled = false + account.params = clonedParams + } + } + func delete() { + if let account = mCore.defaultAccount { + mCore.removeAccount(account: account) + mCore.clearAccounts() + mCore.clearAllAuthInfo() + } + } + + + func createFlexisipChatRoom() { + do { + // In this tutorial we will create a Flexisip one-to-one chat room with end-to-end encryption + // For it to work, the proxy server we connect to must be an instance of Flexisip + // And we must have configured on the Account a conference-factory URI + let params = try mCore.createDefaultChatRoomParams() + + // We won't create a group chat, only a 1-1 with advanced features such as end-to-end encryption + params.backend = ChatRoomBackend.FlexisipChat + params.groupEnabled = false + + // We will rely on LIME encryption backend (we must have configured the core.limex3dhServerUrl first) + params.encryptionEnabled = true + params.encryptionBackend = ChatRoomEncryptionBackend.Lime + + // A flexisip chat room must have a subject + // But as we are doing a 1-1 chat room here we won't display it, so we can set whatever we want + params.subject = "dummy subject" + + if (params.isValid) { + // We also need the SIP address of the person we will chat with + let remote = try Factory.Instance.createAddress(addr: remoteAddress) + + // And finally we will need our local SIP address + let localAddress = mCore.defaultAccount?.params?.identityAddress + mChatroom = try mCore.createChatRoom(params: params, localAddr: localAddress, participants: [remote]) + // If chat room isn't created yet, wait for it to go in state Created + // as Flexisip chat room creation process is asynchronous + mChatroom!.addDelegate(delegate: mChatroomDelegate) + + // Chat room may already be created (for example if you logged in with an account for which the chat room already exists) + if (mChatroom!.state == ChatRoom.State.Created) { + enableEphemeral() + canEditAddress = false + } + } + } catch { NSLog(error.localizedDescription) } + } + + func sendMessage() { + do { + if (mChatroom == nil) { + createFlexisipChatRoom() + } + mChatMessage = nil + mChatMessage = try mChatroom!.createMessageFromUtf8(message: msgToSend) + mChatMessage!.addDelegate(delegate: mChatMessageDelegate) + mChatMessage!.send() + msgToSend.removeAll() + } catch { NSLog(error.localizedDescription) } + } + + func sendFile() { + do { + if (mChatroom == nil) { + createFlexisipChatRoom() + } + + let content = try Factory.Instance.createContent() + content.name = "file_to_transfer.txt" + content.type = "text" + content.subtype = "plain" + content.filePath = fileUrl.path + let chatMessage = try mChatroom!.createFileTransferMessage(initialContent: content) + chatMessage.addDelegate(delegate: mChatMessageDelegate) + mCore.fileTransferServer = "https://www.linphone.org:444/lft.php" + chatMessage.send() + + } catch { NSLog(error.localizedDescription) } + } + + func downloadLastFileMessage() { + if let message = mLastFileMessageReceived { + for content in message.contents { + if (content.isFileTransfer && content.filePath.isEmpty) { + let contentName = content.name + if (!contentName.isEmpty) { + content.filePath = fileFolderUrl!.appendingPathComponent(contentName).path + print("Start downloading \(content.name) into \(content.filePath)") + isDownloading = true + if (!message.downloadContent(content: content)) { + print ("Download of \(contentName) failed") + } + } + } + } + } + } + + + func enableEphemeral() { + // Once chat room has been created, we can enable ephemeral feature + // We enable ephemeral messages at the chat room level + // Please note this only affects messages we send, not the ones we receive + mChatroom?.ephemeralEnabled = true + // Here we ask for a lifetime of 60 seconds, starting the moment the message has been read + mChatroom?.ephemeralLifetime = 60 + } +} diff --git a/ios/swift/6-AdvancedChat/AdvancedChat/AppDelegate.swift b/ios/swift/6-AdvancedChat/AdvancedChat/AppDelegate.swift new file mode 100644 index 0000000..fd0c934 --- /dev/null +++ b/ios/swift/6-AdvancedChat/AdvancedChat/AppDelegate.swift @@ -0,0 +1,38 @@ +// +// AppDelegate.swift +// BasicChat +// +// Created by QuentinArguillere on 31/07/2020. +// Copyright © 2020 BelledonneCommunications. All rights reserved. +// + +import UIKit +import SwiftUI + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + @ObservedObject var tutorialContext = AdvancedChatTutorialContext() + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + +} + diff --git a/ios/swift/6-AdvancedChat/AdvancedChat/Assets.xcassets/AccentColor.colorset/Contents.json b/ios/swift/6-AdvancedChat/AdvancedChat/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/ios/swift/6-AdvancedChat/AdvancedChat/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/swift/6-AdvancedChat/AdvancedChat/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/swift/6-AdvancedChat/AdvancedChat/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9221b9b --- /dev/null +++ b/ios/swift/6-AdvancedChat/AdvancedChat/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/swift/6-AdvancedChat/AdvancedChat/Assets.xcassets/Contents.json b/ios/swift/6-AdvancedChat/AdvancedChat/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/ios/swift/6-AdvancedChat/AdvancedChat/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/swift/6-AdvancedChat/AdvancedChat/Base.lproj/LaunchScreen.storyboard b/ios/swift/6-AdvancedChat/AdvancedChat/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..865e932 --- /dev/null +++ b/ios/swift/6-AdvancedChat/AdvancedChat/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/swift/6-AdvancedChat/AdvancedChat/ContentView.swift b/ios/swift/6-AdvancedChat/AdvancedChat/ContentView.swift new file mode 100644 index 0000000..93d2437 --- /dev/null +++ b/ios/swift/6-AdvancedChat/AdvancedChat/ContentView.swift @@ -0,0 +1,144 @@ +// +// ContentView.swift +// BasicChat +// +// Created by QuentinArguillere on 31/07/2020. +// Copyright © 2020 BelledonneCommunications. All rights reserved. +// + +import SwiftUI + +struct ActivityIndicator: UIViewRepresentable { + func makeUIView(context: UIViewRepresentableContext) -> UIActivityIndicatorView + { + return UIActivityIndicatorView(style: .medium) + } + + func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext) + { + uiView.startAnimating() + } +} + +struct ContentView: View { + + @ObservedObject var tutorialContext : AdvancedChatTutorialContext + + var body: some View { + + VStack { + Group { + HStack { + Text("Username:") + .font(.title) + TextField("", text : $tutorialContext.username) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .disabled(tutorialContext.loggedIn) + } + HStack { + Text("Password:") + .font(.title) + TextField("", text : $tutorialContext.passwd) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .disabled(tutorialContext.loggedIn) + } + HStack { + Text("Domain:") + .font(.title) + TextField("", text : $tutorialContext.domain) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .disabled(tutorialContext.loggedIn) + } + Picker(selection: $tutorialContext.transportType, label: Text("Transport:")) { + Text("TLS").tag("TLS") + Text("TCP").tag("TCP") + Text("UDP").tag("UDP") + }.pickerStyle(SegmentedPickerStyle()).padding() + VStack { + HStack { + Button(action: { + if (self.tutorialContext.loggedIn) + { + self.tutorialContext.unregister() + self.tutorialContext.delete() + } else { + self.tutorialContext.login() + } + }) + { + Text(tutorialContext.loggedIn ? "Log out & \ndelete account" : "Create & \nlog in account") + .font(.largeTitle) + .foregroundColor(Color.white) + .frame(width: 220.0, height: 90) + .background(Color.gray) + } + + } + HStack { + Text("Login State : ") + .font(.footnote) + Text(tutorialContext.loggedIn ? "Looged in" : "Unregistered") + .font(.footnote) + .foregroundColor(tutorialContext.loggedIn ? Color.green : Color.black) + }.padding(.top, 10.0) + HStack { + Text("Chat with:") + TextField("", text : $tutorialContext.remoteAddress) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .disabled(!tutorialContext.canEditAddress) + } + Text("Chat received").bold() + ScrollView { + Text(tutorialContext.messagesReceived) + .font(.footnote) + .frame(width: 330, height: 400) + }.border(Color.gray) + HStack { + TextField("Sent text", text : $tutorialContext.msgToSend) + .textFieldStyle(RoundedBorderTextFieldStyle()) + Button(action: tutorialContext.sendMessage) + { + Text("Send") + .font(.callout) + .foregroundColor(Color.white) + .frame(width: 50.0, height: 30.0) + .background(Color.gray) + } + } + HStack { + Button(action: tutorialContext.sendFile) + { + Text("Send example \n file") + .foregroundColor(Color.white) + .multilineTextAlignment(.center) + .frame(width: 120.0, height: 50.0) + .background(Color.gray) + } + Button(action: tutorialContext.downloadLastFileMessage) + { + Text("Download last files \n received") + .foregroundColor(Color.white) + .multilineTextAlignment(.center) + .frame(width: 150.0, height: 50.0) + .background(Color.gray) + }.disabled(tutorialContext.mLastFileMessageReceived == nil) + if (tutorialContext.isDownloading) { + ActivityIndicator() + } + } + } + } + Group { + Spacer() + Text("Core Version is \(tutorialContext.coreVersion)") + } + } + .padding() + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView(tutorialContext: AdvancedChatTutorialContext()) + } +} diff --git a/ios/swift/6-AdvancedChat/AdvancedChat/Info.plist b/ios/swift/6-AdvancedChat/AdvancedChat/Info.plist new file mode 100644 index 0000000..2688b32 --- /dev/null +++ b/ios/swift/6-AdvancedChat/AdvancedChat/Info.plist @@ -0,0 +1,62 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/ios/swift/6-AdvancedChat/AdvancedChat/Preview Content/Preview Assets.xcassets/Contents.json b/ios/swift/6-AdvancedChat/AdvancedChat/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/ios/swift/6-AdvancedChat/AdvancedChat/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/swift/6-AdvancedChat/AdvancedChat/SceneDelegate.swift b/ios/swift/6-AdvancedChat/AdvancedChat/SceneDelegate.swift new file mode 100644 index 0000000..87c3c91 --- /dev/null +++ b/ios/swift/6-AdvancedChat/AdvancedChat/SceneDelegate.swift @@ -0,0 +1,65 @@ +// +// SceneDelegate.swift +// BasicChat +// +// Created by QuentinArguillere on 31/07/2020. +// Copyright © 2020 BelledonneCommunications. All rights reserved. +// + +import UIKit +import SwiftUI + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + + let delegate = UIApplication.shared.delegate as! AppDelegate + + // Create the SwiftUI view that provides the window contents. + let contentView = ContentView(tutorialContext: delegate.tutorialContext) + + // Use a UIHostingController as window root view controller. + if let windowScene = scene as? UIWindowScene { + let window = UIWindow(windowScene: windowScene) + window.rootViewController = UIHostingController(rootView: contentView) + self.window = window + window.makeKeyAndVisible() + } + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } + + +} + diff --git a/ios/swift/6-AdvancedChat/Podfile b/ios/swift/6-AdvancedChat/Podfile new file mode 100644 index 0000000..a5ac05b --- /dev/null +++ b/ios/swift/6-AdvancedChat/Podfile @@ -0,0 +1,24 @@ +# Uncomment the next line to define a global platform for your project +platform :ios, '11.0' +source "https://gitlab.linphone.org/BC/public/podspec.git" +source "https://github.com/CocoaPods/Specs.git" + +def basic_pods + if ENV['PODFILE_PATH'].nil? + pod 'linphone-sdk', '~> 5.0.0' + else + pod 'linphone-sdk', :path => ENV['PODFILE_PATH'] # local sdk + end + +end + + + +target 'AdvancedChat' do + # Comment the next line if you don't want to use dynamic frameworks + use_frameworks! + + # Pods for AdvancedChat + basic_pods + +end diff --git a/ios/swift/6-AdvancedChat/README.md b/ios/swift/6-AdvancedChat/README.md new file mode 100644 index 0000000..47f7948 --- /dev/null +++ b/ios/swift/6-AdvancedChat/README.md @@ -0,0 +1,6 @@ +Advanced chat tutorial +==================== + +This tutorial will demonstrate how to leverage on our own SIP server named [Flexisip](https://gitlab.linphone.org/BC/public/flexisip) and it's conference server to create end-to-end encrypted chat rooms and send ephemeral messages. + +If you don't have deployed a flexisip server yet, you can create & use a free SIP account using our [free SIP service](https://subscribe.linphone.org/). diff --git a/swift/HelloLinphone/HelloLinphone.xcworkspace/contents.xcworkspacedata b/swift/HelloLinphone/HelloLinphone.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 7ee8337..0000000 --- a/swift/HelloLinphone/HelloLinphone.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/swift/HelloLinphone/HelloLinphone/ContentView.swift b/swift/HelloLinphone/HelloLinphone/ContentView.swift deleted file mode 100644 index 5f1ae9c..0000000 --- a/swift/HelloLinphone/HelloLinphone/ContentView.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// ContentView.swift -// HelloLinphone -// -// Created by Danmei Chen on 23/06/2020. -// Copyright © 2020 belledonne. All rights reserved. -// - -import SwiftUI - -struct ContentView: View { - var coreVersion: String = "" - var body: some View { - Text("Hello, Linphone, Core Version is \(coreVersion)") - } -} - -struct ContentView_Previews: PreviewProvider { - static var previews: some View { - ContentView() - } -}