Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(device_info_plus)!: refactor of device_info_plus platform implementation #1293

Merged
merged 14 commits into from
Oct 27, 2022
10 changes: 3 additions & 7 deletions packages/device_info_plus/device_info_plus/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,18 @@ WebBrowserInfo webBrowserInfo = await deviceInfo.webBrowserInfo;
print('Running on ${webBrowserInfo.userAgent}'); // e.g. "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0"
```

The plugin provides a `toMap` method that returns platform-specific device
The plugin provides a `data` method that returns platform-specific device
information in a generic way, which can be used for crash-reporting purposes.

However, the data provided by this `toMap` method is currently not serializable
However, the data provided by this `data` method is currently not serializable
(i.e. it is not 100% JSON compatible) and shouldn't be treated as such.

That's why the method is currently marked as deprecated and will be eventually
replaced, improved, or removed. We recommend you instead to read the actual
values from each device info platform, or to keep using `toMap` at your own risk.

```dart
import 'package:device_info_plus/device_info_plus.dart';
final deviceInfoPlugin = DeviceInfoPlugin();
final deviceInfo = await deviceInfoPlugin.deviceInfo;
final allInfo = deviceInfo.toMap();
final allInfo = deviceInfo.data;
```

You will find links to the API docs on the [pub page](https://pub.dev/documentation/device_info_plus/latest/).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ internal class MethodCallHandlerImpl(
) : MethodCallHandler {

override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
if (call.method.equals("getAndroidDeviceInfo")) {
if (call.method.equals("getDeviceInfo")) {
val build: MutableMap<String, Any> = HashMap()

build["board"] = Build.BOARD
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {

- (void)handleMethodCall:(FlutterMethodCall *)call
result:(FlutterResult)result {
if ([@"getIosDeviceInfo" isEqualToString:call.method]) {
if ([@"getDeviceInfo" isEqualToString:call.method]) {
UIDevice *device = [UIDevice currentDevice];
struct utsname un;
uname(&un);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,22 @@ import 'dart:io';
import 'package:device_info_plus_platform_interface/device_info_plus_platform_interface.dart';
import 'package:flutter/foundation.dart';

import 'src/model/android_device_info.dart';
import 'src/model/ios_device_info.dart';
import 'src/model/linux_device_info.dart';
import 'src/model/macos_device_info.dart';
import 'src/model/web_browser_info.dart';
import 'src/model/windows_device_info.dart';

export 'package:device_info_plus_platform_interface/device_info_plus_platform_interface.dart'
show
AndroidBuildVersion,
AndroidDeviceInfo,
BaseDeviceInfo,
IosDeviceInfo,
IosUtsname,
LinuxDeviceInfo,
MacOsDeviceInfo,
WindowsDeviceInfo,
WebBrowserInfo,
BrowserName;
show BaseDeviceInfo;

export 'src/model/android_device_info.dart';
export 'src/model/ios_device_info.dart';
export 'src/model/linux_device_info.dart';
export 'src/model/macos_device_info.dart';
export 'src/model/web_browser_info.dart';
export 'src/model/windows_device_info.dart';

export 'src/device_info_plus_linux.dart';
export 'src/device_info_plus_windows.dart'
Expand All @@ -45,45 +49,47 @@ class DeviceInfoPlugin {
///
/// See: https://developer.android.com/reference/android/os/Build.html
Future<AndroidDeviceInfo> get androidInfo async =>
_cachedAndroidDeviceInfo ??= await _platform.androidInfo();
_cachedAndroidDeviceInfo ??=
AndroidDeviceInfo.fromMap((await _platform.deviceInfo()).data);

/// This information does not change from call to call. Cache it.
IosDeviceInfo? _cachedIosDeviceInfo;

/// Information derived from `UIDevice`.
///
/// See: https://developer.apple.com/documentation/uikit/uidevice
Future<IosDeviceInfo> get iosInfo async =>
_cachedIosDeviceInfo ??= await _platform.iosInfo();
Future<IosDeviceInfo> get iosInfo async => _cachedIosDeviceInfo ??=
IosDeviceInfo.fromMap((await _platform.deviceInfo()).data);

/// This information does not change from call to call. Cache it.
LinuxDeviceInfo? _cachedLinuxDeviceInfo;

/// Information derived from `/etc/os-release`.
///
/// See: https://www.freedesktop.org/software/systemd/man/os-release.html
Future<LinuxDeviceInfo> get linuxInfo async =>
_cachedLinuxDeviceInfo ??= await _platform.linuxInfo();
Future<LinuxDeviceInfo> get linuxInfo async => _cachedLinuxDeviceInfo ??=
await _platform.deviceInfo() as LinuxDeviceInfo;

/// This information does not change from call to call. Cache it.
WebBrowserInfo? _cachedWebBrowserInfo;

/// Information derived from `Navigator`.
Future<WebBrowserInfo> get webBrowserInfo async =>
_cachedWebBrowserInfo ??= await _platform.webBrowserInfo();
_cachedWebBrowserInfo ??= await _platform.deviceInfo() as WebBrowserInfo;

/// This information does not change from call to call. Cache it.
MacOsDeviceInfo? _cachedMacosDeviceInfo;

/// Returns device information for macos. Information sourced from Sysctl.
Future<MacOsDeviceInfo> get macOsInfo async =>
_cachedMacosDeviceInfo ??= await _platform.macosInfo();
Future<MacOsDeviceInfo> get macOsInfo async => _cachedMacosDeviceInfo ??=
MacOsDeviceInfo.fromMap((await _platform.deviceInfo()).data);

WindowsDeviceInfo? _cachedWindowsDeviceInfo;

/// Returns device information for Windows.
Future<WindowsDeviceInfo> get windowsInfo async =>
_cachedWindowsDeviceInfo ??= await _platform.windowsInfo()!;
_cachedWindowsDeviceInfo ??=
await _platform.deviceInfo() as WindowsDeviceInfo;

/// Returns device information for the current platform.
Future<BaseDeviceInfo> get deviceInfo async {
Expand All @@ -102,7 +108,7 @@ class DeviceInfoPlugin {
return windowsInfo;
}
}

throw UnsupportedError('Unsupported platform');
// allow for extension of the plugin
return _platform.deviceInfo();
Comment on lines +111 to +112
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any new platform implementing deviceInfo() can now return a generic BaseDeviceInfo with some data

}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:async';

import 'package:device_info_plus/device_info_plus.dart';
import 'package:device_info_plus_platform_interface/device_info_plus_platform_interface.dart';
import 'package:file/file.dart';
import 'package:file/local.dart';
Expand All @@ -20,10 +21,14 @@ class DeviceInfoPlusLinuxPlugin extends DeviceInfoPlatform {
: _fileSystem = fileSystem ?? const LocalFileSystem();

@override
Future<LinuxDeviceInfo> linuxInfo() async {
Future<BaseDeviceInfo> deviceInfo() async {
return _cache ??= await _getInfo();
}

Future<LinuxDeviceInfo> linuxInfo() async {
return (await deviceInfo()) as LinuxDeviceInfo;
}

Future<LinuxDeviceInfo> _getInfo() async {
final os = await _getOsRelease() ?? {};
final lsb = await _getLsbRelease() ?? {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import 'dart:html' as html show window, Navigator;
import 'package:device_info_plus_platform_interface/device_info_plus_platform_interface.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';

import 'model/web_browser_info.dart';

/// The web implementation of the BatteryPlusPlatform of the BatteryPlus plugin.
class DeviceInfoPlusWebPlugin extends DeviceInfoPlatform {
/// Constructs a DeviceInfoPlusPlugin.
Expand All @@ -19,7 +21,7 @@ class DeviceInfoPlusWebPlugin extends DeviceInfoPlatform {
}

@override
Future<WebBrowserInfo> webBrowserInfo() {
Future<BaseDeviceInfo> deviceInfo() {
return Future<WebBrowserInfo>.value(
WebBrowserInfo.fromMap(
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import 'package:ffi/ffi.dart';
import 'package:meta/meta.dart';
import 'package:win32/win32.dart';

import 'model/windows_device_info.dart';

/// The Windows implementation of [DeviceInfoPlatform].
class DeviceInfoPlusWindowsPlugin extends DeviceInfoPlatform {
/// Register this dart class as the platform implementation for windows
Expand All @@ -23,7 +25,7 @@ class DeviceInfoPlusWindowsPlugin extends DeviceInfoPlatform {

/// Returns a [WindowsDeviceInfo] with information about the device.
@override
Future<WindowsDeviceInfo> windowsInfo() {
Future<BaseDeviceInfo> deviceInfo() {
return Future.value(_cache ??= getInfo());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
// found in the LICENSE file.

import 'dart:math' as math show sqrt;
import 'base_device_info.dart';
import 'package:device_info_plus_platform_interface/model/base_device_info.dart';

/// Information derived from `android.os.Build`.
///
/// See: https://developer.android.com/reference/android/os/Build.html
class AndroidDeviceInfo implements BaseDeviceInfo {
/// Android device Info class.
AndroidDeviceInfo({
class AndroidDeviceInfo extends BaseDeviceInfo {
AndroidDeviceInfo._({
required Map<String, dynamic> data,
Comment on lines +12 to +13
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This private constructor and the fromMap method could be refactored further, probably, but I didn't do it yet. At least I marked this constructor as private now, which shouldn't be used outside the plugin.

required this.version,
required this.board,
required this.bootloader,
Expand All @@ -35,7 +35,8 @@ class AndroidDeviceInfo implements BaseDeviceInfo {
}) : supported32BitAbis = List<String>.unmodifiable(supported32BitAbis),
supported64BitAbis = List<String>.unmodifiable(supported64BitAbis),
supportedAbis = List<String>.unmodifiable(supportedAbis),
systemFeatures = List<String>.unmodifiable(systemFeatures);
systemFeatures = List<String>.unmodifiable(systemFeatures),
super(data);

/// Android operating system version values derived from `android.os.Build.VERSION`.
final AndroidBuildVersion version;
Expand Down Expand Up @@ -116,38 +117,10 @@ class AndroidDeviceInfo implements BaseDeviceInfo {
/// Information about the current android display.
final AndroidDisplayMetrics displayMetrics;

/// Serializes [AndroidDeviceInfo] to map.
@Deprecated('[toMap] method will be discontinued')
@override
Map<String, dynamic> toMap() {
return {
'id': id,
Comment on lines -119 to -124
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original toMap disappears and now instead the data returned by the platform channel is returned. Rather than re-creating the map out of the class data, we return the same data the Android code created, which should be 100% serializable.

'host': host,
'tags': tags,
'type': type,
'model': model,
'board': board,
'brand': brand,
'device': device,
'product': product,
'display': display,
'hardware': hardware,
'bootloader': bootloader,
'version': version.toMap(),
'fingerprint': fingerprint,
'manufacturer': manufacturer,
'supportedAbis': supportedAbis,
'systemFeatures': systemFeatures,
'isPhysicalDevice': isPhysicalDevice,
'supported32BitAbis': supported32BitAbis,
'supported64BitAbis': supported64BitAbis,
'displayMetrics': displayMetrics.toMap(),
};
}

/// Deserializes from the message received from [_kChannel].
static AndroidDeviceInfo fromMap(Map<String, dynamic> map) {
return AndroidDeviceInfo(
return AndroidDeviceInfo._(
data: map,
version: AndroidBuildVersion._fromMap(
map['version']?.cast<String, dynamic>() ?? {}),
board: map['board'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'base_device_info.dart';
import 'package:device_info_plus_platform_interface/model/base_device_info.dart';

/// Information derived from `UIDevice`.
///
/// See: https://developer.apple.com/documentation/uikit/uidevice
class IosDeviceInfo implements BaseDeviceInfo {
class IosDeviceInfo extends BaseDeviceInfo {
/// IOS device info class.
const IosDeviceInfo({
IosDeviceInfo._({
required Map<String, dynamic> data,
this.name,
this.systemName,
this.systemVersion,
Expand All @@ -18,7 +19,7 @@ class IosDeviceInfo implements BaseDeviceInfo {
this.identifierForVendor,
required this.isPhysicalDevice,
required this.utsname,
});
}) : super(data);

/// Device name.
final String? name;
Expand Down Expand Up @@ -46,7 +47,8 @@ class IosDeviceInfo implements BaseDeviceInfo {

/// Deserializes from the map message received from [_kChannel].
static IosDeviceInfo fromMap(Map<String, dynamic> map) {
return IosDeviceInfo(
return IosDeviceInfo._(
data: map,
name: map['name'],
systemName: map['systemName'],
systemVersion: map['systemVersion'],
Expand All @@ -58,22 +60,6 @@ class IosDeviceInfo implements BaseDeviceInfo {
IosUtsname._fromMap(map['utsname']?.cast<String, dynamic>() ?? {}),
);
}

/// Serializes [IosDeviceInfo] to a map.
@Deprecated('[toMap] method will be discontinued')
@override
Map<String, dynamic> toMap() {
return {
'name': name,
'model': model,
'systemName': systemName,
'utsname': utsname._toMap(),
'systemVersion': systemVersion,
'localizedModel': localizedModel,
'identifierForVendor': identifierForVendor,
'isPhysicalDevice': isPhysicalDevice.toString(),
};
}
}

/// Information derived from `utsname`.
Expand Down Expand Up @@ -112,15 +98,4 @@ class IosUtsname {
machine: map['machine'],
);
}

/// Serializes [ IosUtsname ] to map.
Map<String, dynamic> _toMap() {
return {
'release': release,
'version': version,
'machine': machine,
'sysname': sysname,
'nodename': nodename,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'base_device_info.dart';
import 'package:device_info_plus_platform_interface/model/base_device_info.dart';

/// Device information for a Linux system.
///
Expand Down Expand Up @@ -139,8 +139,11 @@ class LinuxDeviceInfo implements BaseDeviceInfo {
/// decoded from hexadecimal, this corresponds to a 16-byte/128-bit value.
final String? machineId;

/// Serializes [LinuxDeviceInfo] to a map.
@Deprecated('[toMap] method will be discontinued')
@override
// ignore: deprecated_member_use_from_same_package
Map<String, dynamic> get data => toMap();

@Deprecated('Use [data] getter instead')
Comment on lines +142 to +146
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With dart plugins, they can implement the BaseDeviceInfo directly but still need to provide the data. At the moment, there's no warranty that the data is serializable, but that's out of scope for this PR.

@override
Map<String, dynamic> toMap() {
return {
Expand Down
Loading