Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
## 2.0.0-nullsafety.2
## 2.0.0

* Make `baseOS`, `previewSdkInt`, and `securityPatch` nullable types.
* Remove default values for non-nullable types.

## 2.0.0-nullsafety.1

* Bump Dart SDK to support null safety.

## 2.0.0-nullsafety

* Migrate to null safety.

## 1.0.2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,15 @@ class MethodChannelDeviceInfo extends DeviceInfoPlatform {

// Method channel for Android devices
Future<AndroidDeviceInfo> androidInfo() async {
return AndroidDeviceInfo.fromMap(
(await channel.invokeMethod('getAndroidDeviceInfo'))
.cast<String, dynamic>(),
);
return AndroidDeviceInfo.fromMap((await channel
.invokeMapMethod<String, dynamic>('getAndroidDeviceInfo')) ??
<String, dynamic>{});
}

// Method channel for iOS devices
Future<IosDeviceInfo> iosInfo() async {
return IosDeviceInfo.fromMap(
(await channel.invokeMethod('getIosDeviceInfo')).cast<String, dynamic>(),
);
(await channel.invokeMapMethod<String, dynamic>('getIosDeviceInfo')) ??
<String, dynamic>{});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -113,34 +113,40 @@ class AndroidDeviceInfo {
/// 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']!),
version: AndroidBuildVersion._fromMap(map['version'] != null
? map['version'].cast<String, dynamic>()
: <String, dynamic>{}),
board: map['board'] ?? '',
bootloader: map['bootloader'] ?? '',
brand: map['brand'] ?? '',
device: map['device'] ?? '',
Copy link

Choose a reason for hiding this comment

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

The empty string means that there's a bug on the platform side right? I think we should have a tracking bug to address this with Pigeon, and add TODOs. We should really try to fix this with Pigeon because I can see this being a source of bugs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why does it have anything to do with Pigeon? This plugin doesn't use pigeon.
Anyways, from dart perspective, we should always assume the method channel could return null. Even the native API doesn't return null now, it could in the future as we don't use null safe language on native side.

Copy link

Choose a reason for hiding this comment

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

I guess I was referring to the Dart API. If the empty string is documented on the Android official website, then it's correct to provide empty string values. The point about Pigeon is about making this enforcement more explicit upstream, so you don't have to manually deal with it in Dart.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The point about Pigeon is about making this enforcement more explicit upstream, so you don't have to manually deal with it in Dart.

Did you mean when we eventually migrate this plugin to pigeon?

If the empty string is documented on the Android official website, then it's correct to provide empty string values.

It's not documented. I also don't want to make everything nullable either. I think in a null safe language, it is usually the case that we don't want to make a string nullable if empty string doesn't have a specific meaning.

Copy link

Choose a reason for hiding this comment

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

yes, eventual migration. The suggestion is to look at the official documentation for explicit mentions of "null". For example, the bug that Jason was fixing in Java. https://developer.android.com/reference/android/content/pm/FeatureInfo#name The docs explicitly say that it can be null.

I think that is the type of work that will match the intended behavior of the platform.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Most of the properties in the documentation doesn't say it is nullable or non-null. See https://developer.android.com/reference/kotlin/android/os/Build

So since they are strings, I'd prefer making them non-null and empty string for "no value" on dart side.

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'] ?? false,
androidId: map['androidId'] ?? '',
systemFeatures: _fromList(map['systemFeatures']),
);
}

/// Deserializes message as List<String>
static List<String> _fromList(dynamic message) {
final List<dynamic> list = message;
if (message == null) {
return <String>[];
}
assert(message is List<dynamic>);
final List<dynamic> list = List<dynamic>.from(message)
..removeWhere((value) => value == null);
return List<String>.from(list);
Copy link
Contributor

Choose a reason for hiding this comment

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

Couldn't this just be a .cast<String>() since you're copying in the new step just above?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

}
}
Expand Down Expand Up @@ -192,10 +198,10 @@ class AndroidBuildVersion {
baseOS: map['baseOS'],
previewSdkInt: map['previewSdkInt'],
securityPatch: map['securityPatch'],
codename: map['codename']!,
incremental: map['incremental']!,
release: map['release']!,
sdkInt: map['sdkInt']!,
codename: map['codename'] ?? '',
incremental: map['incremental'] ?? '',
release: map['release'] ?? '',
sdkInt: map['sdkInt'] ?? -1,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,18 @@ class IosDeviceInfo {
/// 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>()),
name: map['name'] ?? '',
systemName: map['systemName'] ?? '',
systemVersion: map['systemVersion'] ?? '',
model: map['model'] ?? '',
localizedModel: map['localizedModel'] ?? '',
identifierForVendor: map['identifierForVendor'] ?? '',
isPhysicalDevice: map['isPhysicalDevice'] != null
? map['isPhysicalDevice'] == 'true'
: false,
utsname: IosUtsname._fromMap(map['utsname'] != null
? map['utsname'].cast<String, dynamic>()
: <String, dynamic>{}),
);
}
}
Expand Down Expand Up @@ -86,11 +90,11 @@ class IosUtsname {
/// 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']!,
sysname: map['sysname'] ?? '',
nodename: map['nodename'] ?? '',
release: map['release'] ?? '',
version: map['version'] ?? '',
machine: map['machine'] ?? '',
);
}
}
12 changes: 6 additions & 6 deletions packages/device_info/device_info_platform_interface/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@ 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: 2.0.0-nullsafety.2
version: 2.0.0

dependencies:
flutter:
sdk: flutter
meta: ^1.3.0-nullsafety.3
plugin_platform_interface: ^1.1.0-nullsafety.1
meta: ^1.3.0
plugin_platform_interface: ">=1.0.0 <3.0.0"

dev_dependencies:
flutter_test:
sdk: flutter
test: ^1.10.0-nullsafety.1
pedantic: ^1.10.0-nullsafety.1
test: ^1.16.3
pedantic: ^1.10.0

environment:
sdk: ">=2.12.0-0 <3.0.0"
sdk: ">=2.12.0-259.9.beta <3.0.0"
flutter: ">=1.9.1+hotfix.4"
Loading