feat(player): add mobile edge adjustments
This commit is contained in:
@@ -1,9 +1,15 @@
|
||||
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]?
|
||||
@@ -29,5 +35,117 @@ import AVFoundation
|
||||
if let registrar = engineBridge.pluginRegistry.registrar(forPlugin: "MpvPlayerPlugin") {
|
||||
MpvPlayerPlugin.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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user