diff --git a/packages/battery/battery_platform_interface/CHANGELOG.md b/packages/battery/battery_platform_interface/CHANGELOG.md new file mode 100644 index 000000000000..6fadda91b380 --- /dev/null +++ b/packages/battery/battery_platform_interface/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +- Initial open-source release. diff --git a/packages/battery/battery_platform_interface/LICENSE b/packages/battery/battery_platform_interface/LICENSE new file mode 100644 index 000000000000..c89293372cf3 --- /dev/null +++ b/packages/battery/battery_platform_interface/LICENSE @@ -0,0 +1,27 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/battery/battery_platform_interface/README.md b/packages/battery/battery_platform_interface/README.md new file mode 100644 index 000000000000..e1a42571c6b3 --- /dev/null +++ b/packages/battery/battery_platform_interface/README.md @@ -0,0 +1,26 @@ +# battery_platform_interface + +A common platform interface for the [`battery`][1] plugin. + +This interface allows platform-specific implementations of the `battery` +plugin, as well as the plugin itself, to ensure they are supporting the +same interface. + +# Usage + +To implement a new platform-specific implementation of `battery`, extend +[`BatteryPlatform`][2] with an implementation that performs the +platform-specific behavior, and when you register your plugin, set the default +`BatteryPlatform` by calling +`BatteryPlatform.instance = MyPlatformBattery()`. + +# Note on breaking changes + +Strongly prefer non-breaking changes (such as adding a method to the interface) +over breaking changes for this package. + +See https://flutter.dev/go/platform-interface-breaking-changes for a discussion +on why a less-clean interface is preferable to a breaking change. + +[1]: ../battery +[2]: lib/battery_platform_interface.dart diff --git a/packages/battery/battery_platform_interface/lib/battery_platform_interface.dart b/packages/battery/battery_platform_interface/lib/battery_platform_interface.dart new file mode 100644 index 000000000000..f803c7aaa8fd --- /dev/null +++ b/packages/battery/battery_platform_interface/lib/battery_platform_interface.dart @@ -0,0 +1,51 @@ +// Copyright 2017 The Chromium 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 'dart:async'; + +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'method_channel/method_channel_battery.dart'; +import 'enums/battery_state.dart'; + +export 'enums/battery_state.dart'; + +/// The interface that implementations of battery must implement. +/// +/// Platform implementations should extend this class rather than implement it as `battery` +/// does not consider newly added methods to be breaking changes. Extending this class +/// (using `extends`) ensures that the subclass will get the default implementation, while +/// platform implementations that `implements` this interface will be broken by newly added +/// [BatteryPlatform] methods. +abstract class BatteryPlatform extends PlatformInterface { + /// Constructs a BatteryPlatform. + BatteryPlatform() : super(token: _token); + + static final Object _token = Object(); + + static BatteryPlatform _instance = MethodChannelBattery(); + + /// The default instance of [BatteryPlatform] to use. + /// + /// Defaults to [MethodChannelBattery]. + static BatteryPlatform get instance => _instance; + + /// Platform-specific plugins should set this with their own platform-specific + /// class that extends [BatteryPlatform] when they register themselves. + static set instance(BatteryPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + /// Gets the battery level from device. + Future batteryLevel() { + throw UnimplementedError('batteryLevel() has not been implemented.'); + } + + /// gets battery state from device. + Stream onBatteryStateChanged() { + throw UnimplementedError( + 'onBatteryStateChanged() has not been implemented.'); + } +} diff --git a/packages/battery/battery_platform_interface/lib/enums/battery_state.dart b/packages/battery/battery_platform_interface/lib/enums/battery_state.dart new file mode 100644 index 000000000000..7dd5e400faf2 --- /dev/null +++ b/packages/battery/battery_platform_interface/lib/enums/battery_state.dart @@ -0,0 +1,11 @@ +/// Indicates the current battery state. +enum BatteryState { + /// The battery is completely full of energy. + full, + + /// The battery is currently storing energy. + charging, + + /// The battery is currently losing energy. + discharging +} diff --git a/packages/battery/battery_platform_interface/lib/method_channel/method_channel_battery.dart b/packages/battery/battery_platform_interface/lib/method_channel/method_channel_battery.dart new file mode 100644 index 000000000000..4a3365cc2475 --- /dev/null +++ b/packages/battery/battery_platform_interface/lib/method_channel/method_channel_battery.dart @@ -0,0 +1,51 @@ +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:meta/meta.dart'; + +import 'package:battery_platform_interface/battery_platform_interface.dart'; + +import '../battery_platform_interface.dart'; + +/// An implementation of [BatteryPlatform] that uses method channels. +class MethodChannelBattery extends BatteryPlatform { + /// The method channel used to interact with the native platform. + @visibleForTesting + MethodChannel channel = MethodChannel('plugins.flutter.io/battery'); + + /// The event channel used to interact with the native platform. + @visibleForTesting + EventChannel eventChannel = EventChannel('plugins.flutter.io/charging'); + + /// Method channel for getting battery level. + Future batteryLevel() async { + return (await channel.invokeMethod('getBatteryLevel')).toInt(); + } + + /// Stream variable for storing battery state. + Stream _onBatteryStateChanged; + + /// Event channel for getting battery change state. + Stream onBatteryStateChanged() { + if (_onBatteryStateChanged == null) { + _onBatteryStateChanged = eventChannel + .receiveBroadcastStream() + .map((dynamic event) => _parseBatteryState(event)); + } + return _onBatteryStateChanged; + } +} + +/// Method for parsing battery state. +BatteryState _parseBatteryState(String state) { + switch (state) { + case 'full': + return BatteryState.full; + case 'charging': + return BatteryState.charging; + case 'discharging': + return BatteryState.discharging; + default: + throw ArgumentError('$state is not a valid BatteryState.'); + } +} diff --git a/packages/battery/battery_platform_interface/pubspec.yaml b/packages/battery/battery_platform_interface/pubspec.yaml new file mode 100644 index 000000000000..6c571debc7b0 --- /dev/null +++ b/packages/battery/battery_platform_interface/pubspec.yaml @@ -0,0 +1,22 @@ +name: battery_platform_interface +description: A common platform interface for the battery plugin. +homepage: https://github.com/flutter/plugins/tree/master/packages/battery +# NOTE: We strongly prefer non-breaking changes, even at the expense of a +# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes +version: 1.0.0 + +dependencies: + flutter: + sdk: flutter + meta: ^1.1.8 + plugin_platform_interface: ^1.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + mockito: ^4.1.1 + pedantic: ^1.8.0 + +environment: + sdk: ">=2.7.0 <3.0.0" + flutter: ">=1.9.1+hotfix.4 <2.0.0" diff --git a/packages/battery/battery_platform_interface/test/method_channel_battery_test.dart b/packages/battery/battery_platform_interface/test/method_channel_battery_test.dart new file mode 100644 index 000000000000..65323e4044de --- /dev/null +++ b/packages/battery/battery_platform_interface/test/method_channel_battery_test.dart @@ -0,0 +1,63 @@ +// Copyright 2017 The Chromium 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 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:battery_platform_interface/battery_platform_interface.dart'; + +import 'package:battery_platform_interface/method_channel/method_channel_battery.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group("$MethodChannelBattery", () { + MethodChannelBattery methodChannelBattery; + + setUp(() async { + methodChannelBattery = MethodChannelBattery(); + + methodChannelBattery.channel + .setMockMethodCallHandler((MethodCall methodCall) async { + switch (methodCall.method) { + case 'getBatteryLevel': + return 90; + default: + return null; + } + }); + + MethodChannel(methodChannelBattery.eventChannel.name) + .setMockMethodCallHandler((MethodCall methodCall) async { + switch (methodCall.method) { + case 'listen': + await ServicesBinding.instance.defaultBinaryMessenger + .handlePlatformMessage( + methodChannelBattery.eventChannel.name, + methodChannelBattery.eventChannel.codec + .encodeSuccessEnvelope('full'), + (_) {}, + ); + break; + case 'cancel': + default: + return null; + } + }); + }); + + /// Test for batetry level call. + test("getBatteryLevel", () async { + final int result = await methodChannelBattery.batteryLevel(); + expect(result, 90); + }); + + /// Test for battery changed state call. + test("onBatteryChanged", () async { + final BatteryState result = + await methodChannelBattery.onBatteryStateChanged().first; + expect(result, BatteryState.full); + }); + }); +}