Hook up CallKit lock screen muting controls to the ElementCall widget

This commit is contained in:
Stefan Ceriu 2024-06-27 14:33:16 +03:00 committed by Stefan Ceriu
parent 2cce310540
commit d16ba9f3ef
6 changed files with 108 additions and 8 deletions

View File

@ -157,8 +157,8 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
switch action {
case .startCall(let roomID):
self?.handleAppRoute(.call(roomID: roomID))
case .endCall:
break // Handled internally in the UserSessionFlowCoordinator
default:
break
}
}
.store(in: &cancellables)

View File

@ -173,10 +173,10 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
.receive(on: DispatchQueue.main)
.sink { [weak self] action in
switch action {
case .startCall:
break
case .endCall:
self?.dismissCallScreenIfNeeded()
default:
break
}
}
.store(in: &cancellables)

View File

@ -1,4 +1,4 @@
// Generated using Sourcery 2.2.4 https://github.com/krzysztofzablocki/Sourcery
// Generated using Sourcery 2.2.5 https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT
// swiftlint:disable all
@ -4902,6 +4902,47 @@ class ElementCallServiceMock: ElementCallServiceProtocol {
tearDownCallSessionCallsCount += 1
tearDownCallSessionClosure?()
}
//MARK: - setCallMuted
var setCallMutedRoomIDUnderlyingCallsCount = 0
var setCallMutedRoomIDCallsCount: Int {
get {
if Thread.isMainThread {
return setCallMutedRoomIDUnderlyingCallsCount
} else {
var returnValue: Int? = nil
DispatchQueue.main.sync {
returnValue = setCallMutedRoomIDUnderlyingCallsCount
}
return returnValue!
}
}
set {
if Thread.isMainThread {
setCallMutedRoomIDUnderlyingCallsCount = newValue
} else {
DispatchQueue.main.sync {
setCallMutedRoomIDUnderlyingCallsCount = newValue
}
}
}
}
var setCallMutedRoomIDCalled: Bool {
return setCallMutedRoomIDCallsCount > 0
}
var setCallMutedRoomIDReceivedArguments: (muted: Bool, roomID: String)?
var setCallMutedRoomIDReceivedInvocations: [(muted: Bool, roomID: String)] = []
var setCallMutedRoomIDClosure: ((Bool, String) -> Void)?
func setCallMuted(_ muted: Bool, roomID: String) {
setCallMutedRoomIDCallsCount += 1
setCallMutedRoomIDReceivedArguments = (muted: muted, roomID: roomID)
DispatchQueue.main.async {
self.setCallMutedRoomIDReceivedInvocations.append((muted: muted, roomID: roomID))
}
setCallMutedRoomIDClosure?(muted, roomID)
}
}
class ElementCallWidgetDriverMock: ElementCallWidgetDriverProtocol {
var widgetID: String {

View File

@ -58,11 +58,35 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol
return
}
// TODO: intercept EC mute state changes and pass them over to CallKit
// elementCallService.setCallMuted(roomID: roomProxy.id, muted: muted)
Task {
await self.widgetDriver.sendMessage(message)
}
}
elementCallService.actions
.receive(on: DispatchQueue.main)
.sink { [weak self] action in
guard let self else { return }
switch action {
case let .setCallMuted(muted, roomID):
guard roomID == roomProxy.id else {
MXLog.error("Received mute request for a different room: \(roomID) != \(roomProxy.id)")
return
}
Task {
await self.setMuted(muted)
}
default:
break
}
}
.store(in: &cancellables)
widgetDriver.messagePublisher
.receive(on: DispatchQueue.main)
.sink { [weak self] receivedMessage in
@ -115,6 +139,8 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol
await elementCallService.setupCallSession(roomID: roomProxy.id, roomDisplayName: roomProxy.roomTitle)
// TODO: Pass over the current EC mute status to CallKit
let _ = await roomProxy.sendCallNotificationIfNeeeded()
}
}
@ -137,6 +163,10 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol
// MARK: - Private
private func setMuted(_ muted: Bool) async {
// Not supported on EC yet
}
private func hangUp() async {
let hangUpMessage = """
{"api":"fromWidget",

View File

@ -103,6 +103,25 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe
func tearDownCallSession() {
tearDownCallSession(sendEndCallAction: true)
}
func setCallMuted(_ muted: Bool, roomID: String) {
guard let ongoingCallID else {
MXLog.error("Failed toggling call microphone, no calls running")
return
}
guard ongoingCallID.roomID == roomID else {
MXLog.error("Failed toggling call microphone, rooms don't match: \(ongoingCallID.roomID) != \(roomID)")
return
}
let transaction = CXTransaction(action: CXSetMutedCallAction(call: ongoingCallID.callKitID, muted: muted))
callController.request(transaction) { error in
if let error {
MXLog.error("Failed toggling call microphone with error: \(error)")
}
}
}
// MARK: - PKPushRegistryDelegate
@ -164,9 +183,16 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe
}
func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
// Forward this to the widget somehow
// webView.evaluateJavaScript("groupCall.setLocalVideoMuted(!groupCall.isLocalVideoMuted())")
// webView.evaluateJavaScript("groupCall.setMicrophoneMuted(!groupCall.isMicrophoneMuted())"
// if let ongoingCallID {
// actionsSubject.send(.setCallMuted(action.isMuted, roomID: ongoingCallID.roomID))
// } else {
// MXLog.error("Failed muting/unmuting call, missing ongoingCallID")
// }
//
// action.fulfill()
// TODO: EC doesn't expose controls for this yet. Fail the action for now.
action.fail()
}
func provider(_ provider: CXProvider, perform action: CXEndCallAction) {

View File

@ -19,6 +19,7 @@ import Combine
enum ElementCallServiceAction {
case startCall(roomID: String)
case endCall(roomID: String)
case setCallMuted(_ muted: Bool, roomID: String)
}
enum ElementCallServiceNotificationKey: String {
@ -35,4 +36,6 @@ protocol ElementCallServiceProtocol {
func setupCallSession(roomID: String, roomDisplayName: String) async
func tearDownCallSession()
func setCallMuted(_ muted: Bool, roomID: String)
}