import Flutter import UIKit import AVFoundation import MediaPlayer @main @objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate { private var deviceAdjustmentChannel: FlutterMethodChannel? private var originalBrightness: CGFloat? private var volumeView: MPVolumeView? private weak var volumeSlider: UISlider? override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { // Configure audio session for media playback. Not activated here: the // session is non-mixing, so activation stops other apps' audio — it is // claimed when playback actually starts. do { let session = AVAudioSession.sharedInstance() try session.setCategory(.playback, mode: .default) } catch { print("Failed to configure audio session: \(error)") } application.beginReceivingRemoteControlEvents() return super.application(application, didFinishLaunchingWithOptions: launchOptions) } func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) { GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry) // Register MPV player plugin if let registrar = engineBridge.pluginRegistry.registrar(forPlugin: "MpvPlayerPlugin") { MpvPlayerPlugin.register(with: registrar) } // Register the audio-only MPV player plugin (music playback) if let registrar = engineBridge.pluginRegistry.registrar(forPlugin: "MpvAudioPlayerPlugin") { MpvAudioPlayerPlugin.register(with: registrar) } if let registrar = engineBridge.pluginRegistry.registrar(forPlugin: "AtmosProbePlugin") { AtmosProbePlugin.register(with: registrar) } if let registrar = engineBridge.pluginRegistry.registrar(forPlugin: "DeviceAdjustmentChannel") { registerDeviceAdjustmentChannel(messenger: registrar.messenger()) } } private func registerDeviceAdjustmentChannel(messenger: FlutterBinaryMessenger) { let channel = FlutterMethodChannel(name: "com.plezy/device_adjustment", binaryMessenger: messenger) channel.setMethodCallHandler { [weak self] call, result in DispatchQueue.main.async { self?.handleDeviceAdjustmentCall(call, result: result) } } deviceAdjustmentChannel = channel } private func handleDeviceAdjustmentCall(_ call: FlutterMethodCall, result: @escaping FlutterResult) { switch call.method { case "getBrightness": result(Double(UIScreen.main.brightness)) case "setBrightness": guard let value = normalizedArgument(call.arguments, result: result) else { return } if originalBrightness == nil { originalBrightness = UIScreen.main.brightness } UIScreen.main.brightness = CGFloat(value) result(nil) case "restoreBrightness": if let originalBrightness = originalBrightness { UIScreen.main.brightness = originalBrightness self.originalBrightness = nil } result(nil) case "getMediaVolume": result(Double(AVAudioSession.sharedInstance().outputVolume)) case "setMediaVolume": guard let value = normalizedArgument(call.arguments, result: result) else { return } if setMediaVolume(value) { result(nil) } else { result( FlutterError(code: "DEVICE_ADJUSTMENT_UNAVAILABLE", message: "System volume slider unavailable", details: nil) ) } default: result(FlutterMethodNotImplemented) } } private func normalizedArgument(_ arguments: Any?, result: @escaping FlutterResult) -> Double? { guard let value = arguments as? NSNumber else { result(FlutterError(code: "INVALID_ARGUMENT", message: "Expected a numeric value", details: nil)) return nil } let doubleValue = value.doubleValue guard doubleValue.isFinite else { result(FlutterError(code: "INVALID_ARGUMENT", message: "Expected a finite numeric value", details: nil)) return nil } return min(1.0, max(0.0, doubleValue)) } private func setMediaVolume(_ value: Double) -> Bool { guard let slider = ensureVolumeSlider() else { return false } slider.setValue(Float(value), animated: false) slider.sendActions(for: .valueChanged) slider.sendActions(for: .touchUpInside) return true } private func ensureVolumeSlider() -> UISlider? { if let volumeSlider = volumeSlider { return volumeSlider } let volumeView = self.volumeView ?? MPVolumeView(frame: CGRect(x: -1000, y: -1000, width: 1, height: 1)) volumeView.showsRouteButton = false volumeView.showsVolumeSlider = true volumeView.alpha = 0.01 volumeView.isUserInteractionEnabled = false if volumeView.superview == nil { activeWindow?.addSubview(volumeView) } volumeView.layoutIfNeeded() self.volumeView = volumeView let slider = findVolumeSlider(in: volumeView) volumeSlider = slider return slider } private func findVolumeSlider(in view: UIView) -> UISlider? { if let slider = view as? UISlider { return slider } for subview in view.subviews { if let slider = findVolumeSlider(in: subview) { return slider } } return nil } private var activeWindow: UIWindow? { if #available(iOS 13.0, *) { for scene in UIApplication.shared.connectedScenes { guard let windowScene = scene as? UIWindowScene else { continue } if let keyWindow = windowScene.windows.first(where: { $0.isKeyWindow }) { return keyWindow } } for scene in UIApplication.shared.connectedScenes { guard let windowScene = scene as? UIWindowScene, let window = windowScene.windows.first else { continue } return window } return nil } return UIApplication.shared.windows.first(where: { $0.isKeyWindow }) ?? UIApplication.shared.windows.first } }