浏览代码

Implement Jitsi meet presentation interface that supports custom PiP solution

master
Daniel Ornelas 7 年前
父节点
当前提交
f8163de765

+ 31
- 7
ios/example-pip-app/PiPApp.xcodeproj/project.pbxproj 查看文件

@@ -7,8 +7,9 @@
7 7
 	objects = {
8 8
 
9 9
 /* Begin PBXBuildFile section */
10
+		C6245F57205044120040BE68 /* JitsiMeet.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C6F99C4F204DE79F0001F710 /* JitsiMeet.framework */; };
11
+		C6245F58205044150040BE68 /* WebRTC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C6A34247204DF18000E062DD /* WebRTC.framework */; };
10 12
 		C6A34249204DF18000E062DD /* WebRTC.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C6A34247204DF18000E062DD /* WebRTC.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
11
-		C6A3424C204DF98E00E062DD /* JitsiViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3424B204DF98E00E062DD /* JitsiViewController.swift */; };
12 13
 		C6F99C3B204DE6BE0001F710 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F99C3A204DE6BE0001F710 /* AppDelegate.swift */; };
13 14
 		C6F99C3D204DE6BE0001F710 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F99C3C204DE6BE0001F710 /* ViewController.swift */; };
14 15
 		C6F99C40204DE6BE0001F710 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C6F99C3E204DE6BE0001F710 /* Main.storyboard */; };
@@ -34,7 +35,6 @@
34 35
 
35 36
 /* Begin PBXFileReference section */
36 37
 		C6A34247204DF18000E062DD /* WebRTC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebRTC.framework; path = "../../node_modules/react-native-webrtc/ios/WebRTC.framework"; sourceTree = "<group>"; };
37
-		C6A3424B204DF98E00E062DD /* JitsiViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JitsiViewController.swift; sourceTree = "<group>"; };
38 38
 		C6F99C37204DE6BE0001F710 /* PiPApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PiPApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
39 39
 		C6F99C3A204DE6BE0001F710 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
40 40
 		C6F99C3C204DE6BE0001F710 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
@@ -50,6 +50,8 @@
50 50
 			isa = PBXFrameworksBuildPhase;
51 51
 			buildActionMask = 2147483647;
52 52
 			files = (
53
+				C6245F57205044120040BE68 /* JitsiMeet.framework in Frameworks */,
54
+				C6245F58205044150040BE68 /* WebRTC.framework in Frameworks */,
53 55
 			);
54 56
 			runOnlyForDeploymentPostprocessing = 0;
55 57
 		};
@@ -78,7 +80,6 @@
78 80
 			children = (
79 81
 				C6F99C3A204DE6BE0001F710 /* AppDelegate.swift */,
80 82
 				C6F99C3C204DE6BE0001F710 /* ViewController.swift */,
81
-				C6A3424B204DF98E00E062DD /* JitsiViewController.swift */,
82 83
 				C6F99C3E204DE6BE0001F710 /* Main.storyboard */,
83 84
 				C6F99C41204DE6BE0001F710 /* Assets.xcassets */,
84 85
 				C6F99C43204DE6BE0001F710 /* LaunchScreen.storyboard */,
@@ -103,12 +104,13 @@
103 104
 			isa = PBXNativeTarget;
104 105
 			buildConfigurationList = C6F99C49204DE6BE0001F710 /* Build configuration list for PBXNativeTarget "PiPApp" */;
105 106
 			buildPhases = (
107
+				C6A3424A204DF91D00E062DD /* Run Adjust ATS for loading JS bundle */,
108
+				C6F99C62204DEFFE0001F710 /* Run React Packager */,
106 109
 				C6F99C33204DE6BE0001F710 /* Sources */,
107 110
 				C6F99C34204DE6BE0001F710 /* Frameworks */,
108
-				C6F99C61204DEDC20001F710 /* Embed Frameworks */,
109 111
 				C6F99C35204DE6BE0001F710 /* Resources */,
110
-				C6F99C62204DEFFE0001F710 /* Run React Packager */,
111
-				C6A3424A204DF91D00E062DD /* Run Adjust ATS for loading JS bundle */,
112
+				C6F99C61204DEDC20001F710 /* Embed Frameworks */,
113
+				C6A3426E20503ECC00E062DD /* Adjust embedded framework architectures */,
112 114
 			);
113 115
 			buildRules = (
114 116
 			);
@@ -132,6 +134,11 @@
132 134
 					C6F99C36204DE6BE0001F710 = {
133 135
 						CreatedOnToolsVersion = 9.2;
134 136
 						ProvisioningStyle = Automatic;
137
+						SystemCapabilities = {
138
+							com.apple.BackgroundModes = {
139
+								enabled = 1;
140
+							};
141
+						};
135 142
 					};
136 143
 				};
137 144
 			};
@@ -181,6 +188,20 @@
181 188
 			shellPath = /bin/sh;
182 189
 			shellScript = "../scripts/fixup-ats.sh";
183 190
 		};
191
+		C6A3426E20503ECC00E062DD /* Adjust embedded framework architectures */ = {
192
+			isa = PBXShellScriptBuildPhase;
193
+			buildActionMask = 2147483647;
194
+			files = (
195
+			);
196
+			inputPaths = (
197
+			);
198
+			name = "Adjust embedded framework architectures";
199
+			outputPaths = (
200
+			);
201
+			runOnlyForDeploymentPostprocessing = 0;
202
+			shellPath = /bin/sh;
203
+			shellScript = "../scripts/fixup-frameworks.sh";
204
+		};
184 205
 		C6F99C62204DEFFE0001F710 /* Run React Packager */ = {
185 206
 			isa = PBXShellScriptBuildPhase;
186 207
 			buildActionMask = 2147483647;
@@ -204,7 +225,6 @@
204 225
 			files = (
205 226
 				C6F99C3D204DE6BE0001F710 /* ViewController.swift in Sources */,
206 227
 				C6F99C3B204DE6BE0001F710 /* AppDelegate.swift in Sources */,
207
-				C6A3424C204DF98E00E062DD /* JitsiViewController.swift in Sources */,
208 228
 			);
209 229
 			runOnlyForDeploymentPostprocessing = 0;
210 230
 		};
@@ -342,7 +362,9 @@
342 362
 			buildSettings = {
343 363
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
344 364
 				CODE_SIGN_STYLE = Automatic;
365
+				FRAMEWORK_SEARCH_PATHS = "../../node_modules/react-native-webrtc/ios";
345 366
 				INFOPLIST_FILE = src/Info.plist;
367
+				IPHONEOS_DEPLOYMENT_TARGET = 10.3;
346 368
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
347 369
 				PRODUCT_BUNDLE_IDENTIFIER = com.jitsi.PiPApp;
348 370
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -356,7 +378,9 @@
356 378
 			buildSettings = {
357 379
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
358 380
 				CODE_SIGN_STYLE = Automatic;
381
+				FRAMEWORK_SEARCH_PATHS = "../../node_modules/react-native-webrtc/ios";
359 382
 				INFOPLIST_FILE = src/Info.plist;
383
+				IPHONEOS_DEPLOYMENT_TARGET = 10.3;
360 384
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
361 385
 				PRODUCT_BUNDLE_IDENTIFIER = com.jitsi.PiPApp;
362 386
 				PRODUCT_NAME = "$(TARGET_NAME)";

+ 1
- 15
ios/example-pip-app/src/Base.lproj/Main.storyboard 查看文件

@@ -27,6 +27,7 @@
27 27
                                 </connections>
28 28
                             </button>
29 29
                         </subviews>
30
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
30 31
                         <constraints>
31 32
                             <constraint firstItem="QxY-C8-fwD" firstAttribute="centerX" secondItem="6Tk-OE-BBY" secondAttribute="centerX" id="6a6-l1-7Ct"/>
32 33
                             <constraint firstItem="QxY-C8-fwD" firstAttribute="centerY" secondItem="6Tk-OE-BBY" secondAttribute="centerY" id="Hfg-TH-0g2"/>
@@ -41,20 +42,5 @@
41 42
             </objects>
42 43
             <point key="canvasLocation" x="32.799999999999997" y="658.92053973013503"/>
43 44
         </scene>
44
-        <!--JitsiViewController-->
45
-        <scene sceneID="V5C-nQ-uqb">
46
-            <objects>
47
-                <viewController id="B84-hY-B21" userLabel="JitsiViewController" customClass="JitsiViewController" customModule="PiPApp" customModuleProvider="target" sceneMemberID="viewController">
48
-                    <view key="view" contentMode="scaleToFill" id="nMf-dX-t08" customClass="JitsiMeetView">
49
-                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
50
-                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
51
-                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
52
-                        <viewLayoutGuide key="safeArea" id="W4q-U5-6O5"/>
53
-                    </view>
54
-                </viewController>
55
-                <placeholder placeholderIdentifier="IBFirstResponder" id="Zdh-Kt-gVo" userLabel="First Responder" sceneMemberID="firstResponder"/>
56
-            </objects>
57
-            <point key="canvasLocation" x="1023" y="643"/>
58
-        </scene>
59 45
     </scenes>
60 46
 </document>

+ 17
- 0
ios/example-pip-app/src/Info.plist 查看文件

@@ -20,6 +20,10 @@
20 20
 	<string>1</string>
21 21
 	<key>LSRequiresIPhoneOS</key>
22 22
 	<true/>
23
+	<key>UIBackgroundModes</key>
24
+	<array>
25
+		<string>audio</string>
26
+	</array>
23 27
 	<key>UILaunchStoryboardName</key>
24 28
 	<string>LaunchScreen</string>
25 29
 	<key>UIMainStoryboardFile</key>
@@ -34,6 +38,19 @@
34 38
 		<string>UIInterfaceOrientationLandscapeLeft</string>
35 39
 		<string>UIInterfaceOrientationLandscapeRight</string>
36 40
 	</array>
41
+	<key>NSAppTransportSecurity</key>
42
+	<dict>
43
+		<key>NSExceptionDomains</key>
44
+		<dict>
45
+			<key>localhost</key>
46
+			<dict>
47
+				<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
48
+				<true/>
49
+			</dict>
50
+		</dict>
51
+		<key>NSAllowsArbitraryLoads</key>
52
+		<true/>
53
+	</dict>
37 54
 	<key>UISupportedInterfaceOrientations~ipad</key>
38 55
 	<array>
39 56
 		<string>UIInterfaceOrientationPortrait</string>

+ 0
- 26
ios/example-pip-app/src/JitsiViewController.swift 查看文件

@@ -1,26 +0,0 @@
1
-//
2
-//  JitsiViewController.swift
3
-//  PiPApp
4
-//
5
-//  Created by Daniel Ornelas on 3/5/18.
6
-//  Copyright © 2018 Atlassian Inc. All rights reserved.
7
-//
8
-
9
-import JitsiMeet
10
-import UIKit
11
-
12
-final class JitsiViewController: UIViewController {
13
-    
14
-    override func viewDidLoad() {
15
-        super.viewDidLoad()
16
-        
17
-        guard let jitsiView = self.view as? JitsiMeetView else { return }
18
-        
19
-        jitsiView.welcomePageEnabled = true
20
-        jitsiView.load(nil)
21
-        
22
-        // TODO: delete me, this is only testing access to swift object in SDK
23
-        let jitsiManager = JitsiManager()
24
-        jitsiManager.testMe()
25
-    }
26
-}

+ 6
- 1
ios/example-pip-app/src/ViewController.swift 查看文件

@@ -13,6 +13,8 @@ class ViewController: UIViewController {
13 13
 
14 14
     @IBOutlet weak var videoButton: UIButton?
15 15
     
16
+    private var jitsiMeetManager: JitsiMeetManager?
17
+    
16 18
     override func viewDidLoad() {
17 19
         super.viewDidLoad()
18 20
     }
@@ -20,7 +22,10 @@ class ViewController: UIViewController {
20 22
     // MARK: - Actions
21 23
     
22 24
     @IBAction func startMeeting(sender: Any?) {
23
-        print("test")
25
+        //let url = URL(string: "")
26
+        self.jitsiMeetManager = JitsiMeetManager()
27
+        jitsiMeetManager?.welcomeScreenEnabled = true
28
+        jitsiMeetManager?.load(withUrl: nil)
24 29
     }
25 30
 
26 31
 }

+ 24
- 4
ios/sdk/sdk.xcodeproj/project.pbxproj 查看文件

@@ -27,7 +27,10 @@
27 27
 		0BCA496C1EC4BBF900B793EE /* jitsi.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0BCA496B1EC4BBF900B793EE /* jitsi.ttf */; };
28 28
 		0BD906EA1EC0C00300C8C18E /* JitsiMeet.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BD906E81EC0C00300C8C18E /* JitsiMeet.h */; settings = {ATTRIBUTES = (Public, ); }; };
29 29
 		0F65EECE1D95DA94561BB47E /* libPods-JitsiMeet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 03F2ADC957FF109849B7FCA1 /* libPods-JitsiMeet.a */; };
30
-		C6F99C14204DB63E0001F710 /* JitsiManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F99C12204DB63D0001F710 /* JitsiManager.swift */; };
30
+		C6A3425F204EF76800E062DD /* JitsiMeetWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425C204EF76800E062DD /* JitsiMeetWindow.swift */; };
31
+		C6A34260204EF76800E062DD /* JitsiMeetManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425D204EF76800E062DD /* JitsiMeetManager.swift */; };
32
+		C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425E204EF76800E062DD /* DragGestureController.swift */; };
33
+		C6A3426D204F1C3300E062DD /* JitsiMeetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3426C204F1C3300E062DD /* JitsiMeetViewController.swift */; };
31 34
 		C6F99C15204DB63E0001F710 /* JitsiMeetView+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */; };
32 35
 /* End PBXBuildFile section */
33 36
 
@@ -57,7 +60,10 @@
57 60
 		0BD906E91EC0C00300C8C18E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
58 61
 		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>"; };
59 62
 		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>"; };
60
-		C6F99C12204DB63D0001F710 /* JitsiManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JitsiManager.swift; sourceTree = "<group>"; };
63
+		C6A3425C204EF76800E062DD /* JitsiMeetWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JitsiMeetWindow.swift; sourceTree = "<group>"; };
64
+		C6A3425D204EF76800E062DD /* JitsiMeetManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JitsiMeetManager.swift; sourceTree = "<group>"; };
65
+		C6A3425E204EF76800E062DD /* DragGestureController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DragGestureController.swift; sourceTree = "<group>"; };
66
+		C6A3426C204F1C3300E062DD /* JitsiMeetViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JitsiMeetViewController.swift; sourceTree = "<group>"; };
61 67
 		C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetView+Private.h"; sourceTree = "<group>"; };
62 68
 /* End PBXFileReference section */
63 69
 
@@ -107,13 +113,13 @@
107 113
 		0BD906E71EC0C00300C8C18E /* src */ = {
108 114
 			isa = PBXGroup;
109 115
 			children = (
116
+				C6A3426B204F127900E062DD /* JitsiMeetManager */,
110 117
 				0BCA495C1EC4B6C600B793EE /* AudioMode.m */,
111 118
 				0BB9AD7C1F60356D001C08DB /* AppInfo.m */,
112 119
 				0BB9AD7A1F5EC8F4001C08DB /* CallKit.m */,
113 120
 				0BA13D301EE83FF8007BEF7F /* ExternalAPI.m */,
114 121
 				0BD906E91EC0C00300C8C18E /* Info.plist */,
115 122
 				0B7C2CFC200F51D60060D076 /* LaunchOptions.m */,
116
-				C6F99C12204DB63D0001F710 /* JitsiManager.swift */,
117 123
 				0BD906E81EC0C00300C8C18E /* JitsiMeet.h */,
118 124
 				0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */,
119 125
 				0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */,
@@ -149,6 +155,17 @@
149 155
 			name = Pods;
150 156
 			sourceTree = "<group>";
151 157
 		};
158
+		C6A3426B204F127900E062DD /* JitsiMeetManager */ = {
159
+			isa = PBXGroup;
160
+			children = (
161
+				C6A3425C204EF76800E062DD /* JitsiMeetWindow.swift */,
162
+				C6A3425D204EF76800E062DD /* JitsiMeetManager.swift */,
163
+				C6A3426C204F1C3300E062DD /* JitsiMeetViewController.swift */,
164
+				C6A3425E204EF76800E062DD /* DragGestureController.swift */,
165
+			);
166
+			path = JitsiMeetManager;
167
+			sourceTree = "<group>";
168
+		};
152 169
 /* End PBXGroup section */
153 170
 
154 171
 /* Begin PBXHeadersBuildPhase section */
@@ -312,15 +329,18 @@
312 329
 			buildActionMask = 2147483647;
313 330
 			files = (
314 331
 				0BB9AD7B1F5EC8F4001C08DB /* CallKit.m in Sources */,
332
+				C6A34260204EF76800E062DD /* JitsiMeetManager.swift in Sources */,
315 333
 				0BB9AD7D1F60356D001C08DB /* AppInfo.m in Sources */,
316 334
 				0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */,
317 335
 				0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */,
318 336
 				0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */,
319 337
 				0B7C2CFD200F51D60060D076 /* LaunchOptions.m in Sources */,
320
-				C6F99C14204DB63E0001F710 /* JitsiManager.swift in Sources */,
338
+				C6A3426D204F1C3300E062DD /* JitsiMeetViewController.swift in Sources */,
339
+				C6A3425F204EF76800E062DD /* JitsiMeetWindow.swift in Sources */,
321 340
 				0BCA495F1EC4B6C600B793EE /* AudioMode.m in Sources */,
322 341
 				0B44A0191F902126009D1D64 /* MPVolumeViewManager.m in Sources */,
323 342
 				0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */,
343
+				C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */,
324 344
 				0B412F191EDEC65D00B1A0A6 /* JitsiMeetView.m in Sources */,
325 345
 			);
326 346
 			runOnlyForDeploymentPostprocessing = 0;

+ 0
- 17
ios/sdk/src/JitsiManager.swift 查看文件

@@ -1,17 +0,0 @@
1
-//
2
-//  JitsiManager.swift
3
-//  JitsiMeet
4
-//
5
-//  Created by Daniel Ornelas on 3/5/18.
6
-//  Copyright © 2018 Jitsi. All rights reserved.
7
-//
8
-
9
-import Foundation
10
-
11
-@objc(JitsiManager)
12
-public class JitsiManager: NSObject {
13
-    
14
-    public func testMe() {
15
-        print("hi there")
16
-    }
17
-}

+ 104
- 0
ios/sdk/src/JitsiMeetManager/DragGestureController.swift 查看文件

@@ -0,0 +1,104 @@
1
+//  Copyright © 2018 Jitsi. All rights reserved.
2
+
3
+final class DragGestureController {
4
+    
5
+    var insets: UIEdgeInsets = UIEdgeInsets.zero
6
+    
7
+    private var frameBeforeDragging: CGRect = CGRect.zero
8
+    private weak var view: UIView?
9
+    private lazy var panGesture: UIPanGestureRecognizer = {
10
+        return UIPanGestureRecognizer(target: self, action: #selector(handlePan(gesture:)))
11
+    }()
12
+    
13
+    func startDragListener(inView view: UIView) {
14
+        self.view = view
15
+        view.addGestureRecognizer(panGesture)
16
+        panGesture.isEnabled = true
17
+    }
18
+    
19
+    func stopDragListener() {
20
+        panGesture.isEnabled = false
21
+        view?.removeGestureRecognizer(panGesture)
22
+        view = nil
23
+    }
24
+    
25
+    @objc private func handlePan(gesture: UIPanGestureRecognizer) {
26
+        guard let view = self.view else { return }
27
+        
28
+        let translation = gesture.translation(in: view.superview)
29
+        let velocity = gesture.velocity(in: view.superview)
30
+        var frame = frameBeforeDragging
31
+        
32
+        switch gesture.state {
33
+        case .began:
34
+            frameBeforeDragging = view.frame
35
+            
36
+        case .changed:
37
+            frame.origin.x = floor(frame.origin.x + translation.x)
38
+            frame.origin.y = floor(frame.origin.y + translation.y)
39
+            view.frame = frame
40
+            
41
+        case .ended:
42
+            let currentPos = view.frame.origin
43
+            let finalPos = calculateFinalPosition()
44
+            
45
+            let distance = CGPoint(x: currentPos.x - finalPos.x,
46
+                                   y: currentPos.y - finalPos.y)
47
+            let distanceMagnitude = magnitude(vector: distance)
48
+            let velocityMagnitude = magnitude(vector: velocity)
49
+            let animationDuration = 0.5
50
+            let initialSpringVelocity = velocityMagnitude / distanceMagnitude / CGFloat(animationDuration)
51
+            
52
+            frame.origin = CGPoint(x: finalPos.x, y: finalPos.y)
53
+            
54
+            UIView.animate(withDuration: animationDuration,
55
+                           delay: 0,
56
+                           usingSpringWithDamping: 0.9,
57
+                           initialSpringVelocity: initialSpringVelocity,
58
+                           options: .curveLinear,
59
+                           animations: {
60
+                            view.frame = frame
61
+            }, completion: nil)
62
+            
63
+        default:
64
+            break
65
+        }
66
+    }
67
+    
68
+    private func calculateFinalPosition() -> CGPoint {
69
+        guard
70
+            let view = self.view,
71
+            let bounds = view.superview?.frame
72
+            else { return CGPoint.zero }
73
+        
74
+        let currentSize = view.frame.size
75
+        let adjustedBounds = UIEdgeInsetsInsetRect(bounds, insets)
76
+        let threshold: CGFloat = 20.0
77
+        let velocity = panGesture.velocity(in: view.superview)
78
+        let location = panGesture.location(in: view.superview)
79
+        
80
+        let goLeft: Bool
81
+        if fabs(velocity.x) > threshold {
82
+            goLeft = velocity.x < -threshold
83
+        } else {
84
+            goLeft = location.x < bounds.midX
85
+        }
86
+        
87
+        let goUp: Bool
88
+        if fabs(velocity.y) > threshold {
89
+            goUp = velocity.y < -threshold
90
+        } else {
91
+            goUp = location.y < bounds.midY
92
+        }
93
+        
94
+        let finalPosX: CGFloat = goLeft ? adjustedBounds.origin.x : bounds.size.width - insets.right  - currentSize.width
95
+        let finalPosY: CGFloat = goUp ? adjustedBounds.origin.y : bounds.size.height - insets.bottom - currentSize.height
96
+        
97
+        return CGPoint(x: finalPosX, y: finalPosY)
98
+    }
99
+    
100
+    private func magnitude(vector: CGPoint) -> CGFloat {
101
+        return sqrt(pow(vector.x, 2) + pow(vector.y, 2))
102
+    }
103
+}
104
+

+ 146
- 0
ios/sdk/src/JitsiMeetManager/JitsiMeetManager.swift 查看文件

@@ -0,0 +1,146 @@
1
+//  Copyright © 2018 Jitsi. All rights reserved.
2
+
3
+import Foundation
4
+
5
+/// Creates and present a JitsiMeetView inside of an external window that can be dragged
6
+/// when minimized (if PiP mode is enabled)
7
+open class JitsiMeetManager: NSObject {
8
+    
9
+    /// The Jitsi meet view delegate
10
+    public weak var delegate: JitsiMeetViewDelegate? = nil
11
+    /// Limits the boundries of meet view position on screen when minimized
12
+    public var dragBoundInsets: UIEdgeInsets = UIEdgeInsets(top: 25, left: 5, bottom: 5, right: 5)
13
+    /// Enables PiP mode for this jitsiMeet
14
+    public var allowPiP: Bool = true
15
+    /// The size ratio for jitsiMeetView when in PiP mode
16
+    public var pipSizeRatio: CGFloat = 0.333
17
+    /// Defines if welcome screen should be on
18
+    public var welcomeScreenEnabled: Bool = false
19
+    
20
+    fileprivate let dragController: DragGestureController = DragGestureController()
21
+    
22
+    fileprivate lazy var meetViewController: JitsiMeetViewController = { return self.makeMeetViewController() }()
23
+    fileprivate lazy var meetWindow: JitsiMeetWindow = { return self.makeMeetWindow() }()
24
+    fileprivate var meetingInPiP: Bool = false
25
+    
26
+    /// Presents and loads a jitsi meet view
27
+    ///
28
+    /// - Parameter url: The url of the presentation
29
+    public func load(withUrl url: URL?) {
30
+        meetWindow.show()
31
+        meetViewController.jitsiMeetView.load(url)
32
+    }
33
+    
34
+    /// Presents and loads a jitsi meet view with configuration
35
+    ///
36
+    /// - Parameter urlObject: A dictionary of keys to be used for configuration
37
+    public func load(withUrlObject urlObject: [AnyHashable : Any]?) {
38
+        meetWindow.show()
39
+        meetViewController.jitsiMeetView.loadURLObject(urlObject)
40
+    }
41
+    
42
+    // MARK: - Manage PiP switching
43
+    
44
+    // update size animation
45
+    fileprivate func updateMeetViewSize(isPiP: Bool) {
46
+        UIView.animate(withDuration: 0.25) {
47
+            self.meetViewController.view.frame = self.meetViewRect(isPiP: isPiP)
48
+            self.meetViewController.view.setNeedsLayout()
49
+        }
50
+    }
51
+
52
+    private func meetViewRect(isPiP: Bool) -> CGRect {
53
+        guard isPiP else {
54
+            return meetWindow.bounds
55
+        }
56
+        let bounds = meetWindow.bounds
57
+
58
+        // resize to suggested ratio and position to the bottom right
59
+        let adjustedBounds = UIEdgeInsetsInsetRect(bounds, dragBoundInsets)
60
+        let size = CGSize(width: bounds.size.width * pipSizeRatio,
61
+                          height: bounds.size.height * pipSizeRatio)
62
+        let x: CGFloat = adjustedBounds.maxX - size.width
63
+        let y: CGFloat = adjustedBounds.maxY - size.height
64
+        return CGRect(x: x, y: y, width: size.width, height: size.height)
65
+    }
66
+    
67
+    // MARK: - helpers
68
+    
69
+    fileprivate func cleanUp() {
70
+        // TODO: more clean up work on this
71
+        
72
+        dragController.stopDragListener()
73
+        meetWindow.isHidden = true
74
+    }
75
+    
76
+    private func makeMeetViewController() -> JitsiMeetViewController {
77
+        let vc = JitsiMeetViewController()
78
+        vc.jitsiMeetView.delegate = self
79
+        vc.jitsiMeetView.welcomePageEnabled = self.welcomeScreenEnabled
80
+        vc.jitsiMeetView.pictureInPictureEnabled = self.allowPiP
81
+        return vc
82
+    }
83
+    
84
+    private func makeMeetWindow() -> JitsiMeetWindow {
85
+        let window = JitsiMeetWindow(frame: UIScreen.main.bounds)
86
+        window.backgroundColor = .clear
87
+        window.windowLevel = UIWindowLevelStatusBar + 100
88
+        window.rootViewController = self.meetViewController
89
+        return window
90
+    }
91
+}
92
+
93
+extension JitsiMeetManager: JitsiMeetViewDelegate {
94
+    
95
+    public func conferenceWillJoin(_ data: [AnyHashable : Any]!) {
96
+        DispatchQueue.main.async {
97
+            self.delegate?.conferenceWillJoin!(data)
98
+        }
99
+    }
100
+
101
+    public func conferenceJoined(_ data: [AnyHashable : Any]!) {
102
+        DispatchQueue.main.async {
103
+            self.delegate?.conferenceJoined!(data)
104
+        }
105
+    }
106
+    
107
+    public func conferenceWillLeave(_ data: [AnyHashable : Any]!) {
108
+        DispatchQueue.main.async {
109
+            self.delegate?.conferenceWillLeave!(data)
110
+        }
111
+    }
112
+
113
+    public func conferenceLeft(_ data: [AnyHashable : Any]!) {
114
+        DispatchQueue.main.async {
115
+            self.cleanUp()
116
+            
117
+            self.delegate?.conferenceLeft!(data)
118
+        }
119
+    }
120
+    
121
+    public func conferenceFailed(_ data: [AnyHashable : Any]!) {
122
+        DispatchQueue.main.async {
123
+            self.cleanUp()
124
+
125
+            self.delegate?.conferenceFailed!(data)
126
+        }
127
+    }
128
+    
129
+    public func loadConfigError(_ data: [AnyHashable : Any]!) {
130
+        DispatchQueue.main.async {
131
+            self.delegate?.loadConfigError!(data)
132
+        }
133
+    }
134
+    
135
+    public func enterPicture(inPicture data: [AnyHashable : Any]!) {
136
+        DispatchQueue.main.async {
137
+            self.dragController.startDragListener(inView: self.meetViewController.view)
138
+            self.dragController.insets = self.dragBoundInsets
139
+            
140
+            self.meetingInPiP = true
141
+            self.updateMeetViewSize(isPiP: true)
142
+            
143
+            self.delegate?.enterPicture!(inPicture: data)
144
+        }
145
+    }
146
+}

+ 16
- 0
ios/sdk/src/JitsiMeetManager/JitsiMeetViewController.swift 查看文件

@@ -0,0 +1,16 @@
1
+//  Copyright © 2018 Jitsi. All rights reserved.
2
+
3
+
4
+/// Wrapper ViewController of a JitsiMeetView
5
+///
6
+/// TODO: should consider refactor and move out several logic of the JitsiMeetView to
7
+/// this class
8
+open class JitsiMeetViewController: UIViewController {
9
+    
10
+    private(set) var jitsiMeetView: JitsiMeetView = JitsiMeetView()
11
+    
12
+    override open func loadView() {
13
+        super.loadView()
14
+        self.view = jitsiMeetView
15
+    }
16
+}

+ 45
- 0
ios/sdk/src/JitsiMeetManager/JitsiMeetWindow.swift 查看文件

@@ -0,0 +1,45 @@
1
+//  Copyright © 2018 Jitsi. All rights reserved.
2
+
3
+open class JitsiMeetWindow: UIWindow {
4
+    
5
+    /// Help out to bubble up the gesture detection outside of the rootVC frame
6
+    open override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
7
+        guard let vc = rootViewController else {
8
+            return super.point(inside: point, with: event)
9
+        }
10
+        return vc.view.frame.contains(point)
11
+    }
12
+    
13
+    /// animate in the window
14
+    open func show() {
15
+        if self.isHidden || self.alpha < 1 {
16
+            self.isHidden = false
17
+            self.alpha = 0
18
+            
19
+            UIView.animate(
20
+                withDuration: 0.1,
21
+                delay: 0,
22
+                options: .beginFromCurrentState,
23
+                animations: {
24
+                    self.alpha = 1
25
+            },
26
+                completion: nil)
27
+        }
28
+    }
29
+    
30
+    /// animate out the window
31
+    open func hide() {
32
+        if !self.isHidden || self.alpha > 0 {
33
+            UIView.animate(
34
+                withDuration: 0.1,
35
+                delay: 0,
36
+                options: .beginFromCurrentState,
37
+                animations: {
38
+                    self.alpha = 0
39
+                    self.isHidden = true
40
+            },
41
+                completion: nil)
42
+        }
43
+    }
44
+}
45
+

+ 8
- 4
react/features/mobile/picture-in-picture/actions.js 查看文件

@@ -2,6 +2,8 @@
2 2
 
3 3
 import { NativeModules } from 'react-native';
4 4
 
5
+import { Platform } from '../../base/react';
6
+
5 7
 import {
6 8
     ENTER_PICTURE_IN_PICTURE,
7 9
     _SET_EMITTER_SUBSCRIPTIONS
@@ -28,10 +30,12 @@ export function enterPictureInPicture() {
28 30
                 && (conference || joining)) {
29 31
             const { PictureInPicture } = NativeModules;
30 32
             const p
31
-                = PictureInPicture
32
-                    ? PictureInPicture.enterPictureInPicture()
33
-                    : Promise.reject(
34
-                        new Error('Picture-in-Picture not supported'));
33
+                = Platform.OS === 'android'
34
+                    ? PictureInPicture
35
+                        ? PictureInPicture.enterPictureInPicture()
36
+                        : Promise.reject(
37
+                            new Error('Picture-in-Picture not supported'))
38
+                    : Promise.resolve();
35 39
 
36 40
             p.then(
37 41
                 () => dispatch({ type: ENTER_PICTURE_IN_PICTURE }),

正在加载...
取消
保存