refactors design to flutter driven service core.
This commit is contained in:
@@ -178,99 +178,3 @@ import AVKit
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
}
|
||||
|
||||
private func handleCastingMethod(call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
switch call.method {
|
||||
case "getStatus":
|
||||
updateCastingStatusForCurrentRoute()
|
||||
result(castingStatus)
|
||||
case "showDevicePicker":
|
||||
showAirPlayPicker()
|
||||
result(nil)
|
||||
case "startCastingLiveStream":
|
||||
// AirPlay route selection controls where iOS outputs audio. No explicit load call needed.
|
||||
result(nil)
|
||||
case "disconnect":
|
||||
// iOS does not expose direct route disconnect. Users select route via picker.
|
||||
showAirPlayPicker()
|
||||
result(nil)
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
private func showAirPlayPicker() {
|
||||
DispatchQueue.main.async {
|
||||
guard let rootViewController = self.activeRootViewController() else {
|
||||
self.emitCastingEvent(status: "error", message: "Unable to open AirPlay picker")
|
||||
return
|
||||
}
|
||||
|
||||
let routePickerView = AVRoutePickerView(frame: CGRect(x: 0, y: 0, width: 1, height: 1))
|
||||
routePickerView.alpha = 0.01
|
||||
rootViewController.view.addSubview(routePickerView)
|
||||
|
||||
for subview in routePickerView.subviews {
|
||||
if let button = subview as? UIButton {
|
||||
button.sendActions(for: .touchUpInside)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
|
||||
routePickerView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func activeRootViewController() -> UIViewController? {
|
||||
let activeScene = UIApplication.shared.connectedScenes
|
||||
.first { $0.activationState == .foregroundActive } as? UIWindowScene
|
||||
let keyWindow = activeScene?.windows.first { $0.isKeyWindow }
|
||||
return keyWindow?.rootViewController
|
||||
}
|
||||
|
||||
private func updateCastingStatusForCurrentRoute() {
|
||||
let currentOutputs = AVAudioSession.sharedInstance().currentRoute.outputs
|
||||
if let airPlayOutput = currentOutputs.first(where: { $0.portType == .airPlay }) {
|
||||
castingStatus = [
|
||||
"status": "connected",
|
||||
"platform": "ios",
|
||||
"deviceName": airPlayOutput.portName
|
||||
]
|
||||
} else {
|
||||
castingStatus = [
|
||||
"status": "idle",
|
||||
"platform": "ios"
|
||||
]
|
||||
}
|
||||
|
||||
castingEventSink?(castingStatus)
|
||||
}
|
||||
|
||||
private func emitCastingEvent(status: String, message: String) {
|
||||
let event: [String: Any] = [
|
||||
"status": status,
|
||||
"platform": "ios",
|
||||
"message": message
|
||||
]
|
||||
|
||||
castingStatus = event
|
||||
castingEventSink?(event)
|
||||
}
|
||||
|
||||
func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
|
||||
castingEventSink = events
|
||||
events(castingStatus)
|
||||
return nil
|
||||
}
|
||||
|
||||
func onCancel(withArguments arguments: Any?) -> FlutterError? {
|
||||
castingEventSink = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user