diff --git a/.AgoraUIKit_macOS.podspec b/.AgoraUIKit_macOS.podspec index 0c37dc6a..d2777589 100644 --- a/.AgoraUIKit_macOS.podspec +++ b/.AgoraUIKit_macOS.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'AgoraUIKit_macOS' - s.version = '1.7.2' + s.version = '1.7.5' s.summary = 'Agora video session AppKit template.' s.description = <<-DESC @@ -25,7 +25,7 @@ Use this Pod to create a video AppKit view that can be easily added to your macO s.source_files = 'Sources/Agora-UIKit/*' s.pod_target_xcconfig = { 'ONLY_ACTIVE_ARCH' => 'YES' } - s.dependency 'AgoraRtcEngine_macOS', '~> 3.5.0' - s.dependency 'AgoraRtm_macOS', '~> 1.4.9' + s.dependency 'AgoraRtcEngine_macOS/RtcBasic' + s.dependency 'AgoraRtm_macOS', '~> 1.4.10' s.static_framework = false end diff --git a/.github/workflows/podlint.yml b/.github/workflows/podlint.yml index 40f44c9c..78f1c2c8 100644 --- a/.github/workflows/podlint.yml +++ b/.github/workflows/podlint.yml @@ -1,14 +1,18 @@ -on: push name: "Pod Lint" + +on: + push: + branches: + - "main" + pull_request: + branches: + - "*" + jobs: podlint: runs-on: macOS-latest steps: - uses: actions/checkout@master - - name: Set XCode Version - run: | - ls /Applications/Xcode* - sudo xcode-select -s /Applications/Xcode_11.7.app - name: Pod Lint run: | pod lib lint --allow-warnings --skip-import-validation diff --git a/AgoraUIKit_iOS.podspec b/AgoraUIKit_iOS.podspec index 27bd5fac..17e74036 100644 --- a/AgoraUIKit_iOS.podspec +++ b/AgoraUIKit_iOS.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'AgoraUIKit_iOS' - s.version = '4.0.0-preview.11' + s.version = '4.0.0-preview.12' s.summary = 'Agora video session UIKit template.' s.description = <<-DESC @@ -24,8 +24,8 @@ Use this Pod to create a video UIKit view that can be easily added to your iOS a s.swift_versions = ['5.0'] s.source_files = 'Sources/Agora-UIKit/*' - s.dependency 'AgoraRtcEngine_iOS_Preview', '4.0.0-preview.5' - s.dependency 'AgoraRtm_iOS', '~> 1.4.9' + s.dependency 'AgoraRtcEngine_iOS_Preview', '4.0.0-preview.3' + s.dependency 'AgoraRtm_iOS', '~> 1.4.10' s.static_framework = true end diff --git a/Package.swift b/Package.swift index dd52886e..891d5852 100644 --- a/Package.swift +++ b/Package.swift @@ -13,18 +13,20 @@ let package = Package( .package( name: "AgoraRtcKit", url: "https://github.com/agorabuilder/AgoraRtcEngine_iOS_Preview", - .upToNextMajor(from: .init(4, 0, 0, prereleaseIdentifiers: ["-preview"], buildMetadataIdentifiers: [])) + .exact("4.0.0-preview.3") +// .upToNextMajor(from: .init(4, 0, 0, prereleaseIdentifiers: ["-preview"], buildMetadataIdentifiers: [])) ), .package( name: "AgoraRtmKit", url: "https://github.com/AgoraIO/AgoraRtm_iOS", - from: "1.4.9" + from: "1.4.10" ) ], targets: [ .target( name: "AgoraUIKit_iOS", dependencies: ["AgoraRtcKit", "AgoraRtmKit"], +// dependencies: [.product(name: "RtcBasic", package: "AgoraRtcKit"), "AgoraRtmKit"], path: "Sources/Agora-UIKit" ), .testTarget(name: "AgoraUIKit-Tests", dependencies: ["AgoraUIKit_iOS"], path: "Tests/Agora-UIKit-Tests") diff --git a/Sources/Agora-UIKit/AgoraRtmController+AgoraRtmDelegate.swift b/Sources/Agora-UIKit/AgoraRtmController+AgoraRtmDelegate.swift index 3abc74eb..57c3b6e8 100644 --- a/Sources/Agora-UIKit/AgoraRtmController+AgoraRtmDelegate.swift +++ b/Sources/Agora-UIKit/AgoraRtmController+AgoraRtmDelegate.swift @@ -95,13 +95,13 @@ extension AgoraRtmController: AgoraRtmDelegate, AgoraRtmChannelDelegate { self.delegate.videoLookup[rtcId]? .showOptions = self.agoraSettings.showRemoteRequestOptions } - case .genericAction(let genericAction): - switch genericAction.type { - case .userdata: + case .dataRequest(let requestVal): + switch requestVal.type { + case .userData: self.sendPersonalData(to: peerId) case .ping: self.sendRaw( - message: RtmGenericRequest(type: .pong), member: peerId + message: RtmDataRequest(type: .pong), member: peerId ) {_ in } case .pong: AgoraVideoViewer.agoraPrint( diff --git a/Sources/Agora-UIKit/AgoraRtmController+Helpers.swift b/Sources/Agora-UIKit/AgoraRtmController+Helpers.swift index 415666a8..c2bfaea0 100644 --- a/Sources/Agora-UIKit/AgoraRtmController+Helpers.swift +++ b/Sources/Agora-UIKit/AgoraRtmController+Helpers.swift @@ -16,7 +16,7 @@ extension AgoraRtmController { /// DecodedRtmAction type containing data about a user (local or remote) case userData(_: UserData) /// Message that contains a small action request, such as a ping or requesting a user's data - case genericAction(_: RtmGenericRequest) + case dataRequest(_: RtmDataRequest) } /// Decode message to a compatible DecodedRtmMessage type. @@ -30,8 +30,8 @@ extension AgoraRtmController { return .userData(userData) } else if let muteReq = try? decoder.decode(MuteRequest.self, from: data) { return .mute(muteReq) - } else if let genericRequest = try? decoder.decode(RtmGenericRequest.self, from: data) { - return .genericAction(genericRequest) + } else if let requestVal = try? decoder.decode(RtmDataRequest.self, from: data) { + return .dataRequest(requestVal) } return nil } diff --git a/Sources/Agora-UIKit/AgoraRtmController+MuteRequests.swift b/Sources/Agora-UIKit/AgoraRtmController+MuteRequests.swift index 069a1ec8..02215ccd 100644 --- a/Sources/Agora-UIKit/AgoraRtmController+MuteRequests.swift +++ b/Sources/Agora-UIKit/AgoraRtmController+MuteRequests.swift @@ -51,9 +51,9 @@ extension AgoraRtmController { } /// Enum of request types being made. - public enum GenericRequestType: String, Codable { + public enum DataRequestType: String, Codable { /// Send this type when you are requesting a user's UserData - case userdata + case userData /// Send this type when you are requesting a user's presence case ping /// Send this type back to a presence request (ping) @@ -61,9 +61,11 @@ extension AgoraRtmController { } /// Request to be sent over RTM that has no content other than the type - public struct RtmGenericRequest: Codable { - /// Type of generic request. - public var type: GenericRequestType + public struct RtmDataRequest: Codable { + /// Type of message being sent + public var messageType: String? = "RtmDataRequest" + /// Type of data request. + public var type: DataRequestType } /// Create and send request to user to mute/unmute a device diff --git a/Sources/Agora-UIKit/AgoraSettings.swift b/Sources/Agora-UIKit/AgoraSettings.swift index ac58b44a..58c4c1ac 100644 --- a/Sources/Agora-UIKit/AgoraSettings.swift +++ b/Sources/Agora-UIKit/AgoraSettings.swift @@ -51,7 +51,7 @@ public struct AgoraSettings { } /// Position, top, left, bottom or right. public enum Position { - /// At the top of the view + /// At the top of the view. Not recommended for floating layout. case top /// At the right of the view case right diff --git a/Sources/Agora-UIKit/AgoraUIKit.swift b/Sources/Agora-UIKit/AgoraUIKit.swift index c48e42e0..fa8394c1 100644 --- a/Sources/Agora-UIKit/AgoraUIKit.swift +++ b/Sources/Agora-UIKit/AgoraUIKit.swift @@ -22,7 +22,7 @@ public struct AgoraUIKit: Codable { /// Framework type of UIKit. "native", "flutter", "reactnative" fileprivate(set) var framework: String /// Version of UIKit being used - static let version = "4.0.0-preview.11" + static let version = "4.0.0-preview.12" /// Framework type of UIKit. "native", "flutter", "reactnative" static let framework = "native" #if os(iOS) diff --git a/Sources/Agora-UIKit/AgoraVideoViewer+Buttons.swift b/Sources/Agora-UIKit/AgoraVideoViewer+Buttons.swift index c63e2279..3ab8be14 100644 --- a/Sources/Agora-UIKit/AgoraVideoViewer+Buttons.swift +++ b/Sources/Agora-UIKit/AgoraVideoViewer+Buttons.swift @@ -16,24 +16,73 @@ import AppKit // leaveChannel, toggleCam, toggleMic, flipCamera, toggleBroadcast, toggleBeautify extension AgoraVideoViewer { + #if os(iOS) + fileprivate func platformContainerSizing( + _ frameOriginX: inout CGFloat, _ frameOriginY: inout CGFloat, _ contWidth: CGFloat, + _ resizeMask: inout UIView.AutoresizingMask, _ containerSize: inout CGSize + ) { + resizeMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleTopMargin] + switch self.agSettings.buttonPosition { + case .top: + frameOriginY = 30 + resizeMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleBottomMargin] + case .left, .right: + containerSize = CGSize(width: containerSize.height, height: containerSize.width) + frameOriginY = (self.bounds.height - CGFloat(contWidth)) / 2 + if self.agSettings.buttonPosition == .left { + frameOriginX = 30 + resizeMask = [.flexibleTopMargin, .flexibleRightMargin, .flexibleBottomMargin] + } else { + frameOriginX = self.bounds.width - self.agoraSettings.buttonSize - 20 - 10 + resizeMask = [.flexibleTopMargin, .flexibleLeftMargin, .flexibleBottomMargin] + } + case .bottom: break + } + } + #elseif os(macOS) + + fileprivate func platformContainerSizing( + _ frameOriginX: inout CGFloat, _ frameOriginY: inout CGFloat, _ contWidth: CGFloat, + _ resizeMask: inout NSView.AutoresizingMask, _ containerSize: inout CGSize + ) { + switch self.agSettings.buttonPosition { + case .top, .bottom: + frameOriginX = (self.bounds.width - CGFloat(contWidth)) / 2 + if self.agSettings.buttonPosition == .top { + frameOriginY = self.bounds.height - self.agoraSettings.buttonSize - 20 - 10 + resizeMask = [.minXMargin, .maxXMargin, .minYMargin] + } else { + frameOriginY = 20 + resizeMask = [.minXMargin, .maxXMargin, .maxYMargin] + } + case .left, .right: + containerSize = CGSize(width: containerSize.height, height: containerSize.width) + frameOriginY = (self.bounds.height - CGFloat(contWidth)) / 2 + if self.agSettings.buttonPosition == .left { + frameOriginX = 20 + resizeMask = [.minYMargin, .maxXMargin, .maxYMargin] + } else { + frameOriginX = self.bounds.width - self.agoraSettings.buttonSize - 20 - 10 + resizeMask = [.minYMargin, .minXMargin, .maxYMargin] + } + } + } + #endif + fileprivate func positionButtonContainer(_ container: MPBlurView, _ contWidth: CGFloat, _ buttonMargin: CGFloat) { + var containerSize = CGSize(width: contWidth, height: self.agoraSettings.buttonSize + buttonMargin * 2) + var frameOriginX = (self.bounds.width - CGFloat(contWidth)) / 2 + var frameOriginY = self.bounds.height - self.agoraSettings.buttonSize - 20 - 10 + var resizeMask: MPView.AutoresizingMask = [] + platformContainerSizing(&frameOriginX, &frameOriginY, contWidth, &resizeMask, &containerSize) #if os(iOS) - container.frame = CGRect( - origin: CGPoint( - x: (self.bounds.width - CGFloat(contWidth)) / 2, - y: (self.bounds.height - self.agoraSettings.buttonSize - 20 - 10) - ), size: CGSize(width: contWidth, height: self.agoraSettings.buttonSize + buttonMargin * 2) - ) - container.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleTopMargin] container.layer.cornerRadius = self.agoraSettings.buttonSize / 3 container.clipsToBounds = true #elseif os(macOS) - container.frame = CGRect( - origin: CGPoint(x: (self.bounds.width - CGFloat(contWidth)) / 2, y: 10), - size: CGSize(width: contWidth, height: self.agoraSettings.buttonSize + buttonMargin * 2)) - container.autoresizingMask = [.minXMargin, .maxXMargin, .maxYMargin] container.layer?.cornerRadius = self.agoraSettings.buttonSize / 3 #endif + container.frame = CGRect(origin: CGPoint(x: frameOriginX, y: frameOriginY), size: containerSize) + container.autoresizingMask = resizeMask } /// Add all the relevant buttons. diff --git a/Sources/Agora-UIKit/AgoraVideoViewer+VideoControl.swift b/Sources/Agora-UIKit/AgoraVideoViewer+VideoControl.swift index 77bc5cd9..98330416 100644 --- a/Sources/Agora-UIKit/AgoraVideoViewer+VideoControl.swift +++ b/Sources/Agora-UIKit/AgoraVideoViewer+VideoControl.swift @@ -21,7 +21,8 @@ extension AgoraVideoViewer { self.agkit.setExternalVideoSource( agSettings.externalVideoSettings.enabled, useTexture: agSettings.externalVideoSettings.texture, - sourceType: agSettings.externalVideoSettings.encoded ? .encodedVideoFrame : .videoFrame + encodedFrame: agSettings.externalVideoSettings.encoded +// sourceType: agSettings.externalVideoSettings.encoded ? .encodedVideoFrame : .videoFrame ) if self.agSettings.externalAudioSettings.enabled { let audioSource = self.agSettings.externalAudioSettings diff --git a/Sources/Agora-UIKit/AgoraVideoViewer.swift b/Sources/Agora-UIKit/AgoraVideoViewer.swift index 49a2fcbd..9e85e045 100644 --- a/Sources/Agora-UIKit/AgoraVideoViewer.swift +++ b/Sources/Agora-UIKit/AgoraVideoViewer.swift @@ -245,7 +245,7 @@ open class AgoraVideoViewer: MPView, SingleVideoViewDelegate { let engine = AgoraRtcEngineKit.sharedEngine( withAppId: connectionData.appId, delegate: self ) - engine.enableAudioVolumeIndication(1000, smooth: 3, reportVad: self.agSettings.reportLocalVolume) + engine.enableAudioVolumeIndication(1000, smooth: 3) // , reportVad: self.agSettings.reportLocalVolume) engine.setChannelProfile(.liveBroadcasting) if self.agoraSettings.usingDualStream { engine.enableDualStreamMode(true) diff --git a/Tests/Agora-UIKit-Tests/RtmMessagingTests.swift b/Tests/Agora-UIKit-Tests/RtmMessagingTests.swift index 22b9ea7f..433b91b5 100644 --- a/Tests/Agora-UIKit-Tests/RtmMessagingTests.swift +++ b/Tests/Agora-UIKit-Tests/RtmMessagingTests.swift @@ -41,8 +41,53 @@ final class RtmMessagesTests: XCTestCase { XCTAssertEqual(decodedMuteReq.isForceful, muteReq.isForceful, "mute invalid!") case .userData: XCTFail("Should not decode to userData") - case .genericAction: - XCTFail("Should not decode to genericAction") + case .dataRequest: + XCTFail("Should not decode to dataRequest") + } + } + + func testEncodeUserData() throws { + let userData = AgoraRtmController.UserData( + rtmId: "1234-5678", rtcId: 190, username: "username", + role: AgoraClientRole.broadcaster.rawValue, agora: .current, uikit: .current + ) + guard let rawMsg = AgoraRtmController.createRawRtm(from: userData) else { + return XCTFail("UserData should be encodable") + } + XCTAssert(rawMsg.text == "AgoraUIKit", "Message text data should be AgoraUIKit") + let msgText = String(data: rawMsg.rawData, encoding: .utf8) + + guard let unencodedJSON = try? JSONSerialization.jsonObject( + with: rawMsg.rawData, options: .allowFragments + ) as? [String: Any] else { + return XCTFail("Could not unencode data") + } + XCTAssertEqual((unencodedJSON["rtcId"] as? UInt), userData.rtcId, "rtcId invalid!") + XCTAssertEqual((unencodedJSON["role"] as? Int), userData.role, "mute invalid!") + XCTAssertEqual((unencodedJSON["username"] as? String), userData.username, "device invalid!") + if let agoraData = unencodedJSON["agora"] as? [String: Any] { + XCTAssertEqual((agoraData["rtc"] as? String), AgoraRtcEngineKit.getSdkVersion(), "rtcId invalid!") + XCTAssertEqual((agoraData["rtm"] as? String), AgoraRtmKit.getSDKVersion(), "mute invalid!") + } else { XCTFail("Could not parse agora version data") } + let msgTextValid = "{\"uikit\":{" + + "\"platform\":\"ios\",\"version\":\"\(AgoraUIKit.version)\",\"framework\":\"native\"}," + + "\"role\":1,\"rtmId\":\"1234-5678\",\"username\":\"username\",\"agora\":{\"rtm\":" + + "\"\(AgoraRtmKit.getSDKVersion()!)\",\"rtc\":\"\(AgoraRtcEngineKit.getSdkVersion())\"}," + + "\"messageType\":\"UserData\",\"rtcId\":190}" + + XCTAssertEqual(msgText, msgTextValid, "Message text not matching msgTextValid") + guard let decodedMsg = AgoraRtmController.decodeRawRtmData(data: rawMsg.rawData, from: "") else { + return XCTFail("Failed to decode message") + } + + switch decodedMsg { + case .userData(let decodedUserData): + XCTAssertEqual(decodedUserData.rtcId, userData.rtcId, "rtcId invalid!") + XCTAssertEqual(decodedUserData.rtmId, userData.rtmId, "rtmId invalid!") + XCTAssertEqual(decodedUserData.agora.rtc, AgoraRtcEngineKit.getSdkVersion(), "RTC version invalid!") + XCTAssertEqual(decodedUserData.uikit.framework, "native", "RTC version invalid!") + default: + XCTFail("Should decode to userData") } }