Skip to content

Commit 93daaf5

Browse files
authored
Handle notifications properly when a call is happening. (#3276)
1 parent ce83aec commit 93daaf5

File tree

6 files changed

+51
-11
lines changed

6 files changed

+51
-11
lines changed

ElementX/Sources/Application/AppCoordinator.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,8 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
643643
.sink { [weak self] action in
644644
guard let self else { return }
645645
switch action {
646+
case .pictureInPictureIsAvailable:
647+
break
646648
case .pictureInPictureStarted, .pictureInPictureStopped:
647649
// Don't allow PiP when signed out - the user could login at which point we'd
648650
// need to hand over the call from here to the user session flow coordinator.

ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
319319
} else {
320320
Task { await self.startRoomFlow(roomID: roomID, via: via, entryPoint: entryPoint, animated: animated) }
321321
}
322+
hideCallScreenOverlay() // Turn any active call into a PiP so that navigation from a notification is visible to the user.
322323
case(.roomList, .deselectRoom, .roomList):
323324
dismissRoomFlow(animated: animated)
324325

@@ -604,15 +605,16 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
604605
.sink { [weak self] action in
605606
guard let self else { return }
606607
switch action {
607-
case .pictureInPictureStarted(let controller):
608-
MXLog.info("Hiding call for PiP presentation.")
608+
case .pictureInPictureIsAvailable(let controller):
609609
callScreenPictureInPictureController = controller
610+
case .pictureInPictureStarted:
611+
MXLog.info("Hiding call for PiP presentation.")
610612
navigationSplitCoordinator.setOverlayPresentationMode(.minimized)
611613
case .pictureInPictureStopped:
612614
MXLog.info("Restoring call after PiP presentation.")
613615
navigationSplitCoordinator.setOverlayPresentationMode(.fullScreen)
614-
callScreenPictureInPictureController = nil
615616
case .dismiss:
617+
callScreenPictureInPictureController = nil
616618
navigationSplitCoordinator.setOverlayCoordinator(nil)
617619
}
618620
}
@@ -623,12 +625,24 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
623625
analytics.track(screen: .RoomCall)
624626
}
625627

628+
private func hideCallScreenOverlay() {
629+
guard let callScreenPictureInPictureController else {
630+
MXLog.warning("Picture in picture isn't available, dismissing the call screen.")
631+
dismissCallScreenIfNeeded()
632+
return
633+
}
634+
635+
MXLog.info("Starting picture in picture to hide the call screen overlay.")
636+
callScreenPictureInPictureController.startPictureInPicture()
637+
navigationSplitCoordinator.setOverlayPresentationMode(.minimized)
638+
}
639+
626640
private func dismissCallScreenIfNeeded() {
627-
guard navigationSplitCoordinator.sheetCoordinator is CallScreenCoordinator else {
641+
guard navigationSplitCoordinator.overlayCoordinator is CallScreenCoordinator else {
628642
return
629643
}
630644

631-
navigationSplitCoordinator.setSheetCoordinator(nil)
645+
navigationSplitCoordinator.setOverlayCoordinator(nil)
632646
}
633647

634648
// MARK: Secure backup confirmation

ElementX/Sources/Screens/CallScreen/CallScreenCoordinator.swift

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,13 @@ struct CallScreenCoordinatorParameters {
1717
}
1818

1919
enum CallScreenCoordinatorAction {
20-
/// The call is still ongoing but the user wishes to navigate around the app.
21-
case pictureInPictureStarted(AVPictureInPictureController)
20+
/// The call is able to be minimised to picture in picture with the provided controller.
21+
///
22+
/// **Note:** Manually starting the PiP will not trigger the action below as we don't want
23+
/// to change the app's navigation when backgrounding the app with the call screen visible.
24+
case pictureInPictureIsAvailable(AVPictureInPictureController)
25+
/// The call is still ongoing but the user requested to navigate around the app.
26+
case pictureInPictureStarted
2227
/// The call is hidden and the user wishes to return to it.
2328
case pictureInPictureStopped
2429
/// The call is finished and the screen is done with.
@@ -46,8 +51,10 @@ final class CallScreenCoordinator: CoordinatorProtocol {
4651
guard let self else { return }
4752

4853
switch action {
49-
case .pictureInPictureStarted(let controller):
50-
actionsSubject.send(.pictureInPictureStarted(controller))
54+
case .pictureInPictureIsAvailable(let controller):
55+
actionsSubject.send(.pictureInPictureIsAvailable(controller))
56+
case .pictureInPictureStarted:
57+
actionsSubject.send(.pictureInPictureStarted)
5158
case .pictureInPictureStopped:
5259
actionsSubject.send(.pictureInPictureStopped)
5360
case .dismiss:

ElementX/Sources/Screens/CallScreen/CallScreenModels.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import AVKit
99
import Foundation
1010

1111
enum CallScreenViewModelAction {
12-
case pictureInPictureStarted(AVPictureInPictureController)
12+
case pictureInPictureIsAvailable(AVPictureInPictureController)
13+
case pictureInPictureStarted
1314
case pictureInPictureStopped
1415
case dismiss
1516
}
@@ -34,6 +35,7 @@ struct Bindings {
3435

3536
enum CallScreenViewAction {
3637
case urlChanged(URL?)
38+
case pictureInPictureIsAvailable(AVPictureInPictureController)
3739
case navigateBack
3840
case pictureInPictureWillStop
3941
case endCall

ElementX/Sources/Screens/CallScreen/CallScreenViewModel.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol
111111
case .urlChanged(let url):
112112
guard let url else { return }
113113
MXLog.info("URL changed to: \(url)")
114+
case .pictureInPictureIsAvailable(let controller):
115+
actionsSubject.send(.pictureInPictureIsAvailable(controller))
114116
case .navigateBack:
115117
Task { await handleBackwardsNavigation() }
116118
case .pictureInPictureWillStop:
@@ -188,7 +190,7 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol
188190

189191
switch await requestPictureInPictureHandler() {
190192
case .success(let controller):
191-
actionsSubject.send(.pictureInPictureStarted(controller))
193+
actionsSubject.send(.pictureInPictureStarted)
192194
case .failure:
193195
actionsSubject.send(.dismiss)
194196
}

ElementX/Sources/Screens/CallScreen/View/CallScreen.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ private struct CallView: UIViewRepresentable {
128128
contentViewController: pictureInPictureViewController))
129129
pictureInPictureController.delegate = self
130130
self.pictureInPictureController = pictureInPictureController
131+
viewModelContext.send(viewAction: .pictureInPictureIsAvailable(pictureInPictureController))
131132
}
132133
}
133134

@@ -222,6 +223,18 @@ private struct CallView: UIViewRepresentable {
222223
}
223224
}
224225

226+
nonisolated func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
227+
Task { @MainActor in
228+
// Double check that the controller is definitely showing a page that supports picture in picture.
229+
// This is necessary as it doesn't get checked when backgrounding the app or tapping a notification.
230+
guard case .success(true) = await webViewCanEnterPictureInPicture() else {
231+
MXLog.error("Picture in picture started on a webpage that doesn't support it. Ending the call.")
232+
viewModelContext?.send(viewAction: .endCall)
233+
return
234+
}
235+
}
236+
}
237+
225238
nonisolated func pictureInPictureControllerWillStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
226239
Task { await viewModelContext?.send(viewAction: .pictureInPictureWillStop) }
227240
}

0 commit comments

Comments
 (0)