Implement Jitsi meet presentation interface that supports custom PiP solution

pull/2592/head
Daniel Ornelas 7 years ago committed by Lyubo Marinov
parent 181580871f
commit f8163de765
  1. 38
      ios/example-pip-app/PiPApp.xcodeproj/project.pbxproj
  2. 16
      ios/example-pip-app/src/Base.lproj/Main.storyboard
  3. 17
      ios/example-pip-app/src/Info.plist
  4. 26
      ios/example-pip-app/src/JitsiViewController.swift
  5. 7
      ios/example-pip-app/src/ViewController.swift
  6. 28
      ios/sdk/sdk.xcodeproj/project.pbxproj
  7. 17
      ios/sdk/src/JitsiManager.swift
  8. 104
      ios/sdk/src/JitsiMeetManager/DragGestureController.swift
  9. 146
      ios/sdk/src/JitsiMeetManager/JitsiMeetManager.swift
  10. 16
      ios/sdk/src/JitsiMeetManager/JitsiMeetViewController.swift
  11. 45
      ios/sdk/src/JitsiMeetManager/JitsiMeetWindow.swift
  12. 12
      react/features/mobile/picture-in-picture/actions.js

@ -7,8 +7,9 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* 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, ); }; }; 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 */; }; C6F99C3B204DE6BE0001F710 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F99C3A204DE6BE0001F710 /* AppDelegate.swift */; };
C6F99C3D204DE6BE0001F710 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F99C3C204DE6BE0001F710 /* ViewController.swift */; }; C6F99C3D204DE6BE0001F710 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6F99C3C204DE6BE0001F710 /* ViewController.swift */; };
C6F99C40204DE6BE0001F710 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C6F99C3E204DE6BE0001F710 /* Main.storyboard */; }; C6F99C40204DE6BE0001F710 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C6F99C3E204DE6BE0001F710 /* Main.storyboard */; };
@ -34,7 +35,6 @@
/* Begin PBXFileReference section */ /* 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>"; }; 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; }; 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>"; }; 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>"; }; C6F99C3C204DE6BE0001F710 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
@ -50,6 +50,8 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
C6245F57205044120040BE68 /* JitsiMeet.framework in Frameworks */,
C6245F58205044150040BE68 /* WebRTC.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -78,7 +80,6 @@
children = ( children = (
C6F99C3A204DE6BE0001F710 /* AppDelegate.swift */, C6F99C3A204DE6BE0001F710 /* AppDelegate.swift */,
C6F99C3C204DE6BE0001F710 /* ViewController.swift */, C6F99C3C204DE6BE0001F710 /* ViewController.swift */,
C6A3424B204DF98E00E062DD /* JitsiViewController.swift */,
C6F99C3E204DE6BE0001F710 /* Main.storyboard */, C6F99C3E204DE6BE0001F710 /* Main.storyboard */,
C6F99C41204DE6BE0001F710 /* Assets.xcassets */, C6F99C41204DE6BE0001F710 /* Assets.xcassets */,
C6F99C43204DE6BE0001F710 /* LaunchScreen.storyboard */, C6F99C43204DE6BE0001F710 /* LaunchScreen.storyboard */,
@ -103,12 +104,13 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = C6F99C49204DE6BE0001F710 /* Build configuration list for PBXNativeTarget "PiPApp" */; buildConfigurationList = C6F99C49204DE6BE0001F710 /* Build configuration list for PBXNativeTarget "PiPApp" */;
buildPhases = ( buildPhases = (
C6A3424A204DF91D00E062DD /* Run Adjust ATS for loading JS bundle */,
C6F99C62204DEFFE0001F710 /* Run React Packager */,
C6F99C33204DE6BE0001F710 /* Sources */, C6F99C33204DE6BE0001F710 /* Sources */,
C6F99C34204DE6BE0001F710 /* Frameworks */, C6F99C34204DE6BE0001F710 /* Frameworks */,
C6F99C61204DEDC20001F710 /* Embed Frameworks */,
C6F99C35204DE6BE0001F710 /* Resources */, C6F99C35204DE6BE0001F710 /* Resources */,
C6F99C62204DEFFE0001F710 /* Run React Packager */, C6F99C61204DEDC20001F710 /* Embed Frameworks */,
C6A3424A204DF91D00E062DD /* Run Adjust ATS for loading JS bundle */, C6A3426E20503ECC00E062DD /* Adjust embedded framework architectures */,
); );
buildRules = ( buildRules = (
); );
@ -132,6 +134,11 @@
C6F99C36204DE6BE0001F710 = { C6F99C36204DE6BE0001F710 = {
CreatedOnToolsVersion = 9.2; CreatedOnToolsVersion = 9.2;
ProvisioningStyle = Automatic; ProvisioningStyle = Automatic;
SystemCapabilities = {
com.apple.BackgroundModes = {
enabled = 1;
};
};
}; };
}; };
}; };
@ -181,6 +188,20 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "../scripts/fixup-ats.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 */ = { C6F99C62204DEFFE0001F710 /* Run React Packager */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -204,7 +225,6 @@
files = ( files = (
C6F99C3D204DE6BE0001F710 /* ViewController.swift in Sources */, C6F99C3D204DE6BE0001F710 /* ViewController.swift in Sources */,
C6F99C3B204DE6BE0001F710 /* AppDelegate.swift in Sources */, C6F99C3B204DE6BE0001F710 /* AppDelegate.swift in Sources */,
C6A3424C204DF98E00E062DD /* JitsiViewController.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -342,7 +362,9 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
FRAMEWORK_SEARCH_PATHS = "../../node_modules/react-native-webrtc/ios";
INFOPLIST_FILE = src/Info.plist; INFOPLIST_FILE = src/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.jitsi.PiPApp; PRODUCT_BUNDLE_IDENTIFIER = com.jitsi.PiPApp;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@ -356,7 +378,9 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
FRAMEWORK_SEARCH_PATHS = "../../node_modules/react-native-webrtc/ios";
INFOPLIST_FILE = src/Info.plist; INFOPLIST_FILE = src/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.jitsi.PiPApp; PRODUCT_BUNDLE_IDENTIFIER = com.jitsi.PiPApp;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";

@ -27,6 +27,7 @@
</connections> </connections>
</button> </button>
</subviews> </subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints> <constraints>
<constraint firstItem="QxY-C8-fwD" firstAttribute="centerX" secondItem="6Tk-OE-BBY" secondAttribute="centerX" id="6a6-l1-7Ct"/> <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"/> <constraint firstItem="QxY-C8-fwD" firstAttribute="centerY" secondItem="6Tk-OE-BBY" secondAttribute="centerY" id="Hfg-TH-0g2"/>
@ -41,20 +42,5 @@
</objects> </objects>
<point key="canvasLocation" x="32.799999999999997" y="658.92053973013503"/> <point key="canvasLocation" x="32.799999999999997" y="658.92053973013503"/>
</scene> </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> </scenes>
</document> </document>

@ -20,6 +20,10 @@
<string>1</string> <string>1</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string>LaunchScreen</string> <string>LaunchScreen</string>
<key>UIMainStoryboardFile</key> <key>UIMainStoryboardFile</key>
@ -34,6 +38,19 @@
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </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> <key>UISupportedInterfaceOrientations~ipad</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <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? @IBOutlet weak var videoButton: UIButton?
private var jitsiMeetManager: JitsiMeetManager?
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
} }
@ -20,7 +22,10 @@ class ViewController: UIViewController {
// MARK: - Actions // MARK: - Actions
@IBAction func startMeeting(sender: Any?) { @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 */; }; 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, ); }; }; 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 */; }; 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 */; }; C6F99C15204DB63E0001F710 /* JitsiMeetView+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
@ -57,7 +60,10 @@
0BD906E91EC0C00300C8C18E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 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>"; }; 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>"; }; 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>"; }; C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetView+Private.h"; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
@ -107,13 +113,13 @@
0BD906E71EC0C00300C8C18E /* src */ = { 0BD906E71EC0C00300C8C18E /* src */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
C6A3426B204F127900E062DD /* JitsiMeetManager */,
0BCA495C1EC4B6C600B793EE /* AudioMode.m */, 0BCA495C1EC4B6C600B793EE /* AudioMode.m */,
0BB9AD7C1F60356D001C08DB /* AppInfo.m */, 0BB9AD7C1F60356D001C08DB /* AppInfo.m */,
0BB9AD7A1F5EC8F4001C08DB /* CallKit.m */, 0BB9AD7A1F5EC8F4001C08DB /* CallKit.m */,
0BA13D301EE83FF8007BEF7F /* ExternalAPI.m */, 0BA13D301EE83FF8007BEF7F /* ExternalAPI.m */,
0BD906E91EC0C00300C8C18E /* Info.plist */, 0BD906E91EC0C00300C8C18E /* Info.plist */,
0B7C2CFC200F51D60060D076 /* LaunchOptions.m */, 0B7C2CFC200F51D60060D076 /* LaunchOptions.m */,
C6F99C12204DB63D0001F710 /* JitsiManager.swift */,
0BD906E81EC0C00300C8C18E /* JitsiMeet.h */, 0BD906E81EC0C00300C8C18E /* JitsiMeet.h */,
0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */, 0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */,
0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */, 0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */,
@ -149,6 +155,17 @@
name = Pods; name = Pods;
sourceTree = "<group>"; 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 */ /* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */ /* Begin PBXHeadersBuildPhase section */
@ -312,15 +329,18 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
0BB9AD7B1F5EC8F4001C08DB /* CallKit.m in Sources */, 0BB9AD7B1F5EC8F4001C08DB /* CallKit.m in Sources */,
C6A34260204EF76800E062DD /* JitsiMeetManager.swift in Sources */,
0BB9AD7D1F60356D001C08DB /* AppInfo.m in Sources */, 0BB9AD7D1F60356D001C08DB /* AppInfo.m in Sources */,
0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */, 0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */,
0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */, 0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */,
0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */, 0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */,
0B7C2CFD200F51D60060D076 /* LaunchOptions.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 */, 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 */,
C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */,
0B412F191EDEC65D00B1A0A6 /* JitsiMeetView.m in Sources */, 0B412F191EDEC65D00B1A0A6 /* JitsiMeetView.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; 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 { NativeModules } from 'react-native';
import { Platform } from '../../base/react';
import { import {
ENTER_PICTURE_IN_PICTURE, ENTER_PICTURE_IN_PICTURE,
_SET_EMITTER_SUBSCRIPTIONS _SET_EMITTER_SUBSCRIPTIONS
@ -28,10 +30,12 @@ export function enterPictureInPicture() {
&& (conference || joining)) { && (conference || joining)) {
const { PictureInPicture } = NativeModules; const { PictureInPicture } = NativeModules;
const p const p
= PictureInPicture = Platform.OS === 'android'
? PictureInPicture.enterPictureInPicture() ? PictureInPicture
: Promise.reject( ? PictureInPicture.enterPictureInPicture()
new Error('Picture-in-Picture not supported')); : Promise.reject(
new Error('Picture-in-Picture not supported'))
: Promise.resolve();
p.then( p.then(
() => dispatch({ type: ENTER_PICTURE_IN_PICTURE }), () => dispatch({ type: ENTER_PICTURE_IN_PICTURE }),

Loading…
Cancel
Save