[device_info_platform_interface] Introduce package (#2929)
This is the platform interface package of the federated device_info plugin.
diff --git a/packages/device_info/device_info_platform_interface/CHANGELOG.md b/packages/device_info/device_info_platform_interface/CHANGELOG.md
new file mode 100644
index 0000000..6fadda9
--- /dev/null
+++ b/packages/device_info/device_info_platform_interface/CHANGELOG.md
@@ -0,0 +1,3 @@
+## 1.0.0
+
+- Initial open-source release.
diff --git a/packages/device_info/device_info_platform_interface/LICENSE b/packages/device_info/device_info_platform_interface/LICENSE
new file mode 100644
index 0000000..c892933
--- /dev/null
+++ b/packages/device_info/device_info_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/device_info/device_info_platform_interface/README.md b/packages/device_info/device_info_platform_interface/README.md
new file mode 100644
index 0000000..1391ffd
--- /dev/null
+++ b/packages/device_info/device_info_platform_interface/README.md
@@ -0,0 +1,26 @@
+# device_info_platform_interface
+
+A common platform interface for the [`device_info`][1] plugin.
+
+This interface allows platform-specific implementations of the `device_info`
+plugin, as well as the plugin itself, to ensure they are supporting the
+same interface.
+
+# Usage
+
+To implement a new platform-specific implementation of `device_info`, extend
+[`DeviceInfoPlatform`][2] with an implementation that performs the
+platform-specific behavior, and when you register your plugin, set the default
+`DeviceInfoPlatform` by calling
+`DeviceInfoPlatform.instance = MyPlatformDeviceInfo()`.
+
+# 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]: ../device_info
+[2]: lib/device_info_platform_interface.dart
diff --git a/packages/device_info/device_info_platform_interface/lib/device_info_platform_interface.dart b/packages/device_info/device_info_platform_interface/lib/device_info_platform_interface.dart
new file mode 100644
index 0000000..253d6d0
--- /dev/null
+++ b/packages/device_info/device_info_platform_interface/lib/device_info_platform_interface.dart
@@ -0,0 +1,55 @@
+// 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_device_info.dart';
+
+import 'model/android_device_info.dart';
+import 'model/ios_device_info.dart';
+
+export 'model/android_device_info.dart';
+export 'model/ios_device_info.dart';
+
+/// The interface that implementations of device_info must implement.
+///
+/// Platform implementations should extend this class rather than implement it as `device_info`
+/// 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
+/// [DeviceInfoPlatform] methods.
+abstract class DeviceInfoPlatform extends PlatformInterface {
+ /// Constructs a UrlLauncherPlatform.
+ DeviceInfoPlatform() : super(token: _token);
+
+ static final Object _token = Object();
+
+ static DeviceInfoPlatform _instance = MethodChannelDeviceInfo();
+
+ /// The default instance of [DeviceInfoPlatform] to use.
+ ///
+ /// Defaults to [MethodChannelDeviceInfo].
+ static DeviceInfoPlatform get instance => _instance;
+
+ /// Platform-specific plugins should set this with their own platform-specific
+ /// class that extends [DeviceInfoPlatform] when they register themselves.
+ static set instance(DeviceInfoPlatform instance) {
+ PlatformInterface.verifyToken(instance, _token);
+ _instance = instance;
+ }
+
+ // Gets the Android device information.
+ // ignore: public_member_api_docs
+ Future<AndroidDeviceInfo> androidInfo() {
+ throw UnimplementedError('androidInfo() has not been implemented.');
+ }
+
+ // Gets the iOS device information.
+ // ignore: public_member_api_docs
+ Future<IosDeviceInfo> iosInfo() {
+ throw UnimplementedError('iosInfo() has not been implemented.');
+ }
+}
diff --git a/packages/device_info/device_info_platform_interface/lib/method_channel/method_channel_device_info.dart b/packages/device_info/device_info_platform_interface/lib/method_channel/method_channel_device_info.dart
new file mode 100644
index 0000000..7bd02e9
--- /dev/null
+++ b/packages/device_info/device_info_platform_interface/lib/method_channel/method_channel_device_info.dart
@@ -0,0 +1,28 @@
+import 'dart:async';
+
+import 'package:flutter/services.dart';
+import 'package:meta/meta.dart';
+
+import 'package:device_info_platform_interface/device_info_platform_interface.dart';
+
+/// An implementation of [DeviceInfoPlatform] that uses method channels.
+class MethodChannelDeviceInfo extends DeviceInfoPlatform {
+ /// The method channel used to interact with the native platform.
+ @visibleForTesting
+ MethodChannel channel = MethodChannel('plugins.flutter.io/device_info');
+
+ // Method channel for Android devices
+ Future<AndroidDeviceInfo> androidInfo() async {
+ return AndroidDeviceInfo.fromMap(
+ (await channel.invokeMethod('getAndroidDeviceInfo'))
+ .cast<String, dynamic>(),
+ );
+ }
+
+ // Method channel for iOS devices
+ Future<IosDeviceInfo> iosInfo() async {
+ return IosDeviceInfo.fromMap(
+ (await channel.invokeMethod('getIosDeviceInfo')).cast<String, dynamic>(),
+ );
+ }
+}
diff --git a/packages/device_info/device_info_platform_interface/lib/model/android_device_info.dart b/packages/device_info/device_info_platform_interface/lib/model/android_device_info.dart
new file mode 100644
index 0000000..5b326cc
--- /dev/null
+++ b/packages/device_info/device_info_platform_interface/lib/model/android_device_info.dart
@@ -0,0 +1,198 @@
+// 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.
+
+/// Information derived from `android.os.Build`.
+///
+/// See: https://developer.android.com/reference/android/os/Build.html
+class AndroidDeviceInfo {
+ /// Android device Info class.
+ AndroidDeviceInfo({
+ this.version,
+ this.board,
+ this.bootloader,
+ this.brand,
+ this.device,
+ this.display,
+ this.fingerprint,
+ this.hardware,
+ this.host,
+ this.id,
+ this.manufacturer,
+ this.model,
+ this.product,
+ List<String> supported32BitAbis,
+ List<String> supported64BitAbis,
+ List<String> supportedAbis,
+ this.tags,
+ this.type,
+ this.isPhysicalDevice,
+ this.androidId,
+ List<String> systemFeatures,
+ }) : supported32BitAbis = List<String>.unmodifiable(supported32BitAbis),
+ supported64BitAbis = List<String>.unmodifiable(supported64BitAbis),
+ supportedAbis = List<String>.unmodifiable(supportedAbis),
+ systemFeatures = List<String>.unmodifiable(systemFeatures);
+
+ /// Android operating system version values derived from `android.os.Build.VERSION`.
+ final AndroidBuildVersion version;
+
+ /// The name of the underlying board, like "goldfish".
+ final String board;
+
+ /// The system bootloader version number.
+ final String bootloader;
+
+ /// The consumer-visible brand with which the product/hardware will be associated, if any.
+ final String brand;
+
+ /// The name of the industrial design.
+ final String device;
+
+ /// A build ID string meant for displaying to the user.
+ final String display;
+
+ /// A string that uniquely identifies this build.
+ final String fingerprint;
+
+ /// The name of the hardware (from the kernel command line or /proc).
+ final String hardware;
+
+ /// Hostname.
+ final String host;
+
+ /// Either a changelist number, or a label like "M4-rc20".
+ final String id;
+
+ /// The manufacturer of the product/hardware.
+ final String manufacturer;
+
+ /// The end-user-visible name for the end product.
+ final String model;
+
+ /// The name of the overall product.
+ final String product;
+
+ /// An ordered list of 32 bit ABIs supported by this device.
+ final List<String> supported32BitAbis;
+
+ /// An ordered list of 64 bit ABIs supported by this device.
+ final List<String> supported64BitAbis;
+
+ /// An ordered list of ABIs supported by this device.
+ final List<String> supportedAbis;
+
+ /// Comma-separated tags describing the build, like "unsigned,debug".
+ final String tags;
+
+ /// The type of build, like "user" or "eng".
+ final String type;
+
+ /// `false` if the application is running in an emulator, `true` otherwise.
+ final bool isPhysicalDevice;
+
+ /// The Android hardware device ID that is unique between the device + user and app signing.
+ final String androidId;
+
+ /// Describes what features are available on the current device.
+ ///
+ /// This can be used to check if the device has, for example, a front-facing
+ /// camera, or a touchscreen. However, in many cases this is not the best
+ /// API to use. For example, if you are interested in bluetooth, this API
+ /// can tell you if the device has a bluetooth radio, but it cannot tell you
+ /// if bluetooth is currently enabled, or if you have been granted the
+ /// necessary permissions to use it. Please *only* use this if there is no
+ /// other way to determine if a feature is supported.
+ ///
+ /// This data comes from Android's PackageManager.getSystemAvailableFeatures,
+ /// and many of the common feature strings to look for are available in
+ /// PackageManager's public documentation:
+ /// https://developer.android.com/reference/android/content/pm/PackageManager
+ final List<String> systemFeatures;
+
+ /// Deserializes from the message received from [_kChannel].
+ static AndroidDeviceInfo fromMap(Map<String, dynamic> map) {
+ return AndroidDeviceInfo(
+ version: AndroidBuildVersion._fromMap(
+ map['version']?.cast<String, dynamic>() ?? {}),
+ board: map['board'],
+ bootloader: map['bootloader'],
+ brand: map['brand'],
+ device: map['device'],
+ display: map['display'],
+ fingerprint: map['fingerprint'],
+ hardware: map['hardware'],
+ host: map['host'],
+ id: map['id'],
+ manufacturer: map['manufacturer'],
+ model: map['model'],
+ product: map['product'],
+ supported32BitAbis: _fromList(map['supported32BitAbis'] ?? []),
+ supported64BitAbis: _fromList(map['supported64BitAbis'] ?? []),
+ supportedAbis: _fromList(map['supportedAbis'] ?? []),
+ tags: map['tags'],
+ type: map['type'],
+ isPhysicalDevice: map['isPhysicalDevice'],
+ androidId: map['androidId'],
+ systemFeatures: _fromList(map['systemFeatures'] ?? []),
+ );
+ }
+
+ /// Deserializes message as List<String>
+ static List<String> _fromList(dynamic message) {
+ final List<dynamic> list = message;
+ return List<String>.from(list);
+ }
+}
+
+/// Version values of the current Android operating system build derived from
+/// `android.os.Build.VERSION`.
+///
+/// See: https://developer.android.com/reference/android/os/Build.VERSION.html
+class AndroidBuildVersion {
+ AndroidBuildVersion._({
+ this.baseOS,
+ this.codename,
+ this.incremental,
+ this.previewSdkInt,
+ this.release,
+ this.sdkInt,
+ this.securityPatch,
+ });
+
+ /// The base OS build the product is based on.
+ final String baseOS;
+
+ /// The current development codename, or the string "REL" if this is a release build.
+ final String codename;
+
+ /// The internal value used by the underlying source control to represent this build.
+ final String incremental;
+
+ /// The developer preview revision of a prerelease SDK.
+ final int previewSdkInt;
+
+ /// The user-visible version string.
+ final String release;
+
+ /// The user-visible SDK version of the framework.
+ ///
+ /// Possible values are defined in: https://developer.android.com/reference/android/os/Build.VERSION_CODES.html
+ final int sdkInt;
+
+ /// The user-visible security patch level.
+ final String securityPatch;
+
+ /// Deserializes from the map message received from [_kChannel].
+ static AndroidBuildVersion _fromMap(Map<String, dynamic> map) {
+ return AndroidBuildVersion._(
+ baseOS: map['baseOS'],
+ codename: map['codename'],
+ incremental: map['incremental'],
+ previewSdkInt: map['previewSdkInt'],
+ release: map['release'],
+ sdkInt: map['sdkInt'],
+ securityPatch: map['securityPatch'],
+ );
+ }
+}
diff --git a/packages/device_info/device_info_platform_interface/lib/model/ios_device_info.dart b/packages/device_info/device_info_platform_interface/lib/model/ios_device_info.dart
new file mode 100644
index 0000000..d412024
--- /dev/null
+++ b/packages/device_info/device_info_platform_interface/lib/model/ios_device_info.dart
@@ -0,0 +1,97 @@
+// 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.
+
+/// Information derived from `UIDevice`.
+///
+/// See: https://developer.apple.com/documentation/uikit/uidevice
+class IosDeviceInfo {
+ /// IOS device info class.
+ IosDeviceInfo({
+ this.name,
+ this.systemName,
+ this.systemVersion,
+ this.model,
+ this.localizedModel,
+ this.identifierForVendor,
+ this.isPhysicalDevice,
+ this.utsname,
+ });
+
+ /// Device name.
+ final String name;
+
+ /// The name of the current operating system.
+ final String systemName;
+
+ /// The current operating system version.
+ final String systemVersion;
+
+ /// Device model.
+ final String model;
+
+ /// Localized name of the device model.
+ final String localizedModel;
+
+ /// Unique UUID value identifying the current device.
+ final String identifierForVendor;
+
+ /// `false` if the application is running in a simulator, `true` otherwise.
+ final bool isPhysicalDevice;
+
+ /// Operating system information derived from `sys/utsname.h`.
+ final IosUtsname utsname;
+
+ /// Deserializes from the map message received from [_kChannel].
+ static IosDeviceInfo fromMap(Map<String, dynamic> map) {
+ return IosDeviceInfo(
+ name: map['name'],
+ systemName: map['systemName'],
+ systemVersion: map['systemVersion'],
+ model: map['model'],
+ localizedModel: map['localizedModel'],
+ identifierForVendor: map['identifierForVendor'],
+ isPhysicalDevice: map['isPhysicalDevice'] == 'true',
+ utsname:
+ IosUtsname._fromMap(map['utsname']?.cast<String, dynamic>() ?? {}),
+ );
+ }
+}
+
+/// Information derived from `utsname`.
+/// See http://pubs.opengroup.org/onlinepubs/7908799/xsh/sysutsname.h.html for details.
+class IosUtsname {
+ IosUtsname._({
+ this.sysname,
+ this.nodename,
+ this.release,
+ this.version,
+ this.machine,
+ });
+
+ /// Operating system name.
+ final String sysname;
+
+ /// Network node name.
+ final String nodename;
+
+ /// Release level.
+ final String release;
+
+ /// Version level.
+ final String version;
+
+ /// Hardware type (e.g. 'iPhone7,1' for iPhone 6 Plus).
+ final String machine;
+
+ /// Deserializes from the map message received from [_kChannel].
+ static IosUtsname _fromMap(Map<String, dynamic> map) {
+ return IosUtsname._(
+ sysname: map['sysname'],
+ nodename: map['nodename'],
+ release: map['release'],
+ version: map['version'],
+ machine: map['machine'],
+ );
+ }
+}
diff --git a/packages/device_info/device_info_platform_interface/pubspec.yaml b/packages/device_info/device_info_platform_interface/pubspec.yaml
new file mode 100644
index 0000000..3adfb93
--- /dev/null
+++ b/packages/device_info/device_info_platform_interface/pubspec.yaml
@@ -0,0 +1,22 @@
+name: device_info_platform_interface
+description: A common platform interface for the device_info plugin.
+homepage: https://github.com/flutter/plugins/tree/master/packages/device_info
+# 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/device_info/device_info_platform_interface/test/method_channel_device_info_test.dart b/packages/device_info/device_info_platform_interface/test/method_channel_device_info_test.dart
new file mode 100644
index 0000000..1da52e2
--- /dev/null
+++ b/packages/device_info/device_info_platform_interface/test/method_channel_device_info_test.dart
@@ -0,0 +1,49 @@
+// 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:device_info_platform_interface/device_info_platform_interface.dart';
+
+import 'package:device_info_platform_interface/method_channel/method_channel_device_info.dart';
+
+void main() {
+ TestWidgetsFlutterBinding.ensureInitialized();
+
+ group("$MethodChannelDeviceInfo", () {
+ MethodChannelDeviceInfo methodChannelDeviceInfo;
+
+ setUp(() async {
+ methodChannelDeviceInfo = MethodChannelDeviceInfo();
+
+ methodChannelDeviceInfo.channel
+ .setMockMethodCallHandler((MethodCall methodCall) async {
+ switch (methodCall.method) {
+ case 'getAndroidDeviceInfo':
+ return ({
+ "brand": "Google",
+ });
+ case 'getIosDeviceInfo':
+ return ({
+ "name": "iPhone 10",
+ });
+ default:
+ return null;
+ }
+ });
+ });
+
+ test("androidInfo", () async {
+ final AndroidDeviceInfo result =
+ await methodChannelDeviceInfo.androidInfo();
+ expect(result.brand, "Google");
+ });
+
+ test("iosInfo", () async {
+ final IosDeviceInfo result = await methodChannelDeviceInfo.iosInfo();
+ expect(result.name, "iPhone 10");
+ });
+ });
+}