Implement Jitsi meet presentation interface that supports custom PiP solution
This commit is contained in:
parent
181580871f
commit
f8163de765
|
@ -7,8 +7,9 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
C6245F57205044120040BE68 /* JitsiMeet.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C6F99C4F204DE79F0001F710 /* JitsiMeet.framework */; };
|
||||
C6245F58205044150040BE68 /* WebRTC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C6A34247204DF18000E062DD /* WebRTC.framework */; };
|
||||
C6A34249204DF18000E062DD /* WebRTC.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C6A34247204DF18000E062DD /* WebRTC.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
C6A3424C204DF98E00E062DD /* JitsiViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3424B204DF98E00E062DD /* JitsiViewController.swift */; };
|
||||
C6F99C3B204DE6BE0001F710 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F99C3A204DE6BE0001F710 /* AppDelegate.swift */; };
|
||||
C6F99C3D204DE6BE0001F710 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F99C3C204DE6BE0001F710 /* ViewController.swift */; };
|
||||
C6F99C40204DE6BE0001F710 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C6F99C3E204DE6BE0001F710 /* Main.storyboard */; };
|
||||
|
@ -34,7 +35,6 @@
|
|||
|
||||
/* Begin PBXFileReference section */
|
||||
C6A34247204DF18000E062DD /* WebRTC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebRTC.framework; path = "../../node_modules/react-native-webrtc/ios/WebRTC.framework"; sourceTree = "<group>"; };
|
||||
C6A3424B204DF98E00E062DD /* JitsiViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JitsiViewController.swift; sourceTree = "<group>"; };
|
||||
C6F99C37204DE6BE0001F710 /* PiPApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PiPApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C6F99C3A204DE6BE0001F710 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
C6F99C3C204DE6BE0001F710 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
||||
|
@ -50,6 +50,8 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C6245F57205044120040BE68 /* JitsiMeet.framework in Frameworks */,
|
||||
C6245F58205044150040BE68 /* WebRTC.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -78,7 +80,6 @@
|
|||
children = (
|
||||
C6F99C3A204DE6BE0001F710 /* AppDelegate.swift */,
|
||||
C6F99C3C204DE6BE0001F710 /* ViewController.swift */,
|
||||
C6A3424B204DF98E00E062DD /* JitsiViewController.swift */,
|
||||
C6F99C3E204DE6BE0001F710 /* Main.storyboard */,
|
||||
C6F99C41204DE6BE0001F710 /* Assets.xcassets */,
|
||||
C6F99C43204DE6BE0001F710 /* LaunchScreen.storyboard */,
|
||||
|
@ -103,12 +104,13 @@
|
|||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = C6F99C49204DE6BE0001F710 /* Build configuration list for PBXNativeTarget "PiPApp" */;
|
||||
buildPhases = (
|
||||
C6A3424A204DF91D00E062DD /* Run Adjust ATS for loading JS bundle */,
|
||||
C6F99C62204DEFFE0001F710 /* Run React Packager */,
|
||||
C6F99C33204DE6BE0001F710 /* Sources */,
|
||||
C6F99C34204DE6BE0001F710 /* Frameworks */,
|
||||
C6F99C61204DEDC20001F710 /* Embed Frameworks */,
|
||||
C6F99C35204DE6BE0001F710 /* Resources */,
|
||||
C6F99C62204DEFFE0001F710 /* Run React Packager */,
|
||||
C6A3424A204DF91D00E062DD /* Run Adjust ATS for loading JS bundle */,
|
||||
C6F99C61204DEDC20001F710 /* Embed Frameworks */,
|
||||
C6A3426E20503ECC00E062DD /* Adjust embedded framework architectures */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
|
@ -132,6 +134,11 @@
|
|||
C6F99C36204DE6BE0001F710 = {
|
||||
CreatedOnToolsVersion = 9.2;
|
||||
ProvisioningStyle = Automatic;
|
||||
SystemCapabilities = {
|
||||
com.apple.BackgroundModes = {
|
||||
enabled = 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -181,6 +188,20 @@
|
|||
shellPath = /bin/sh;
|
||||
shellScript = "../scripts/fixup-ats.sh";
|
||||
};
|
||||
C6A3426E20503ECC00E062DD /* Adjust embedded framework architectures */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Adjust embedded framework architectures";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "../scripts/fixup-frameworks.sh";
|
||||
};
|
||||
C6F99C62204DEFFE0001F710 /* Run React Packager */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -204,7 +225,6 @@
|
|||
files = (
|
||||
C6F99C3D204DE6BE0001F710 /* ViewController.swift in Sources */,
|
||||
C6F99C3B204DE6BE0001F710 /* AppDelegate.swift in Sources */,
|
||||
C6A3424C204DF98E00E062DD /* JitsiViewController.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -342,7 +362,9 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
FRAMEWORK_SEARCH_PATHS = "../../node_modules/react-native-webrtc/ios";
|
||||
INFOPLIST_FILE = src/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.jitsi.PiPApp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -356,7 +378,9 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
FRAMEWORK_SEARCH_PATHS = "../../node_modules/react-native-webrtc/ios";
|
||||
INFOPLIST_FILE = src/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.jitsi.PiPApp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="QxY-C8-fwD" firstAttribute="centerX" secondItem="6Tk-OE-BBY" secondAttribute="centerX" id="6a6-l1-7Ct"/>
|
||||
<constraint firstItem="QxY-C8-fwD" firstAttribute="centerY" secondItem="6Tk-OE-BBY" secondAttribute="centerY" id="Hfg-TH-0g2"/>
|
||||
|
@ -41,20 +42,5 @@
|
|||
</objects>
|
||||
<point key="canvasLocation" x="32.799999999999997" y="658.92053973013503"/>
|
||||
</scene>
|
||||
<!--JitsiViewController-->
|
||||
<scene sceneID="V5C-nQ-uqb">
|
||||
<objects>
|
||||
<viewController id="B84-hY-B21" userLabel="JitsiViewController" customClass="JitsiViewController" customModule="PiPApp" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="nMf-dX-t08" customClass="JitsiMeetView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<viewLayoutGuide key="safeArea" id="W4q-U5-6O5"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Zdh-Kt-gVo" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1023" y="643"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
|
|
|
@ -20,6 +20,10 @@
|
|||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>audio</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
|
@ -34,6 +38,19 @@
|
|||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSExceptionDomains</key>
|
||||
<dict>
|
||||
<key>localhost</key>
|
||||
<dict>
|
||||
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
//
|
||||
// JitsiViewController.swift
|
||||
// PiPApp
|
||||
//
|
||||
// Created by Daniel Ornelas on 3/5/18.
|
||||
// Copyright © 2018 Atlassian Inc. All rights reserved.
|
||||
//
|
||||
|
||||
import JitsiMeet
|
||||
import UIKit
|
||||
|
||||
final class JitsiViewController: UIViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
guard let jitsiView = self.view as? JitsiMeetView else { return }
|
||||
|
||||
jitsiView.welcomePageEnabled = true
|
||||
jitsiView.load(nil)
|
||||
|
||||
// TODO: delete me, this is only testing access to swift object in SDK
|
||||
let jitsiManager = JitsiManager()
|
||||
jitsiManager.testMe()
|
||||
}
|
||||
}
|
|
@ -13,6 +13,8 @@ class ViewController: UIViewController {
|
|||
|
||||
@IBOutlet weak var videoButton: UIButton?
|
||||
|
||||
private var jitsiMeetManager: JitsiMeetManager?
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
}
|
||||
|
@ -20,7 +22,10 @@ class ViewController: UIViewController {
|
|||
// MARK: - Actions
|
||||
|
||||
@IBAction func startMeeting(sender: Any?) {
|
||||
print("test")
|
||||
//let url = URL(string: "")
|
||||
self.jitsiMeetManager = JitsiMeetManager()
|
||||
jitsiMeetManager?.welcomeScreenEnabled = true
|
||||
jitsiMeetManager?.load(withUrl: nil)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,7 +27,10 @@
|
|||
0BCA496C1EC4BBF900B793EE /* jitsi.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0BCA496B1EC4BBF900B793EE /* jitsi.ttf */; };
|
||||
0BD906EA1EC0C00300C8C18E /* JitsiMeet.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BD906E81EC0C00300C8C18E /* JitsiMeet.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
0F65EECE1D95DA94561BB47E /* libPods-JitsiMeet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 03F2ADC957FF109849B7FCA1 /* libPods-JitsiMeet.a */; };
|
||||
C6F99C14204DB63E0001F710 /* JitsiManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F99C12204DB63D0001F710 /* JitsiManager.swift */; };
|
||||
C6A3425F204EF76800E062DD /* JitsiMeetWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425C204EF76800E062DD /* JitsiMeetWindow.swift */; };
|
||||
C6A34260204EF76800E062DD /* JitsiMeetManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425D204EF76800E062DD /* JitsiMeetManager.swift */; };
|
||||
C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425E204EF76800E062DD /* DragGestureController.swift */; };
|
||||
C6A3426D204F1C3300E062DD /* JitsiMeetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3426C204F1C3300E062DD /* JitsiMeetViewController.swift */; };
|
||||
C6F99C15204DB63E0001F710 /* JitsiMeetView+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
|
@ -57,7 +60,10 @@
|
|||
0BD906E91EC0C00300C8C18E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
98E09B5C73D9036B4ED252FC /* Pods-JitsiMeet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
9C77CA3CC919B081F1A52982 /* Pods-JitsiMeet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.release.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.release.xcconfig"; sourceTree = "<group>"; };
|
||||
C6F99C12204DB63D0001F710 /* JitsiManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JitsiManager.swift; sourceTree = "<group>"; };
|
||||
C6A3425C204EF76800E062DD /* JitsiMeetWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JitsiMeetWindow.swift; sourceTree = "<group>"; };
|
||||
C6A3425D204EF76800E062DD /* JitsiMeetManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JitsiMeetManager.swift; sourceTree = "<group>"; };
|
||||
C6A3425E204EF76800E062DD /* DragGestureController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DragGestureController.swift; sourceTree = "<group>"; };
|
||||
C6A3426C204F1C3300E062DD /* JitsiMeetViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JitsiMeetViewController.swift; sourceTree = "<group>"; };
|
||||
C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetView+Private.h"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
|
@ -107,13 +113,13 @@
|
|||
0BD906E71EC0C00300C8C18E /* src */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C6A3426B204F127900E062DD /* JitsiMeetManager */,
|
||||
0BCA495C1EC4B6C600B793EE /* AudioMode.m */,
|
||||
0BB9AD7C1F60356D001C08DB /* AppInfo.m */,
|
||||
0BB9AD7A1F5EC8F4001C08DB /* CallKit.m */,
|
||||
0BA13D301EE83FF8007BEF7F /* ExternalAPI.m */,
|
||||
0BD906E91EC0C00300C8C18E /* Info.plist */,
|
||||
0B7C2CFC200F51D60060D076 /* LaunchOptions.m */,
|
||||
C6F99C12204DB63D0001F710 /* JitsiManager.swift */,
|
||||
0BD906E81EC0C00300C8C18E /* JitsiMeet.h */,
|
||||
0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */,
|
||||
0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */,
|
||||
|
@ -149,6 +155,17 @@
|
|||
name = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C6A3426B204F127900E062DD /* JitsiMeetManager */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C6A3425C204EF76800E062DD /* JitsiMeetWindow.swift */,
|
||||
C6A3425D204EF76800E062DD /* JitsiMeetManager.swift */,
|
||||
C6A3426C204F1C3300E062DD /* JitsiMeetViewController.swift */,
|
||||
C6A3425E204EF76800E062DD /* DragGestureController.swift */,
|
||||
);
|
||||
path = JitsiMeetManager;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
|
@ -312,15 +329,18 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0BB9AD7B1F5EC8F4001C08DB /* CallKit.m in Sources */,
|
||||
C6A34260204EF76800E062DD /* JitsiMeetManager.swift in Sources */,
|
||||
0BB9AD7D1F60356D001C08DB /* AppInfo.m in Sources */,
|
||||
0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */,
|
||||
0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */,
|
||||
0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */,
|
||||
0B7C2CFD200F51D60060D076 /* LaunchOptions.m in Sources */,
|
||||
C6F99C14204DB63E0001F710 /* JitsiManager.swift in Sources */,
|
||||
C6A3426D204F1C3300E062DD /* JitsiMeetViewController.swift in Sources */,
|
||||
C6A3425F204EF76800E062DD /* JitsiMeetWindow.swift in Sources */,
|
||||
0BCA495F1EC4B6C600B793EE /* AudioMode.m in Sources */,
|
||||
0B44A0191F902126009D1D64 /* MPVolumeViewManager.m in Sources */,
|
||||
0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */,
|
||||
C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */,
|
||||
0B412F191EDEC65D00B1A0A6 /* JitsiMeetView.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
//
|
||||
// JitsiManager.swift
|
||||
// JitsiMeet
|
||||
//
|
||||
// Created by Daniel Ornelas on 3/5/18.
|
||||
// Copyright © 2018 Jitsi. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc(JitsiManager)
|
||||
public class JitsiManager: NSObject {
|
||||
|
||||
public func testMe() {
|
||||
print("hi there")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
// Copyright © 2018 Jitsi. All rights reserved.
|
||||
|
||||
final class DragGestureController {
|
||||
|
||||
var insets: UIEdgeInsets = UIEdgeInsets.zero
|
||||
|
||||
private var frameBeforeDragging: CGRect = CGRect.zero
|
||||
private weak var view: UIView?
|
||||
private lazy var panGesture: UIPanGestureRecognizer = {
|
||||
return UIPanGestureRecognizer(target: self, action: #selector(handlePan(gesture:)))
|
||||
}()
|
||||
|
||||
func startDragListener(inView view: UIView) {
|
||||
self.view = view
|
||||
view.addGestureRecognizer(panGesture)
|
||||
panGesture.isEnabled = true
|
||||
}
|
||||
|
||||
func stopDragListener() {
|
||||
panGesture.isEnabled = false
|
||||
view?.removeGestureRecognizer(panGesture)
|
||||
view = nil
|
||||
}
|
||||
|
||||
@objc private func handlePan(gesture: UIPanGestureRecognizer) {
|
||||
guard let view = self.view else { return }
|
||||
|
||||
let translation = gesture.translation(in: view.superview)
|
||||
let velocity = gesture.velocity(in: view.superview)
|
||||
var frame = frameBeforeDragging
|
||||
|
||||
switch gesture.state {
|
||||
case .began:
|
||||
frameBeforeDragging = view.frame
|
||||
|
||||
case .changed:
|
||||
frame.origin.x = floor(frame.origin.x + translation.x)
|
||||
frame.origin.y = floor(frame.origin.y + translation.y)
|
||||
view.frame = frame
|
||||
|
||||
case .ended:
|
||||
let currentPos = view.frame.origin
|
||||
let finalPos = calculateFinalPosition()
|
||||
|
||||
let distance = CGPoint(x: currentPos.x - finalPos.x,
|
||||
y: currentPos.y - finalPos.y)
|
||||
let distanceMagnitude = magnitude(vector: distance)
|
||||
let velocityMagnitude = magnitude(vector: velocity)
|
||||
let animationDuration = 0.5
|
||||
let initialSpringVelocity = velocityMagnitude / distanceMagnitude / CGFloat(animationDuration)
|
||||
|
||||
frame.origin = CGPoint(x: finalPos.x, y: finalPos.y)
|
||||
|
||||
UIView.animate(withDuration: animationDuration,
|
||||
delay: 0,
|
||||
usingSpringWithDamping: 0.9,
|
||||
initialSpringVelocity: initialSpringVelocity,
|
||||
options: .curveLinear,
|
||||
animations: {
|
||||
view.frame = frame
|
||||
}, completion: nil)
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private func calculateFinalPosition() -> CGPoint {
|
||||
guard
|
||||
let view = self.view,
|
||||
let bounds = view.superview?.frame
|
||||
else { return CGPoint.zero }
|
||||
|
||||
let currentSize = view.frame.size
|
||||
let adjustedBounds = UIEdgeInsetsInsetRect(bounds, insets)
|
||||
let threshold: CGFloat = 20.0
|
||||
let velocity = panGesture.velocity(in: view.superview)
|
||||
let location = panGesture.location(in: view.superview)
|
||||
|
||||
let goLeft: Bool
|
||||
if fabs(velocity.x) > threshold {
|
||||
goLeft = velocity.x < -threshold
|
||||
} else {
|
||||
goLeft = location.x < bounds.midX
|
||||
}
|
||||
|
||||
let goUp: Bool
|
||||
if fabs(velocity.y) > threshold {
|
||||
goUp = velocity.y < -threshold
|
||||
} else {
|
||||
goUp = location.y < bounds.midY
|
||||
}
|
||||
|
||||
let finalPosX: CGFloat = goLeft ? adjustedBounds.origin.x : bounds.size.width - insets.right - currentSize.width
|
||||
let finalPosY: CGFloat = goUp ? adjustedBounds.origin.y : bounds.size.height - insets.bottom - currentSize.height
|
||||
|
||||
return CGPoint(x: finalPosX, y: finalPosY)
|
||||
}
|
||||
|
||||
private func magnitude(vector: CGPoint) -> CGFloat {
|
||||
return sqrt(pow(vector.x, 2) + pow(vector.y, 2))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
// Copyright © 2018 Jitsi. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Creates and present a JitsiMeetView inside of an external window that can be dragged
|
||||
/// when minimized (if PiP mode is enabled)
|
||||
open class JitsiMeetManager: NSObject {
|
||||
|
||||
/// The Jitsi meet view delegate
|
||||
public weak var delegate: JitsiMeetViewDelegate? = nil
|
||||
/// Limits the boundries of meet view position on screen when minimized
|
||||
public var dragBoundInsets: UIEdgeInsets = UIEdgeInsets(top: 25, left: 5, bottom: 5, right: 5)
|
||||
/// Enables PiP mode for this jitsiMeet
|
||||
public var allowPiP: Bool = true
|
||||
/// The size ratio for jitsiMeetView when in PiP mode
|
||||
public var pipSizeRatio: CGFloat = 0.333
|
||||
/// Defines if welcome screen should be on
|
||||
public var welcomeScreenEnabled: Bool = false
|
||||
|
||||
fileprivate let dragController: DragGestureController = DragGestureController()
|
||||
|
||||
fileprivate lazy var meetViewController: JitsiMeetViewController = { return self.makeMeetViewController() }()
|
||||
fileprivate lazy var meetWindow: JitsiMeetWindow = { return self.makeMeetWindow() }()
|
||||
fileprivate var meetingInPiP: Bool = false
|
||||
|
||||
/// Presents and loads a jitsi meet view
|
||||
///
|
||||
/// - Parameter url: The url of the presentation
|
||||
public func load(withUrl url: URL?) {
|
||||
meetWindow.show()
|
||||
meetViewController.jitsiMeetView.load(url)
|
||||
}
|
||||
|
||||
/// Presents and loads a jitsi meet view with configuration
|
||||
///
|
||||
/// - Parameter urlObject: A dictionary of keys to be used for configuration
|
||||
public func load(withUrlObject urlObject: [AnyHashable : Any]?) {
|
||||
meetWindow.show()
|
||||
meetViewController.jitsiMeetView.loadURLObject(urlObject)
|
||||
}
|
||||
|
||||
// MARK: - Manage PiP switching
|
||||
|
||||
// update size animation
|
||||
fileprivate func updateMeetViewSize(isPiP: Bool) {
|
||||
UIView.animate(withDuration: 0.25) {
|
||||
self.meetViewController.view.frame = self.meetViewRect(isPiP: isPiP)
|
||||
self.meetViewController.view.setNeedsLayout()
|
||||
}
|
||||
}
|
||||
|
||||
private func meetViewRect(isPiP: Bool) -> CGRect {
|
||||
guard isPiP else {
|
||||
return meetWindow.bounds
|
||||
}
|
||||
let bounds = meetWindow.bounds
|
||||
|
||||
// resize to suggested ratio and position to the bottom right
|
||||
let adjustedBounds = UIEdgeInsetsInsetRect(bounds, dragBoundInsets)
|
||||
let size = CGSize(width: bounds.size.width * pipSizeRatio,
|
||||
height: bounds.size.height * pipSizeRatio)
|
||||
let x: CGFloat = adjustedBounds.maxX - size.width
|
||||
let y: CGFloat = adjustedBounds.maxY - size.height
|
||||
return CGRect(x: x, y: y, width: size.width, height: size.height)
|
||||
}
|
||||
|
||||
// MARK: - helpers
|
||||
|
||||
fileprivate func cleanUp() {
|
||||
// TODO: more clean up work on this
|
||||
|
||||
dragController.stopDragListener()
|
||||
meetWindow.isHidden = true
|
||||
}
|
||||
|
||||
private func makeMeetViewController() -> JitsiMeetViewController {
|
||||
let vc = JitsiMeetViewController()
|
||||
vc.jitsiMeetView.delegate = self
|
||||
vc.jitsiMeetView.welcomePageEnabled = self.welcomeScreenEnabled
|
||||
vc.jitsiMeetView.pictureInPictureEnabled = self.allowPiP
|
||||
return vc
|
||||
}
|
||||
|
||||
private func makeMeetWindow() -> JitsiMeetWindow {
|
||||
let window = JitsiMeetWindow(frame: UIScreen.main.bounds)
|
||||
window.backgroundColor = .clear
|
||||
window.windowLevel = UIWindowLevelStatusBar + 100
|
||||
window.rootViewController = self.meetViewController
|
||||
return window
|
||||
}
|
||||
}
|
||||
|
||||
extension JitsiMeetManager: JitsiMeetViewDelegate {
|
||||
|
||||
public func conferenceWillJoin(_ data: [AnyHashable : Any]!) {
|
||||
DispatchQueue.main.async {
|
||||
self.delegate?.conferenceWillJoin!(data)
|
||||
}
|
||||
}
|
||||
|
||||
public func conferenceJoined(_ data: [AnyHashable : Any]!) {
|
||||
DispatchQueue.main.async {
|
||||
self.delegate?.conferenceJoined!(data)
|
||||
}
|
||||
}
|
||||
|
||||
public func conferenceWillLeave(_ data: [AnyHashable : Any]!) {
|
||||
DispatchQueue.main.async {
|
||||
self.delegate?.conferenceWillLeave!(data)
|
||||
}
|
||||
}
|
||||
|
||||
public func conferenceLeft(_ data: [AnyHashable : Any]!) {
|
||||
DispatchQueue.main.async {
|
||||
self.cleanUp()
|
||||
|
||||
self.delegate?.conferenceLeft!(data)
|
||||
}
|
||||
}
|
||||
|
||||
public func conferenceFailed(_ data: [AnyHashable : Any]!) {
|
||||
DispatchQueue.main.async {
|
||||
self.cleanUp()
|
||||
|
||||
self.delegate?.conferenceFailed!(data)
|
||||
}
|
||||
}
|
||||
|
||||
public func loadConfigError(_ data: [AnyHashable : Any]!) {
|
||||
DispatchQueue.main.async {
|
||||
self.delegate?.loadConfigError!(data)
|
||||
}
|
||||
}
|
||||
|
||||
public func enterPicture(inPicture data: [AnyHashable : Any]!) {
|
||||
DispatchQueue.main.async {
|
||||
self.dragController.startDragListener(inView: self.meetViewController.view)
|
||||
self.dragController.insets = self.dragBoundInsets
|
||||
|
||||
self.meetingInPiP = true
|
||||
self.updateMeetViewSize(isPiP: true)
|
||||
|
||||
self.delegate?.enterPicture!(inPicture: data)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright © 2018 Jitsi. All rights reserved.
|
||||
|
||||
|
||||
/// Wrapper ViewController of a JitsiMeetView
|
||||
///
|
||||
/// TODO: should consider refactor and move out several logic of the JitsiMeetView to
|
||||
/// this class
|
||||
open class JitsiMeetViewController: UIViewController {
|
||||
|
||||
private(set) var jitsiMeetView: JitsiMeetView = JitsiMeetView()
|
||||
|
||||
override open func loadView() {
|
||||
super.loadView()
|
||||
self.view = jitsiMeetView
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright © 2018 Jitsi. All rights reserved.
|
||||
|
||||
open class JitsiMeetWindow: UIWindow {
|
||||
|
||||
/// Help out to bubble up the gesture detection outside of the rootVC frame
|
||||
open override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
||||
guard let vc = rootViewController else {
|
||||
return super.point(inside: point, with: event)
|
||||
}
|
||||
return vc.view.frame.contains(point)
|
||||
}
|
||||
|
||||
/// animate in the window
|
||||
open func show() {
|
||||
if self.isHidden || self.alpha < 1 {
|
||||
self.isHidden = false
|
||||
self.alpha = 0
|
||||
|
||||
UIView.animate(
|
||||
withDuration: 0.1,
|
||||
delay: 0,
|
||||
options: .beginFromCurrentState,
|
||||
animations: {
|
||||
self.alpha = 1
|
||||
},
|
||||
completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
/// animate out the window
|
||||
open func hide() {
|
||||
if !self.isHidden || self.alpha > 0 {
|
||||
UIView.animate(
|
||||
withDuration: 0.1,
|
||||
delay: 0,
|
||||
options: .beginFromCurrentState,
|
||||
animations: {
|
||||
self.alpha = 0
|
||||
self.isHidden = true
|
||||
},
|
||||
completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
import { Platform } from '../../base/react';
|
||||
|
||||
import {
|
||||
ENTER_PICTURE_IN_PICTURE,
|
||||
_SET_EMITTER_SUBSCRIPTIONS
|
||||
|
@ -28,10 +30,12 @@ export function enterPictureInPicture() {
|
|||
&& (conference || joining)) {
|
||||
const { PictureInPicture } = NativeModules;
|
||||
const p
|
||||
= PictureInPicture
|
||||
= Platform.OS === 'android'
|
||||
? PictureInPicture
|
||||
? PictureInPicture.enterPictureInPicture()
|
||||
: Promise.reject(
|
||||
new Error('Picture-in-Picture not supported'));
|
||||
new Error('Picture-in-Picture not supported'))
|
||||
: Promise.resolve();
|
||||
|
||||
p.then(
|
||||
() => dispatch({ type: ENTER_PICTURE_IN_PICTURE }),
|
||||
|
|
Loading…
Reference in New Issue