EC: Native switch for audio outputs/inputs and earpiece (#4136)

* EC: native switch for audio outputs/inputs and earpiece

also includes proximity sensor enabling when the earpiece is used.

* test disabling and re-enabling audio on audio change

* Revert "test disabling and re-enabling audio on audio change"

This reverts commit 907c791b8df7009214f5c164ecf8fe7d90a8addc.

* pr suggestions

* pr suggestion and small refactor of the widgetAction handler
This commit is contained in:
Mauro
2025-06-05 14:07:41 +02:00
committed by GitHub
parent 97e23b556f
commit b628f5d2c2
4 changed files with 138 additions and 31 deletions
@@ -75,6 +75,7 @@ private struct CallView: UIViewRepresentable {
private var webView: WKWebView!
private var pictureInPictureController: AVPictureInPictureController?
private let pictureInPictureViewController: AVPictureInPictureVideoCallViewController
private var routePickerView: AVRoutePickerView!
/// The view to be shown in the app. This will contain the web view when picture in picture isn't running.
let webViewWrapper = WebViewWrapper(frame: .zero)
@@ -97,7 +98,9 @@ private struct CallView: UIViewRepresentable {
let configuration = WKWebViewConfiguration()
let userContentController = WKUserContentController()
userContentController.add(WKScriptMessageHandlerWrapper(self), name: viewModelContext.viewState.messageHandler)
CallScreenJavaScriptMessageName.allCases.forEach {
userContentController.add(WKScriptMessageHandlerWrapper(self), name: $0.rawValue)
}
// Required to allow a webview that uses file URL to load its own assets
configuration.preferences.setValue(true, forKey: "allowFileAccessFromFileURLs")
@@ -123,6 +126,12 @@ private struct CallView: UIViewRepresentable {
webView.backgroundColor = .compound.bgCanvasDefault
webView.scrollView.backgroundColor = .compound.bgCanvasDefault
// This button is always hidden and is only used to be programmaticaly tapped
routePickerView = AVRoutePickerView(frame: .zero)
routePickerView.isHidden = true
routePickerView.isUserInteractionEnabled = false
webView.addSubview(routePickerView)
webViewWrapper.addMatchedSubview(webView)
if AVPictureInPictureController.isPictureInPictureSupported() {
@@ -161,7 +170,32 @@ private struct CallView: UIViewRepresentable {
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
viewModelContext?.javaScriptMessageHandler?(message.body)
guard let handlerID = CallScreenJavaScriptMessageName(rawValue: message.name) else {
return
}
switch handlerID {
case .widgetAction:
guard let message = message.body as? String else { return }
viewModelContext?.send(viewAction: .widgetAction(message: message))
case .showNativeOutputDevicePicker:
DispatchQueue.main.async {
self.tapRoutePickerView()
}
case .onOutputDeviceSelect:
guard let deviceID = message.body as? String else { return }
viewModelContext?.send(viewAction: .outputDeviceSelected(deviceID: deviceID))
}
}
// This function is called by the webview output routing button
// it allows to open the OS output selector using the hidden button.
private func tapRoutePickerView() {
guard let button = routePickerView.subviews.first(where: { $0 is UIButton }) as? UIButton else {
return
}
button.sendActions(for: .touchUpInside)
}
// MARK: - WKUIDelegate
@@ -172,6 +206,7 @@ private struct CallView: UIViewRepresentable {
return .deny
}
viewModelContext?.send(viewAction: .mediaCapturePermissionGranted)
return .grant
}