From 49c5c66db6251a0e3481b2e2692541b7c5ef2f18 Mon Sep 17 00:00:00 2001 From: Robin Date: Wed, 29 Jan 2025 22:38:32 +0100 Subject: [PATCH 1/6] Update build.gradle --- android/build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/android/build.gradle b/android/build.gradle index de10f50..8de104d 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -22,6 +22,8 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { + namespace 'top.huic.perfect_volume_control' + compileSdkVersion 30 defaultConfig { From b45eedd75321835675523a8dcc2a4320c83532ac Mon Sep 17 00:00:00 2001 From: Robin Date: Wed, 29 Jan 2025 22:50:48 +0100 Subject: [PATCH 2/6] Update AndroidManifest.xml --- android/src/main/AndroidManifest.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 75aae3e..a2f47b6 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,3 +1,2 @@ - + From adbc1e0816f5aa51f6d7cfa4f2fccd8889c4ce4b Mon Sep 17 00:00:00 2001 From: Robin Date: Wed, 29 Jan 2025 22:55:56 +0100 Subject: [PATCH 3/6] Update build.gradle --- android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index 8de104d..e955eec 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - namespace 'top.huic.perfect_volume_control' + namespace 'top.huic.perfect_volume_control.perfect_volume_control' compileSdkVersion 30 From 6fba92cf781c6623f43a301d6ce3ea215485bfcd Mon Sep 17 00:00:00 2001 From: Robin Date: Wed, 29 Jan 2025 23:14:48 +0100 Subject: [PATCH 4/6] Update build.gradle --- android/build.gradle | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index e955eec..dcb2349 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -4,18 +4,18 @@ version '1.0' buildscript { repositories { google() - jcenter() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.android.tools.build:gradle:7.3.1' } } rootProject.allprojects { repositories { google() - jcenter() + mavenCentral() } } @@ -24,9 +24,10 @@ apply plugin: 'com.android.library' android { namespace 'top.huic.perfect_volume_control.perfect_volume_control' - compileSdkVersion 30 + compileSdkVersion 31 defaultConfig { - minSdkVersion 16 + minSdkVersion 16 + targetSdkVersion 31 } -} +} \ No newline at end of file From e2af4197aaf256ab555ba241a7791f297798a5c6 Mon Sep 17 00:00:00 2001 From: Robin Date: Mon, 24 Feb 2025 15:41:21 +0100 Subject: [PATCH 5/6] Improvements --- .../SwiftPerfectVolumeControlPlugin.swift | 106 +++++++++--------- 1 file changed, 51 insertions(+), 55 deletions(-) diff --git a/ios/Classes/SwiftPerfectVolumeControlPlugin.swift b/ios/Classes/SwiftPerfectVolumeControlPlugin.swift index df29fdd..85bd6ee 100644 --- a/ios/Classes/SwiftPerfectVolumeControlPlugin.swift +++ b/ios/Classes/SwiftPerfectVolumeControlPlugin.swift @@ -4,108 +4,104 @@ import MediaPlayer import AVFoundation public class SwiftPerfectVolumeControlPlugin: NSObject, FlutterPlugin { - /// 音量视图 - let volumeView = MPVolumeView(); - - /// Flutter 消息通道 - var channel: FlutterMethodChannel?; + let volumeView = MPVolumeView() + var channel: FlutterMethodChannel? + + private var volumeObserverContext = 0 override init() { - super.init(); + super.init() } - + + deinit { + AVAudioSession.sharedInstance().removeObserver(self, forKeyPath: "outputVolume", context: &volumeObserverContext) + NotificationCenter.default.removeObserver(self) + } + public static func register(with registrar: FlutterPluginRegistrar) { let instance = SwiftPerfectVolumeControlPlugin() instance.channel = FlutterMethodChannel(name: "perfect_volume_control", binaryMessenger: registrar.messenger()) instance.bindListener() registrar.addMethodCallDelegate(instance, channel: instance.channel!) } - + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { switch call.method { case "getVolume": - self.getVolume(call, result: result); - break; + self.getVolume(call, result: result) case "setVolume": - self.setVolume(call, result: result); - break; + self.setVolume(call, result: result) case "hideUI": - self.hideUI(call, result: result); - break; + self.hideUI(call, result: result) default: - result(FlutterMethodNotImplemented); + result(FlutterMethodNotImplemented) } - } - - /// 获得系统当前音量 + public func getVolume(_ call: FlutterMethodCall, result: @escaping FlutterResult) { do { try AVAudioSession.sharedInstance().setActive(true) - result(AVAudioSession.sharedInstance().outputVolume); + result(AVAudioSession.sharedInstance().outputVolume) } catch let error as NSError { - result(FlutterError(code: String(error.code), message: "\(error.localizedDescription)", details: "\(error.localizedDescription)")); + result(FlutterError(code: String(error.code), message: error.localizedDescription, details: error.localizedDescription)) } } - - /// 设置音量 + public func setVolume(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - let volume = ((call.arguments as! [String: Any])["volume"]) as! Double; - var slider: UISlider?; + let volume = ((call.arguments as! [String: Any])["volume"]) as! Double + var slider: UISlider? for item in volumeView.subviews { - if item is UISlider { - slider = (item as! UISlider); - break; + if let s = item as? UISlider { + slider = s + break } } - + if slider == nil { - result(FlutterError(code: "-1", message: "Unable to get uislider", details: "Unable to get uislider")); - return; + result(FlutterError(code: "-1", message: "Unable to get UISlider", details: "Unable to get UISlider")) + return } - - // 异步设置 - slider!.setValue((Float)(volume), animated: false) - result(nil); + + slider!.setValue(Float(volume), animated: false) + result(nil) } - - /// 隐藏UI + public func hideUI(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - let hide = ((call.arguments as! [String: Any])["hide"]) as! Bool; + let hide = ((call.arguments as! [String: Any])["hide"]) as! Bool if hide { volumeView.frame = CGRect(x: -1000, y: -1000, width: 1, height: 1) volumeView.showsRouteButton = false - UIApplication.shared.delegate!.window!?.rootViewController!.view.addSubview(volumeView); + UIApplication.shared.delegate!.window!?.rootViewController!.view.addSubview(volumeView) } else { - volumeView.removeFromSuperview(); + volumeView.removeFromSuperview() } - result(nil); + result(nil) } - - /// 绑定监听器 + public func bindListener() { do { try AVAudioSession.sharedInstance().setActive(true) - AVAudioSession.sharedInstance().addObserver(self, forKeyPath: "outputVolume", options: [.new, .old], context: nil) + AVAudioSession.sharedInstance().addObserver(self, forKeyPath: "outputVolume", options: [.new, .old], context: &volumeObserverContext) } catch let error as NSError { print("\(error)") } - - // 绑定音量监听器 - NotificationCenter.default.addObserver(self, selector: #selector(self.volumeChangeListener), name: NSNotification.Name(rawValue: "AVSystemController_SystemVolumeDidChangeNotification"), object: nil) - UIApplication.shared.beginReceivingRemoteControlEvents(); + NotificationCenter.default.addObserver(self, selector: #selector(self.volumeChangeListener), name: NSNotification.Name(rawValue: "AVSystemController_SystemVolumeDidChangeNotification"), object: nil) + UIApplication.shared.beginReceivingRemoteControlEvents() } - - /// 音量监听 + @objc func volumeChangeListener(notification: NSNotification) { - let volume = notification.userInfo!["AVSystemController_AudioVolumeNotificationParameter"] as! Float - channel?.invokeMethod("volumeChangeListener", arguments: volume) + if let volume = notification.userInfo?["AVSystemController_AudioVolumeNotificationParameter"] as? Float { + channel?.invokeMethod("volumeChangeListener", arguments: volume) + } } - /// 音量监听(KVO方式) public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { - let volume = AVAudioSession.sharedInstance().outputVolume - channel?.invokeMethod("volumeChangeListener", arguments: volume) + if context == &volumeObserverContext && keyPath == "outputVolume" { + let volume = AVAudioSession.sharedInstance().outputVolume + channel?.invokeMethod("volumeChangeListener", arguments: volume) + } else { + super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) + } } -} +} \ No newline at end of file From c5bc35a6513ef6fe5986e1d914522518d262f824 Mon Sep 17 00:00:00 2001 From: Robin Date: Thu, 27 Mar 2025 14:36:33 +0100 Subject: [PATCH 6/6] Removed error when slider not available --- ios/Classes/SwiftPerfectVolumeControlPlugin.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ios/Classes/SwiftPerfectVolumeControlPlugin.swift b/ios/Classes/SwiftPerfectVolumeControlPlugin.swift index 85bd6ee..1a2606f 100644 --- a/ios/Classes/SwiftPerfectVolumeControlPlugin.swift +++ b/ios/Classes/SwiftPerfectVolumeControlPlugin.swift @@ -57,12 +57,10 @@ public class SwiftPerfectVolumeControlPlugin: NSObject, FlutterPlugin { } } - if slider == nil { - result(FlutterError(code: "-1", message: "Unable to get UISlider", details: "Unable to get UISlider")) - return + if let slider = slider { + slider.setValue(Float(volume), animated: false) } - slider!.setValue(Float(volume), animated: false) result(nil) }