Skip to content

Commit

Permalink
[path_provider] Migrate path_provider_windows to nullsafety (flutter#…
Browse files Browse the repository at this point in the history
…3410)

Migrates path_provider_windows to null-safety.

Part of flutter#70229
  • Loading branch information
stuartmorgan authored Jan 21, 2021
1 parent f302473 commit 31923c8
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 42 deletions.
4 changes: 4 additions & 0 deletions packages/path_provider/path_provider_windows/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.1.0-nullsafety

* Migrate to null safety

## 0.0.4+4

* Update Flutter SDK constraint.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,19 @@ import 'folders.dart';
class VersionInfoQuerier {
/// Returns the value for [key] in [versionInfo]s English strings section, or
/// null if there is no such entry, or if versionInfo is null.
getStringValue(Pointer<Uint8> versionInfo, key) {
getStringValue(Pointer<Uint8>? versionInfo, key) {
if (versionInfo == null) {
return null;
}
const kEnUsLanguageCode = '040904e4';
final keyPath = TEXT('\\StringFileInfo\\$kEnUsLanguageCode\\$key');
final length = allocate<Uint32>();
final valueAddress = allocate<IntPtr>();
final valueAddress = allocate<Pointer<Utf16>>();
try {
if (VerQueryValue(versionInfo, keyPath, valueAddress, length) == 0) {
return null;
}
return Pointer<Utf16>.fromAddress(valueAddress.value)
.unpackString(length.value);
return valueAddress.value.unpackString(length.value);
} finally {
free(keyPath);
free(length);
Expand All @@ -54,7 +53,7 @@ class PathProviderWindows extends PathProviderPlatform {

/// This is typically the same as the TMP environment variable.
@override
Future<String> getTemporaryPath() async {
Future<String?> getTemporaryPath() async {
final buffer = allocate<Uint16>(count: MAX_PATH + 1).cast<Utf16>();
String path;

Expand Down Expand Up @@ -88,7 +87,7 @@ class PathProviderWindows extends PathProviderPlatform {
}

@override
Future<String> getApplicationSupportPath() async {
Future<String?> getApplicationSupportPath() async {
final appDataRoot = await getPath(WindowsKnownFolder.RoamingAppData);
final directory = Directory(
path.join(appDataRoot, _getApplicationSpecificSubdirectory()));
Expand All @@ -105,25 +104,23 @@ class PathProviderWindows extends PathProviderPlatform {
}

@override
Future<String> getApplicationDocumentsPath() =>
Future<String?> getApplicationDocumentsPath() =>
getPath(WindowsKnownFolder.Documents);

@override
Future<String> getDownloadsPath() => getPath(WindowsKnownFolder.Downloads);
Future<String?> getDownloadsPath() => getPath(WindowsKnownFolder.Downloads);

/// Retrieve any known folder from Windows.
///
/// folderID is a GUID that represents a specific known folder ID, drawn from
/// [WindowsKnownFolder].
Future<String> getPath(String folderID) {
final pathPtrPtr = allocate<IntPtr>();
Pointer<Utf16> pathPtr;
final pathPtrPtr = allocate<Pointer<Utf16>>();
final Pointer<GUID> knownFolderID = calloc<GUID>()..setGUID(folderID);

try {
GUID knownFolderID = GUID.fromString(folderID);

final hr = SHGetKnownFolderPath(
knownFolderID.addressOf, // ignore: deprecated_member_use
knownFolderID,
KF_FLAG_DEFAULT,
NULL,
pathPtrPtr,
Expand All @@ -135,12 +132,11 @@ class PathProviderWindows extends PathProviderPlatform {
}
}

pathPtr = Pointer<Utf16>.fromAddress(pathPtrPtr.value);
final path = pathPtr.unpackString(MAX_PATH);
final path = pathPtrPtr.value.unpackString(MAX_PATH);
return Future.value(path);
} finally {
CoTaskMemFree(pathPtr.cast());
free(pathPtrPtr);
free(knownFolderID);
}
}

Expand All @@ -155,13 +151,13 @@ class PathProviderWindows extends PathProviderPlatform {
/// - If the product name isn't there, it will use the exe's filename (without
/// extension).
String _getApplicationSpecificSubdirectory() {
String companyName;
String productName;
String? companyName;
String? productName;

final Pointer<Utf16> moduleNameBuffer =
allocate<Uint16>(count: MAX_PATH + 1).cast<Utf16>();
final Pointer<Uint32> unused = allocate<Uint32>();
Pointer<Uint8> infoBuffer;
Pointer<Uint8>? infoBuffer;
try {
// Get the module name.
final moduleNameLength = GetModuleFileName(0, moduleNameBuffer, MAX_PATH);
Expand Down Expand Up @@ -207,7 +203,7 @@ class PathProviderWindows extends PathProviderPlatform {
/// https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions
///
/// If after sanitizing the string is empty, returns null.
String _sanitizedDirectoryName(String rawString) {
String? _sanitizedDirectoryName(String? rawString) {
if (rawString == null) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class PathProviderWindows extends PathProviderPlatform {
}

/// Stub; see comment on VersionInfoQuerier.
VersionInfoQuerier versionInfoQuerier;
VersionInfoQuerier versionInfoQuerier = VersionInfoQuerier();

/// Match PathProviderWindows so that the analyzer won't report invalid
/// overrides if tests provide fake PathProviderWindows implementations.
Expand Down
16 changes: 8 additions & 8 deletions packages/path_provider/path_provider_windows/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: path_provider_windows
description: Windows implementation of the path_provider plugin
homepage: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_windows
version: 0.0.4+4
version: 0.1.0-nullsafety

flutter:
plugin:
Expand All @@ -11,19 +11,19 @@ flutter:
pluginClass: none

dependencies:
path_provider_platform_interface: ^1.0.3
meta: ^1.0.5
path: ^1.6.4
path_provider_platform_interface: ^2.0.0-nullsafety
meta: ^1.3.0-nullsafety.6
path: ^1.8.0-nullsafety.3
flutter:
sdk: flutter
ffi: ^0.1.3
win32: ^1.7.1
ffi: ^0.2.0-nullsafety.1
win32: ^2.0.0-nullsafety.8

dev_dependencies:
flutter_test:
sdk: flutter
pedantic: ^1.8.0
pedantic: ^1.10.0-nullsafety.3

environment:
sdk: ">=2.1.0 <3.0.0"
sdk: '>=2.12.0-0 <3.0.0'
flutter: ">=1.12.13+hotfix.4"
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class FakeVersionInfoQuerier implements VersionInfoQuerier {

final Map<String, String> responses;

getStringValue(Pointer<Uint8> versionInfo, key) => responses[key];
getStringValue(Pointer<Uint8>? versionInfo, key) => responses[key];
}

void main() {
Expand All @@ -40,8 +40,11 @@ void main() {
'ProductName': 'Amazing App',
});
final path = await pathProvider.getApplicationSupportPath();
expect(path, endsWith(r'AppData\Roaming\A Company\Amazing App'));
expect(Directory(path).existsSync(), isTrue);
expect(path, isNotNull);
if (path != null) {
expect(path, endsWith(r'AppData\Roaming\A Company\Amazing App'));
expect(Directory(path).existsSync(), isTrue);
}
}, skip: !Platform.isWindows);

test('getApplicationSupportPath with missing company', () async {
Expand All @@ -50,8 +53,11 @@ void main() {
'ProductName': 'Amazing App',
});
final path = await pathProvider.getApplicationSupportPath();
expect(path, endsWith(r'AppData\Roaming\Amazing App'));
expect(Directory(path).existsSync(), isTrue);
expect(path, isNotNull);
if (path != null) {
expect(path, endsWith(r'AppData\Roaming\Amazing App'));
expect(Directory(path).existsSync(), isTrue);
}
}, skip: !Platform.isWindows);

test('getApplicationSupportPath with problematic values', () async {
Expand All @@ -61,12 +67,15 @@ void main() {
'ProductName': r'A"/Terrible\|App?*Name',
});
final path = await pathProvider.getApplicationSupportPath();
expect(
path,
endsWith(r'AppData\Roaming\'
r'A _Bad_ Company_ Name\'
r'A__Terrible__App__Name'));
expect(Directory(path).existsSync(), isTrue);
expect(path, isNotNull);
if (path != null) {
expect(
path,
endsWith(r'AppData\Roaming\'
r'A _Bad_ Company_ Name\'
r'A__Terrible__App__Name'));
expect(Directory(path).existsSync(), isTrue);
}
}, skip: !Platform.isWindows);

test('getApplicationSupportPath with a completely invalid company', () async {
Expand All @@ -76,8 +85,11 @@ void main() {
'ProductName': r'Amazing App',
});
final path = await pathProvider.getApplicationSupportPath();
expect(path, endsWith(r'AppData\Roaming\Amazing App'));
expect(Directory(path).existsSync(), isTrue);
expect(path, isNotNull);
if (path != null) {
expect(path, endsWith(r'AppData\Roaming\Amazing App'));
expect(Directory(path).existsSync(), isTrue);
}
}, skip: !Platform.isWindows);

test('getApplicationSupportPath with very long app name', () async {
Expand Down

0 comments on commit 31923c8

Please sign in to comment.