|  | // Copyright 2014 The Flutter Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | import UIKit | 
|  | import Flutter | 
|  |  | 
|  | enum ChannelName { | 
|  | static let battery = "samples.flutter.io/battery" | 
|  | static let charging = "samples.flutter.io/charging" | 
|  | } | 
|  |  | 
|  | enum BatteryState { | 
|  | static let charging = "charging" | 
|  | static let discharging = "discharging" | 
|  | } | 
|  |  | 
|  | enum MyFlutterErrorCode { | 
|  | static let unavailable = "UNAVAILABLE" | 
|  | } | 
|  |  | 
|  | @UIApplicationMain | 
|  | @objc class AppDelegate: FlutterAppDelegate, FlutterStreamHandler { | 
|  | private var eventSink: FlutterEventSink? | 
|  |  | 
|  | override func application( | 
|  | _ application: UIApplication, | 
|  | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { | 
|  | GeneratedPluginRegistrant.register(with: self) | 
|  | guard let controller = window?.rootViewController as? FlutterViewController else { | 
|  | fatalError("rootViewController is not type FlutterViewController") | 
|  | } | 
|  | let batteryChannel = FlutterMethodChannel(name: ChannelName.battery, | 
|  | binaryMessenger: controller.binaryMessenger) | 
|  | batteryChannel.setMethodCallHandler({ | 
|  | [weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in | 
|  | guard call.method == "getBatteryLevel" else { | 
|  | result(FlutterMethodNotImplemented) | 
|  | return | 
|  | } | 
|  | self?.receiveBatteryLevel(result: result) | 
|  | }) | 
|  |  | 
|  | let chargingChannel = FlutterEventChannel(name: ChannelName.charging, | 
|  | binaryMessenger: controller.binaryMessenger) | 
|  | chargingChannel.setStreamHandler(self) | 
|  | return super.application(application, didFinishLaunchingWithOptions: launchOptions) | 
|  | } | 
|  |  | 
|  | private func receiveBatteryLevel(result: FlutterResult) { | 
|  | let device = UIDevice.current | 
|  | device.isBatteryMonitoringEnabled = true | 
|  | guard device.batteryState != .unknown  else { | 
|  | result(FlutterError(code: MyFlutterErrorCode.unavailable, | 
|  | message: "Battery info unavailable", | 
|  | details: nil)) | 
|  | return | 
|  | } | 
|  | result(Int(device.batteryLevel * 100)) | 
|  | } | 
|  |  | 
|  | public func onListen(withArguments arguments: Any?, | 
|  | eventSink: @escaping FlutterEventSink) -> FlutterError? { | 
|  | self.eventSink = eventSink | 
|  | UIDevice.current.isBatteryMonitoringEnabled = true | 
|  | sendBatteryStateEvent() | 
|  | NotificationCenter.default.addObserver( | 
|  | self, | 
|  | selector: #selector(AppDelegate.onBatteryStateDidChange), | 
|  | name: UIDevice.batteryStateDidChangeNotification, | 
|  | object: nil) | 
|  | return nil | 
|  | } | 
|  |  | 
|  | @objc private func onBatteryStateDidChange(notification: NSNotification) { | 
|  | sendBatteryStateEvent() | 
|  | } | 
|  |  | 
|  | private func sendBatteryStateEvent() { | 
|  | guard let eventSink = eventSink else { | 
|  | return | 
|  | } | 
|  |  | 
|  | switch UIDevice.current.batteryState { | 
|  | case .full: | 
|  | eventSink(BatteryState.charging) | 
|  | case .charging: | 
|  | eventSink(BatteryState.charging) | 
|  | case .unplugged: | 
|  | eventSink(BatteryState.discharging) | 
|  | default: | 
|  | eventSink(FlutterError(code: MyFlutterErrorCode.unavailable, | 
|  | message: "Charging status unavailable", | 
|  | details: nil)) | 
|  | } | 
|  | } | 
|  |  | 
|  | public func onCancel(withArguments arguments: Any?) -> FlutterError? { | 
|  | NotificationCenter.default.removeObserver(self) | 
|  | eventSink = nil | 
|  | return nil | 
|  | } | 
|  | } |