Addressing feedback from PR

This commit is contained in:
Daniel Ornelas 2018-03-20 11:47:41 -05:00 committed by Lyubo Marinov
parent 8428dd95c2
commit 5858859838
14 changed files with 277 additions and 210 deletions

View File

@ -56,13 +56,13 @@
</dict>
</dict>
<key>NSCalendarsUsageDescription</key>
<string>Displays the user's meetings in the app.</string>
<string>See your scheduled conferences in the app.</string>
<key>NSCameraUsageDescription</key>
<string>Participate in conferences with video.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string></string>
<key>NSMicrophoneUsageDescription</key>
<string>Participate in conferences with audio.</string>
<string>Participate in conferences with voice.</string>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>

View File

@ -35,7 +35,7 @@
/* 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>"; };
C6F99C37204DE6BE0001F710 /* PiPApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PiPApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
C6F99C37204DE6BE0001F710 /* example-pip-app.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example-pip-app.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>"; };
C6F99C3F204DE6BE0001F710 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
@ -70,7 +70,7 @@
C6F99C38204DE6BE0001F710 /* Products */ = {
isa = PBXGroup;
children = (
C6F99C37204DE6BE0001F710 /* PiPApp.app */,
C6F99C37204DE6BE0001F710 /* example-pip-app.app */,
);
name = Products;
sourceTree = "<group>";
@ -100,9 +100,9 @@
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
C6F99C36204DE6BE0001F710 /* PiPApp */ = {
C6F99C36204DE6BE0001F710 /* example-pip-app */ = {
isa = PBXNativeTarget;
buildConfigurationList = C6F99C49204DE6BE0001F710 /* Build configuration list for PBXNativeTarget "PiPApp" */;
buildConfigurationList = C6F99C49204DE6BE0001F710 /* Build configuration list for PBXNativeTarget "example-pip-app" */;
buildPhases = (
C6A3424A204DF91D00E062DD /* Run Adjust ATS for loading JS bundle */,
C6F99C62204DEFFE0001F710 /* Run React Packager */,
@ -116,9 +116,9 @@
);
dependencies = (
);
name = PiPApp;
productName = ExampleAppUsingJitsiWithPiP;
productReference = C6F99C37204DE6BE0001F710 /* PiPApp.app */;
name = example-pip-app;
productName = example-pip-app;
productReference = C6F99C37204DE6BE0001F710 /* example-pip-app.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
@ -142,7 +142,7 @@
};
};
};
buildConfigurationList = C6F99C32204DE6BE0001F710 /* Build configuration list for PBXProject "PiPApp" */;
buildConfigurationList = C6F99C32204DE6BE0001F710 /* Build configuration list for PBXProject "example-pip-app" */;
compatibilityVersion = "Xcode 8.0";
developmentRegion = en;
hasScannedForEncodings = 0;
@ -155,7 +155,7 @@
projectDirPath = "";
projectRoot = "";
targets = (
C6F99C36204DE6BE0001F710 /* PiPApp */,
C6F99C36204DE6BE0001F710 /* example-pip-app */,
);
};
/* End PBXProject section */
@ -366,7 +366,7 @@
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_BUNDLE_IDENTIFIER = com.jitsi.example-pip-app;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -382,7 +382,7 @@
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_BUNDLE_IDENTIFIER = com.jitsi.example-pip-app;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -392,7 +392,7 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
C6F99C32204DE6BE0001F710 /* Build configuration list for PBXProject "PiPApp" */ = {
C6F99C32204DE6BE0001F710 /* Build configuration list for PBXProject "example-pip-app" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C6F99C47204DE6BE0001F710 /* Debug */,
@ -401,7 +401,7 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C6F99C49204DE6BE0001F710 /* Build configuration list for PBXNativeTarget "PiPApp" */ = {
C6F99C49204DE6BE0001F710 /* Build configuration list for PBXNativeTarget "example-pip-app" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C6F99C4A204DE6BE0001F710 /* Debug */,

View File

@ -1,10 +1,18 @@
//
// AppDelegate.swift
// ExampleAppUsingJitsiWithPiP
//
// Created by Daniel Ornelas on 3/5/18.
// Copyright © 2018 Atlassian Inc. All rights reserved.
//
/*
* Copyright @ 2017-present Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import JitsiMeet
@ -30,6 +38,4 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
return JitsiMeetView.application(application, open: url, sourceApplication: sourceApplication, annotation: annotation)
}
}

View File

@ -13,17 +13,17 @@
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="PiPApp" customModuleProvider="target" sceneMemberID="viewController">
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="example-pip-app" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="QxY-C8-fwD">
<rect key="frame" x="144.5" y="324.5" width="86" height="38"/>
<rect key="frame" x="116" y="324.5" width="143" height="38"/>
<fontDescription key="fontDescription" type="system" pointSize="21"/>
<state key="normal" title="Start Jitsi"/>
<state key="normal" title="Open Jitsi Meet"/>
<connections>
<action selector="startMeetingWithSender:" destination="BYZ-38-t0r" eventType="touchUpInside" id="79C-XR-05w"/>
<action selector="openJitsiMeetWithSender:" destination="BYZ-38-t0r" eventType="touchUpInside" id="79C-XR-05w"/>
</connections>
</button>
</subviews>

View File

@ -1,10 +1,18 @@
//
// ViewController.swift
// ExampleAppUsingJitsiWithPiP
//
// Created by Daniel Ornelas on 3/5/18.
// Copyright © 2018 Atlassian Inc. All rights reserved.
//
/*
* Copyright @ 2017-present Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import UIKit
import JitsiMeet
@ -13,7 +21,7 @@ class ViewController: UIViewController {
@IBOutlet weak var videoButton: UIButton?
private var jitsiMeetManager: JitsiMeetManager?
private var jitsiMeetCoordinator: JitsiMeetPresentationCoordinator?
override func viewDidLoad() {
super.viewDidLoad()
@ -21,12 +29,11 @@ class ViewController: UIViewController {
// MARK: - Actions
@IBAction func startMeeting(sender: Any?) {
//let url = URL(string: "")
self.jitsiMeetManager = JitsiMeetManager()
jitsiMeetManager?.welcomeScreenEnabled = true
jitsiMeetManager?.load(withUrl: nil)
@IBAction func openJitsiMeet(sender: Any?) {
let jitsiMeetCoordinator = JitsiMeetPresentationCoordinator()
self.jitsiMeetCoordinator = jitsiMeetCoordinator
jitsiMeetCoordinator.jitsiMeetView().welcomePageEnabled = true
jitsiMeetCoordinator.jitsiMeetView().load(nil)
jitsiMeetCoordinator.show()
}
}

View File

@ -2,7 +2,7 @@
<Workspace
version = "1.0">
<FileRef
location = "group:example-pip-app/PiPApp.xcodeproj">
location = "group:example-pip-app/example-pip-app.xcodeproj">
</FileRef>
<FileRef
location = "group:app/app.xcodeproj">

View File

@ -30,7 +30,7 @@
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 */; };
C6A3425F204EF76800E062DD /* PiPWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425C204EF76800E062DD /* PiPWindow.swift */; };
C6A34260204EF76800E062DD /* JitsiMeetManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425D204EF76800E062DD /* JitsiMeetManager.swift */; };
C6A34260204EF76800E062DD /* JitsiMeetPresentationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425D204EF76800E062DD /* JitsiMeetPresentationCoordinator.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 */; };
@ -62,10 +62,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>"; };
C6245F5B2053091D0040BE68 /* image-resize@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "image-resize@2x.png"; sourceTree = "<group>"; };
C6245F5C2053091D0040BE68 /* image-resize@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "image-resize@3x.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>"; };
C6A3425C204EF76800E062DD /* PiPWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PiPWindow.swift; sourceTree = "<group>"; };
C6A3425D204EF76800E062DD /* JitsiMeetManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JitsiMeetManager.swift; sourceTree = "<group>"; };
C6A3425D204EF76800E062DD /* JitsiMeetPresentationCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JitsiMeetPresentationCoordinator.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>"; };
@ -119,7 +119,7 @@
0BD906E71EC0C00300C8C18E /* src */ = {
isa = PBXGroup;
children = (
C6A3426B204F127900E062DD /* JitsiMeetManager */,
C6A3426B204F127900E062DD /* picture-in-picture */,
0BCA495C1EC4B6C600B793EE /* AudioMode.m */,
0BB9AD7C1F60356D001C08DB /* AppInfo.m */,
0BB9AD7A1F5EC8F4001C08DB /* CallKit.m */,
@ -161,23 +161,15 @@
name = Pods;
sourceTree = "<group>";
};
C6245F5A2052043F0040BE68 /* PiPWindow */ = {
C6A3426B204F127900E062DD /* picture-in-picture */ = {
isa = PBXGroup;
children = (
C6A3425E204EF76800E062DD /* DragGestureController.swift */,
C6A3425D204EF76800E062DD /* JitsiMeetPresentationCoordinator.swift */,
C6A3426C204F1C3300E062DD /* JitsiMeetViewController.swift */,
C6A3425C204EF76800E062DD /* PiPWindow.swift */,
);
path = PiPWindow;
sourceTree = "<group>";
};
C6A3426B204F127900E062DD /* JitsiMeetManager */ = {
isa = PBXGroup;
children = (
C6245F5A2052043F0040BE68 /* PiPWindow */,
C6A3425D204EF76800E062DD /* JitsiMeetManager.swift */,
C6A3426C204F1C3300E062DD /* JitsiMeetViewController.swift */,
);
path = JitsiMeetManager;
path = "picture-in-picture";
sourceTree = "<group>";
};
/* End PBXGroup section */
@ -345,7 +337,7 @@
buildActionMask = 2147483647;
files = (
0BB9AD7B1F5EC8F4001C08DB /* CallKit.m in Sources */,
C6A34260204EF76800E062DD /* JitsiMeetManager.swift in Sources */,
C6A34260204EF76800E062DD /* JitsiMeetPresentationCoordinator.swift in Sources */,
0BB9AD7D1F60356D001C08DB /* AppInfo.m in Sources */,
0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */,
0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */,

View File

@ -1,101 +0,0 @@
// Copyright © 2018 Jitsi. All rights reserved.
import Foundation
/// Creates and coordinates the presentation of JitsiMeetViewController inside of an external window
/// which can be resized and dragged with custom PiP mode
open class JitsiMeetManager: NSObject {
/// Defines if welcome screen should be on
public var welcomeScreenEnabled: Bool = false {
didSet {
meetViewController?.jitsiMeetView.welcomePageEnabled = welcomeScreenEnabled
}
}
private(set) var meetViewController: JitsiMeetViewController?
private(set) var meetWindow: PiPWindow?
override public init() {
super.init()
self.meetViewController = makeMeetViewController()
self.meetWindow = makeMeetWindow()
}
public init(meetViewController: JitsiMeetViewController? = nil, meetWindow: PiPWindow? = nil) {
super.init()
self.meetViewController = meetViewController ?? makeMeetViewController()
self.meetWindow = meetWindow ?? makeMeetWindow()
}
/// 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)
}
deinit {
cleanUp()
}
// MARK: - helpers
fileprivate func cleanUp() {
// TODO: more clean up work on this
meetWindow?.isHidden = true
meetWindow?.stopDragGesture()
}
private func makeMeetViewController() -> JitsiMeetViewController {
let vc = JitsiMeetViewController()
vc.jitsiMeetView.welcomePageEnabled = self.welcomeScreenEnabled
vc.jitsiMeetView.pictureInPictureEnabled = true
vc.delegate = self
return vc
}
private func makeMeetWindow() -> PiPWindow {
let window = PiPWindow(frame: UIScreen.main.bounds)
window.backgroundColor = .clear
window.windowLevel = UIWindowLevelStatusBar + 100
window.rootViewController = self.meetViewController
return window
}
}
extension JitsiMeetManager: JitsiMeetViewControllerDelegate {
open func performPresentationUpdate(to: JitsiMeetPresentationUpdate) {
guard let meetWindow = self.meetWindow else { return }
switch to {
case .enterPiP:
meetWindow.goToPiP()
case .traitChange:
// resize to full screen if rotation happens
if meetWindow.isInPiP {
meetWindow.goToFullScreen()
}
}
}
open func meetingStarted() {
// do something
}
open func meetingEnded(wasFailure: Bool) {
cleanUp()
}
}

View File

@ -1,4 +1,18 @@
// Copyright © 2018 Jitsi. All rights reserved.
/*
* Copyright @ 2017-present Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class DragGestureController {
@ -7,7 +21,8 @@ final class DragGestureController {
private var frameBeforeDragging: CGRect = CGRect.zero
private weak var view: UIView?
private lazy var panGesture: UIPanGestureRecognizer = {
return UIPanGestureRecognizer(target: self, action: #selector(handlePan(gesture:)))
return UIPanGestureRecognizer(target: self,
action: #selector(handlePan(gesture:)))
}()
func startDragListener(inView view: UIView) {
@ -47,7 +62,8 @@ final class DragGestureController {
let distanceMagnitude = magnitude(vector: distance)
let velocityMagnitude = magnitude(vector: velocity)
let animationDuration = 0.5
let initialSpringVelocity = velocityMagnitude / distanceMagnitude / CGFloat(animationDuration)
let initialSpringVelocity =
velocityMagnitude / distanceMagnitude / CGFloat(animationDuration)
frame.origin = CGPoint(x: finalPos.x, y: finalPos.y)
@ -91,8 +107,14 @@ final class DragGestureController {
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
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)
}
@ -101,4 +123,3 @@ final class DragGestureController {
return sqrt(pow(vector.x, 2) + pow(vector.y, 2))
}
}

View File

@ -0,0 +1,97 @@
/*
* Copyright @ 2017-present Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Foundation
/// Coordinates the presentation of JitsiMeetViewController inside of
/// an external window that can be resized and dragged with custom PiP mode
open class JitsiMeetPresentationCoordinator: NSObject {
fileprivate let meetViewController: JitsiMeetViewController
fileprivate let meetWindow: PiPWindow
public init(meetViewController: JitsiMeetViewController? = nil,
meetWindow: PiPWindow? = nil) {
self.meetViewController = meetViewController ?? JitsiMeetViewController()
self.meetWindow = meetWindow ?? PiPWindow(frame: UIScreen.main.bounds)
super.init()
configureMeetWindow()
configureMeetViewController()
}
public func jitsiMeetView() -> JitsiMeetView {
return meetViewController.jitsiMeetView
}
public func show() {
meetWindow.show()
}
public func hide() {
meetWindow.hide()
}
deinit {
cleanUp()
}
// MARK: - helpers
fileprivate func cleanUp() {
// TODO: more clean up work on this
meetWindow.isHidden = true
meetWindow.stopDragGesture()
}
private func configureMeetViewController() {
meetViewController.jitsiMeetView.pictureInPictureEnabled = true
meetViewController.delegate = self
}
private func configureMeetWindow() {
meetWindow.backgroundColor = .clear
meetWindow.windowLevel = UIWindowLevelStatusBar + 100
meetWindow.rootViewController = self.meetViewController
}
}
extension JitsiMeetPresentationCoordinator: JitsiMeetViewControllerDelegate {
open func performPresentationUpdate(to: JitsiMeetPresentationUpdate) {
switch to {
case .enterPictureInPicture:
meetWindow.enterPictureInPicture()
case .traitChange:
// resize to full screen if rotation happens
if meetWindow.isInPiP {
meetWindow.exitPictureInPicture()
}
}
}
open func conferenceStarted() {
if meetWindow.isHidden {
meetWindow.show()
}
}
open func conferenceEnded(didFail: Bool) {
cleanUp()
}
}

View File

@ -1,21 +1,42 @@
// Copyright © 2018 Jitsi. All rights reserved.
/*
* Copyright @ 2017-present Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public enum JitsiMeetPresentationUpdate {
/// The conference wants to enter Picture-in-Picture
case enterPictureInPicture
/// A system traitCollectionChange (usually screen rotation)
case traitChange
/// Meeting wants to enter PiP mode
case enterPiP
}
public protocol JitsiMeetViewControllerDelegate: class {
/// Notifies a change of the meeting presentation style.
/// Notifies a change of the conference presentation style.
///
/// - Parameter to: The presentation state that will be changed to
func performPresentationUpdate(to: JitsiMeetPresentationUpdate)
func meetingStarted()
func meetingEnded(wasFailure: Bool)
/// The conference started
func conferenceStarted()
/// The conference ended
///
/// - Parameter didFail: The reason of ending the conference
func conferenceEnded(didFail: Bool)
}
/// Wrapper ViewController of a JitsiMeetView
@ -52,7 +73,7 @@ extension JitsiMeetViewController: JitsiMeetViewDelegate {
open func conferenceJoined(_ data: [AnyHashable : Any]!) {
DispatchQueue.main.async {
self.delegate?.meetingStarted()
self.delegate?.conferenceStarted()
}
}
@ -62,13 +83,13 @@ extension JitsiMeetViewController: JitsiMeetViewDelegate {
open func conferenceLeft(_ data: [AnyHashable : Any]!) {
DispatchQueue.main.async {
self.delegate?.meetingEnded(wasFailure: true)
self.delegate?.conferenceEnded(didFail: true)
}
}
open func conferenceFailed(_ data: [AnyHashable : Any]!) {
DispatchQueue.main.async {
self.delegate?.meetingEnded(wasFailure: true)
self.delegate?.conferenceEnded(didFail: true)
}
}
@ -78,7 +99,7 @@ extension JitsiMeetViewController: JitsiMeetViewDelegate {
open func enterPicture(inPicture data: [AnyHashable : Any]!) {
DispatchQueue.main.async {
self.delegate?.performPresentationUpdate(to: .enterPiP)
self.delegate?.performPresentationUpdate(to: .enterPictureInPicture)
}
}
}

View File

@ -1,11 +1,28 @@
// Copyright © 2018 Jitsi. All rights reserved.
/*
* Copyright @ 2017-present Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// A window that allows its root view controller to be presented
/// in full screen or in a custom Picture in Picture mode
open class PiPWindow: UIWindow {
/// Limits the boundries of root view position on screen when minimized
public var dragBoundInsets: UIEdgeInsets = UIEdgeInsets(top: 25, left: 5, bottom: 5, right: 5) {
public var dragBoundInsets: UIEdgeInsets = UIEdgeInsets(top: 25,
left: 5,
bottom: 5,
right: 5) {
didSet {
dragController.insets = dragBoundInsets
}
@ -24,7 +41,8 @@ open class PiPWindow: UIWindow {
private var exitPiPButton: UIButton?
/// Help out to bubble up the gesture detection outside of the rootVC frame
open override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
open override func point(inside point: CGPoint,
with event: UIEvent?) -> Bool {
guard let vc = rootViewController else {
return super.point(inside: point, with: event)
}
@ -36,35 +54,24 @@ open class PiPWindow: UIWindow {
if self.isHidden || self.alpha < 1 {
self.isHidden = false
self.alpha = 0
UIView.animate(
withDuration: 0.1,
delay: 0,
options: .beginFromCurrentState,
animations: {
animateTransition {
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: {
animateTransition {
self.alpha = 0
self.isHidden = true
},
completion: nil)
}
}
}
/// Resize the root view to PiP mode
open func goToPiP() {
open func enterPictureInPicture() {
guard let view = rootViewController?.view else { return }
isInPiP = true
animateRootViewChange()
@ -72,13 +79,15 @@ open class PiPWindow: UIWindow {
dragController.insets = dragBoundInsets
// add single tap gesture recognition for displaying exit PiP UI
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(toggleExitPiP))
let exitSelector = #selector(toggleExitPiP)
let tapGestureRecognizer = UITapGestureRecognizer(target: self,
action: exitSelector)
self.tapGestureRecognizer = tapGestureRecognizer
view.addGestureRecognizer(tapGestureRecognizer)
}
/// Resize the root view to full screen
open func goToFullScreen() {
open func exitPictureInPicture() {
isInPiP = false
animateRootViewChange()
dragController.stopDragListener()
@ -88,7 +97,8 @@ open class PiPWindow: UIWindow {
exitPiPButton = nil
// remove gesture
tapGestureRecognizer?.removeTarget(self, action: #selector(toggleExitPiP))
let exitSelector = #selector(toggleExitPiP)
tapGestureRecognizer?.removeTarget(self, action: exitSelector)
tapGestureRecognizer = nil
}
@ -98,8 +108,11 @@ open class PiPWindow: UIWindow {
}
/// Customize the presentation of exit pip button
open func configureExitPiPButton(target: Any, action: Selector) -> UIButton {
let buttonImage = UIImage.init(named: "image-resize", in: Bundle(for: type(of: self)), compatibleWith: nil)
open func configureExitPiPButton(target: Any,
action: Selector) -> UIButton {
let buttonImage = UIImage.init(named: "image-resize",
in: Bundle(for: type(of: self)),
compatibleWith: nil)
let button = UIButton(type: .custom)
let size: CGSize = CGSize(width: 44, height: 44)
button.setImage(buttonImage, for: .normal)
@ -143,7 +156,9 @@ open class PiPWindow: UIWindow {
if exitPiPButton == nil {
// show button
let button = configureExitPiPButton(target: self, action: #selector(exitPiP))
let exitSelector = #selector(exitPictureInPicture)
let button = configureExitPiPButton(target: self,
action: exitSelector)
view.addSubview(button)
exitPiPButton = button
@ -152,10 +167,19 @@ open class PiPWindow: UIWindow {
exitPiPButton?.removeFromSuperview()
exitPiPButton = nil
}
}
@objc private func exitPiP() {
goToFullScreen()
exitPictureInPicture()
}
// MARK: - Animation transition
private func animateTransition(animations: @escaping () -> Void) {
UIView.animate(withDuration: 0.1,
delay: 0,
options: .beginFromCurrentState,
animations: animations,
completion: nil)
}
}

View File

Before

Width:  |  Height:  |  Size: 509 B

After

Width:  |  Height:  |  Size: 509 B

View File

Before

Width:  |  Height:  |  Size: 724 B

After

Width:  |  Height:  |  Size: 724 B