blob: 74ce2835d18c03cf8dda691c54169a7767e5f74a [file] [log] [blame]
// Copyright 2013 The Flutter 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: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');
/// 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;
/// Registers the macOS implementation.
static void registerWith() {
FileSelectorPlatform.instance = FileSelectorMacOS();
}
@override
Future<XFile?> openFile({
List<XTypeGroup>? acceptedTypeGroups,
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);
}
@override
Future<List<XFile>> openFiles({
List<XTypeGroup>? acceptedTypeGroups,
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>[];
}
@override
Future<String?> getSavePath({
List<XTypeGroup>? acceptedTypeGroups,
String? initialDirectory,
String? suggestedName,
String? confirmButtonText,
}) async {
return _channel.invokeMethod<String>(
'getSavePath',
<String, dynamic>{
'acceptedTypes': _allowedTypeListFromTypeGroups(acceptedTypeGroups),
'initialDirectory': initialDirectory,
'suggestedName': suggestedName,
'confirmButtonText': confirmButtonText,
},
);
}
@override
Future<String?> getDirectoryPath({
String? initialDirectory,
String? confirmButtonText,
}) async {
return _channel.invokeMethod<String>(
'getDirectoryPath',
<String, dynamic>{
'initialDirectory': initialDirectory,
'confirmButtonText': confirmButtonText,
},
);
}
// 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';
if (typeGroups == null || typeGroups.isEmpty) {
return null;
}
final Map<String, List<String>> allowedTypes = <String, List<String>>{
extensionKey: <String>[],
mimeTypeKey: <String>[],
utiKey: <String>[],
};
for (final XTypeGroup typeGroup in typeGroups) {
// If any group allows everything, no filtering should be done.
if (typeGroup.allowsAny) {
return null;
}
// Reject a filter that isn't an allow-any, but doesn't set any
// macOS-supported filter categories.
if ((typeGroup.extensions?.isEmpty ?? true) &&
(typeGroup.macUTIs?.isEmpty ?? true) &&
(typeGroup.mimeTypes?.isEmpty ?? true)) {
throw ArgumentError('Provided type group $typeGroup does not allow '
'all files, but does not set any of the macOS-supported filter '
'categories. At least one of "extensions", "macUTIs", or '
'"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>[]);
}
return allowedTypes;
}
}