Codying style: naming, formatting, comments
This commit is contained in:
parent
e5309a6482
commit
5e79bbecef
|
@ -110,7 +110,7 @@ public class AddPeopleController {
|
||||||
|
|
||||||
if (owner != null) {
|
if (owner != null) {
|
||||||
WritableArray invitees = new WritableNativeArray();
|
WritableArray invitees = new WritableNativeArray();
|
||||||
|
|
||||||
for(int i = 0, size = ids.size(); i < size; i++) {
|
for(int i = 0, size = ids.size(); i < size; i++) {
|
||||||
String id = ids.get(i);
|
String id = ids.get(i);
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ public class AddPeopleController {
|
||||||
// so just skip it.
|
// so just skip it.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
owner.invite(this, invitees);
|
owner.invite(this, invitees);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,11 +79,11 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C6F99C3A204DE6BE0001F710 /* AppDelegate.swift */,
|
C6F99C3A204DE6BE0001F710 /* AppDelegate.swift */,
|
||||||
C6F99C3C204DE6BE0001F710 /* ViewController.swift */,
|
|
||||||
C6F99C3E204DE6BE0001F710 /* Main.storyboard */,
|
|
||||||
C6F99C41204DE6BE0001F710 /* Assets.xcassets */,
|
C6F99C41204DE6BE0001F710 /* Assets.xcassets */,
|
||||||
C6F99C43204DE6BE0001F710 /* LaunchScreen.storyboard */,
|
|
||||||
C6F99C46204DE6BE0001F710 /* Info.plist */,
|
C6F99C46204DE6BE0001F710 /* Info.plist */,
|
||||||
|
C6F99C43204DE6BE0001F710 /* LaunchScreen.storyboard */,
|
||||||
|
C6F99C3E204DE6BE0001F710 /* Main.storyboard */,
|
||||||
|
C6F99C3C204DE6BE0001F710 /* ViewController.swift */,
|
||||||
);
|
);
|
||||||
path = src;
|
path = src;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
|
|
@ -20,21 +20,21 @@ import JitsiMeet
|
||||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
|
|
||||||
var window: UIWindow?
|
var window: UIWindow?
|
||||||
|
|
||||||
func application(_ application: UIApplication,
|
func application(_ application: UIApplication,
|
||||||
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
|
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
|
||||||
guard let launchOptions = launchOptions else { return false }
|
guard let launchOptions = launchOptions else { return false }
|
||||||
return JitsiMeetView.application(application, didFinishLaunchingWithOptions: launchOptions)
|
return JitsiMeetView.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Linking delegate methods
|
// MARK: - Linking delegate methods
|
||||||
|
|
||||||
func application(_ application: UIApplication,
|
func application(_ application: UIApplication,
|
||||||
continue userActivity: NSUserActivity,
|
continue userActivity: NSUserActivity,
|
||||||
restorationHandler: @escaping ([Any]?) -> Void) -> Bool {
|
restorationHandler: @escaping ([Any]?) -> Void) -> Bool {
|
||||||
return JitsiMeetView.application(application, continue: userActivity, restorationHandler: restorationHandler)
|
return JitsiMeetView.application(application, continue: userActivity, restorationHandler: restorationHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
|
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
|
||||||
return JitsiMeetView.application(application, open: url, sourceApplication: sourceApplication, annotation: annotation)
|
return JitsiMeetView.application(application, open: url, sourceApplication: sourceApplication, annotation: annotation)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,27 +20,27 @@ import JitsiMeet
|
||||||
class ViewController: UIViewController {
|
class ViewController: UIViewController {
|
||||||
|
|
||||||
@IBOutlet weak var videoButton: UIButton?
|
@IBOutlet weak var videoButton: UIButton?
|
||||||
|
|
||||||
fileprivate var pipViewCoordinator: PiPViewCoordinator?
|
fileprivate var pipViewCoordinator: PiPViewCoordinator?
|
||||||
fileprivate var jitsiMeetView: JitsiMeetView?
|
fileprivate var jitsiMeetView: JitsiMeetView?
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillTransition(to size: CGSize,
|
override func viewWillTransition(to size: CGSize,
|
||||||
with coordinator: UIViewControllerTransitionCoordinator) {
|
with coordinator: UIViewControllerTransitionCoordinator) {
|
||||||
super.viewWillTransition(to: size, with: coordinator)
|
super.viewWillTransition(to: size, with: coordinator)
|
||||||
|
|
||||||
let rect = CGRect(origin: CGPoint.zero, size: size)
|
let rect = CGRect(origin: CGPoint.zero, size: size)
|
||||||
pipViewCoordinator?.resetBounds(bounds: rect)
|
pipViewCoordinator?.resetBounds(bounds: rect)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Actions
|
// MARK: - Actions
|
||||||
|
|
||||||
@IBAction func openJitsiMeet(sender: Any?) {
|
@IBAction func openJitsiMeet(sender: Any?) {
|
||||||
cleanUp()
|
cleanUp()
|
||||||
|
|
||||||
// create and configure jitsimeet view
|
// create and configure jitsimeet view
|
||||||
let jitsiMeetView = JitsiMeetView()
|
let jitsiMeetView = JitsiMeetView()
|
||||||
jitsiMeetView.welcomePageEnabled = true
|
jitsiMeetView.welcomePageEnabled = true
|
||||||
|
@ -48,18 +48,18 @@ class ViewController: UIViewController {
|
||||||
jitsiMeetView.load(nil)
|
jitsiMeetView.load(nil)
|
||||||
jitsiMeetView.delegate = self
|
jitsiMeetView.delegate = self
|
||||||
self.jitsiMeetView = jitsiMeetView
|
self.jitsiMeetView = jitsiMeetView
|
||||||
|
|
||||||
// Enable jitsimeet view to be a view that can be displayed
|
// Enable jitsimeet view to be a view that can be displayed
|
||||||
// on top of all the things, and let the coordinator to manage
|
// on top of all the things, and let the coordinator to manage
|
||||||
// the view state and interactions
|
// the view state and interactions
|
||||||
pipViewCoordinator = PiPViewCoordinator(withView: jitsiMeetView)
|
pipViewCoordinator = PiPViewCoordinator(withView: jitsiMeetView)
|
||||||
pipViewCoordinator?.configureAsStickyView(withParentView: view)
|
pipViewCoordinator?.configureAsStickyView(withParentView: view)
|
||||||
|
|
||||||
// animate in
|
// animate in
|
||||||
jitsiMeetView.alpha = 0
|
jitsiMeetView.alpha = 0
|
||||||
pipViewCoordinator?.show()
|
pipViewCoordinator?.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func cleanUp() {
|
fileprivate func cleanUp() {
|
||||||
jitsiMeetView?.removeFromSuperview()
|
jitsiMeetView?.removeFromSuperview()
|
||||||
jitsiMeetView = nil
|
jitsiMeetView = nil
|
||||||
|
@ -68,21 +68,21 @@ class ViewController: UIViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ViewController: JitsiMeetViewDelegate {
|
extension ViewController: JitsiMeetViewDelegate {
|
||||||
|
|
||||||
func conferenceFailed(_ data: [AnyHashable : Any]!) {
|
func conferenceFailed(_ data: [AnyHashable : Any]!) {
|
||||||
hideJitsiMeetViewAndCleanUp()
|
hideJitsiMeetViewAndCleanUp()
|
||||||
}
|
}
|
||||||
|
|
||||||
func conferenceLeft(_ data: [AnyHashable : Any]!) {
|
func conferenceLeft(_ data: [AnyHashable : Any]!) {
|
||||||
hideJitsiMeetViewAndCleanUp()
|
hideJitsiMeetViewAndCleanUp()
|
||||||
}
|
}
|
||||||
|
|
||||||
func enterPicture(inPicture data: [AnyHashable : Any]!) {
|
func enterPicture(inPicture data: [AnyHashable : Any]!) {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.pipViewCoordinator?.enterPictureInPicture()
|
self.pipViewCoordinator?.enterPictureInPicture()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func hideJitsiMeetViewAndCleanUp() {
|
private func hideJitsiMeetViewAndCleanUp() {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.pipViewCoordinator?.hide() { _ in
|
self.pipViewCoordinator?.hide() { _ in
|
||||||
|
|
|
@ -38,9 +38,9 @@
|
||||||
B386B85D20981A75000DEF7A /* Invite.m in Sources */ = {isa = PBXBuildFile; fileRef = B386B85620981A75000DEF7A /* Invite.m */; };
|
B386B85D20981A75000DEF7A /* Invite.m in Sources */ = {isa = PBXBuildFile; fileRef = B386B85620981A75000DEF7A /* Invite.m */; };
|
||||||
C6245F5D2053091D0040BE68 /* image-resize@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C6245F5B2053091D0040BE68 /* image-resize@2x.png */; };
|
C6245F5D2053091D0040BE68 /* image-resize@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C6245F5B2053091D0040BE68 /* image-resize@2x.png */; };
|
||||||
C6245F5E2053091D0040BE68 /* image-resize@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = C6245F5C2053091D0040BE68 /* image-resize@3x.png */; };
|
C6245F5E2053091D0040BE68 /* image-resize@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = C6245F5C2053091D0040BE68 /* image-resize@3x.png */; };
|
||||||
C69EFA0C209A0F660027712B /* JMCallKitNotifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69EFA09209A0F650027712B /* JMCallKitNotifier.swift */; };
|
C69EFA0C209A0F660027712B /* JMCallKitEmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69EFA09209A0F650027712B /* JMCallKitEmitter.swift */; };
|
||||||
C69EFA0D209A0F660027712B /* JMCallKitProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69EFA0A209A0F660027712B /* JMCallKitProxy.swift */; };
|
C69EFA0D209A0F660027712B /* JMCallKitProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69EFA0A209A0F660027712B /* JMCallKitProxy.swift */; };
|
||||||
C69EFA0E209A0F660027712B /* JMCallKitEventListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69EFA0B209A0F660027712B /* JMCallKitEventListener.swift */; };
|
C69EFA0E209A0F660027712B /* JMCallKitListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69EFA0B209A0F660027712B /* JMCallKitListener.swift */; };
|
||||||
C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425E204EF76800E062DD /* DragGestureController.swift */; };
|
C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425E204EF76800E062DD /* DragGestureController.swift */; };
|
||||||
C6CC49AF207412CF000DFA42 /* PiPViewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */; };
|
C6CC49AF207412CF000DFA42 /* PiPViewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */; };
|
||||||
C6F99C15204DB63E0001F710 /* JitsiMeetView+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */; };
|
C6F99C15204DB63E0001F710 /* JitsiMeetView+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */; };
|
||||||
|
@ -86,9 +86,9 @@
|
||||||
B386B85620981A75000DEF7A /* Invite.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Invite.m; sourceTree = "<group>"; };
|
B386B85620981A75000DEF7A /* Invite.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Invite.m; sourceTree = "<group>"; };
|
||||||
C6245F5B2053091D0040BE68 /* image-resize@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "image-resize@2x.png"; path = "src/picture-in-picture/image-resize@2x.png"; sourceTree = "<group>"; };
|
C6245F5B2053091D0040BE68 /* image-resize@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "image-resize@2x.png"; path = "src/picture-in-picture/image-resize@2x.png"; sourceTree = "<group>"; };
|
||||||
C6245F5C2053091D0040BE68 /* image-resize@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "image-resize@3x.png"; path = "src/picture-in-picture/image-resize@3x.png"; sourceTree = "<group>"; };
|
C6245F5C2053091D0040BE68 /* image-resize@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "image-resize@3x.png"; path = "src/picture-in-picture/image-resize@3x.png"; sourceTree = "<group>"; };
|
||||||
C69EFA09209A0F650027712B /* JMCallKitNotifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JMCallKitNotifier.swift; sourceTree = "<group>"; };
|
C69EFA09209A0F650027712B /* JMCallKitEmitter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JMCallKitEmitter.swift; sourceTree = "<group>"; };
|
||||||
C69EFA0A209A0F660027712B /* JMCallKitProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JMCallKitProxy.swift; sourceTree = "<group>"; };
|
C69EFA0A209A0F660027712B /* JMCallKitProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JMCallKitProxy.swift; sourceTree = "<group>"; };
|
||||||
C69EFA0B209A0F660027712B /* JMCallKitEventListener.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JMCallKitEventListener.swift; sourceTree = "<group>"; };
|
C69EFA0B209A0F660027712B /* JMCallKitListener.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JMCallKitListener.swift; sourceTree = "<group>"; };
|
||||||
C6A3425E204EF76800E062DD /* DragGestureController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DragGestureController.swift; sourceTree = "<group>"; };
|
C6A3425E204EF76800E062DD /* DragGestureController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DragGestureController.swift; sourceTree = "<group>"; };
|
||||||
C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PiPViewCoordinator.swift; sourceTree = "<group>"; };
|
C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PiPViewCoordinator.swift; sourceTree = "<group>"; };
|
||||||
C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetView+Private.h"; sourceTree = "<group>"; };
|
C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetView+Private.h"; sourceTree = "<group>"; };
|
||||||
|
@ -112,12 +112,12 @@
|
||||||
0BCA49681EC4BBE500B793EE /* Resources */ = {
|
0BCA49681EC4BBE500B793EE /* Resources */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
75635B0820751D6D00F29C9F /* joined.wav */,
|
|
||||||
75635B0920751D6D00F29C9F /* left.wav */,
|
|
||||||
0BC4B8681F8C01E100CE8B21 /* CallKitIcon.png */,
|
0BC4B8681F8C01E100CE8B21 /* CallKitIcon.png */,
|
||||||
C6245F5B2053091D0040BE68 /* image-resize@2x.png */,
|
C6245F5B2053091D0040BE68 /* image-resize@2x.png */,
|
||||||
C6245F5C2053091D0040BE68 /* image-resize@3x.png */,
|
C6245F5C2053091D0040BE68 /* image-resize@3x.png */,
|
||||||
0BCA496B1EC4BBF900B793EE /* jitsi.ttf */,
|
0BCA496B1EC4BBF900B793EE /* jitsi.ttf */,
|
||||||
|
75635B0820751D6D00F29C9F /* joined.wav */,
|
||||||
|
75635B0920751D6D00F29C9F /* left.wav */,
|
||||||
);
|
);
|
||||||
name = Resources;
|
name = Resources;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -144,25 +144,24 @@
|
||||||
0BD906E71EC0C00300C8C18E /* src */ = {
|
0BD906E71EC0C00300C8C18E /* src */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C69EFA02209A0EFD0027712B /* callkit */,
|
|
||||||
B386B84F20981A11000DEF7A /* invite */,
|
|
||||||
C6A3426B204F127900E062DD /* picture-in-picture */,
|
|
||||||
0BCA495C1EC4B6C600B793EE /* AudioMode.m */,
|
|
||||||
0BB9AD7C1F60356D001C08DB /* AppInfo.m */,
|
0BB9AD7C1F60356D001C08DB /* AppInfo.m */,
|
||||||
0BB9AD7A1F5EC8F4001C08DB /* CallKit.m */,
|
0BCA495C1EC4B6C600B793EE /* AudioMode.m */,
|
||||||
|
C69EFA02209A0EFD0027712B /* callkit */,
|
||||||
0BA13D301EE83FF8007BEF7F /* ExternalAPI.m */,
|
0BA13D301EE83FF8007BEF7F /* ExternalAPI.m */,
|
||||||
0BD906E91EC0C00300C8C18E /* Info.plist */,
|
0BD906E91EC0C00300C8C18E /* Info.plist */,
|
||||||
0B7C2CFC200F51D60060D076 /* LaunchOptions.m */,
|
B386B84F20981A11000DEF7A /* invite */,
|
||||||
0BD906E81EC0C00300C8C18E /* JitsiMeet.h */,
|
0BD906E81EC0C00300C8C18E /* JitsiMeet.h */,
|
||||||
0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */,
|
0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */,
|
||||||
0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */,
|
0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */,
|
||||||
C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */,
|
C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */,
|
||||||
0B412F1B1EDEC80100B1A0A6 /* JitsiMeetViewDelegate.h */,
|
0B412F1B1EDEC80100B1A0A6 /* JitsiMeetViewDelegate.h */,
|
||||||
|
0B7C2CFC200F51D60060D076 /* LaunchOptions.m */,
|
||||||
0B44A0181F902126009D1D64 /* MPVolumeViewManager.m */,
|
0B44A0181F902126009D1D64 /* MPVolumeViewManager.m */,
|
||||||
0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */,
|
C6A3426B204F127900E062DD /* picture-in-picture */,
|
||||||
0B93EF7D1EC9DDCD0030D24D /* RCTBridgeWrapper.m */,
|
|
||||||
0BCA495D1EC4B6C600B793EE /* POSIX.m */,
|
0BCA495D1EC4B6C600B793EE /* POSIX.m */,
|
||||||
0BCA495E1EC4B6C600B793EE /* Proximity.m */,
|
0BCA495E1EC4B6C600B793EE /* Proximity.m */,
|
||||||
|
0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */,
|
||||||
|
0B93EF7D1EC9DDCD0030D24D /* RCTBridgeWrapper.m */,
|
||||||
);
|
);
|
||||||
path = src;
|
path = src;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -170,11 +169,11 @@
|
||||||
9C3C6FA2341729836589B856 /* Frameworks */ = {
|
9C3C6FA2341729836589B856 /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
0BB9AD781F5EC6D7001C08DB /* Intents.framework */,
|
|
||||||
0BB9AD761F5EC6CE001C08DB /* CallKit.framework */,
|
0BB9AD761F5EC6CE001C08DB /* CallKit.framework */,
|
||||||
0B93EF7A1EC608550030D24D /* CoreText.framework */,
|
0B93EF7A1EC608550030D24D /* CoreText.framework */,
|
||||||
0BCA49631EC4B76D00B793EE /* WebRTC.framework */,
|
0BB9AD781F5EC6D7001C08DB /* Intents.framework */,
|
||||||
03F2ADC957FF109849B7FCA1 /* libPods-JitsiMeet.a */,
|
03F2ADC957FF109849B7FCA1 /* libPods-JitsiMeet.a */,
|
||||||
|
0BCA49631EC4B76D00B793EE /* WebRTC.framework */,
|
||||||
);
|
);
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -208,8 +207,9 @@
|
||||||
C69EFA02209A0EFD0027712B /* callkit */ = {
|
C69EFA02209A0EFD0027712B /* callkit */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C69EFA0B209A0F660027712B /* JMCallKitEventListener.swift */,
|
0BB9AD7A1F5EC8F4001C08DB /* CallKit.m */,
|
||||||
C69EFA09209A0F650027712B /* JMCallKitNotifier.swift */,
|
C69EFA09209A0F650027712B /* JMCallKitEmitter.swift */,
|
||||||
|
C69EFA0B209A0F660027712B /* JMCallKitListener.swift */,
|
||||||
C69EFA0A209A0F660027712B /* JMCallKitProxy.swift */,
|
C69EFA0A209A0F660027712B /* JMCallKitProxy.swift */,
|
||||||
);
|
);
|
||||||
path = callkit;
|
path = callkit;
|
||||||
|
@ -407,10 +407,10 @@
|
||||||
0BCA495F1EC4B6C600B793EE /* AudioMode.m in Sources */,
|
0BCA495F1EC4B6C600B793EE /* AudioMode.m in Sources */,
|
||||||
0B44A0191F902126009D1D64 /* MPVolumeViewManager.m in Sources */,
|
0B44A0191F902126009D1D64 /* MPVolumeViewManager.m in Sources */,
|
||||||
0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */,
|
0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */,
|
||||||
C69EFA0C209A0F660027712B /* JMCallKitNotifier.swift in Sources */,
|
C69EFA0C209A0F660027712B /* JMCallKitEmitter.swift in Sources */,
|
||||||
C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */,
|
C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */,
|
||||||
C69EFA0D209A0F660027712B /* JMCallKitProxy.swift in Sources */,
|
C69EFA0D209A0F660027712B /* JMCallKitProxy.swift in Sources */,
|
||||||
C69EFA0E209A0F660027712B /* JMCallKitEventListener.swift in Sources */,
|
C69EFA0E209A0F660027712B /* JMCallKitListener.swift in Sources */,
|
||||||
0B412F191EDEC65D00B1A0A6 /* JitsiMeetView.m in Sources */,
|
0B412F191EDEC65D00B1A0A6 /* JitsiMeetView.m in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|
|
@ -39,7 +39,7 @@ static NSString * const RNCallKitPerformSetMutedCallAction
|
||||||
static NSString * const RNCallKitProviderDidReset
|
static NSString * const RNCallKitProviderDidReset
|
||||||
= @"providerDidReset";
|
= @"providerDidReset";
|
||||||
|
|
||||||
@interface RNCallKit : RCTEventEmitter <JMCallKitEventListener>
|
@interface RNCallKit : RCTEventEmitter <JMCallKitListener>
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation RNCallKit
|
@implementation RNCallKit
|
||||||
|
@ -112,12 +112,12 @@ RCT_EXPORT_METHOD(setProviderConfiguration:(NSDictionary *)dictionary) {
|
||||||
dictionary);
|
dictionary);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (![JMCallKitProxy hasProviderBeenConfigurated]) {
|
if (![JMCallKitProxy isProviderConfigured]) {
|
||||||
[self configureProviderFromDictionary:dictionary];
|
[self configureProviderFromDictionary:dictionary];
|
||||||
}
|
}
|
||||||
|
|
||||||
// register to receive CallKit proxy events
|
// register to receive CallKit proxy events
|
||||||
[JMCallKitProxy addListener: self];
|
[JMCallKitProxy addListener:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start outgoing call
|
// Start outgoing call
|
||||||
|
@ -130,18 +130,16 @@ RCT_EXPORT_METHOD(startCall:(NSString *)callUUID
|
||||||
NSLog(@"[RNCallKit][startCall] callUUID = %@", callUUID);
|
NSLog(@"[RNCallKit][startCall] callUUID = %@", callUUID);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
|
// Don't start a new call if there's an active call for the specified
|
||||||
|
// callUUID. JitsiMeetView was configured for an incoming call.
|
||||||
// Don't start a call action if there's
|
|
||||||
// an active call for this UUID
|
|
||||||
// (i.e. JitsiMeetView was configured from an incoming call
|
|
||||||
if ([JMCallKitProxy hasActiveCallForUUID:callUUID]) {
|
if ([JMCallKitProxy hasActiveCallForUUID:callUUID]) {
|
||||||
resolve(nil);
|
resolve(nil);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CXHandle *handle_
|
CXHandle *handle_
|
||||||
= [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:handle];
|
= [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:handle];
|
||||||
|
NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
|
||||||
CXStartCallAction *action
|
CXStartCallAction *action
|
||||||
= [[CXStartCallAction alloc] initWithCallUUID:callUUID_
|
= [[CXStartCallAction alloc] initWithCallUUID:callUUID_
|
||||||
handle:handle_];
|
handle:handle_];
|
||||||
|
@ -156,8 +154,8 @@ RCT_EXPORT_METHOD(reportCallFailed:(NSString *)callUUID
|
||||||
reject:(RCTPromiseRejectBlock)reject) {
|
reject:(RCTPromiseRejectBlock)reject) {
|
||||||
NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
|
NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
|
||||||
[JMCallKitProxy reportCallWith:callUUID_
|
[JMCallKitProxy reportCallWith:callUUID_
|
||||||
endedAt:nil
|
endedAt:nil
|
||||||
reason:CXCallEndedReasonFailed];
|
reason:CXCallEndedReasonFailed];
|
||||||
resolve(nil);
|
resolve(nil);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,7 +165,7 @@ RCT_EXPORT_METHOD(reportConnectedOutgoingCall:(NSString *)callUUID
|
||||||
reject:(RCTPromiseRejectBlock)reject) {
|
reject:(RCTPromiseRejectBlock)reject) {
|
||||||
NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
|
NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
|
||||||
[JMCallKitProxy reportOutgoingCallWith:callUUID_
|
[JMCallKitProxy reportOutgoingCallWith:callUUID_
|
||||||
connectedAt:nil];
|
connectedAt:nil];
|
||||||
resolve(nil);
|
resolve(nil);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,12 +184,12 @@ RCT_EXPORT_METHOD(updateCall:(NSString *)callUUID
|
||||||
NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
|
NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
|
||||||
NSString *displayName = options[@"displayName"];
|
NSString *displayName = options[@"displayName"];
|
||||||
BOOL hasVideo = [(NSNumber*)options[@"hasVideo"] boolValue];
|
BOOL hasVideo = [(NSNumber*)options[@"hasVideo"] boolValue];
|
||||||
|
|
||||||
[JMCallKitProxy reportCallUpdateWith:callUUID_
|
[JMCallKitProxy reportCallUpdateWith:callUUID_
|
||||||
handle:nil
|
handle:nil
|
||||||
displayName:displayName
|
displayName:displayName
|
||||||
hasVideo:hasVideo];
|
hasVideo:hasVideo];
|
||||||
|
|
||||||
resolve(nil);
|
resolve(nil);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,10 +226,11 @@ RCT_EXPORT_METHOD(updateCall:(NSString *)callUUID
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString *ringtoneSound = dictionary[@"ringtoneSound"];
|
NSString *ringtoneSound = dictionary[@"ringtoneSound"];
|
||||||
|
|
||||||
[JMCallKitProxy configureCallKitProviderWithLocalizedName:localizedName
|
[JMCallKitProxy
|
||||||
ringtoneSound:ringtoneSound
|
configureProviderWithLocalizedName:localizedName
|
||||||
iconTemplateImageData:iconTemplateImageData];
|
ringtoneSound:ringtoneSound
|
||||||
|
iconTemplateImageData:iconTemplateImageData];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)requestTransaction:(CXTransaction *)transaction
|
- (void)requestTransaction:(CXTransaction *)transaction
|
||||||
|
@ -242,7 +241,7 @@ RCT_EXPORT_METHOD(updateCall:(NSString *)callUUID
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
[JMCallKitProxy request:transaction
|
[JMCallKitProxy request:transaction
|
||||||
completion:^(NSError * _Nullable error) {
|
completion:^(NSError * _Nullable error) {
|
||||||
if (error) {
|
if (error) {
|
||||||
NSLog(
|
NSLog(
|
||||||
@"[RNCallKit][requestTransaction] Error requesting transaction (%@): (%@)",
|
@"[RNCallKit][requestTransaction] Error requesting transaction (%@): (%@)",
|
||||||
|
@ -255,7 +254,7 @@ RCT_EXPORT_METHOD(updateCall:(NSString *)callUUID
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - JitsiMeetCallKitListener
|
#pragma mark - JMCallKitListener
|
||||||
|
|
||||||
// Called when the provider has been reset. We should terminate all calls.
|
// Called when the provider has been reset. We should terminate all calls.
|
||||||
- (void)providerDidReset {
|
- (void)providerDidReset {
|
||||||
|
@ -307,7 +306,7 @@ RCT_EXPORT_METHOD(updateCall:(NSString *)callUUID
|
||||||
NSLog(@"[RNCallKit][CXProviderDelegate][provider:performStartCallAction:]");
|
NSLog(@"[RNCallKit][CXProviderDelegate][provider:performStartCallAction:]");
|
||||||
#endif
|
#endif
|
||||||
[JMCallKitProxy reportOutgoingCallWith:UUID
|
[JMCallKitProxy reportOutgoingCallWith:UUID
|
||||||
startedConnectingAt:nil];
|
startedConnectingAt:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following just help with debugging:
|
// The following just help with debugging:
|
|
@ -18,87 +18,113 @@ import AVKit
|
||||||
import CallKit
|
import CallKit
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
internal final class JMCallKitNotifier: NSObject, CXProviderDelegate {
|
internal final class JMCallKitEmitter: NSObject, CXProviderDelegate {
|
||||||
|
|
||||||
private var listeners = Set<JMCallKitEventListenerWrapper>()
|
private var listeners = Set<JMCallKitEventListenerWrapper>()
|
||||||
|
|
||||||
internal override init() {}
|
internal override init() {}
|
||||||
|
|
||||||
// MARK: - Add/remove listeners
|
// MARK: - Add/remove listeners
|
||||||
|
|
||||||
func addListener(_ listener: JMCallKitEventListener) {
|
func addListener(_ listener: JMCallKitListener) {
|
||||||
let wrapper = JMCallKitEventListenerWrapper(listener: listener)
|
let wrapper = JMCallKitEventListenerWrapper(listener: listener)
|
||||||
objc_sync_enter(listeners)
|
objc_sync_enter(listeners)
|
||||||
listeners.insert(wrapper)
|
listeners.insert(wrapper)
|
||||||
objc_sync_exit(listeners)
|
objc_sync_exit(listeners)
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeListener(_ listener: JMCallKitEventListener) {
|
func removeListener(_ listener: JMCallKitListener) {
|
||||||
let wrapper = JMCallKitEventListenerWrapper(listener: listener)
|
let wrapper = JMCallKitEventListenerWrapper(listener: listener)
|
||||||
objc_sync_enter(listeners)
|
objc_sync_enter(listeners)
|
||||||
listeners.remove(wrapper)
|
listeners.remove(wrapper)
|
||||||
objc_sync_exit(listeners)
|
objc_sync_exit(listeners)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - CXProviderDelegate
|
// MARK: - CXProviderDelegate
|
||||||
|
|
||||||
func providerDidReset(_ provider: CXProvider) {
|
func providerDidReset(_ provider: CXProvider) {
|
||||||
objc_sync_enter(listeners)
|
objc_sync_enter(listeners)
|
||||||
listeners.forEach { $0.listener?.providerDidReset?() }
|
listeners.forEach { $0.listener?.providerDidReset?() }
|
||||||
objc_sync_exit(listeners)
|
objc_sync_exit(listeners)
|
||||||
}
|
}
|
||||||
|
|
||||||
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
|
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
|
||||||
objc_sync_enter(listeners)
|
objc_sync_enter(listeners)
|
||||||
listeners.forEach { $0.listener?.performAnswerCall?(UUID: action.callUUID) }
|
listeners.forEach {
|
||||||
|
$0.listener?.performAnswerCall?(UUID: action.callUUID)
|
||||||
|
}
|
||||||
objc_sync_exit(listeners)
|
objc_sync_exit(listeners)
|
||||||
|
|
||||||
action.fulfill()
|
action.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
|
func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
|
||||||
objc_sync_enter(listeners)
|
objc_sync_enter(listeners)
|
||||||
listeners.forEach { $0.listener?.performEndCall?(UUID: action.callUUID) }
|
listeners.forEach {
|
||||||
|
$0.listener?.performEndCall?(UUID: action.callUUID)
|
||||||
|
}
|
||||||
objc_sync_exit(listeners)
|
objc_sync_exit(listeners)
|
||||||
|
|
||||||
action.fulfill()
|
action.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
|
func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
|
||||||
objc_sync_enter(listeners)
|
objc_sync_enter(listeners)
|
||||||
listeners.forEach {
|
listeners.forEach {
|
||||||
$0.listener?.performSetMutedCall?(UUID: action.callUUID,
|
$0.listener?.performSetMutedCall?(UUID: action.callUUID,
|
||||||
isMuted: action.isMuted)
|
isMuted: action.isMuted)
|
||||||
}
|
}
|
||||||
objc_sync_exit(listeners)
|
objc_sync_exit(listeners)
|
||||||
|
|
||||||
action.fulfill()
|
action.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
|
func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
|
||||||
objc_sync_enter(listeners)
|
objc_sync_enter(listeners)
|
||||||
listeners.forEach {
|
listeners.forEach {
|
||||||
$0.listener?.performStartCall?(UUID: action.callUUID,
|
$0.listener?.performStartCall?(UUID: action.callUUID,
|
||||||
isVideo: action.isVideo)
|
isVideo: action.isVideo)
|
||||||
}
|
}
|
||||||
objc_sync_exit(listeners)
|
objc_sync_exit(listeners)
|
||||||
|
|
||||||
action.fulfill()
|
action.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
|
func provider(_ provider: CXProvider,
|
||||||
|
didActivate audioSession: AVAudioSession) {
|
||||||
objc_sync_enter(listeners)
|
objc_sync_enter(listeners)
|
||||||
listeners.forEach {
|
listeners.forEach {
|
||||||
$0.listener?.providerDidActivateAudioSession?(session: audioSession)
|
$0.listener?.providerDidActivateAudioSession?(session: audioSession)
|
||||||
}
|
}
|
||||||
objc_sync_exit(listeners)
|
objc_sync_exit(listeners)
|
||||||
}
|
}
|
||||||
|
|
||||||
func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
|
func provider(_ provider: CXProvider,
|
||||||
|
didDeactivate audioSession: AVAudioSession) {
|
||||||
objc_sync_enter(listeners)
|
objc_sync_enter(listeners)
|
||||||
listeners.forEach {
|
listeners.forEach {
|
||||||
$0.listener?.providerDidDeactivateAudioSession?(session: audioSession)
|
$0.listener?.providerDidDeactivateAudioSession?(
|
||||||
|
session: audioSession)
|
||||||
}
|
}
|
||||||
objc_sync_exit(listeners)
|
objc_sync_exit(listeners)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fileprivate struct JMCallKitEventListenerWrapper: Hashable {
|
||||||
|
|
||||||
|
public var hashValue: Int
|
||||||
|
|
||||||
|
internal weak var listener: JMCallKitListener?
|
||||||
|
|
||||||
|
public init(listener: JMCallKitListener) {
|
||||||
|
self.listener = listener
|
||||||
|
self.hashValue = listener.hash
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: JMCallKitEventListenerWrapper,
|
||||||
|
rhs: JMCallKitEventListenerWrapper) -> Bool {
|
||||||
|
// XXX We're aware that "[t]wo instances with equal hash values are not
|
||||||
|
// necessarily equal to each other."
|
||||||
|
return lhs.hashValue == rhs.hashValue
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,46 +18,29 @@ import AVKit
|
||||||
import CallKit
|
import CallKit
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
@objc public protocol JMCallKitEventListener: NSObjectProtocol {
|
@objc public protocol JMCallKitListener: NSObjectProtocol {
|
||||||
|
|
||||||
@available(iOS 10.0, *)
|
@available(iOS 10.0, *)
|
||||||
@objc optional func providerDidReset()
|
@objc optional func providerDidReset()
|
||||||
|
|
||||||
@available(iOS 10.0, *)
|
@available(iOS 10.0, *)
|
||||||
@objc optional func performAnswerCall(UUID: UUID)
|
@objc optional func performAnswerCall(UUID: UUID)
|
||||||
|
|
||||||
@available(iOS 10.0, *)
|
@available(iOS 10.0, *)
|
||||||
@objc optional func performEndCall(UUID: UUID)
|
@objc optional func performEndCall(UUID: UUID)
|
||||||
|
|
||||||
@available(iOS 10.0, *)
|
@available(iOS 10.0, *)
|
||||||
@objc optional func performSetMutedCall(UUID: UUID, isMuted: Bool)
|
@objc optional func performSetMutedCall(UUID: UUID, isMuted: Bool)
|
||||||
|
|
||||||
@available(iOS 10.0, *)
|
@available(iOS 10.0, *)
|
||||||
@objc optional func performStartCall(UUID: UUID, isVideo: Bool)
|
@objc optional func performStartCall(UUID: UUID, isVideo: Bool)
|
||||||
|
|
||||||
@available(iOS 10.0, *)
|
@available(iOS 10.0, *)
|
||||||
@objc optional func providerDidActivateAudioSession(session: AVAudioSession)
|
@objc optional func providerDidActivateAudioSession(session: AVAudioSession)
|
||||||
|
|
||||||
@available(iOS 10.0, *)
|
@available(iOS 10.0, *)
|
||||||
@objc optional func providerDidDeactivateAudioSession(session: AVAudioSession)
|
@objc optional func providerDidDeactivateAudioSession(session: AVAudioSession)
|
||||||
|
|
||||||
@available(iOS 10.0, *)
|
@available(iOS 10.0, *)
|
||||||
@objc optional func providerTimedOutPerformingAction(action: CXAction)
|
@objc optional func providerTimedOutPerformingAction(action: CXAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal struct JMCallKitEventListenerWrapper: Hashable {
|
|
||||||
|
|
||||||
public var hashValue: Int
|
|
||||||
|
|
||||||
internal weak var listener: JMCallKitEventListener?
|
|
||||||
|
|
||||||
public init(listener: JMCallKitEventListener) {
|
|
||||||
self.listener = listener
|
|
||||||
self.hashValue = listener.hash
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func ==(lhs: JMCallKitEventListenerWrapper,
|
|
||||||
rhs: JMCallKitEventListenerWrapper) -> Bool {
|
|
||||||
return lhs.hashValue == rhs.hashValue
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -20,138 +20,142 @@ import Foundation
|
||||||
/// JitsiMeet CallKit proxy
|
/// JitsiMeet CallKit proxy
|
||||||
@available(iOS 10.0, *)
|
@available(iOS 10.0, *)
|
||||||
@objc public final class JMCallKitProxy: NSObject {
|
@objc public final class JMCallKitProxy: NSObject {
|
||||||
|
|
||||||
override private init() {}
|
private override init() {}
|
||||||
|
|
||||||
// MARK: - CallKit proxy
|
// MARK: - CallKit proxy
|
||||||
|
|
||||||
internal static let cxProvider: CXProvider = {
|
private static let provider: CXProvider = {
|
||||||
let config = CXProviderConfiguration(localizedName: "")
|
let configuration = CXProviderConfiguration(localizedName: "")
|
||||||
let provider = CXProvider(configuration: config)
|
return CXProvider(configuration: configuration)
|
||||||
return provider
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
internal static let cxCallController: CXCallController = {
|
private static var providerConfiguration: CXProviderConfiguration? {
|
||||||
return CXCallController()
|
|
||||||
}()
|
|
||||||
|
|
||||||
internal static let callKitNotifier: JMCallKitNotifier = {
|
|
||||||
return JMCallKitNotifier()
|
|
||||||
}()
|
|
||||||
|
|
||||||
internal static var cxProviderConfiguration: CXProviderConfiguration? {
|
|
||||||
didSet {
|
didSet {
|
||||||
guard let providerConfiguration = cxProviderConfiguration else { return }
|
guard let configuration = providerConfiguration else { return }
|
||||||
cxProvider.configuration = providerConfiguration
|
provider.configuration = configuration
|
||||||
cxProvider.setDelegate(callKitNotifier, queue: nil)
|
provider.setDelegate(emitter, queue: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enables the proxy in between callkit and the consumers of the SDK
|
private static let callController: CXCallController = {
|
||||||
/// Default to enabled, set to false when you don't want to use callkit
|
return CXCallController()
|
||||||
|
}()
|
||||||
|
|
||||||
|
private static let emitter: JMCallKitEmitter = {
|
||||||
|
return JMCallKitEmitter()
|
||||||
|
}()
|
||||||
|
|
||||||
|
/// Enables the proxy in between CallKit and the consumers of the SDK.
|
||||||
|
/// Defaults to enabled, set to false when you don't want to use CallKit.
|
||||||
@objc public static var enabled: Bool = true {
|
@objc public static var enabled: Bool = true {
|
||||||
didSet {
|
didSet {
|
||||||
if enabled == false {
|
if enabled == false {
|
||||||
cxProvider.setDelegate(nil, queue: nil)
|
provider.setDelegate(nil, queue: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public static func hasProviderBeenConfigurated() -> Bool {
|
@objc public static func configureProvider(localizedName: String,
|
||||||
return cxProviderConfiguration != nil
|
ringtoneSound: String?,
|
||||||
}
|
iconTemplateImageData: Data?) {
|
||||||
|
|
||||||
@objc public static func configureCallKitProvider(localizedName: String,
|
|
||||||
ringtoneSound: String?,
|
|
||||||
iconTemplateImageData: Data?) {
|
|
||||||
let configuration = CXProviderConfiguration(localizedName: localizedName)
|
let configuration = CXProviderConfiguration(localizedName: localizedName)
|
||||||
configuration.ringtoneSound = ringtoneSound
|
|
||||||
configuration.iconTemplateImageData = iconTemplateImageData
|
configuration.iconTemplateImageData = iconTemplateImageData
|
||||||
|
|
||||||
configuration.maximumCallGroups = 1
|
configuration.maximumCallGroups = 1
|
||||||
configuration.maximumCallsPerCallGroup = 1
|
configuration.maximumCallsPerCallGroup = 1
|
||||||
|
configuration.ringtoneSound = ringtoneSound
|
||||||
configuration.supportedHandleTypes = [CXHandle.HandleType.generic]
|
configuration.supportedHandleTypes = [CXHandle.HandleType.generic]
|
||||||
configuration.supportsVideo = true
|
configuration.supportsVideo = true
|
||||||
cxProviderConfiguration = configuration
|
|
||||||
|
providerConfiguration = configuration
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public static func addListener(_ listener: JMCallKitEventListener) {
|
@objc public static func isProviderConfigured() -> Bool {
|
||||||
callKitNotifier.addListener(listener)
|
return providerConfiguration != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public static func removeListener(_ listener: JMCallKitEventListener) {
|
@objc public static func addListener(_ listener: JMCallKitListener) {
|
||||||
callKitNotifier.removeListener(listener)
|
emitter.addListener(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc public static func removeListener(_ listener: JMCallKitListener) {
|
||||||
|
emitter.removeListener(listener)
|
||||||
|
}
|
||||||
|
|
||||||
@objc public static func hasActiveCallForUUID(_ callUUID: String) -> Bool {
|
@objc public static func hasActiveCallForUUID(_ callUUID: String) -> Bool {
|
||||||
let activeCallForUUID = cxCallController.callObserver.calls.first {
|
let activeCallForUUID = callController.callObserver.calls.first {
|
||||||
$0.uuid == UUID(uuidString: callUUID)
|
$0.uuid == UUID(uuidString: callUUID)
|
||||||
}
|
}
|
||||||
guard activeCallForUUID != nil else { return false }
|
guard activeCallForUUID != nil else { return false }
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public static func reportNewIncomingCall(UUID: UUID,
|
@objc public static func reportNewIncomingCall(
|
||||||
handle: String?,
|
UUID: UUID,
|
||||||
displayName: String?,
|
handle: String?,
|
||||||
hasVideo: Bool,
|
displayName: String?,
|
||||||
completion: @escaping (Error?) -> Void) {
|
hasVideo: Bool,
|
||||||
|
completion: @escaping (Error?) -> Void) {
|
||||||
guard enabled else { return }
|
guard enabled else { return }
|
||||||
|
|
||||||
let callUpdate = makeCXUpdate(handle: handle,
|
let callUpdate = makeCXUpdate(handle: handle,
|
||||||
displayName: displayName,
|
displayName: displayName,
|
||||||
hasVideo: hasVideo)
|
hasVideo: hasVideo)
|
||||||
cxProvider.reportNewIncomingCall(with: UUID,
|
provider.reportNewIncomingCall(with: UUID,
|
||||||
update: callUpdate,
|
update: callUpdate,
|
||||||
completion: completion)
|
completion: completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public static func reportCallUpdate(with UUID: UUID,
|
@objc public static func reportCallUpdate(with UUID: UUID,
|
||||||
handle: String?,
|
handle: String?,
|
||||||
displayName: String?,
|
displayName: String?,
|
||||||
hasVideo: Bool) {
|
hasVideo: Bool) {
|
||||||
guard enabled else { return }
|
guard enabled else { return }
|
||||||
|
|
||||||
let callUpdate = makeCXUpdate(handle: handle,
|
let callUpdate = makeCXUpdate(handle: handle,
|
||||||
displayName: displayName,
|
displayName: displayName,
|
||||||
hasVideo: hasVideo)
|
hasVideo: hasVideo)
|
||||||
cxProvider.reportCall(with: UUID, updated: callUpdate)
|
provider.reportCall(with: UUID, updated: callUpdate)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public static func reportCall(with UUID: UUID,
|
@objc public static func reportCall(
|
||||||
endedAt dateEnded: Date?,
|
with UUID: UUID,
|
||||||
reason endedReason: CXCallEndedReason) {
|
endedAt dateEnded: Date?,
|
||||||
|
reason endedReason: CXCallEndedReason) {
|
||||||
guard enabled else { return }
|
guard enabled else { return }
|
||||||
|
|
||||||
cxProvider.reportCall(with: UUID,
|
provider.reportCall(with: UUID,
|
||||||
endedAt: dateEnded,
|
endedAt: dateEnded,
|
||||||
reason: endedReason)
|
reason: endedReason)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public static func reportOutgoingCall(with UUID: UUID,
|
@objc public static func reportOutgoingCall(
|
||||||
startedConnectingAt dateStartedConnecting: Date?) {
|
with UUID: UUID,
|
||||||
|
startedConnectingAt dateStartedConnecting: Date?) {
|
||||||
guard enabled else { return }
|
guard enabled else { return }
|
||||||
|
|
||||||
cxProvider.reportOutgoingCall(with: UUID,
|
provider.reportOutgoingCall(with: UUID,
|
||||||
startedConnectingAt: dateStartedConnecting)
|
startedConnectingAt: dateStartedConnecting)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public static func reportOutgoingCall(with UUID: UUID,
|
@objc public static func reportOutgoingCall(
|
||||||
connectedAt dateConnected: Date?) {
|
with UUID: UUID,
|
||||||
|
connectedAt dateConnected: Date?) {
|
||||||
guard enabled else { return }
|
guard enabled else { return }
|
||||||
|
|
||||||
cxProvider.reportOutgoingCall(with: UUID, connectedAt: dateConnected)
|
provider.reportOutgoingCall(with: UUID, connectedAt: dateConnected)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc public static func request(_ transaction: CXTransaction,
|
@objc public static func request(
|
||||||
completion: @escaping (Error?) -> Swift.Void) {
|
_ transaction: CXTransaction,
|
||||||
|
completion: @escaping (Error?) -> Swift.Void) {
|
||||||
guard enabled else { return }
|
guard enabled else { return }
|
||||||
|
|
||||||
cxCallController.request(transaction, completion: completion)
|
callController.request(transaction, completion: completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Callkit Proxy helpers
|
// MARK: - Callkit Proxy helpers
|
||||||
|
|
||||||
private static func makeCXUpdate(handle: String?,
|
private static func makeCXUpdate(handle: String?,
|
||||||
displayName: String?,
|
displayName: String?,
|
||||||
hasVideo: Bool) -> CXCallUpdate {
|
hasVideo: Bool) -> CXCallUpdate {
|
||||||
|
@ -161,17 +165,16 @@ import Foundation
|
||||||
update.supportsGrouping = false
|
update.supportsGrouping = false
|
||||||
update.supportsUngrouping = false
|
update.supportsUngrouping = false
|
||||||
update.hasVideo = hasVideo
|
update.hasVideo = hasVideo
|
||||||
|
|
||||||
if let handle = handle {
|
if let handle = handle {
|
||||||
update.remoteHandle = CXHandle(type: .generic,
|
update.remoteHandle = CXHandle(type: .generic,
|
||||||
value: handle)
|
value: handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let displayName = displayName {
|
if let displayName = displayName {
|
||||||
update.localizedCallerName = displayName
|
update.localizedCallerName = displayName
|
||||||
}
|
}
|
||||||
|
|
||||||
return update
|
return update
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,48 +15,48 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
final class DragGestureController {
|
final class DragGestureController {
|
||||||
|
|
||||||
var insets: UIEdgeInsets = UIEdgeInsets.zero
|
var insets: UIEdgeInsets = UIEdgeInsets.zero
|
||||||
|
|
||||||
private var frameBeforeDragging: CGRect = CGRect.zero
|
private var frameBeforeDragging: CGRect = CGRect.zero
|
||||||
private weak var view: UIView?
|
private weak var view: UIView?
|
||||||
private lazy var panGesture: UIPanGestureRecognizer = {
|
private lazy var panGesture: UIPanGestureRecognizer = {
|
||||||
return UIPanGestureRecognizer(target: self,
|
return UIPanGestureRecognizer(target: self,
|
||||||
action: #selector(handlePan(gesture:)))
|
action: #selector(handlePan(gesture:)))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
func startDragListener(inView view: UIView) {
|
func startDragListener(inView view: UIView) {
|
||||||
self.view = view
|
self.view = view
|
||||||
view.addGestureRecognizer(panGesture)
|
view.addGestureRecognizer(panGesture)
|
||||||
panGesture.isEnabled = true
|
panGesture.isEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func stopDragListener() {
|
func stopDragListener() {
|
||||||
panGesture.isEnabled = false
|
panGesture.isEnabled = false
|
||||||
view?.removeGestureRecognizer(panGesture)
|
view?.removeGestureRecognizer(panGesture)
|
||||||
view = nil
|
view = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func handlePan(gesture: UIPanGestureRecognizer) {
|
@objc private func handlePan(gesture: UIPanGestureRecognizer) {
|
||||||
guard let view = self.view else { return }
|
guard let view = self.view else { return }
|
||||||
|
|
||||||
let translation = gesture.translation(in: view.superview)
|
let translation = gesture.translation(in: view.superview)
|
||||||
let velocity = gesture.velocity(in: view.superview)
|
let velocity = gesture.velocity(in: view.superview)
|
||||||
var frame = frameBeforeDragging
|
var frame = frameBeforeDragging
|
||||||
|
|
||||||
switch gesture.state {
|
switch gesture.state {
|
||||||
case .began:
|
case .began:
|
||||||
frameBeforeDragging = view.frame
|
frameBeforeDragging = view.frame
|
||||||
|
|
||||||
case .changed:
|
case .changed:
|
||||||
frame.origin.x = floor(frame.origin.x + translation.x)
|
frame.origin.x = floor(frame.origin.x + translation.x)
|
||||||
frame.origin.y = floor(frame.origin.y + translation.y)
|
frame.origin.y = floor(frame.origin.y + translation.y)
|
||||||
view.frame = frame
|
view.frame = frame
|
||||||
|
|
||||||
case .ended:
|
case .ended:
|
||||||
let currentPos = view.frame.origin
|
let currentPos = view.frame.origin
|
||||||
let finalPos = calculateFinalPosition()
|
let finalPos = calculateFinalPosition()
|
||||||
|
|
||||||
let distance = CGPoint(x: currentPos.x - finalPos.x,
|
let distance = CGPoint(x: currentPos.x - finalPos.x,
|
||||||
y: currentPos.y - finalPos.y)
|
y: currentPos.y - finalPos.y)
|
||||||
let distanceMagnitude = magnitude(vector: distance)
|
let distanceMagnitude = magnitude(vector: distance)
|
||||||
|
@ -64,9 +64,9 @@ final class DragGestureController {
|
||||||
let animationDuration = 0.5
|
let animationDuration = 0.5
|
||||||
let initialSpringVelocity =
|
let initialSpringVelocity =
|
||||||
velocityMagnitude / distanceMagnitude / CGFloat(animationDuration)
|
velocityMagnitude / distanceMagnitude / CGFloat(animationDuration)
|
||||||
|
|
||||||
frame.origin = CGPoint(x: finalPos.x, y: finalPos.y)
|
frame.origin = CGPoint(x: finalPos.x, y: finalPos.y)
|
||||||
|
|
||||||
UIView.animate(withDuration: animationDuration,
|
UIView.animate(withDuration: animationDuration,
|
||||||
delay: 0,
|
delay: 0,
|
||||||
usingSpringWithDamping: 0.9,
|
usingSpringWithDamping: 0.9,
|
||||||
|
@ -75,38 +75,38 @@ final class DragGestureController {
|
||||||
animations: {
|
animations: {
|
||||||
view.frame = frame
|
view.frame = frame
|
||||||
}, completion: nil)
|
}, completion: nil)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func calculateFinalPosition() -> CGPoint {
|
private func calculateFinalPosition() -> CGPoint {
|
||||||
guard
|
guard
|
||||||
let view = self.view,
|
let view = self.view,
|
||||||
let bounds = view.superview?.frame
|
let bounds = view.superview?.frame
|
||||||
else { return CGPoint.zero }
|
else { return CGPoint.zero }
|
||||||
|
|
||||||
let currentSize = view.frame.size
|
let currentSize = view.frame.size
|
||||||
let adjustedBounds = UIEdgeInsetsInsetRect(bounds, insets)
|
let adjustedBounds = UIEdgeInsetsInsetRect(bounds, insets)
|
||||||
let threshold: CGFloat = 20.0
|
let threshold: CGFloat = 20.0
|
||||||
let velocity = panGesture.velocity(in: view.superview)
|
let velocity = panGesture.velocity(in: view.superview)
|
||||||
let location = panGesture.location(in: view.superview)
|
let location = panGesture.location(in: view.superview)
|
||||||
|
|
||||||
let goLeft: Bool
|
let goLeft: Bool
|
||||||
if fabs(velocity.x) > threshold {
|
if fabs(velocity.x) > threshold {
|
||||||
goLeft = velocity.x < -threshold
|
goLeft = velocity.x < -threshold
|
||||||
} else {
|
} else {
|
||||||
goLeft = location.x < bounds.midX
|
goLeft = location.x < bounds.midX
|
||||||
}
|
}
|
||||||
|
|
||||||
let goUp: Bool
|
let goUp: Bool
|
||||||
if fabs(velocity.y) > threshold {
|
if fabs(velocity.y) > threshold {
|
||||||
goUp = velocity.y < -threshold
|
goUp = velocity.y < -threshold
|
||||||
} else {
|
} else {
|
||||||
goUp = location.y < bounds.midY
|
goUp = location.y < bounds.midY
|
||||||
}
|
}
|
||||||
|
|
||||||
let finalPosX: CGFloat =
|
let finalPosX: CGFloat =
|
||||||
goLeft
|
goLeft
|
||||||
? adjustedBounds.origin.x
|
? adjustedBounds.origin.x
|
||||||
|
@ -115,10 +115,10 @@ final class DragGestureController {
|
||||||
goUp
|
goUp
|
||||||
? adjustedBounds.origin.y
|
? adjustedBounds.origin.y
|
||||||
: bounds.size.height - insets.bottom - currentSize.height
|
: bounds.size.height - insets.bottom - currentSize.height
|
||||||
|
|
||||||
return CGPoint(x: finalPosX, y: finalPosY)
|
return CGPoint(x: finalPosX, y: finalPosY)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func magnitude(vector: CGPoint) -> CGFloat {
|
private func magnitude(vector: CGPoint) -> CGFloat {
|
||||||
return sqrt(pow(vector.x, 2) + pow(vector.y, 2))
|
return sqrt(pow(vector.x, 2) + pow(vector.y, 2))
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ public typealias AnimationCompletion = (Bool) -> Void
|
||||||
/// This object will also provide the drag and tap interactions of the view
|
/// This object will also provide the drag and tap interactions of the view
|
||||||
/// when is presented in Picure in Picture mode.
|
/// when is presented in Picure in Picture mode.
|
||||||
public class PiPViewCoordinator {
|
public class PiPViewCoordinator {
|
||||||
|
|
||||||
/// Limits the boundries of view position on screen when minimized
|
/// Limits the boundries of view position on screen when minimized
|
||||||
public var dragBoundInsets: UIEdgeInsets = UIEdgeInsets(top: 25,
|
public var dragBoundInsets: UIEdgeInsets = UIEdgeInsets(top: 25,
|
||||||
left: 5,
|
left: 5,
|
||||||
|
@ -31,7 +31,7 @@ public class PiPViewCoordinator {
|
||||||
dragController.insets = dragBoundInsets
|
dragController.insets = dragBoundInsets
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The size ratio of the view when in PiP mode
|
/// The size ratio of the view when in PiP mode
|
||||||
public var pipSizeRatio: CGFloat = {
|
public var pipSizeRatio: CGFloat = {
|
||||||
let deviceIdiom = UIScreen.main.traitCollection.userInterfaceIdiom
|
let deviceIdiom = UIScreen.main.traitCollection.userInterfaceIdiom
|
||||||
|
@ -44,21 +44,21 @@ public class PiPViewCoordinator {
|
||||||
return 0.25
|
return 0.25
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private(set) var isInPiP: Bool = false // true if view is in PiP mode
|
private(set) var isInPiP: Bool = false // true if view is in PiP mode
|
||||||
|
|
||||||
private(set) var view: UIView
|
private(set) var view: UIView
|
||||||
private var currentBounds: CGRect = CGRect.zero
|
private var currentBounds: CGRect = CGRect.zero
|
||||||
|
|
||||||
private var tapGestureRecognizer: UITapGestureRecognizer?
|
private var tapGestureRecognizer: UITapGestureRecognizer?
|
||||||
private var exitPiPButton: UIButton?
|
private var exitPiPButton: UIButton?
|
||||||
|
|
||||||
private let dragController: DragGestureController = DragGestureController()
|
private let dragController: DragGestureController = DragGestureController()
|
||||||
|
|
||||||
public init(withView view: UIView) {
|
public init(withView view: UIView) {
|
||||||
self.view = view
|
self.view = view
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configure the view to be always on top of all the contents
|
/// Configure the view to be always on top of all the contents
|
||||||
/// of the provided parent view.
|
/// of the provided parent view.
|
||||||
/// If a parentView is not provided it will try to use the main window
|
/// If a parentView is not provided it will try to use the main window
|
||||||
|
@ -66,25 +66,25 @@ public class PiPViewCoordinator {
|
||||||
guard
|
guard
|
||||||
let parentView = parentView ?? UIApplication.shared.keyWindow
|
let parentView = parentView ?? UIApplication.shared.keyWindow
|
||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
parentView.addSubview(view)
|
parentView.addSubview(view)
|
||||||
currentBounds = parentView.bounds
|
currentBounds = parentView.bounds
|
||||||
view.frame = currentBounds
|
view.frame = currentBounds
|
||||||
view.layer.zPosition = CGFloat(Float.greatestFiniteMagnitude)
|
view.layer.zPosition = CGFloat(Float.greatestFiniteMagnitude)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show view with fade in animation
|
/// Show view with fade in animation
|
||||||
public func show(completion: AnimationCompletion? = nil) {
|
public func show(completion: AnimationCompletion? = nil) {
|
||||||
if view.isHidden || view.alpha < 1 {
|
if view.isHidden || view.alpha < 1 {
|
||||||
view.isHidden = false
|
view.isHidden = false
|
||||||
view.alpha = 0
|
view.alpha = 0
|
||||||
|
|
||||||
animateTransition(animations: { [weak self] in
|
animateTransition(animations: { [weak self] in
|
||||||
self?.view.alpha = 1
|
self?.view.alpha = 1
|
||||||
}, completion: completion)
|
}, completion: completion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Hide view with fade out animation
|
/// Hide view with fade out animation
|
||||||
public func hide(completion: AnimationCompletion? = nil) {
|
public func hide(completion: AnimationCompletion? = nil) {
|
||||||
if view.isHidden || view.alpha > 0 {
|
if view.isHidden || view.alpha > 0 {
|
||||||
|
@ -94,7 +94,7 @@ public class PiPViewCoordinator {
|
||||||
}, completion: completion)
|
}, completion: completion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resize view to and change state to custom PictureInPicture mode
|
/// Resize view to and change state to custom PictureInPicture mode
|
||||||
/// This will resize view, add a gesture to enable user to "drag" view
|
/// This will resize view, add a gesture to enable user to "drag" view
|
||||||
/// around screen, and add a button of top of the view to be able to exit mode
|
/// around screen, and add a button of top of the view to be able to exit mode
|
||||||
|
@ -103,7 +103,7 @@ public class PiPViewCoordinator {
|
||||||
animateViewChange()
|
animateViewChange()
|
||||||
dragController.startDragListener(inView: view)
|
dragController.startDragListener(inView: view)
|
||||||
dragController.insets = dragBoundInsets
|
dragController.insets = dragBoundInsets
|
||||||
|
|
||||||
// add single tap gesture recognition for displaying exit PiP UI
|
// add single tap gesture recognition for displaying exit PiP UI
|
||||||
let exitSelector = #selector(toggleExitPiP)
|
let exitSelector = #selector(toggleExitPiP)
|
||||||
let tapGestureRecognizer = UITapGestureRecognizer(target: self,
|
let tapGestureRecognizer = UITapGestureRecognizer(target: self,
|
||||||
|
@ -111,36 +111,36 @@ public class PiPViewCoordinator {
|
||||||
self.tapGestureRecognizer = tapGestureRecognizer
|
self.tapGestureRecognizer = tapGestureRecognizer
|
||||||
view.addGestureRecognizer(tapGestureRecognizer)
|
view.addGestureRecognizer(tapGestureRecognizer)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Exit Picture in picture mode, this will resize view, remove
|
/// Exit Picture in picture mode, this will resize view, remove
|
||||||
/// exit pip button, and disable the drag gesture
|
/// exit pip button, and disable the drag gesture
|
||||||
@objc public func exitPictureInPicture() {
|
@objc public func exitPictureInPicture() {
|
||||||
isInPiP = false
|
isInPiP = false
|
||||||
animateViewChange()
|
animateViewChange()
|
||||||
dragController.stopDragListener()
|
dragController.stopDragListener()
|
||||||
|
|
||||||
// hide PiP UI
|
// hide PiP UI
|
||||||
exitPiPButton?.removeFromSuperview()
|
exitPiPButton?.removeFromSuperview()
|
||||||
exitPiPButton = nil
|
exitPiPButton = nil
|
||||||
|
|
||||||
// remove gesture
|
// remove gesture
|
||||||
let exitSelector = #selector(toggleExitPiP)
|
let exitSelector = #selector(toggleExitPiP)
|
||||||
tapGestureRecognizer?.removeTarget(self, action: exitSelector)
|
tapGestureRecognizer?.removeTarget(self, action: exitSelector)
|
||||||
tapGestureRecognizer = nil
|
tapGestureRecognizer = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reset view to provide bounds, use this method on rotation or
|
/// Reset view to provide bounds, use this method on rotation or
|
||||||
/// screen size changes
|
/// screen size changes
|
||||||
public func resetBounds(bounds: CGRect) {
|
public func resetBounds(bounds: CGRect) {
|
||||||
currentBounds = bounds
|
currentBounds = bounds
|
||||||
exitPictureInPicture()
|
exitPictureInPicture()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stop the dragging gesture of the root view
|
/// Stop the dragging gesture of the root view
|
||||||
public func stopDragGesture() {
|
public func stopDragGesture() {
|
||||||
dragController.stopDragListener()
|
dragController.stopDragListener()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Customize the presentation of exit pip button
|
/// Customize the presentation of exit pip button
|
||||||
open func configureExitPiPButton(target: Any,
|
open func configureExitPiPButton(target: Any,
|
||||||
action: Selector) -> UIButton {
|
action: Selector) -> UIButton {
|
||||||
|
@ -157,9 +157,9 @@ public class PiPViewCoordinator {
|
||||||
button.addTarget(target, action: action, for: .touchUpInside)
|
button.addTarget(target, action: action, for: .touchUpInside)
|
||||||
return button
|
return button
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Interactions
|
// MARK: - Interactions
|
||||||
|
|
||||||
@objc private func toggleExitPiP() {
|
@objc private func toggleExitPiP() {
|
||||||
if exitPiPButton == nil {
|
if exitPiPButton == nil {
|
||||||
// show button
|
// show button
|
||||||
|
@ -168,30 +168,30 @@ public class PiPViewCoordinator {
|
||||||
action: exitSelector)
|
action: exitSelector)
|
||||||
view.addSubview(button)
|
view.addSubview(button)
|
||||||
exitPiPButton = button
|
exitPiPButton = button
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// hide button
|
// hide button
|
||||||
exitPiPButton?.removeFromSuperview()
|
exitPiPButton?.removeFromSuperview()
|
||||||
exitPiPButton = nil
|
exitPiPButton = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Size calculation
|
// MARK: - Size calculation
|
||||||
|
|
||||||
private func animateViewChange() {
|
private func animateViewChange() {
|
||||||
UIView.animate(withDuration: 0.25) {
|
UIView.animate(withDuration: 0.25) {
|
||||||
self.view.frame = self.changeViewRect()
|
self.view.frame = self.changeViewRect()
|
||||||
self.view.setNeedsLayout()
|
self.view.setNeedsLayout()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func changeViewRect() -> CGRect {
|
private func changeViewRect() -> CGRect {
|
||||||
let bounds = currentBounds
|
let bounds = currentBounds
|
||||||
|
|
||||||
guard isInPiP else {
|
guard isInPiP else {
|
||||||
return bounds
|
return bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
// resize to suggested ratio and position to the bottom right
|
// resize to suggested ratio and position to the bottom right
|
||||||
let adjustedBounds = UIEdgeInsetsInsetRect(bounds, dragBoundInsets)
|
let adjustedBounds = UIEdgeInsetsInsetRect(bounds, dragBoundInsets)
|
||||||
let size = CGSize(width: bounds.size.width * pipSizeRatio,
|
let size = CGSize(width: bounds.size.width * pipSizeRatio,
|
||||||
|
@ -200,9 +200,9 @@ public class PiPViewCoordinator {
|
||||||
let y: CGFloat = adjustedBounds.maxY - size.height
|
let y: CGFloat = adjustedBounds.maxY - size.height
|
||||||
return CGRect(x: x, y: y, width: size.width, height: size.height)
|
return CGRect(x: x, y: y, width: size.width, height: size.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Animation helpers
|
// MARK: - Animation helpers
|
||||||
|
|
||||||
private func animateTransition(animations: @escaping () -> Void,
|
private func animateTransition(animations: @escaping () -> Void,
|
||||||
completion: AnimationCompletion?) {
|
completion: AnimationCompletion?) {
|
||||||
UIView.animate(withDuration: 0.1,
|
UIView.animate(withDuration: 0.1,
|
||||||
|
@ -211,5 +211,5 @@ public class PiPViewCoordinator {
|
||||||
animations: animations,
|
animations: animations,
|
||||||
completion: completion)
|
completion: completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,7 @@
|
||||||
|
|
||||||
import uuid from 'uuid';
|
import uuid from 'uuid';
|
||||||
|
|
||||||
import {
|
import { createTrackMutedEvent, sendAnalytics } from '../../analytics';
|
||||||
createTrackMutedEvent,
|
|
||||||
sendAnalytics
|
|
||||||
} from '../../analytics';
|
|
||||||
import {
|
import {
|
||||||
APP_WILL_MOUNT,
|
APP_WILL_MOUNT,
|
||||||
APP_WILL_UNMOUNT,
|
APP_WILL_UNMOUNT,
|
||||||
|
@ -29,16 +26,13 @@ import {
|
||||||
setAudioMuted
|
setAudioMuted
|
||||||
} from '../../base/media';
|
} from '../../base/media';
|
||||||
import { MiddlewareRegistry } from '../../base/redux';
|
import { MiddlewareRegistry } from '../../base/redux';
|
||||||
import {
|
import { TRACK_CREATE_ERROR, isLocalTrackMuted } from '../../base/tracks';
|
||||||
TRACK_CREATE_ERROR,
|
|
||||||
isLocalTrackMuted
|
|
||||||
} from '../../base/tracks';
|
|
||||||
|
|
||||||
import { _SET_CALLKIT_SUBSCRIPTIONS } from './actionTypes';
|
import { _SET_CALLKIT_SUBSCRIPTIONS } from './actionTypes';
|
||||||
import CallKit from './CallKit';
|
import CallKit from './CallKit';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Middleware that captures several system actions and hooks up CallKit.
|
* Middleware that captures system actions and hooks up CallKit.
|
||||||
*
|
*
|
||||||
* @param {Store} store - The redux store.
|
* @param {Store} store - The redux store.
|
||||||
* @returns {Function}
|
* @returns {Function}
|
||||||
|
@ -84,17 +78,17 @@ CallKit && MiddlewareRegistry.register(store => next => action => {
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies the feature jwt that the action {@link APP_WILL_MOUNT} is being
|
* Notifies the feature callkit that the action {@link APP_WILL_MOUNT} is being
|
||||||
* dispatched within a specific redux {@code store}.
|
* dispatched within a specific redux {@code store}.
|
||||||
*
|
*
|
||||||
* @param {Store} store - The redux store in which the specified {@code action}
|
* @param {Store} store - The redux store in which the specified {@code action}
|
||||||
* is being dispatched.
|
* is being dispatched.
|
||||||
* @param {Dispatch} next - The redux dispatch function to dispatch the
|
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
|
||||||
* specified {@code action} to the specified {@code store}.
|
* specified {@code action} in the specified {@code store}.
|
||||||
* @param {Action} action - The redux action {@code APP_WILL_MOUNT} which is
|
* @param {Action} action - The redux action {@code APP_WILL_MOUNT} which is
|
||||||
* being dispatched in the specified {@code store}.
|
* being dispatched in the specified {@code store}.
|
||||||
* @private
|
* @private
|
||||||
* @returns {*}
|
* @returns {*} The value returned by {@code next(action)}.
|
||||||
*/
|
*/
|
||||||
function _appWillMount({ dispatch, getState }, next, action) {
|
function _appWillMount({ dispatch, getState }, next, action) {
|
||||||
const result = next(action);
|
const result = next(action);
|
||||||
|
@ -119,8 +113,8 @@ function _appWillMount({ dispatch, getState }, next, action) {
|
||||||
context),
|
context),
|
||||||
|
|
||||||
// According to CallKit's documentation, when the system resets we
|
// According to CallKit's documentation, when the system resets we
|
||||||
// should terminate all calls. Hence, providerDidReset is the same
|
// should terminate all calls. Hence, providerDidReset is the same to us
|
||||||
// to us as performEndCallAction.
|
// as performEndCallAction.
|
||||||
CallKit.addListener(
|
CallKit.addListener(
|
||||||
'providerDidReset',
|
'providerDidReset',
|
||||||
_onPerformEndCallAction,
|
_onPerformEndCallAction,
|
||||||
|
@ -136,17 +130,17 @@ function _appWillMount({ dispatch, getState }, next, action) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies the feature jwt that the action {@link CONFERENCE_FAILED} is being
|
* Notifies the feature callkit that the action {@link CONFERENCE_FAILED} is
|
||||||
* dispatched within a specific redux {@code store}.
|
* being dispatched within a specific redux {@code store}.
|
||||||
*
|
*
|
||||||
* @param {Store} store - The redux store in which the specified {@code action}
|
* @param {Store} store - The redux store in which the specified {@code action}
|
||||||
* is being dispatched.
|
* is being dispatched.
|
||||||
* @param {Dispatch} next - The redux dispatch function to dispatch the
|
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
|
||||||
* specified {@code action} to the specified {@code store}.
|
* specified {@code action} in the specified {@code store}.
|
||||||
* @param {Action} action - The redux action {@code CONFERENCE_FAILED} which is
|
* @param {Action} action - The redux action {@code CONFERENCE_FAILED} which is
|
||||||
* being dispatched in the specified {@code store}.
|
* being dispatched in the specified {@code store}.
|
||||||
* @private
|
* @private
|
||||||
* @returns {*}
|
* @returns {*} The value returned by {@code next(action)}.
|
||||||
*/
|
*/
|
||||||
function _conferenceFailed(store, next, action) {
|
function _conferenceFailed(store, next, action) {
|
||||||
const result = next(action);
|
const result = next(action);
|
||||||
|
@ -166,17 +160,17 @@ function _conferenceFailed(store, next, action) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies the feature jwt that the action {@link CONFERENCE_JOINED} is being
|
* Notifies the feature callkit that the action {@link CONFERENCE_JOINED} is
|
||||||
* dispatched within a specific redux {@code store}.
|
* being dispatched within a specific redux {@code store}.
|
||||||
*
|
*
|
||||||
* @param {Store} store - The redux store in which the specified {@code action}
|
* @param {Store} store - The redux store in which the specified {@code action}
|
||||||
* is being dispatched.
|
* is being dispatched.
|
||||||
* @param {Dispatch} next - The redux dispatch function to dispatch the
|
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
|
||||||
* specified {@code action} to the specified {@code store}.
|
* specified {@code action} in the specified {@code store}.
|
||||||
* @param {Action} action - The redux action {@code CONFERENCE_JOINED} which is
|
* @param {Action} action - The redux action {@code CONFERENCE_JOINED} which is
|
||||||
* being dispatched in the specified {@code store}.
|
* being dispatched in the specified {@code store}.
|
||||||
* @private
|
* @private
|
||||||
* @returns {*}
|
* @returns {*} The value returned by {@code next(action)}.
|
||||||
*/
|
*/
|
||||||
function _conferenceJoined(store, next, action) {
|
function _conferenceJoined(store, next, action) {
|
||||||
const result = next(action);
|
const result = next(action);
|
||||||
|
@ -191,17 +185,17 @@ function _conferenceJoined(store, next, action) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies the feature jwt that the action {@link CONFERENCE_LEFT} is being
|
* Notifies the feature callkit that the action {@link CONFERENCE_LEFT} is being
|
||||||
* dispatched within a specific redux {@code store}.
|
* dispatched within a specific redux {@code store}.
|
||||||
*
|
*
|
||||||
* @param {Store} store - The redux store in which the specified {@code action}
|
* @param {Store} store - The redux store in which the specified {@code action}
|
||||||
* is being dispatched.
|
* is being dispatched.
|
||||||
* @param {Dispatch} next - The redux dispatch function to dispatch the
|
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
|
||||||
* specified {@code action} to the specified {@code store}.
|
* specified {@code action} in the specified {@code store}.
|
||||||
* @param {Action} action - The redux action {@code CONFERENCE_LEFT} which is
|
* @param {Action} action - The redux action {@code CONFERENCE_LEFT} which is
|
||||||
* being dispatched in the specified {@code store}.
|
* being dispatched in the specified {@code store}.
|
||||||
* @private
|
* @private
|
||||||
* @returns {*}
|
* @returns {*} The value returned by {@code next(action)}.
|
||||||
*/
|
*/
|
||||||
function _conferenceLeft(store, next, action) {
|
function _conferenceLeft(store, next, action) {
|
||||||
const result = next(action);
|
const result = next(action);
|
||||||
|
@ -216,29 +210,29 @@ function _conferenceLeft(store, next, action) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies the feature jwt that the action {@link CONFERENCE_WILL_JOIN} is
|
* Notifies the feature callkit that the action {@link CONFERENCE_WILL_JOIN} is
|
||||||
* being dispatched within a specific redux {@code store}.
|
* being dispatched within a specific redux {@code store}.
|
||||||
*
|
*
|
||||||
* @param {Store} store - The redux store in which the specified {@code action}
|
* @param {Store} store - The redux store in which the specified {@code action}
|
||||||
* is being dispatched.
|
* is being dispatched.
|
||||||
* @param {Dispatch} next - The redux dispatch function to dispatch the
|
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
|
||||||
* specified {@code action} to the specified {@code store}.
|
* specified {@code action} in the specified {@code store}.
|
||||||
* @param {Action} action - The redux action {@code CONFERENCE_WILL_JOIN} which
|
* @param {Action} action - The redux action {@code CONFERENCE_WILL_JOIN} which
|
||||||
* is being dispatched in the specified {@code store}.
|
* is being dispatched in the specified {@code store}.
|
||||||
* @private
|
* @private
|
||||||
* @returns {*}
|
* @returns {*} The value returned by {@code next(action)}.
|
||||||
*/
|
*/
|
||||||
function _conferenceWillJoin({ getState }, next, action) {
|
function _conferenceWillJoin({ getState }, next, action) {
|
||||||
const result = next(action);
|
const result = next(action);
|
||||||
|
|
||||||
const { conference } = action;
|
const { conference } = action;
|
||||||
const state = getState();
|
const state = getState();
|
||||||
|
const { callUUID } = state['features/base/config'];
|
||||||
const url = getInviteURL(state);
|
const url = getInviteURL(state);
|
||||||
const hasVideo = !isVideoMutedByAudioOnly(state);
|
const hasVideo = !isVideoMutedByAudioOnly(state);
|
||||||
const { callUUID } = state['features/base/config'];
|
|
||||||
|
|
||||||
// When assigning the call UUID, do so in upper case, since iOS will
|
// When assigning the call UUID, do so in upper case, since iOS will return
|
||||||
// return it upper cased.
|
// it upper cased.
|
||||||
conference.callUUID = (callUUID || uuid.v4()).toUpperCase();
|
conference.callUUID = (callUUID || uuid.v4()).toUpperCase();
|
||||||
|
|
||||||
CallKit.startCall(conference.callUUID, url.toString(), hasVideo)
|
CallKit.startCall(conference.callUUID, url.toString(), hasVideo)
|
||||||
|
@ -306,17 +300,17 @@ function _onPerformSetMutedCallAction({ callUUID, muted: newValue }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies the feature jwt that the action {@link SET_AUDIO_MUTED} is being
|
* Notifies the feature callkit that the action {@link SET_AUDIO_MUTED} is being
|
||||||
* dispatched within a specific redux {@code store}.
|
* dispatched within a specific redux {@code store}.
|
||||||
*
|
*
|
||||||
* @param {Store} store - The redux store in which the specified {@code action}
|
* @param {Store} store - The redux store in which the specified {@code action}
|
||||||
* is being dispatched.
|
* is being dispatched.
|
||||||
* @param {Dispatch} next - The redux dispatch function to dispatch the
|
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
|
||||||
* specified {@code action} to the specified {@code store}.
|
* specified {@code action} in the specified {@code store}.
|
||||||
* @param {Action} action - The redux action {@code SET_AUDIO_MUTED} which is
|
* @param {Action} action - The redux action {@code SET_AUDIO_MUTED} which is
|
||||||
* being dispatched in the specified {@code store}.
|
* being dispatched in the specified {@code store}.
|
||||||
* @private
|
* @private
|
||||||
* @returns {*}
|
* @returns {*} The value returned by {@code next(action)}.
|
||||||
*/
|
*/
|
||||||
function _setAudioMuted({ getState }, next, action) {
|
function _setAudioMuted({ getState }, next, action) {
|
||||||
const result = next(action);
|
const result = next(action);
|
||||||
|
@ -331,17 +325,18 @@ function _setAudioMuted({ getState }, next, action) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies the feature jwt that the action {@link _SET_CALLKIT_SUBSCRIPTIONS}
|
* Notifies the feature callkit that the action
|
||||||
* is being dispatched within a specific redux {@code store}.
|
* {@link _SET_CALLKIT_SUBSCRIPTIONS} is being dispatched within a specific
|
||||||
|
* redux {@code store}.
|
||||||
*
|
*
|
||||||
* @param {Store} store - The redux store in which the specified {@code action}
|
* @param {Store} store - The redux store in which the specified {@code action}
|
||||||
* is being dispatched.
|
* is being dispatched.
|
||||||
* @param {Dispatch} next - The redux dispatch function to dispatch the
|
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
|
||||||
* specified {@code action} to the specified {@code store}.
|
* specified {@code action} in the specified {@code store}.
|
||||||
* @param {Action} action - The redux action {@code _SET_CALLKIT_SUBSCRIPTIONS}
|
* @param {Action} action - The redux action {@code _SET_CALLKIT_SUBSCRIPTIONS}
|
||||||
* which is being dispatched in the specified {@code store}.
|
* which is being dispatched in the specified {@code store}.
|
||||||
* @private
|
* @private
|
||||||
* @returns {*}
|
* @returns {*} The value returned by {@code next(action)}.
|
||||||
*/
|
*/
|
||||||
function _setCallKitSubscriptions({ getState }, next, action) {
|
function _setCallKitSubscriptions({ getState }, next, action) {
|
||||||
const { subscriptions } = getState()['features/callkit'];
|
const { subscriptions } = getState()['features/callkit'];
|
||||||
|
@ -356,17 +351,17 @@ function _setCallKitSubscriptions({ getState }, next, action) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies the feature jwt that the action {@link SET_VIDEO_MUTED} is being
|
* Notifies the feature callkit that the action {@link SET_VIDEO_MUTED} is being
|
||||||
* dispatched within a specific redux {@code store}.
|
* dispatched within a specific redux {@code store}.
|
||||||
*
|
*
|
||||||
* @param {Store} store - The redux store in which the specified {@code action}
|
* @param {Store} store - The redux store in which the specified {@code action}
|
||||||
* is being dispatched.
|
* is being dispatched.
|
||||||
* @param {Dispatch} next - The redux dispatch function to dispatch the
|
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
|
||||||
* specified {@code action} to the specified {@code store}.
|
* specified {@code action} in the specified {@code store}.
|
||||||
* @param {Action} action - The redux action {@code SET_VIDEO_MUTED} which is
|
* @param {Action} action - The redux action {@code SET_VIDEO_MUTED} which is
|
||||||
* being dispatched in the specified {@code store}.
|
* being dispatched in the specified {@code store}.
|
||||||
* @private
|
* @private
|
||||||
* @returns {*}
|
* @returns {*} The value returned by {@code next(action)}.
|
||||||
*/
|
*/
|
||||||
function _setVideoMuted({ getState }, next, action) {
|
function _setVideoMuted({ getState }, next, action) {
|
||||||
const result = next(action);
|
const result = next(action);
|
||||||
|
@ -385,17 +380,17 @@ function _setVideoMuted({ getState }, next, action) {
|
||||||
/**
|
/**
|
||||||
* Handles a track creation failure. This is relevant to us in the following
|
* Handles a track creation failure. This is relevant to us in the following
|
||||||
* (corner) case: if the user never gave their permission to use the microphone
|
* (corner) case: if the user never gave their permission to use the microphone
|
||||||
* and try to unmute from the CallKit interface, this will fail, and we need
|
* and try to unmute from the CallKit interface, this will fail, and we need to
|
||||||
* to sync back the CallKit button state.
|
* sync back the CallKit button state.
|
||||||
*
|
*
|
||||||
* @param {Store} store - The redux store in which the specified {@code action}
|
* @param {Store} store - The redux store in which the specified {@code action}
|
||||||
* is being dispatched.
|
* is being dispatched.
|
||||||
* @param {Dispatch} next - The redux dispatch function to dispatch the
|
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
|
||||||
* specified {@code action} to the specified {@code store}.
|
* specified {@code action} in the specified {@code store}.
|
||||||
* @param {Action} action - The redux action {@code TRACK_CREARE_ERROR} which is
|
* @param {Action} action - The redux action {@code TRACK_CREARE_ERROR} which is
|
||||||
* being dispatched in the specified {@code store}.
|
* being dispatched in the specified {@code store}.
|
||||||
* @private
|
* @private
|
||||||
* @returns {*}
|
* @returns {*} The value returned by {@code next(action)}.
|
||||||
*/
|
*/
|
||||||
function _trackCreateError({ getState }, next, action) {
|
function _trackCreateError({ getState }, next, action) {
|
||||||
const result = next(action);
|
const result = next(action);
|
||||||
|
|
Loading…
Reference in New Issue