Skip to content

Commit

Permalink
[file_selector] Switch to Pigeon for macOS (flutter#6902)
Browse files Browse the repository at this point in the history
* Initial pigeon definition

* Update Dart, and add Dart test coverage

* Update Swift and native tests

* Version bump

* Format

* Revert SDK change
  • Loading branch information
stuartmorgan authored Jan 10, 2023
1 parent b9206bc commit b1797c2
Show file tree
Hide file tree
Showing 12 changed files with 1,042 additions and 340 deletions.
4 changes: 4 additions & 0 deletions packages/file_selector/file_selector_macos/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.9.0+4

* Converts platform channel to Pigeon.

## 0.9.0+3

* Changes XTypeGroup initialization from final to const.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,13 @@ class exampleTests: XCTestCase {
panelController.openURLs = [URL(fileURLWithPath: returnPath)]

let called = XCTestExpectation()
let call = FlutterMethodCall(methodName: "openFile", arguments: [:])
plugin.handle(call) { result in
XCTAssertEqual((result as! [String]?)![0], returnPath)
let options = OpenPanelOptions(
allowsMultipleSelection: false,
canChooseDirectories: false,
canChooseFiles: true,
baseOptions: SavePanelOptions())
plugin.displayOpenPanel(options: options) { paths in
XCTAssertEqual(paths[0], returnPath)
called.fulfill()
}

Expand All @@ -72,16 +76,16 @@ class exampleTests: XCTestCase {
panelController.openURLs = [URL(fileURLWithPath: returnPath)]

let called = XCTestExpectation()
let call = FlutterMethodCall(
methodName: "openFile",
arguments: [
"initialDirectory": "/some/dir",
"suggestedName": "a name",
"confirmButtonText": "Open it!",
]
)
plugin.handle(call) { result in
XCTAssertEqual((result as! [String]?)![0], returnPath)
let options = OpenPanelOptions(
allowsMultipleSelection: false,
canChooseDirectories: false,
canChooseFiles: true,
baseOptions: SavePanelOptions(
directoryPath: "/some/dir",
nameFieldStringValue: "a name",
prompt: "Open it!"))
plugin.displayOpenPanel(options: options) { paths in
XCTAssertEqual(paths[0], returnPath)
called.fulfill()
}

Expand All @@ -104,12 +108,12 @@ class exampleTests: XCTestCase {
panelController.openURLs = returnPaths.map({ path in URL(fileURLWithPath: path) })

let called = XCTestExpectation()
let call = FlutterMethodCall(
methodName: "openFile",
arguments: ["multiple": true]
)
plugin.handle(call) { result in
let paths = (result as! [String]?)!
let options = OpenPanelOptions(
allowsMultipleSelection: true,
canChooseDirectories: false,
canChooseFiles: true,
baseOptions: SavePanelOptions())
plugin.displayOpenPanel(options: options) { paths in
XCTAssertEqual(paths.count, returnPaths.count)
XCTAssertEqual(paths[0], returnPaths[0])
XCTAssertEqual(paths[1], returnPaths[1])
Expand All @@ -130,17 +134,17 @@ class exampleTests: XCTestCase {
panelController.openURLs = [URL(fileURLWithPath: returnPath)]

let called = XCTestExpectation()
let call = FlutterMethodCall(
methodName: "openFile",
arguments: [
"acceptedTypes": [
"extensions": ["txt", "json"],
"UTIs": ["public.text", "public.image"],
]
]
)
plugin.handle(call) { result in
XCTAssertEqual((result as! [String]?)![0], returnPath)
let options = OpenPanelOptions(
allowsMultipleSelection: true,
canChooseDirectories: false,
canChooseFiles: true,
baseOptions: SavePanelOptions(
allowedFileTypes: AllowedTypes(
extensions: ["txt", "json"],
mimeTypes: [],
utis: ["public.text", "public.image"])))
plugin.displayOpenPanel(options: options) { paths in
XCTAssertEqual(paths[0], returnPath)
called.fulfill()
}

Expand All @@ -158,9 +162,13 @@ class exampleTests: XCTestCase {
panelController: panelController)

let called = XCTestExpectation()
let call = FlutterMethodCall(methodName: "openFile", arguments: [:])
plugin.handle(call) { result in
XCTAssertNil(result)
let options = OpenPanelOptions(
allowsMultipleSelection: false,
canChooseDirectories: false,
canChooseFiles: true,
baseOptions: SavePanelOptions())
plugin.displayOpenPanel(options: options) { paths in
XCTAssertEqual(paths.count, 0)
called.fulfill()
}

Expand All @@ -178,9 +186,9 @@ class exampleTests: XCTestCase {
panelController.saveURL = URL(fileURLWithPath: returnPath)

let called = XCTestExpectation()
let call = FlutterMethodCall(methodName: "getSavePath", arguments: [:])
plugin.handle(call) { result in
XCTAssertEqual(result as! String?, returnPath)
let options = SavePanelOptions()
plugin.displaySavePanel(options: options) { path in
XCTAssertEqual(path, returnPath)
called.fulfill()
}

Expand All @@ -198,15 +206,11 @@ class exampleTests: XCTestCase {
panelController.saveURL = URL(fileURLWithPath: returnPath)

let called = XCTestExpectation()
let call = FlutterMethodCall(
methodName: "getSavePath",
arguments: [
"initialDirectory": "/some/dir",
"confirmButtonText": "Save it!",
]
)
plugin.handle(call) { result in
XCTAssertEqual(result as! String?, returnPath)
let options = SavePanelOptions(
directoryPath: "/some/dir",
prompt: "Save it!")
plugin.displaySavePanel(options: options) { path in
XCTAssertEqual(path, returnPath)
called.fulfill()
}

Expand All @@ -225,9 +229,9 @@ class exampleTests: XCTestCase {
panelController: panelController)

let called = XCTestExpectation()
let call = FlutterMethodCall(methodName: "getSavePath", arguments: [:])
plugin.handle(call) { result in
XCTAssertNil(result)
let options = SavePanelOptions()
plugin.displaySavePanel(options: options) { path in
XCTAssertNil(path)
called.fulfill()
}

Expand All @@ -245,9 +249,13 @@ class exampleTests: XCTestCase {
panelController.openURLs = [URL(fileURLWithPath: returnPath)]

let called = XCTestExpectation()
let call = FlutterMethodCall(methodName: "getDirectoryPath", arguments: [:])
plugin.handle(call) { result in
XCTAssertEqual(result as! String?, returnPath)
let options = OpenPanelOptions(
allowsMultipleSelection: false,
canChooseDirectories: true,
canChooseFiles: false,
baseOptions: SavePanelOptions())
plugin.displayOpenPanel(options: options) { paths in
XCTAssertEqual(paths[0], returnPath)
called.fulfill()
}

Expand All @@ -270,9 +278,13 @@ class exampleTests: XCTestCase {
panelController: panelController)

let called = XCTestExpectation()
let call = FlutterMethodCall(methodName: "getDirectoryPath", arguments: [:])
plugin.handle(call) { result in
XCTAssertNil(result)
let options = OpenPanelOptions(
allowsMultipleSelection: false,
canChooseDirectories: true,
canChooseFiles: false,
baseOptions: SavePanelOptions())
plugin.displayOpenPanel(options: options) { paths in
XCTAssertEqual(paths.count, 0)
called.fulfill()
}

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

import 'package:file_selector_platform_interface/file_selector_platform_interface.dart';
import 'package:flutter/foundation.dart' show visibleForTesting;
import 'package:flutter/services.dart';

const MethodChannel _channel =
MethodChannel('plugins.flutter.io/file_selector_macos');
import 'src/messages.g.dart';

/// An implementation of [FileSelectorPlatform] for macOS.
class FileSelectorMacOS extends FileSelectorPlatform {
/// The MethodChannel that is being used by this implementation of the plugin.
@visibleForTesting
MethodChannel get channel => _channel;
final FileSelectorApi _hostApi = FileSelectorApi();

/// Registers the macOS implementation.
static void registerWith() {
Expand All @@ -26,16 +21,17 @@ class FileSelectorMacOS extends FileSelectorPlatform {
String? initialDirectory,
String? confirmButtonText,
}) async {
final List<String>? path = await _channel.invokeListMethod<String>(
'openFile',
<String, dynamic>{
'acceptedTypes': _allowedTypeListFromTypeGroups(acceptedTypeGroups),
'initialDirectory': initialDirectory,
'confirmButtonText': confirmButtonText,
'multiple': false,
},
);
return path == null ? null : XFile(path.first);
final List<String?> paths =
await _hostApi.displayOpenPanel(OpenPanelOptions(
allowsMultipleSelection: false,
canChooseDirectories: false,
canChooseFiles: true,
baseOptions: SavePanelOptions(
allowedFileTypes: _allowedTypesFromTypeGroups(acceptedTypeGroups),
directoryPath: initialDirectory,
prompt: confirmButtonText,
)));
return paths.isEmpty ? null : XFile(paths.first!);
}

@override
Expand All @@ -44,16 +40,17 @@ class FileSelectorMacOS extends FileSelectorPlatform {
String? initialDirectory,
String? confirmButtonText,
}) async {
final List<String>? pathList = await _channel.invokeListMethod<String>(
'openFile',
<String, dynamic>{
'acceptedTypes': _allowedTypeListFromTypeGroups(acceptedTypeGroups),
'initialDirectory': initialDirectory,
'confirmButtonText': confirmButtonText,
'multiple': true,
},
);
return pathList?.map((String path) => XFile(path)).toList() ?? <XFile>[];
final List<String?> paths =
await _hostApi.displayOpenPanel(OpenPanelOptions(
allowsMultipleSelection: true,
canChooseDirectories: false,
canChooseFiles: true,
baseOptions: SavePanelOptions(
allowedFileTypes: _allowedTypesFromTypeGroups(acceptedTypeGroups),
directoryPath: initialDirectory,
prompt: confirmButtonText,
)));
return paths.map((String? path) => XFile(path!)).toList();
}

@override
Expand All @@ -63,46 +60,42 @@ class FileSelectorMacOS extends FileSelectorPlatform {
String? suggestedName,
String? confirmButtonText,
}) async {
return _channel.invokeMethod<String>(
'getSavePath',
<String, dynamic>{
'acceptedTypes': _allowedTypeListFromTypeGroups(acceptedTypeGroups),
'initialDirectory': initialDirectory,
'suggestedName': suggestedName,
'confirmButtonText': confirmButtonText,
},
);
return _hostApi.displaySavePanel(SavePanelOptions(
allowedFileTypes: _allowedTypesFromTypeGroups(acceptedTypeGroups),
directoryPath: initialDirectory,
nameFieldStringValue: suggestedName,
prompt: confirmButtonText,
));
}

@override
Future<String?> getDirectoryPath({
String? initialDirectory,
String? confirmButtonText,
}) async {
return _channel.invokeMethod<String>(
'getDirectoryPath',
<String, dynamic>{
'initialDirectory': initialDirectory,
'confirmButtonText': confirmButtonText,
},
);
final List<String?> paths =
await _hostApi.displayOpenPanel(OpenPanelOptions(
allowsMultipleSelection: false,
canChooseDirectories: true,
canChooseFiles: false,
baseOptions: SavePanelOptions(
directoryPath: initialDirectory,
prompt: confirmButtonText,
)));
return paths.isEmpty ? null : paths.first;
}

// Converts the type group list into a flat list of all allowed types, since
// macOS doesn't support filter groups.
Map<String, List<String>>? _allowedTypeListFromTypeGroups(
List<XTypeGroup>? typeGroups) {
const String extensionKey = 'extensions';
const String mimeTypeKey = 'mimeTypes';
const String utiKey = 'UTIs';
AllowedTypes? _allowedTypesFromTypeGroups(List<XTypeGroup>? typeGroups) {
if (typeGroups == null || typeGroups.isEmpty) {
return null;
}
final Map<String, List<String>> allowedTypes = <String, List<String>>{
extensionKey: <String>[],
mimeTypeKey: <String>[],
utiKey: <String>[],
};
final AllowedTypes allowedTypes = AllowedTypes(
extensions: <String>[],
mimeTypes: <String>[],
utis: <String>[],
);
for (final XTypeGroup typeGroup in typeGroups) {
// If any group allows everything, no filtering should be done.
if (typeGroup.allowsAny) {
Expand All @@ -119,9 +112,9 @@ class FileSelectorMacOS extends FileSelectorPlatform {
'"mimeTypes" must be non-empty for macOS if anything is '
'non-empty.');
}
allowedTypes[extensionKey]!.addAll(typeGroup.extensions ?? <String>[]);
allowedTypes[mimeTypeKey]!.addAll(typeGroup.mimeTypes ?? <String>[]);
allowedTypes[utiKey]!.addAll(typeGroup.macUTIs ?? <String>[]);
allowedTypes.extensions.addAll(typeGroup.extensions ?? <String>[]);
allowedTypes.mimeTypes.addAll(typeGroup.mimeTypes ?? <String>[]);
allowedTypes.utis.addAll(typeGroup.macUTIs ?? <String>[]);
}

return allowedTypes;
Expand Down
Loading

0 comments on commit b1797c2

Please sign in to comment.