Extra readme, some formatting, and missing file in Helloworld
This commit is contained in:
parent
98eb474c42
commit
d24946be87
1
.gitignore
vendored
1
.gitignore
vendored
@ -32,4 +32,5 @@ ios/swift/*/*.xcodeproj/project.xcworkspace/xcuserdata/
|
||||
ios/swift/*/*.xcodeproj/xcuserdata/
|
||||
ios/swift/*/*.xcworkspace/xcuserdata/
|
||||
ios/swift/*/build
|
||||
ios/swift/*/*.xcworkspace
|
||||
|
||||
|
12
ios/README.md
Normal file
12
ios/README.md
Normal file
@ -0,0 +1,12 @@
|
||||
IOS tutorials
|
||||
====================
|
||||
|
||||
Tutorials are written in swift, but the same features can be achieved in objective-C.
|
||||
|
||||
Tutorials are numbered 0 to 6, and we recommend you to read them in that order as features from previous tutorials are sometimes used in the next ones, such as account login.
|
||||
|
||||
Each tutorial is a full project, so you can generate your Xcode workspace with Cocoapods, build it and deploy it on a real device or an emulator.
|
||||
|
||||
Code is being kept as short and simple as possible, and comments explain how and why things are being done.
|
||||
|
||||
Full swift API is available [here](http://linphone.org/snapshots/docs/liblinphone/latest/swift).
|
@ -13,7 +13,7 @@
|
||||
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 */; };
|
||||
667C3B4826F1E7B2004D4F14 /* HelloworldTutorial.swift in Sources */ = {isa = PBXBuildFile; fileRef = 667C3B4726F1E7B2004D4F14 /* HelloworldTutorial.swift */; };
|
||||
AE5DCE54FB07B00CDB39CF8C /* Pods_Helloworld.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 658B93FC56A6311767BE2F24 /* Pods_Helloworld.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
663D8CE726E8E35500EE487F /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||
663D8CEA26E8E35500EE487F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
663D8CEC26E8E35500EE487F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
663D8CF226E8E73700EE487F /* HelloworldTutorial.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = HelloworldTutorial.swift; path = ../../../../Downloads/HelloworldTutorial.swift; sourceTree = "<group>"; };
|
||||
667C3B4726F1E7B2004D4F14 /* HelloworldTutorial.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HelloworldTutorial.swift; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@ -71,8 +71,8 @@
|
||||
663D8CE426E8E35500EE487F /* Assets.xcassets */,
|
||||
663D8CE926E8E35500EE487F /* LaunchScreen.storyboard */,
|
||||
663D8CEC26E8E35500EE487F /* Info.plist */,
|
||||
667C3B4726F1E7B2004D4F14 /* HelloworldTutorial.swift */,
|
||||
663D8CE626E8E35500EE487F /* Preview Content */,
|
||||
663D8CF226E8E73700EE487F /* HelloworldTutorial.swift */,
|
||||
);
|
||||
path = Helloworld;
|
||||
sourceTree = "<group>";
|
||||
@ -219,7 +219,7 @@
|
||||
663D8CDF26E8E35400EE487F /* AppDelegate.swift in Sources */,
|
||||
663D8CE126E8E35400EE487F /* SceneDelegate.swift in Sources */,
|
||||
663D8CE326E8E35400EE487F /* ContentView.swift in Sources */,
|
||||
663D8CF326E8E73700EE487F /* HelloworldTutorial.swift in Sources */,
|
||||
667C3B4826F1E7B2004D4F14 /* HelloworldTutorial.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
58
ios/swift/0-Helloworld/Helloworld/HelloworldTutorial.swift
Normal file
58
ios/swift/0-Helloworld/Helloworld/HelloworldTutorial.swift
Normal file
@ -0,0 +1,58 @@
|
||||
//
|
||||
// HellowolrdTutorial.swift
|
||||
// HellowolrdTutorial
|
||||
//
|
||||
// Created by QuentinArguillere on 08/09/2021.
|
||||
// Copyright © 2021 BelledonneCommunications. All rights reserved.
|
||||
//
|
||||
|
||||
// Check the Podfile to see how to import the LibLinphone SDK !!!
|
||||
import linphonesw
|
||||
|
||||
class HelloworldTutorialContext : 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()
|
||||
{
|
||||
|
||||
// Some configuration can be done before the Core is created, for example enable debug logs.
|
||||
LoggingService.Instance.logLevel = LogLevel.Debug
|
||||
|
||||
// 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.
|
||||
try! mCore = factory.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)
|
||||
coreVersion = Core.getVersion
|
||||
// Now we can start using the Core object
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -9,27 +9,27 @@
|
||||
import SwiftUI
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
|
||||
@ObservedObject var tutorialContext : LoginTutorialContext
|
||||
|
||||
var body: some View {
|
||||
|
||||
VStack {
|
||||
Group {
|
||||
HStack {
|
||||
Text("Username:")
|
||||
.font(.title)
|
||||
|
||||
var body: some View {
|
||||
|
||||
VStack {
|
||||
Group {
|
||||
HStack {
|
||||
Text("Username:")
|
||||
.font(.title)
|
||||
TextField("", text : $tutorialContext.username)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.disabled(tutorialContext.loggedIn)
|
||||
}
|
||||
HStack {
|
||||
Text("Password:")
|
||||
.font(.title)
|
||||
TextField("", text : $tutorialContext.passwd)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
}
|
||||
HStack {
|
||||
Text("Password:")
|
||||
.font(.title)
|
||||
TextField("", text : $tutorialContext.passwd)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.disabled(tutorialContext.loggedIn)
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
Text("Domain:")
|
||||
.font(.title)
|
||||
@ -38,50 +38,50 @@ struct ContentView: View {
|
||||
.disabled(tutorialContext.loggedIn)
|
||||
}
|
||||
Picker(selection: $tutorialContext.transportType, label: Text("Transport:")) {
|
||||
Text("TLS").tag("TLS")
|
||||
Text("TCP").tag("TCP")
|
||||
Text("UDP").tag("UDP")
|
||||
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()
|
||||
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)
|
||||
} 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()
|
||||
}
|
||||
.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())
|
||||
}
|
||||
static var previews: some View {
|
||||
ContentView(tutorialContext: LoginTutorialContext())
|
||||
}
|
||||
}
|
||||
|
@ -10,24 +10,24 @@ import linphonesw
|
||||
|
||||
class LoginTutorialContext : ObservableObject
|
||||
{
|
||||
var mCore: Core!
|
||||
@Published var coreVersion: String = Core.getVersion
|
||||
|
||||
/*------------ Login tutorial related variables -------*/
|
||||
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 username : String = "user"
|
||||
@Published var passwd : String = "pwd"
|
||||
@Published var domain : String = "sip.example.org"
|
||||
@Published var loggedIn: Bool = false
|
||||
@Published var loggedIn: Bool = false
|
||||
@Published var transportType : String = "TLS"
|
||||
|
||||
init()
|
||||
{
|
||||
init()
|
||||
{
|
||||
|
||||
LoggingService.Instance.logLevel = LogLevel.Debug
|
||||
|
||||
try? mCore = Factory.Instance.createCore(configPath: "", factoryConfigPath: "", systemContext: nil)
|
||||
try? mCore.start()
|
||||
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
|
||||
@ -43,8 +43,8 @@ class LoginTutorialContext : ObservableObject
|
||||
}
|
||||
})
|
||||
mCore.addDelegate(delegate: mRegistrationDelegate)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func login() {
|
||||
|
||||
do {
|
||||
@ -64,39 +64,39 @@ class LoginTutorialContext : ObservableObject
|
||||
// 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()
|
||||
{
|
||||
func unregister()
|
||||
{
|
||||
// Here we will disable the registration of our Account
|
||||
if let account = mCore.defaultAccount {
|
||||
|
||||
@ -110,19 +110,19 @@ class LoginTutorialContext : ObservableObject
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
import SwiftUI
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
|
||||
@ObservedObject var tutorialContext : IncomingCallTutorialContext
|
||||
|
||||
func callStateString() -> String {
|
||||
@ -22,24 +22,24 @@ struct ContentView: View {
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
|
||||
VStack {
|
||||
Group {
|
||||
HStack {
|
||||
Text("Username:")
|
||||
.font(.title)
|
||||
var body: some View {
|
||||
|
||||
VStack {
|
||||
Group {
|
||||
HStack {
|
||||
Text("Username:")
|
||||
.font(.title)
|
||||
TextField("", text : $tutorialContext.username)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.disabled(tutorialContext.loggedIn)
|
||||
}
|
||||
HStack {
|
||||
Text("Password:")
|
||||
.font(.title)
|
||||
TextField("", text : $tutorialContext.passwd)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
}
|
||||
HStack {
|
||||
Text("Password:")
|
||||
.font(.title)
|
||||
TextField("", text : $tutorialContext.passwd)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.disabled(tutorialContext.loggedIn)
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
Text("Domain:")
|
||||
.font(.title)
|
||||
@ -48,37 +48,37 @@ struct ContentView: View {
|
||||
.disabled(tutorialContext.loggedIn)
|
||||
}
|
||||
Picker(selection: $tutorialContext.transportType, label: Text("Transport:")) {
|
||||
Text("TLS").tag("TLS")
|
||||
Text("TCP").tag("TCP")
|
||||
Text("UDP").tag("UDP")
|
||||
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()
|
||||
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)
|
||||
} 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)
|
||||
}
|
||||
.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: {
|
||||
@ -90,10 +90,10 @@ struct ContentView: View {
|
||||
})
|
||||
{
|
||||
Text( (tutorialContext.isCallRunning) ? "Terminate" : "Accept")
|
||||
.font(.largeTitle)
|
||||
.foregroundColor(Color.white)
|
||||
.frame(width: 180.0, height: 42.0)
|
||||
.background(Color.gray)
|
||||
.font(.largeTitle)
|
||||
.foregroundColor(Color.white)
|
||||
.frame(width: 180.0, height: 42.0)
|
||||
.background(Color.gray)
|
||||
}
|
||||
.disabled(!tutorialContext.isCallIncoming && !tutorialContext.isCallRunning)
|
||||
HStack {
|
||||
@ -132,18 +132,18 @@ struct ContentView: View {
|
||||
.disabled(!tutorialContext.isCallRunning)
|
||||
}.padding(.top, 10)
|
||||
}.padding(.top, 30)
|
||||
}
|
||||
Group {
|
||||
Spacer()
|
||||
Text("Core Version is \(tutorialContext.coreVersion)")
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
Group {
|
||||
Spacer()
|
||||
Text("Core Version is \(tutorialContext.coreVersion)")
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
struct ContentView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ContentView(tutorialContext: IncomingCallTutorialContext())
|
||||
}
|
||||
static var previews: some View {
|
||||
ContentView(tutorialContext: IncomingCallTutorialContext())
|
||||
}
|
||||
}
|
||||
|
@ -10,15 +10,15 @@ import linphonesw
|
||||
|
||||
class IncomingCallTutorialContext : ObservableObject
|
||||
{
|
||||
var mCore: Core!
|
||||
@Published var coreVersion: String = Core.getVersion
|
||||
|
||||
var mAccount: Account?
|
||||
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 username : String = "user"
|
||||
@Published var passwd : String = "pwd"
|
||||
@Published var domain : String = "sip.example.org"
|
||||
@Published var loggedIn: Bool = false
|
||||
@Published var loggedIn: Bool = false
|
||||
@Published var transportType : String = "TLS"
|
||||
|
||||
// Incoming call related variables
|
||||
@ -29,12 +29,12 @@ class IncomingCallTutorialContext : ObservableObject
|
||||
@Published var isSpeakerEnabled : Bool = false
|
||||
@Published var isMicrophoneEnabled : Bool = false
|
||||
|
||||
init()
|
||||
{
|
||||
init()
|
||||
{
|
||||
LoggingService.Instance.logLevel = LogLevel.Debug
|
||||
|
||||
try? mCore = Factory.Instance.createCore(configPath: "", factoryConfigPath: "", systemContext: nil)
|
||||
try? mCore.start()
|
||||
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
|
||||
@ -64,8 +64,8 @@ class IncomingCallTutorialContext : ObservableObject
|
||||
}
|
||||
})
|
||||
mCore.addDelegate(delegate: mCoreDelegate)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func login() {
|
||||
|
||||
do {
|
||||
@ -86,19 +86,19 @@ class IncomingCallTutorialContext : ObservableObject
|
||||
mCore.addAuthInfo(info: authInfo)
|
||||
try mCore.addAccount(account: mAccount!)
|
||||
mCore.defaultAccount = mAccount
|
||||
|
||||
|
||||
} catch { NSLog(error.localizedDescription) }
|
||||
}
|
||||
|
||||
func unregister()
|
||||
{
|
||||
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)
|
||||
@ -106,7 +106,7 @@ class IncomingCallTutorialContext : ObservableObject
|
||||
mCore.clearAllAuthInfo()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func terminateCall() {
|
||||
do {
|
||||
// Terminates the call, whether it is ringing or running
|
||||
@ -130,12 +130,12 @@ class IncomingCallTutorialContext : ObservableObject
|
||||
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
|
||||
@ -155,7 +155,7 @@ class IncomingCallTutorialContext : ObservableObject
|
||||
}
|
||||
/* If we wanted to route the audio to a bluetooth headset
|
||||
else if (audioDevice.type == AudioDevice.Type.Bluetooth) {
|
||||
core.currentCall?.outputAudioDevice = audioDevice
|
||||
core.currentCall?.outputAudioDevice = audioDevice
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import SwiftUI
|
||||
import linphonesw
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
|
||||
@ObservedObject var tutorialContext : OutgoingCallTutorialContext
|
||||
|
||||
func callStateString() -> String {
|
||||
@ -21,24 +21,24 @@ struct ContentView: View {
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
|
||||
VStack {
|
||||
Group {
|
||||
HStack {
|
||||
Text("Username:")
|
||||
.font(.title)
|
||||
var body: some View {
|
||||
|
||||
VStack {
|
||||
Group {
|
||||
HStack {
|
||||
Text("Username:")
|
||||
.font(.title)
|
||||
TextField("", text : $tutorialContext.username)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.disabled(tutorialContext.loggedIn)
|
||||
}
|
||||
HStack {
|
||||
Text("Password:")
|
||||
.font(.title)
|
||||
TextField("", text : $tutorialContext.passwd)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
}
|
||||
HStack {
|
||||
Text("Password:")
|
||||
.font(.title)
|
||||
TextField("", text : $tutorialContext.passwd)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.disabled(tutorialContext.loggedIn)
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
Text("Domain:")
|
||||
.font(.title)
|
||||
@ -47,37 +47,37 @@ struct ContentView: View {
|
||||
.disabled(tutorialContext.loggedIn)
|
||||
}
|
||||
Picker(selection: $tutorialContext.transportType, label: Text("Transport:")) {
|
||||
Text("TLS").tag("TLS")
|
||||
Text("TCP").tag("TCP")
|
||||
Text("UDP").tag("UDP")
|
||||
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()
|
||||
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)
|
||||
} 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)
|
||||
}
|
||||
.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:")
|
||||
@ -96,10 +96,10 @@ struct ContentView: View {
|
||||
})
|
||||
{
|
||||
Text( (tutorialContext.isCallRunning) ? "End" : "Call")
|
||||
.font(.largeTitle)
|
||||
.foregroundColor(Color.white)
|
||||
.frame(width: 180.0, height: 42.0)
|
||||
.background(Color.gray)
|
||||
.font(.largeTitle)
|
||||
.foregroundColor(Color.white)
|
||||
.frame(width: 180.0, height: 42.0)
|
||||
.background(Color.gray)
|
||||
}
|
||||
HStack {
|
||||
Text(tutorialContext.isCallRunning ? "Running" : "")
|
||||
@ -154,18 +154,18 @@ struct ContentView: View {
|
||||
}
|
||||
}
|
||||
}.padding(.top, 30)
|
||||
}
|
||||
Group {
|
||||
Spacer()
|
||||
Text("Core Version is \(tutorialContext.coreVersion)")
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
Group {
|
||||
Spacer()
|
||||
Text("Core Version is \(tutorialContext.coreVersion)")
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
struct ContentView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ContentView(tutorialContext: OutgoingCallTutorialContext())
|
||||
}
|
||||
static var previews: some View {
|
||||
ContentView(tutorialContext: OutgoingCallTutorialContext())
|
||||
}
|
||||
}
|
||||
|
@ -10,15 +10,15 @@ import linphonesw
|
||||
|
||||
class OutgoingCallTutorialContext : ObservableObject
|
||||
{
|
||||
var mCore: Core!
|
||||
@Published var coreVersion: String = Core.getVersion
|
||||
|
||||
var mAccount: Account?
|
||||
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 username : String = "user"
|
||||
@Published var passwd : String = "pwd"
|
||||
@Published var domain : String = "sip.example.org"
|
||||
@Published var loggedIn: Bool = false
|
||||
@Published var loggedIn: Bool = false
|
||||
@Published var transportType : String = "TLS"
|
||||
|
||||
// Outgoing call related variables
|
||||
@ -28,11 +28,11 @@ class OutgoingCallTutorialContext : ObservableObject
|
||||
@Published var canChangeCamera : Bool = false
|
||||
@Published var remoteAddress : String = "sip:arguillq@sip.linphone.org"
|
||||
|
||||
init()
|
||||
{
|
||||
init()
|
||||
{
|
||||
LoggingService.Instance.logLevel = LogLevel.Debug
|
||||
|
||||
try? mCore = Factory.Instance.createCore(configPath: "", factoryConfigPath: "", systemContext: nil)
|
||||
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
|
||||
@ -50,7 +50,7 @@ class OutgoingCallTutorialContext : ObservableObject
|
||||
// If the following property is enabled, it will automatically configure created call params with video enabled
|
||||
//core.videoActivationPolicy.automaticallyInitiate = true
|
||||
|
||||
try? mCore.start()
|
||||
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,
|
||||
@ -71,7 +71,7 @@ class OutgoingCallTutorialContext : ObservableObject
|
||||
// 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
|
||||
@ -89,7 +89,7 @@ class OutgoingCallTutorialContext : ObservableObject
|
||||
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")
|
||||
@ -100,8 +100,8 @@ class OutgoingCallTutorialContext : ObservableObject
|
||||
}
|
||||
})
|
||||
mCore.addDelegate(delegate: mCoreDelegate)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func login() {
|
||||
|
||||
do {
|
||||
@ -122,19 +122,19 @@ class OutgoingCallTutorialContext : ObservableObject
|
||||
mCore.addAuthInfo(info: authInfo)
|
||||
try mCore.addAccount(account: mAccount!)
|
||||
mCore.defaultAccount = mAccount
|
||||
|
||||
|
||||
} catch { NSLog(error.localizedDescription) }
|
||||
}
|
||||
|
||||
func unregister()
|
||||
{
|
||||
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)
|
||||
@ -142,17 +142,17 @@ class OutgoingCallTutorialContext : ObservableObject
|
||||
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
|
||||
@ -169,10 +169,10 @@ class OutgoingCallTutorialContext : ObservableObject
|
||||
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()
|
||||
@ -207,7 +207,7 @@ class OutgoingCallTutorialContext : ObservableObject
|
||||
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
|
||||
@ -223,7 +223,7 @@ class OutgoingCallTutorialContext : ObservableObject
|
||||
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
|
||||
|
@ -14,53 +14,53 @@ 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
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
|
||||
do {
|
||||
if (tutorialContext.mCall?.state != .End && tutorialContext.mCall?.state != .Released) {
|
||||
try tutorialContext.mCall?.terminate()
|
||||
@ -69,31 +69,31 @@ extension CallKitProviderDelegate: CXProviderDelegate {
|
||||
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -11,14 +11,14 @@ import AVFoundation
|
||||
|
||||
class CallKitExampleContext : ObservableObject
|
||||
{
|
||||
var mCore: Core!
|
||||
@Published var coreVersion: String = Core.getVersion
|
||||
|
||||
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 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"
|
||||
|
||||
@ -35,8 +35,8 @@ class CallKitExampleContext : ObservableObject
|
||||
var mProviderDelegate : CallKitProviderDelegate!
|
||||
var mCallAlreadyStopped : Bool = false;
|
||||
|
||||
init()
|
||||
{
|
||||
init()
|
||||
{
|
||||
LoggingService.Instance.logLevel = LogLevel.Debug
|
||||
|
||||
let factory = Factory.Instance
|
||||
@ -92,8 +92,8 @@ class CallKitExampleContext : ObservableObject
|
||||
}
|
||||
})
|
||||
mCore.addDelegate(delegate: mCoreDelegate)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func login() {
|
||||
|
||||
do {
|
||||
@ -118,7 +118,7 @@ class CallKitExampleContext : ObservableObject
|
||||
mCore.addAuthInfo(info: authInfo)
|
||||
try mCore.addAccount(account: mAccount!)
|
||||
mCore.defaultAccount = mAccount
|
||||
|
||||
|
||||
} catch { NSLog(error.localizedDescription) }
|
||||
}
|
||||
|
||||
|
@ -48,9 +48,9 @@ struct ContentView: View {
|
||||
.disabled(tutorialContext.loggedIn)
|
||||
}
|
||||
Picker(selection: $tutorialContext.transportType, label: Text("Transport:")) {
|
||||
Text("TLS").tag("TLS")
|
||||
Text("TCP").tag("TCP")
|
||||
Text("UDP").tag("UDP")
|
||||
Text("TLS").tag("TLS")
|
||||
Text("TCP").tag("TCP")
|
||||
Text("UDP").tag("UDP")
|
||||
}.pickerStyle(SegmentedPickerStyle()).padding()
|
||||
VStack {
|
||||
HStack {
|
||||
|
@ -12,14 +12,14 @@ import linphonesw
|
||||
|
||||
class BasicChatTutorialContext : ObservableObject
|
||||
{
|
||||
var mCore: Core!
|
||||
@Published var coreVersion: String = Core.getVersion
|
||||
|
||||
var mCore: Core!
|
||||
@Published var coreVersion: String = Core.getVersion
|
||||
|
||||
var mRegistrationDelegate : CoreDelegate!
|
||||
@Published var username : String = "user"
|
||||
@Published var passwd : String = "pwd"
|
||||
@Published var username : String = "user"
|
||||
@Published var passwd : String = "pwd"
|
||||
@Published var domain : String = "sip.example.org"
|
||||
@Published var loggedIn: Bool = false
|
||||
@Published var loggedIn: Bool = false
|
||||
@Published var transportType : String = "TLS"
|
||||
|
||||
/*------------ Basic chat tutorial related variables -------*/
|
||||
@ -35,12 +35,12 @@ class BasicChatTutorialContext : ObservableObject
|
||||
var fileFolderUrl : URL!
|
||||
var fileUrl : URL!
|
||||
|
||||
init()
|
||||
{
|
||||
init()
|
||||
{
|
||||
LoggingService.Instance.logLevel = LogLevel.Debug
|
||||
|
||||
try? mCore = Factory.Instance.createCore(configPath: "", factoryConfigPath: "", systemContext: nil)
|
||||
try? mCore.start()
|
||||
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
|
||||
@ -58,7 +58,7 @@ class BasicChatTutorialContext : ObservableObject
|
||||
}
|
||||
// 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
|
||||
@ -98,8 +98,8 @@ class BasicChatTutorialContext : ObservableObject
|
||||
}catch let error as NSError{
|
||||
print("Unable to create d)irectory",error)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func login() {
|
||||
|
||||
do {
|
||||
@ -117,24 +117,24 @@ class BasicChatTutorialContext : ObservableObject
|
||||
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()
|
||||
{
|
||||
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)
|
||||
@ -153,7 +153,7 @@ class BasicChatTutorialContext : ObservableObject
|
||||
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)
|
||||
@ -166,7 +166,7 @@ class BasicChatTutorialContext : ObservableObject
|
||||
}
|
||||
} catch { NSLog(error.localizedDescription) }
|
||||
}
|
||||
|
||||
|
||||
func sendMessage() {
|
||||
do {
|
||||
if (mChatroom == nil) {
|
||||
@ -176,50 +176,50 @@ class BasicChatTutorialContext : ObservableObject
|
||||
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 {
|
||||
|
@ -21,27 +21,27 @@ struct ActivityIndicator: UIViewRepresentable {
|
||||
}
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
|
||||
@ObservedObject var tutorialContext : BasicChatTutorialContext
|
||||
|
||||
var body: some View {
|
||||
|
||||
VStack {
|
||||
Group {
|
||||
HStack {
|
||||
Text("Username:")
|
||||
.font(.title)
|
||||
|
||||
var body: some View {
|
||||
|
||||
VStack {
|
||||
Group {
|
||||
HStack {
|
||||
Text("Username:")
|
||||
.font(.title)
|
||||
TextField("", text : $tutorialContext.username)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.disabled(tutorialContext.loggedIn)
|
||||
}
|
||||
HStack {
|
||||
Text("Password:")
|
||||
.font(.title)
|
||||
TextField("", text : $tutorialContext.passwd)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
}
|
||||
HStack {
|
||||
Text("Password:")
|
||||
.font(.title)
|
||||
TextField("", text : $tutorialContext.passwd)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.disabled(tutorialContext.loggedIn)
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
Text("Domain:")
|
||||
.font(.title)
|
||||
@ -50,37 +50,37 @@ struct ContentView: View {
|
||||
.disabled(tutorialContext.loggedIn)
|
||||
}
|
||||
Picker(selection: $tutorialContext.transportType, label: Text("Transport:")) {
|
||||
Text("TLS").tag("TLS")
|
||||
Text("TCP").tag("TCP")
|
||||
Text("UDP").tag("UDP")
|
||||
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()
|
||||
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)
|
||||
} 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)
|
||||
.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)
|
||||
@ -109,36 +109,36 @@ struct ContentView: View {
|
||||
Button(action: tutorialContext.sendFile)
|
||||
{
|
||||
Text("Send example \n file")
|
||||
.foregroundColor(Color.white)
|
||||
.multilineTextAlignment(.center)
|
||||
.frame(width: 120.0, height: 50.0)
|
||||
.background(Color.gray)
|
||||
.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)
|
||||
.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()
|
||||
}
|
||||
}
|
||||
}
|
||||
Group {
|
||||
Spacer()
|
||||
Text("Core Version is \(tutorialContext.coreVersion)")
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
struct ContentView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ContentView(tutorialContext: BasicChatTutorialContext())
|
||||
}
|
||||
static var previews: some View {
|
||||
ContentView(tutorialContext: BasicChatTutorialContext())
|
||||
}
|
||||
}
|
||||
|
@ -12,14 +12,14 @@ import linphonesw
|
||||
|
||||
class AdvancedChatTutorialContext : ObservableObject
|
||||
{
|
||||
var mCore: Core!
|
||||
@Published var coreVersion: String = Core.getVersion
|
||||
|
||||
var mCore: Core!
|
||||
@Published var coreVersion: String = Core.getVersion
|
||||
|
||||
var mRegistrationDelegate : CoreDelegate!
|
||||
@Published var username : String = "user"
|
||||
@Published var passwd : String = "pwd"
|
||||
@Published var username : String = "user"
|
||||
@Published var passwd : String = "pwd"
|
||||
@Published var domain : String = "sip.example.org"
|
||||
@Published var loggedIn: Bool = false
|
||||
@Published var loggedIn: Bool = false
|
||||
@Published var transportType : String = "TLS"
|
||||
|
||||
/*------------ Advanced chat tutorial related variables -------*/
|
||||
@ -36,12 +36,12 @@ class AdvancedChatTutorialContext : ObservableObject
|
||||
var fileFolderUrl : URL!
|
||||
var fileUrl : URL!
|
||||
|
||||
init()
|
||||
{
|
||||
init()
|
||||
{
|
||||
LoggingService.Instance.logLevel = LogLevel.Debug
|
||||
|
||||
try? mCore = Factory.Instance.createCore(configPath: "", factoryConfigPath: "", systemContext: nil)
|
||||
try? mCore.start()
|
||||
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) {
|
||||
@ -60,7 +60,7 @@ class AdvancedChatTutorialContext : ObservableObject
|
||||
}
|
||||
|
||||
chatRoom.markAsRead()
|
||||
|
||||
|
||||
for content in message.contents {
|
||||
if (content.isFileTransfer) {
|
||||
self.mLastFileMessageReceived = message
|
||||
@ -124,8 +124,8 @@ class AdvancedChatTutorialContext : ObservableObject
|
||||
}catch let error as NSError{
|
||||
print("Unable to create d)irectory",error)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func login() {
|
||||
|
||||
do {
|
||||
@ -151,21 +151,21 @@ class AdvancedChatTutorialContext : ObservableObject
|
||||
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()
|
||||
{
|
||||
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)
|
||||
@ -174,26 +174,26 @@ class AdvancedChatTutorialContext : ObservableObject
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
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)
|
||||
@ -204,7 +204,7 @@ class AdvancedChatTutorialContext : ObservableObject
|
||||
// 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()
|
||||
@ -212,8 +212,8 @@ class AdvancedChatTutorialContext : ObservableObject
|
||||
}
|
||||
}
|
||||
} catch { NSLog(error.localizedDescription) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func sendMessage() {
|
||||
do {
|
||||
if (mChatroom == nil) {
|
||||
@ -226,13 +226,13 @@ class AdvancedChatTutorialContext : ObservableObject
|
||||
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"
|
||||
@ -245,7 +245,7 @@ class AdvancedChatTutorialContext : ObservableObject
|
||||
|
||||
} catch { NSLog(error.localizedDescription) }
|
||||
}
|
||||
|
||||
|
||||
func downloadLastFileMessage() {
|
||||
if let message = mLastFileMessageReceived {
|
||||
for content in message.contents {
|
||||
|
@ -21,27 +21,27 @@ struct ActivityIndicator: UIViewRepresentable {
|
||||
}
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
|
||||
@ObservedObject var tutorialContext : AdvancedChatTutorialContext
|
||||
|
||||
var body: some View {
|
||||
|
||||
VStack {
|
||||
Group {
|
||||
HStack {
|
||||
Text("Username:")
|
||||
.font(.title)
|
||||
|
||||
var body: some View {
|
||||
|
||||
VStack {
|
||||
Group {
|
||||
HStack {
|
||||
Text("Username:")
|
||||
.font(.title)
|
||||
TextField("", text : $tutorialContext.username)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.disabled(tutorialContext.loggedIn)
|
||||
}
|
||||
HStack {
|
||||
Text("Password:")
|
||||
.font(.title)
|
||||
TextField("", text : $tutorialContext.passwd)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
}
|
||||
HStack {
|
||||
Text("Password:")
|
||||
.font(.title)
|
||||
TextField("", text : $tutorialContext.passwd)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.disabled(tutorialContext.loggedIn)
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
Text("Domain:")
|
||||
.font(.title)
|
||||
@ -50,37 +50,37 @@ struct ContentView: View {
|
||||
.disabled(tutorialContext.loggedIn)
|
||||
}
|
||||
Picker(selection: $tutorialContext.transportType, label: Text("Transport:")) {
|
||||
Text("TLS").tag("TLS")
|
||||
Text("TCP").tag("TCP")
|
||||
Text("UDP").tag("UDP")
|
||||
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()
|
||||
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)
|
||||
} 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)
|
||||
.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)
|
||||
@ -109,36 +109,36 @@ struct ContentView: View {
|
||||
Button(action: tutorialContext.sendFile)
|
||||
{
|
||||
Text("Send example \n file")
|
||||
.foregroundColor(Color.white)
|
||||
.multilineTextAlignment(.center)
|
||||
.frame(width: 120.0, height: 50.0)
|
||||
.background(Color.gray)
|
||||
.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)
|
||||
.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()
|
||||
}
|
||||
}
|
||||
}
|
||||
Group {
|
||||
Spacer()
|
||||
Text("Core Version is \(tutorialContext.coreVersion)")
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
struct ContentView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ContentView(tutorialContext: AdvancedChatTutorialContext())
|
||||
}
|
||||
static var previews: some View {
|
||||
ContentView(tutorialContext: AdvancedChatTutorialContext())
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user