diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index ba0303e9a74c..241b18780c56 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -544d30dbaddf17edbed6cbc52c8877482497e049 +c40173f114fa8b830531578586f4f4eedd2b2c1f diff --git a/.ci/flutter_stable.version b/.ci/flutter_stable.version index f99b2a7b9d5e..7b3a58cd94cc 100644 --- a/.ci/flutter_stable.version +++ b/.ci/flutter_stable.version @@ -1 +1 @@ -796c8ef79279f9c774545b3771238c3098dbefab +f468f3366c26a5092eb964a230ce7892fda8f2f8 diff --git a/.ci/targets/android_platform_tests.yaml b/.ci/targets/android_platform_tests.yaml index ee5de6e56b1e..40dbc7d2d017 100644 --- a/.ci/targets/android_platform_tests.yaml +++ b/.ci/targets/android_platform_tests.yaml @@ -1,6 +1,11 @@ tasks: - name: prepare tool script: .ci/scripts/prepare_tool.sh + infra_step: true # Note infra steps failing prevents "always" from running. + - name: download android deps + script: script/tool_runner.sh + infra_step: true + args: ["fetch-deps"] - name: build examples script: script/tool_runner.sh args: ["build-examples", "--apk"] diff --git a/packages/file_selector/file_selector_macos/CHANGELOG.md b/packages/file_selector/file_selector_macos/CHANGELOG.md index 7a197838bb95..a80935d5f76d 100644 --- a/packages/file_selector/file_selector_macos/CHANGELOG.md +++ b/packages/file_selector/file_selector_macos/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.3+1 + +* Updates to the latest version of `pigeon`. + ## 0.9.3 * Adds `getSaveLocation` and deprecates `getSavePath`. diff --git a/packages/file_selector/file_selector_macos/lib/src/messages.g.dart b/packages/file_selector/file_selector_macos/lib/src/messages.g.dart index e8d5a85147a4..cbf8dd10007a 100644 --- a/packages/file_selector/file_selector_macos/lib/src/messages.g.dart +++ b/packages/file_selector/file_selector_macos/lib/src/messages.g.dart @@ -1,7 +1,7 @@ // 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. -// Autogenerated from Pigeon (v9.2.4), do not edit directly. +// Autogenerated from Pigeon (v10.1.3), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import diff --git a/packages/file_selector/file_selector_macos/macos/Classes/messages.g.swift b/packages/file_selector/file_selector_macos/macos/Classes/messages.g.swift index 67007e100811..7cff0d721df8 100644 --- a/packages/file_selector/file_selector_macos/macos/Classes/messages.g.swift +++ b/packages/file_selector/file_selector_macos/macos/Classes/messages.g.swift @@ -1,7 +1,7 @@ // 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. -// Autogenerated from Pigeon (v9.2.4), do not edit directly. +// Autogenerated from Pigeon (v10.1.3), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation @@ -35,7 +35,7 @@ private func wrapError(_ error: Any) -> [Any?] { private func nilOrValue(_ value: Any?) -> T? { if value is NSNull { return nil } - return (value as Any) as! T? + return value as! T? } /// A Pigeon representation of the macOS portion of an `XTypeGroup`. @@ -46,7 +46,7 @@ struct AllowedTypes { var mimeTypes: [String?] var utis: [String?] - static func fromList(_ list: [Any]) -> AllowedTypes? { + static func fromList(_ list: [Any?]) -> AllowedTypes? { let extensions = list[0] as! [String?] let mimeTypes = list[1] as! [String?] let utis = list[2] as! [String?] @@ -78,9 +78,9 @@ struct SavePanelOptions { var nameFieldStringValue: String? = nil var prompt: String? = nil - static func fromList(_ list: [Any]) -> SavePanelOptions? { + static func fromList(_ list: [Any?]) -> SavePanelOptions? { var allowedFileTypes: AllowedTypes? = nil - if let allowedFileTypesList = list[0] as! [Any]? { + if let allowedFileTypesList: [Any?] = nilOrValue(list[0]) { allowedFileTypes = AllowedTypes.fromList(allowedFileTypesList) } let directoryPath: String? = nilOrValue(list[1]) @@ -115,11 +115,11 @@ struct OpenPanelOptions { var canChooseFiles: Bool var baseOptions: SavePanelOptions - static func fromList(_ list: [Any]) -> OpenPanelOptions? { + static func fromList(_ list: [Any?]) -> OpenPanelOptions? { let allowsMultipleSelection = list[0] as! Bool let canChooseDirectories = list[1] as! Bool let canChooseFiles = list[2] as! Bool - let baseOptions = SavePanelOptions.fromList(list[3] as! [Any])! + let baseOptions = SavePanelOptions.fromList(list[3] as! [Any?])! return OpenPanelOptions( allowsMultipleSelection: allowsMultipleSelection, @@ -142,11 +142,11 @@ private class FileSelectorApiCodecReader: FlutterStandardReader { override func readValue(ofType type: UInt8) -> Any? { switch type { case 128: - return AllowedTypes.fromList(self.readValue() as! [Any]) + return AllowedTypes.fromList(self.readValue() as! [Any?]) case 129: - return OpenPanelOptions.fromList(self.readValue() as! [Any]) + return OpenPanelOptions.fromList(self.readValue() as! [Any?]) case 130: - return SavePanelOptions.fromList(self.readValue() as! [Any]) + return SavePanelOptions.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) } @@ -214,7 +214,7 @@ class FileSelectorApiSetup { codec: codec) if let api = api { displayOpenPanelChannel.setMessageHandler { message, reply in - let args = message as! [Any] + let args = message as! [Any?] let optionsArg = args[0] as! OpenPanelOptions api.displayOpenPanel(options: optionsArg) { result in switch result { @@ -236,7 +236,7 @@ class FileSelectorApiSetup { codec: codec) if let api = api { displaySavePanelChannel.setMessageHandler { message, reply in - let args = message as! [Any] + let args = message as! [Any?] let optionsArg = args[0] as! SavePanelOptions api.displaySavePanel(options: optionsArg) { result in switch result { diff --git a/packages/file_selector/file_selector_macos/pubspec.yaml b/packages/file_selector/file_selector_macos/pubspec.yaml index 047c7caca4f3..3bbf2624b3e3 100644 --- a/packages/file_selector/file_selector_macos/pubspec.yaml +++ b/packages/file_selector/file_selector_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_macos description: macOS implementation of the file_selector plugin. repository: https://github.com/flutter/packages/tree/main/packages/file_selector/file_selector_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.9.3 +version: 0.9.3+1 environment: sdk: ">=2.18.0 <4.0.0" @@ -27,4 +27,4 @@ dev_dependencies: flutter_test: sdk: flutter mockito: 5.4.1 - pigeon: ^9.2.4 + pigeon: ^10.1.3 diff --git a/packages/file_selector/file_selector_macos/test/messages_test.g.dart b/packages/file_selector/file_selector_macos/test/messages_test.g.dart index f72a987c83d6..893161f8e811 100644 --- a/packages/file_selector/file_selector_macos/test/messages_test.g.dart +++ b/packages/file_selector/file_selector_macos/test/messages_test.g.dart @@ -1,7 +1,7 @@ // 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. -// Autogenerated from Pigeon (v9.2.4), do not edit directly. +// Autogenerated from Pigeon (v10.1.3), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import // ignore_for_file: avoid_relative_lib_imports diff --git a/packages/go_router_builder/example/lib/all_types.dart b/packages/go_router_builder/example/lib/all_types.dart index ce86905afc0e..165523b1b3c0 100644 --- a/packages/go_router_builder/example/lib/all_types.dart +++ b/packages/go_router_builder/example/lib/all_types.dart @@ -550,9 +550,7 @@ class AllTypesApp extends StatelessWidget { @override Widget build(BuildContext context) => MaterialApp.router( - routeInformationParser: _router.routeInformationParser, - routerDelegate: _router.routerDelegate, - routeInformationProvider: _router.routeInformationProvider, + routerConfig: _router, ); late final GoRouter _router = GoRouter( diff --git a/packages/go_router_builder/example/lib/main.dart b/packages/go_router_builder/example/lib/main.dart index 84b28c2ae562..773b6687ed93 100644 --- a/packages/go_router_builder/example/lib/main.dart +++ b/packages/go_router_builder/example/lib/main.dart @@ -26,9 +26,7 @@ class App extends StatelessWidget { Widget build(BuildContext context) => ChangeNotifierProvider.value( value: loginInfo, child: MaterialApp.router( - routeInformationParser: _router.routeInformationParser, - routerDelegate: _router.routerDelegate, - routeInformationProvider: _router.routeInformationProvider, + routerConfig: _router, title: title, debugShowCheckedModeBanner: false, ), diff --git a/packages/go_router_builder/example/lib/simple_example.dart b/packages/go_router_builder/example/lib/simple_example.dart index dee29d0c867c..dbb0a15a69c9 100644 --- a/packages/go_router_builder/example/lib/simple_example.dart +++ b/packages/go_router_builder/example/lib/simple_example.dart @@ -18,8 +18,7 @@ class App extends StatelessWidget { @override Widget build(BuildContext context) => MaterialApp.router( - routeInformationParser: _router.routeInformationParser, - routerDelegate: _router.routerDelegate, + routerConfig: _router, title: _appTitle, ); diff --git a/packages/go_router_builder/example/test/simple_example_test.dart b/packages/go_router_builder/example/test/simple_example_test.dart new file mode 100644 index 000000000000..80c51864740a --- /dev/null +++ b/packages/go_router_builder/example/test/simple_example_test.dart @@ -0,0 +1,20 @@ +// 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:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:go_router_builder_example/shared/data.dart'; +import 'package:go_router_builder_example/simple_example.dart'; + +void main() { + testWidgets('App starts on HomeScreen and displays families', + (WidgetTester tester) async { + await tester.pumpWidget(App()); + expect(find.byType(HomeScreen), findsOneWidget); + expect(find.byType(ListTile), findsNWidgets(familyData.length)); + for (final Family family in familyData) { + expect(find.text(family.name), findsOneWidget); + } + }); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md index c93d3e2fa5bc..c47578b208a4 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.0 + +* Adds a `cloudMapId` parameter to support cloud-based map styling. + ## 2.2.7 * Removes obsolete null checks on non-nullable values. diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/map_configuration.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/map_configuration.dart index 4b43caffe5b6..5580e9eea98b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/map_configuration.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/map_configuration.dart @@ -33,6 +33,7 @@ class MapConfiguration { this.indoorViewEnabled, this.trafficEnabled, this.buildingsEnabled, + this.cloudMapId, }); /// True if the compass UI should be shown. @@ -90,6 +91,12 @@ class MapConfiguration { /// True if 3D building display should be enabled. final bool? buildingsEnabled; + /// Identifier that's associated with a specific cloud-based map style. + /// + /// See https://developers.google.com/maps/documentation/get-map-id + /// for more details. + final String? cloudMapId; + /// Returns a new options object containing only the values of this instance /// that are different from [other]. MapConfiguration diffFrom(MapConfiguration other) { @@ -143,6 +150,7 @@ class MapConfiguration { trafficEnabled != other.trafficEnabled ? trafficEnabled : null, buildingsEnabled: buildingsEnabled != other.buildingsEnabled ? buildingsEnabled : null, + cloudMapId: cloudMapId != other.cloudMapId ? cloudMapId : null, ); } @@ -171,6 +179,7 @@ class MapConfiguration { indoorViewEnabled: diff.indoorViewEnabled ?? indoorViewEnabled, trafficEnabled: diff.trafficEnabled ?? trafficEnabled, buildingsEnabled: diff.buildingsEnabled ?? buildingsEnabled, + cloudMapId: diff.cloudMapId ?? cloudMapId, ); } @@ -193,7 +202,8 @@ class MapConfiguration { padding == null && indoorViewEnabled == null && trafficEnabled == null && - buildingsEnabled == null; + buildingsEnabled == null && + cloudMapId == null; @override bool operator ==(Object other) { @@ -221,7 +231,8 @@ class MapConfiguration { padding == other.padding && indoorViewEnabled == other.indoorViewEnabled && trafficEnabled == other.trafficEnabled && - buildingsEnabled == other.buildingsEnabled; + buildingsEnabled == other.buildingsEnabled && + cloudMapId == other.cloudMapId; } @override @@ -244,5 +255,6 @@ class MapConfiguration { indoorViewEnabled, trafficEnabled, buildingsEnabled, + cloudMapId, ); } diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/utils/map_configuration_serialization.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/utils/map_configuration_serialization.dart index 01f4fa054570..3c3e0b714cb6 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/utils/map_configuration_serialization.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/utils/map_configuration_serialization.dart @@ -58,5 +58,6 @@ Map jsonForMapConfiguration(MapConfiguration config) { if (config.trafficEnabled != null) 'trafficEnabled': config.trafficEnabled!, if (config.buildingsEnabled != null) 'buildingsEnabled': config.buildingsEnabled!, + if (config.cloudMapId != null) 'cloudMapId': config.cloudMapId!, }; } diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml index 4970ff8b6510..a8d7b126ca35 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/packages/tree/main/packages/google_maps_f issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 # 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.2.7 +version: 2.3.0 environment: sdk: ">=2.18.0 <4.0.0" diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/map_configuration_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/map_configuration_test.dart index edd1fd091073..e57d2c73eefc 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/map_configuration_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/map_configuration_test.dart @@ -6,6 +6,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; +const String _kCloudMapId = '000000000000000'; // Dummy map ID. + void main() { group('diffs', () { // A options instance with every field set, to test diffs against. @@ -53,6 +55,7 @@ void main() { expect(updated.liteModeEnabled, isNot(null)); expect(updated.padding, isNot(null)); expect(updated.trafficEnabled, isNot(null)); + expect(updated.cloudMapId, null); }); test('handle compassEnabled', () async { @@ -281,6 +284,18 @@ void main() { // A diff applied to non-empty options should update that field. expect(updated.buildingsEnabled, true); }); + + test('handle cloudMapId', () async { + const MapConfiguration diff = MapConfiguration(cloudMapId: _kCloudMapId); + + const MapConfiguration empty = MapConfiguration(); + final MapConfiguration updated = diffBase.applyDiff(diff); + + // A diff applied to empty options should be the diff itself. + expect(empty.applyDiff(diff), diff); + // A diff applied to non-empty options should update that field. + expect(updated.cloudMapId, _kCloudMapId); + }); }); group('isEmpty', () { @@ -408,5 +423,11 @@ void main() { expect(diff.isEmpty, false); }); + + test('is false with cloudMapId', () async { + const MapConfiguration diff = MapConfiguration(cloudMapId: _kCloudMapId); + + expect(diff.isEmpty, false); + }); }); } diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/utils/map_configuration_serialization_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/utils/map_configuration_serialization_test.dart index 71a0f8c4b1b1..80618356c1c9 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/utils/map_configuration_serialization_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/utils/map_configuration_serialization_test.dart @@ -7,6 +7,8 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'package:google_maps_flutter_platform_interface/src/types/utils/map_configuration_serialization.dart'; +const String _kCloudMapId = '000000000000000'; // Dummy map ID. + void main() { test('empty serialization', () async { const MapConfiguration config = MapConfiguration(); @@ -18,26 +20,26 @@ void main() { test('complete serialization', () async { final MapConfiguration config = MapConfiguration( - compassEnabled: false, - mapToolbarEnabled: false, - cameraTargetBounds: CameraTargetBounds(LatLngBounds( - northeast: const LatLng(30, 20), southwest: const LatLng(10, 40))), - mapType: MapType.normal, - minMaxZoomPreference: const MinMaxZoomPreference(1.0, 10.0), - rotateGesturesEnabled: false, - scrollGesturesEnabled: false, - tiltGesturesEnabled: false, - trackCameraPosition: false, - zoomControlsEnabled: false, - zoomGesturesEnabled: false, - liteModeEnabled: false, - myLocationEnabled: false, - myLocationButtonEnabled: false, - padding: const EdgeInsets.all(5.0), - indoorViewEnabled: false, - trafficEnabled: false, - buildingsEnabled: false, - ); + compassEnabled: false, + mapToolbarEnabled: false, + cameraTargetBounds: CameraTargetBounds(LatLngBounds( + northeast: const LatLng(30, 20), southwest: const LatLng(10, 40))), + mapType: MapType.normal, + minMaxZoomPreference: const MinMaxZoomPreference(1.0, 10.0), + rotateGesturesEnabled: false, + scrollGesturesEnabled: false, + tiltGesturesEnabled: false, + trackCameraPosition: false, + zoomControlsEnabled: false, + zoomGesturesEnabled: false, + liteModeEnabled: false, + myLocationEnabled: false, + myLocationButtonEnabled: false, + padding: const EdgeInsets.all(5.0), + indoorViewEnabled: false, + trafficEnabled: false, + buildingsEnabled: false, + cloudMapId: _kCloudMapId); final Map json = jsonForMapConfiguration(config); @@ -69,7 +71,8 @@ void main() { 'padding': [5.0, 5.0, 5.0, 5.0], 'indoorEnabled': false, 'trafficEnabled': false, - 'buildingsEnabled': false + 'buildingsEnabled': false, + 'cloudMapId': _kCloudMapId }); }); } diff --git a/packages/path_provider/path_provider_foundation/CHANGELOG.md b/packages/path_provider/path_provider_foundation/CHANGELOG.md index 51dbbfdabb50..1bbba4de2215 100644 --- a/packages/path_provider/path_provider_foundation/CHANGELOG.md +++ b/packages/path_provider/path_provider_foundation/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.2.4 +* Updates to the latest version of `pigeon`. * Updates minimum supported macOS version to 10.14. ## 2.2.3 diff --git a/packages/path_provider/path_provider_foundation/darwin/Classes/messages.g.swift b/packages/path_provider/path_provider_foundation/darwin/Classes/messages.g.swift index 7bfb25724f5b..402e14f2d32d 100644 --- a/packages/path_provider/path_provider_foundation/darwin/Classes/messages.g.swift +++ b/packages/path_provider/path_provider_foundation/darwin/Classes/messages.g.swift @@ -1,16 +1,17 @@ // 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. -// Autogenerated from Pigeon (v9.2.4), do not edit directly. +// Autogenerated from Pigeon (v10.1.3), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation + #if os(iOS) -import Flutter + import Flutter #elseif os(macOS) -import FlutterMacOS + import FlutterMacOS #else -#error("Unsupported platform.") + #error("Unsupported platform.") #endif private func wrapResult(_ result: Any?) -> [Any?] { @@ -22,19 +23,19 @@ private func wrapError(_ error: Any) -> [Any?] { return [ flutterError.code, flutterError.message, - flutterError.details + flutterError.details, ] } return [ "\(error)", "\(type(of: error))", - "Stacktrace: \(Thread.callStackSymbols)" + "Stacktrace: \(Thread.callStackSymbols)", ] } private func nilOrValue(_ value: Any?) -> T? { if value is NSNull { return nil } - return (value as Any) as! T? + return value as! T? } enum DirectoryType: Int { @@ -55,10 +56,11 @@ class PathProviderApiSetup { /// The codec used by PathProviderApi. /// Sets up an instance of `PathProviderApi` to handle messages through the `binaryMessenger`. static func setUp(binaryMessenger: FlutterBinaryMessenger, api: PathProviderApi?) { - let getDirectoryPathChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.PathProviderApi.getDirectoryPath", binaryMessenger: binaryMessenger) + let getDirectoryPathChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.PathProviderApi.getDirectoryPath", binaryMessenger: binaryMessenger) if let api = api { getDirectoryPathChannel.setMessageHandler { message, reply in - let args = message as! [Any] + let args = message as! [Any?] let typeArg = DirectoryType(rawValue: args[0] as! Int)! do { let result = try api.getDirectoryPath(type: typeArg) @@ -70,10 +72,11 @@ class PathProviderApiSetup { } else { getDirectoryPathChannel.setMessageHandler(nil) } - let getContainerPathChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.PathProviderApi.getContainerPath", binaryMessenger: binaryMessenger) + let getContainerPathChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.PathProviderApi.getContainerPath", binaryMessenger: binaryMessenger) if let api = api { getContainerPathChannel.setMessageHandler { message, reply in - let args = message as! [Any] + let args = message as! [Any?] let appGroupIdentifierArg = args[0] as! String do { let result = try api.getContainerPath(appGroupIdentifier: appGroupIdentifierArg) diff --git a/packages/path_provider/path_provider_foundation/lib/messages.g.dart b/packages/path_provider/path_provider_foundation/lib/messages.g.dart index 7d8d87d849d3..7893dbd25e6c 100644 --- a/packages/path_provider/path_provider_foundation/lib/messages.g.dart +++ b/packages/path_provider/path_provider_foundation/lib/messages.g.dart @@ -1,7 +1,7 @@ // 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. -// Autogenerated from Pigeon (v9.2.4), do not edit directly. +// Autogenerated from Pigeon (v10.1.3), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import diff --git a/packages/path_provider/path_provider_foundation/pubspec.yaml b/packages/path_provider/path_provider_foundation/pubspec.yaml index a86646c9e824..936e182f263a 100644 --- a/packages/path_provider/path_provider_foundation/pubspec.yaml +++ b/packages/path_provider/path_provider_foundation/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_foundation description: iOS and macOS implementation of the path_provider plugin repository: https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_foundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.2.3 +version: 2.2.4 environment: sdk: ">=2.18.0 <4.0.0" @@ -32,4 +32,4 @@ dev_dependencies: sdk: flutter mockito: 5.4.1 path: ^1.8.0 - pigeon: ^9.2.4 + pigeon: ^10.1.3 diff --git a/packages/path_provider/path_provider_foundation/test/messages_test.g.dart b/packages/path_provider/path_provider_foundation/test/messages_test.g.dart index ed00e2a4858c..c1477dfe4157 100644 --- a/packages/path_provider/path_provider_foundation/test/messages_test.g.dart +++ b/packages/path_provider/path_provider_foundation/test/messages_test.g.dart @@ -1,7 +1,7 @@ // 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. -// Autogenerated from Pigeon (v9.2.4), do not edit directly. +// Autogenerated from Pigeon (v10.1.3), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import // ignore_for_file: avoid_relative_lib_imports diff --git a/packages/shared_preferences/shared_preferences_foundation/CHANGELOG.md b/packages/shared_preferences/shared_preferences_foundation/CHANGELOG.md index 7bbf5322a081..17b024e41078 100644 --- a/packages/shared_preferences/shared_preferences_foundation/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_foundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.2 + +* Updates to the latest version of `pigeon`. + ## 2.3.1 * Fixes variable binding bug on older versions of Xcode. diff --git a/packages/shared_preferences/shared_preferences_foundation/darwin/Classes/messages.g.swift b/packages/shared_preferences/shared_preferences_foundation/darwin/Classes/messages.g.swift index 723fbe263999..2e2616073c4c 100644 --- a/packages/shared_preferences/shared_preferences_foundation/darwin/Classes/messages.g.swift +++ b/packages/shared_preferences/shared_preferences_foundation/darwin/Classes/messages.g.swift @@ -1,16 +1,17 @@ // 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. -// Autogenerated from Pigeon (v9.2.5), do not edit directly. +// Autogenerated from Pigeon (v10.1.3), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation + #if os(iOS) -import Flutter + import Flutter #elseif os(macOS) -import FlutterMacOS + import FlutterMacOS #else -#error("Unsupported platform.") + #error("Unsupported platform.") #endif private func wrapResult(_ result: Any?) -> [Any?] { @@ -22,19 +23,19 @@ private func wrapError(_ error: Any) -> [Any?] { return [ flutterError.code, flutterError.message, - flutterError.details + flutterError.details, ] } return [ "\(error)", "\(type(of: error))", - "Stacktrace: \(Thread.callStackSymbols)" + "Stacktrace: \(Thread.callStackSymbols)", ] } private func nilOrValue(_ value: Any?) -> T? { if value is NSNull { return nil } - return (value as Any) as! T? + return value as! T? } /// Generated protocol from Pigeon that represents a handler of messages from Flutter. protocol UserDefaultsApi { @@ -51,10 +52,11 @@ class UserDefaultsApiSetup { /// The codec used by UserDefaultsApi. /// Sets up an instance of `UserDefaultsApi` to handle messages through the `binaryMessenger`. static func setUp(binaryMessenger: FlutterBinaryMessenger, api: UserDefaultsApi?) { - let removeChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.UserDefaultsApi.remove", binaryMessenger: binaryMessenger) + let removeChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.UserDefaultsApi.remove", binaryMessenger: binaryMessenger) if let api = api { removeChannel.setMessageHandler { message, reply in - let args = message as! [Any] + let args = message as! [Any?] let keyArg = args[0] as! String do { try api.remove(key: keyArg) @@ -66,10 +68,11 @@ class UserDefaultsApiSetup { } else { removeChannel.setMessageHandler(nil) } - let setBoolChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.UserDefaultsApi.setBool", binaryMessenger: binaryMessenger) + let setBoolChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.UserDefaultsApi.setBool", binaryMessenger: binaryMessenger) if let api = api { setBoolChannel.setMessageHandler { message, reply in - let args = message as! [Any] + let args = message as! [Any?] let keyArg = args[0] as! String let valueArg = args[1] as! Bool do { @@ -82,10 +85,11 @@ class UserDefaultsApiSetup { } else { setBoolChannel.setMessageHandler(nil) } - let setDoubleChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.UserDefaultsApi.setDouble", binaryMessenger: binaryMessenger) + let setDoubleChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.UserDefaultsApi.setDouble", binaryMessenger: binaryMessenger) if let api = api { setDoubleChannel.setMessageHandler { message, reply in - let args = message as! [Any] + let args = message as! [Any?] let keyArg = args[0] as! String let valueArg = args[1] as! Double do { @@ -98,12 +102,13 @@ class UserDefaultsApiSetup { } else { setDoubleChannel.setMessageHandler(nil) } - let setValueChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.UserDefaultsApi.setValue", binaryMessenger: binaryMessenger) + let setValueChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.UserDefaultsApi.setValue", binaryMessenger: binaryMessenger) if let api = api { setValueChannel.setMessageHandler { message, reply in - let args = message as! [Any] + let args = message as! [Any?] let keyArg = args[0] as! String - let valueArg = args[1] + let valueArg = args[1]! do { try api.setValue(key: keyArg, value: valueArg) reply(wrapResult(nil)) @@ -114,10 +119,11 @@ class UserDefaultsApiSetup { } else { setValueChannel.setMessageHandler(nil) } - let getAllChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.UserDefaultsApi.getAll", binaryMessenger: binaryMessenger) + let getAllChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.UserDefaultsApi.getAll", binaryMessenger: binaryMessenger) if let api = api { getAllChannel.setMessageHandler { message, reply in - let args = message as! [Any] + let args = message as! [Any?] let prefixArg = args[0] as! String let allowListArg: [String]? = nilOrValue(args[1]) do { @@ -130,10 +136,11 @@ class UserDefaultsApiSetup { } else { getAllChannel.setMessageHandler(nil) } - let clearChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.UserDefaultsApi.clear", binaryMessenger: binaryMessenger) + let clearChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.UserDefaultsApi.clear", binaryMessenger: binaryMessenger) if let api = api { clearChannel.setMessageHandler { message, reply in - let args = message as! [Any] + let args = message as! [Any?] let prefixArg = args[0] as! String let allowListArg: [String]? = nilOrValue(args[1]) do { diff --git a/packages/shared_preferences/shared_preferences_foundation/lib/messages.g.dart b/packages/shared_preferences/shared_preferences_foundation/lib/messages.g.dart index 9ef912414c77..70b23362bbff 100644 --- a/packages/shared_preferences/shared_preferences_foundation/lib/messages.g.dart +++ b/packages/shared_preferences/shared_preferences_foundation/lib/messages.g.dart @@ -1,7 +1,7 @@ // 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. -// Autogenerated from Pigeon (v9.2.5), do not edit directly. +// Autogenerated from Pigeon (v10.1.3), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import diff --git a/packages/shared_preferences/shared_preferences_foundation/pubspec.yaml b/packages/shared_preferences/shared_preferences_foundation/pubspec.yaml index 1fd7a8c66605..2962614d8f06 100644 --- a/packages/shared_preferences/shared_preferences_foundation/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_foundation/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_foundation description: iOS and macOS implementation of the shared_preferences plugin. repository: https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences_foundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.3.1 +version: 2.3.2 environment: sdk: ">=2.18.0 <4.0.0" @@ -29,4 +29,4 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pigeon: ^9.2.4 + pigeon: ^10.1.3 diff --git a/packages/shared_preferences/shared_preferences_foundation/test/test_api.g.dart b/packages/shared_preferences/shared_preferences_foundation/test/test_api.g.dart index 6dd35a9df1b0..7d7386f8e3ba 100644 --- a/packages/shared_preferences/shared_preferences_foundation/test/test_api.g.dart +++ b/packages/shared_preferences/shared_preferences_foundation/test/test_api.g.dart @@ -1,7 +1,7 @@ // 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. -// Autogenerated from Pigeon (v9.2.5), do not edit directly. +// Autogenerated from Pigeon (v10.1.3), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import // ignore_for_file: avoid_relative_lib_imports diff --git a/packages/url_launcher/url_launcher_macos/CHANGELOG.md b/packages/url_launcher/url_launcher_macos/CHANGELOG.md index bddec952ff75..1eb9359b1663 100644 --- a/packages/url_launcher/url_launcher_macos/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_macos/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 3.0.6 +* Updates to the latest version of `pigeon`. * Updates minimum supported macOS version to 10.14. * Updates minimum supported SDK version to Flutter 3.3/Dart 2.18. diff --git a/packages/url_launcher/url_launcher_macos/lib/src/messages.g.dart b/packages/url_launcher/url_launcher_macos/lib/src/messages.g.dart index e9e527cc6f2c..c519b7d6bd19 100644 --- a/packages/url_launcher/url_launcher_macos/lib/src/messages.g.dart +++ b/packages/url_launcher/url_launcher_macos/lib/src/messages.g.dart @@ -1,7 +1,7 @@ // 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. -// Autogenerated from Pigeon (v9.2.4), do not edit directly. +// Autogenerated from Pigeon (v10.1.3), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import diff --git a/packages/url_launcher/url_launcher_macos/macos/Classes/messages.g.swift b/packages/url_launcher/url_launcher_macos/macos/Classes/messages.g.swift index d9f2c6fea0a5..bada84a91ad8 100644 --- a/packages/url_launcher/url_launcher_macos/macos/Classes/messages.g.swift +++ b/packages/url_launcher/url_launcher_macos/macos/Classes/messages.g.swift @@ -1,7 +1,7 @@ // 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. -// Autogenerated from Pigeon (v9.2.4), do not edit directly. +// Autogenerated from Pigeon (v10.1.3), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation @@ -35,7 +35,7 @@ private func wrapError(_ error: Any) -> [Any?] { private func nilOrValue(_ value: Any?) -> T? { if value is NSNull { return nil } - return (value as Any) as! T? + return value as! T? } /// Possible error conditions for [UrlLauncherApi] calls. @@ -51,7 +51,7 @@ struct UrlLauncherBoolResult { var value: Bool var error: UrlLauncherError? = nil - static func fromList(_ list: [Any]) -> UrlLauncherBoolResult? { + static func fromList(_ list: [Any?]) -> UrlLauncherBoolResult? { let value = list[0] as! Bool var error: UrlLauncherError? = nil let errorEnumVal: Int? = nilOrValue(list[1]) @@ -75,7 +75,7 @@ private class UrlLauncherApiCodecReader: FlutterStandardReader { override func readValue(ofType type: UInt8) -> Any? { switch type { case 128: - return UrlLauncherBoolResult.fromList(self.readValue() as! [Any]) + return UrlLauncherBoolResult.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) } @@ -127,7 +127,7 @@ class UrlLauncherApiSetup { codec: codec) if let api = api { canLaunchUrlChannel.setMessageHandler { message, reply in - let args = message as! [Any] + let args = message as! [Any?] let urlArg = args[0] as! String do { let result = try api.canLaunch(url: urlArg) @@ -145,7 +145,7 @@ class UrlLauncherApiSetup { codec: codec) if let api = api { launchUrlChannel.setMessageHandler { message, reply in - let args = message as! [Any] + let args = message as! [Any?] let urlArg = args[0] as! String do { let result = try api.launch(url: urlArg) diff --git a/packages/url_launcher/url_launcher_macos/pubspec.yaml b/packages/url_launcher/url_launcher_macos/pubspec.yaml index 1c09ae7f43bf..951d09cf5559 100644 --- a/packages/url_launcher/url_launcher_macos/pubspec.yaml +++ b/packages/url_launcher/url_launcher_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_macos description: macOS implementation of the url_launcher plugin. repository: https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 3.0.5 +version: 3.0.6 environment: sdk: ">=2.18.0 <4.0.0" @@ -25,5 +25,5 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pigeon: ^9.2.4 + pigeon: ^10.1.3 test: ^1.16.3 diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index a54aef55877f..e8642b6e04a4 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 2.4.7 * Updates minimum supported SDK version to Flutter 3.3/Dart 2.18. +* Adds iOS exception on incorrect asset path ## 2.4.6 diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m index ccece1127699..ba45d4d3ea08 100644 --- a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m +++ b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m @@ -620,10 +620,15 @@ - (FLTTextureMessage *)create:(FLTCreateMessage *)input error:(FlutterError **)e } else { assetPath = [_registrar lookupKeyForAsset:input.asset]; } - player = [[FLTVideoPlayer alloc] initWithAsset:assetPath - frameUpdater:frameUpdater - playerFactory:_playerFactory]; - return [self onPlayerSetup:player frameUpdater:frameUpdater]; + @try { + player = [[FLTVideoPlayer alloc] initWithAsset:assetPath + frameUpdater:frameUpdater + playerFactory:_playerFactory]; + return [self onPlayerSetup:player frameUpdater:frameUpdater]; + } @catch (NSException *exception) { + *error = [FlutterError errorWithCode:@"video_player" message:exception.reason details:nil]; + return nil; + } } else if (input.uri) { player = [[FLTVideoPlayer alloc] initWithURL:[NSURL URLWithString:input.uri] frameUpdater:frameUpdater diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index 093f5ebbb51b..a0c2f4ebae8f 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS implementation of the video_player plugin. repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.4.6 +version: 2.4.7 environment: sdk: ">=2.18.0 <4.0.0" diff --git a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart index 1b25da8bbaa8..671256ae07f6 100644 --- a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart +++ b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart @@ -135,6 +135,18 @@ void main() { expect(textureId, 3); }); + test('create with incorrect asset throws exception', () async { + try { + await player.create(DataSource( + sourceType: DataSourceType.asset, + asset: '/path/to/incorrect_asset', + )); + fail('should throw PlatformException'); + } catch (e) { + expect(e, isException); + } + }); + test('create with network', () async { final int? textureId = await player.create(DataSource( sourceType: DataSourceType.network, diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 114f2a74eb77..9dc13ccbd388 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.9.0 + +* Adds support for `WebResouceError.url`. + ## 3.8.2 * Fixes unawaited_futures violations. diff --git a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart index 462027ed97a8..53b8a0078fd0 100644 --- a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart @@ -826,9 +826,7 @@ Future main() async { expect(error.errorType, isNotNull); expect( - (error as AndroidWebResourceError) - .failingUrl - ?.startsWith('https://www.notawebsite..com'), + error.url?.startsWith('https://www.notawebsite..com'), isTrue, ); }); diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart index b618c8f3e2cc..0c3e3411bd09 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart @@ -114,6 +114,7 @@ Page resource error: description: ${error.description} errorType: ${error.errorType} isForMainFrame: ${error.isForMainFrame} + url: ${error.url} '''); }) ..setOnNavigationRequest((NavigationRequest request) { diff --git a/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml index 16cc964357f2..0b23ead3a90c 100644 --- a/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - webview_flutter_platform_interface: ^2.3.0 + webview_flutter_platform_interface: ^2.4.0 dev_dependencies: espresso: ^0.2.0 diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_controller.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_controller.dart index 6a4c918f95a5..7c5f0929def8 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_controller.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_controller.dart @@ -825,12 +825,14 @@ class AndroidWebResourceError extends WebResourceError { required super.errorCode, required super.description, super.isForMainFrame, - this.failingUrl, - }) : super( + super.url, + }) : failingUrl = url, + super( errorType: _errorCodeToErrorType(errorCode), ); /// Gets the URL for which the failing resource request was made. + @Deprecated('Please use `url`.') final String? failingUrl; static WebResourceErrorType? _errorCodeToErrorType(int errorCode) { @@ -954,7 +956,7 @@ class AndroidNavigationDelegate extends PlatformNavigationDelegate { callback(AndroidWebResourceError._( errorCode: error.errorCode, description: error.description, - failingUrl: request.url, + url: request.url, isForMainFrame: request.isForMainFrame, )); } @@ -971,7 +973,7 @@ class AndroidNavigationDelegate extends PlatformNavigationDelegate { callback(AndroidWebResourceError._( errorCode: errorCode, description: description, - failingUrl: failingUrl, + url: failingUrl, isForMainFrame: true, )); } diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index a58c031b82b7..6755ed363b43 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. repository: https://github.com/flutter/packages/tree/main/packages/webview_flutter/webview_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 3.8.2 +version: 3.9.0 environment: sdk: ">=2.18.0 <4.0.0" @@ -20,7 +20,7 @@ flutter: dependencies: flutter: sdk: flutter - webview_flutter_platform_interface: ^2.3.0 + webview_flutter_platform_interface: ^2.4.0 dev_dependencies: build_runner: ^2.1.4 diff --git a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md index a407df5f5a04..30c3ab2a8d66 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.4.0 + +* Adds support to retrieve the url from a web resource loading error. See `WebResourceError.url`. + ## 2.3.1 * Removes obsolete null checks on non-nullable values. diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/web_resource_error.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/web_resource_error.dart index e2522da859f7..cf0af4a4e703 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/web_resource_error.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/web_resource_error.dart @@ -107,6 +107,7 @@ class WebResourceError { required this.description, this.errorType, this.isForMainFrame, + this.url, }); /// Raw code of the error from the respective platform. @@ -120,4 +121,7 @@ class WebResourceError { /// Whether the error originated from the main frame. final bool? isForMainFrame; + + /// The URL for which the failing resource request was made. + final String? url; } diff --git a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml index e5377f0fbab1..eca7b4927be3 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/packages/tree/main/packages/webview_flutt issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview_flutter%22 # 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.3.1 +version: 2.4.0 environment: sdk: ">=2.18.0 <4.0.0" diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index e24e3e0f0057..26cd8a2a42f8 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.7.0 + +* Adds support for `WebResouceError.url`. + ## 3.6.3 * Introduces `NSError.toString` for better diagnostics. @@ -16,7 +20,7 @@ * Adds support to enable debugging of web contents on the latest versions of WebKit. See `WebKitWebViewController.setInspectable`. - + ## 3.5.0 * Adds support to limit navigation to pages within the app’s domain. See diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart index 4b457d0e4ca4..edbe5e366fee 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart @@ -834,6 +834,10 @@ Future main() async { final WebResourceError error = await errorCompleter.future; expect(error, isNotNull); + expect( + error.url?.startsWith('https://www.notawebsite..com'), + isTrue, + ); expect((error as WebKitWebResourceError).domain, isNotNull); }); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj index 19e2b39a5c12..32c0bf9872cd 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 8F4FF949299ADC2D000A6586 /* FWFWebViewFlutterWKWebViewExternalAPITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F4FF948299ADC2D000A6586 /* FWFWebViewFlutterWKWebViewExternalAPITests.m */; }; 8F4FF94B29AC223F000A6586 /* FWFURLTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F4FF94A29AC223F000A6586 /* FWFURLTests.m */; }; + 8F78EAAA2A02CB9100C2E520 /* FWFErrorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F78EAA92A02CB9100C2E520 /* FWFErrorTests.m */; }; 8FA6A87928062CD000A4B183 /* FWFInstanceManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */; }; 8FB79B5328134C3100C101D3 /* FWFWebViewHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B5228134C3100C101D3 /* FWFWebViewHostApiTests.m */; }; 8FB79B55281B24F600C101D3 /* FWFDataConvertersTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B54281B24F600C101D3 /* FWFDataConvertersTests.m */; }; @@ -80,6 +81,7 @@ 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 8F4FF948299ADC2D000A6586 /* FWFWebViewFlutterWKWebViewExternalAPITests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FWFWebViewFlutterWKWebViewExternalAPITests.m; sourceTree = ""; }; 8F4FF94A29AC223F000A6586 /* FWFURLTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFURLTests.m; sourceTree = ""; }; + 8F78EAA92A02CB9100C2E520 /* FWFErrorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFErrorTests.m; sourceTree = ""; }; 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFInstanceManagerTests.m; sourceTree = ""; }; 8FB79B5228134C3100C101D3 /* FWFWebViewHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFWebViewHostApiTests.m; sourceTree = ""; }; 8FB79B54281B24F600C101D3 /* FWFDataConvertersTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFDataConvertersTests.m; sourceTree = ""; }; @@ -165,6 +167,7 @@ 8FB79B902820BAC700C101D3 /* FWFUIViewHostApiTests.m */, 8FB79B962821985200C101D3 /* FWFObjectHostApiTests.m */, 8F4FF94A29AC223F000A6586 /* FWFURLTests.m */, + 8F78EAA92A02CB9100C2E520 /* FWFErrorTests.m */, ); path = RunnerTests; sourceTree = ""; @@ -466,6 +469,7 @@ buildActionMask = 2147483647; files = ( 8FA6A87928062CD000A4B183 /* FWFInstanceManagerTests.m in Sources */, + 8F78EAAA2A02CB9100C2E520 /* FWFErrorTests.m in Sources */, 8F4FF94B29AC223F000A6586 /* FWFURLTests.m in Sources */, 8FB79B852820A3A400C101D3 /* FWFUIDelegateHostApiTests.m in Sources */, 8FB79B972821985200C101D3 /* FWFObjectHostApiTests.m in Sources */, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFDataConvertersTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFDataConvertersTests.m index f580b6022c5e..1a64148b4868 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFDataConvertersTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFDataConvertersTests.m @@ -98,14 +98,20 @@ - (void)testFWFWKFrameInfoDataFromWKFrameInfo { } - (void)testFWFNSErrorDataFromNSError { + NSObject *unsupportedType = [[NSObject alloc] init]; NSError *error = [NSError errorWithDomain:@"domain" code:23 - userInfo:@{NSLocalizedDescriptionKey : @"description"}]; + userInfo:@{@"a" : @"b", @"c" : unsupportedType}]; FWFNSErrorData *data = FWFNSErrorDataFromNativeNSError(error); XCTAssertEqualObjects(data.code, @23); XCTAssertEqualObjects(data.domain, @"domain"); - XCTAssertEqualObjects(data.localizedDescription, @"description"); + + NSDictionary *userInfo = @{ + @"a" : @"b", + @"c" : [NSString stringWithFormat:@"Unsupported Type: %@", unsupportedType.description] + }; + XCTAssertEqualObjects(data.userInfo, userInfo); } - (void)testFWFWKScriptMessageDataFromWKScriptMessage { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFErrorTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFErrorTests.m new file mode 100644 index 000000000000..422b7e49aee9 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFErrorTests.m @@ -0,0 +1,18 @@ +// 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 XCTest; + +#import + +@interface FWFErrorTests : XCTestCase +@end + +@implementation FWFErrorTests +- (void)testNSErrorUserInfoKey { + // These MUST match the String values in the Dart class NSErrorUserInfoKey. + XCTAssertEqualObjects(NSLocalizedDescriptionKey, @"NSLocalizedDescription"); + XCTAssertEqualObjects(NSURLErrorFailingURLStringErrorKey, @"NSErrorFailingURLStringKey"); +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m index 248f947a846f..a89bf338c3fe 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m @@ -408,7 +408,7 @@ - (void)testEvaluateJavaScriptReturnsNSErrorData { XCTAssertTrue([errorData isKindOfClass:[FWFNSErrorData class]]); XCTAssertEqualObjects(errorData.code, @0); XCTAssertEqualObjects(errorData.domain, @"errorDomain"); - XCTAssertEqualObjects(errorData.localizedDescription, @"description"); + XCTAssertEqualObjects(errorData.userInfo, @{NSLocalizedDescriptionKey : @"description"}); } - (void)testWebViewContentInsetBehaviorShouldBeNever { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart index 8c3f75312943..7367828dbedf 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart @@ -115,6 +115,7 @@ Page resource error: description: ${error.description} errorType: ${error.errorType} isForMainFrame: ${error.isForMainFrame} + url: ${error.url} '''); }) ..setOnNavigationRequest((NavigationRequest request) { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml index 2ddb76ad79b7..aae7c82df047 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml @@ -10,7 +10,7 @@ dependencies: flutter: sdk: flutter path_provider: ^2.0.6 - webview_flutter_platform_interface: ^2.3.0 + webview_flutter_platform_interface: ^2.4.0 webview_flutter_wkwebview: # When depending on this package from a real application you should use: # webview_flutter: ^x.y.z diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m index 20607daf4aa0..28c029888a3a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m @@ -192,9 +192,19 @@ WKNavigationActionPolicy FWFNativeWKNavigationActionPolicyFromEnumData( } FWFNSErrorData *FWFNSErrorDataFromNativeNSError(NSError *error) { - return [FWFNSErrorData makeWithCode:@(error.code) - domain:error.domain - localizedDescription:error.localizedDescription]; + NSMutableDictionary *userInfo; + if (error.userInfo) { + userInfo = [NSMutableDictionary dictionary]; + for (NSErrorUserInfoKey key in error.userInfo.allKeys) { + NSObject *value = error.userInfo[key]; + if ([value isKindOfClass:[NSString class]]) { + userInfo[key] = value; + } else { + userInfo[key] = [NSString stringWithFormat:@"Unsupported Type: %@", value.description]; + } + } + } + return [FWFNSErrorData makeWithCode:@(error.code) domain:error.domain userInfo:userInfo]; } FWFNSKeyValueChangeKeyEnumData *FWFNSKeyValueChangeKeyEnumDataFromNativeNSKeyValueChangeKey( diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h index e302568d8dad..3aa3fb7c9987 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h @@ -342,10 +342,10 @@ typedef NS_ENUM(NSUInteger, FWFWKMediaCaptureType) { - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithCode:(NSNumber *)code domain:(NSString *)domain - localizedDescription:(NSString *)localizedDescription; + userInfo:(nullable NSDictionary *)userInfo; @property(nonatomic, strong) NSNumber *code; @property(nonatomic, copy) NSString *domain; -@property(nonatomic, copy) NSString *localizedDescription; +@property(nonatomic, strong, nullable) NSDictionary *userInfo; @end /// Mirror of WKScriptMessage. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m index 8104a5df99f8..66264ebe3100 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m @@ -455,11 +455,11 @@ - (NSArray *)toList { @implementation FWFNSErrorData + (instancetype)makeWithCode:(NSNumber *)code domain:(NSString *)domain - localizedDescription:(NSString *)localizedDescription { + userInfo:(nullable NSDictionary *)userInfo { FWFNSErrorData *pigeonResult = [[FWFNSErrorData alloc] init]; pigeonResult.code = code; pigeonResult.domain = domain; - pigeonResult.localizedDescription = localizedDescription; + pigeonResult.userInfo = userInfo; return pigeonResult; } + (FWFNSErrorData *)fromList:(NSArray *)list { @@ -468,8 +468,7 @@ + (FWFNSErrorData *)fromList:(NSArray *)list { NSAssert(pigeonResult.code != nil, @""); pigeonResult.domain = GetNullableObjectAtIndex(list, 1); NSAssert(pigeonResult.domain != nil, @""); - pigeonResult.localizedDescription = GetNullableObjectAtIndex(list, 2); - NSAssert(pigeonResult.localizedDescription != nil, @""); + pigeonResult.userInfo = GetNullableObjectAtIndex(list, 2); return pigeonResult; } + (nullable FWFNSErrorData *)nullableFromList:(NSArray *)list { @@ -479,7 +478,7 @@ - (NSArray *)toList { return @[ (self.code ?: [NSNull null]), (self.domain ?: [NSNull null]), - (self.localizedDescription ?: [NSNull null]), + (self.userInfo ?: [NSNull null]), ]; } @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.g.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.g.dart index 91c81feb369c..0f7fd8fa0f1d 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.g.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.g.dart @@ -522,20 +522,20 @@ class NSErrorData { NSErrorData({ required this.code, required this.domain, - required this.localizedDescription, + this.userInfo, }); int code; String domain; - String localizedDescription; + Map? userInfo; Object encode() { return [ code, domain, - localizedDescription, + userInfo, ]; } @@ -544,7 +544,7 @@ class NSErrorData { return NSErrorData( code: result[0]! as int, domain: result[1]! as String, - localizedDescription: result[2]! as String, + userInfo: (result[2] as Map?)?.cast(), ); } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart index e8dde5e03dd7..3fe1a57c1b2e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart @@ -201,6 +201,23 @@ class NSUrlRequest { final Map allHttpHeaderFields; } +/// Keys that may exist in the user info map of `NSError`. +class NSErrorUserInfoKey { + NSErrorUserInfoKey._(); + + /// The corresponding value is a localized string representation of the error + /// that, if present, will be returned by [NSError.localizedDescription]. + /// + /// See https://developer.apple.com/documentation/foundation/nslocalizeddescriptionkey. + static const String NSLocalizedDescription = 'NSLocalizedDescription'; + + /// The URL which caused a load to fail. + /// + /// See https://developer.apple.com/documentation/foundation/nsurlerrorfailingurlstringerrorkey?language=objc. + static const String NSURLErrorFailingURLStringError = + 'NSErrorFailingURLStringKey'; +} + /// Information about an error condition. /// /// Wraps [NSError](https://developer.apple.com/documentation/foundation/nserror?language=objc). @@ -210,7 +227,7 @@ class NSError { const NSError({ required this.code, required this.domain, - required this.localizedDescription, + this.userInfo = const {}, }); /// The error code. @@ -221,15 +238,23 @@ class NSError { /// A string containing the error domain. final String domain; + /// Map of arbitrary data. + /// + /// See [NSErrorUserInfoKey] for possible keys (non-exhaustive). + /// + /// This currently only supports values that are a String. + final Map userInfo; + /// A string containing the localized description of the error. - final String localizedDescription; + String? get localizedDescription => + userInfo[NSErrorUserInfoKey.NSLocalizedDescription] as String?; @override String toString() { - if (localizedDescription.isEmpty) { - return 'Error $domain:$code'; + if (localizedDescription?.isEmpty ?? true) { + return 'Error $domain:$code:$userInfo'; } - return '$localizedDescription ($domain:$code)'; + return '$localizedDescription ($domain:$code:$userInfo)'; } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/legacy/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/legacy/web_kit_webview_widget.dart index da0745a44217..f344edb00bb6 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/legacy/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/legacy/web_kit_webview_widget.dart @@ -596,7 +596,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { return WebResourceError( errorCode: error.code, domain: error.domain, - description: error.localizedDescription, + description: error.localizedDescription ?? '', errorType: errorType, ); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart index b0ce51915656..ccc87377cb04 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart @@ -199,7 +199,7 @@ extension _WKNSErrorDataConverter on NSErrorData { return NSError( domain: domain, code: code, - localizedDescription: localizedDescription, + userInfo: userInfo?.cast() ?? {}, ); } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_webview_controller.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_webview_controller.dart index 5df538ca0c2f..14cba330a747 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_webview_controller.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_webview_controller.dart @@ -665,10 +665,13 @@ class WebKitWebViewWidget extends PlatformWebViewWidget { /// An implementation of [WebResourceError] with the WebKit API. class WebKitWebResourceError extends WebResourceError { - WebKitWebResourceError._(this._nsError, {required bool isForMainFrame}) - : super( + WebKitWebResourceError._( + this._nsError, { + required bool isForMainFrame, + required super.url, + }) : super( errorCode: _nsError.code, - description: _nsError.localizedDescription, + description: _nsError.localizedDescription ?? '', errorType: _toWebResourceErrorType(_nsError.code), isForMainFrame: isForMainFrame, ); @@ -766,14 +769,24 @@ class WebKitNavigationDelegate extends PlatformNavigationDelegate { didFailNavigation: (WKWebView webView, NSError error) { if (weakThis.target?._onWebResourceError != null) { weakThis.target!._onWebResourceError!( - WebKitWebResourceError._(error, isForMainFrame: true), + WebKitWebResourceError._( + error, + isForMainFrame: true, + url: error.userInfo[NSErrorUserInfoKey + .NSURLErrorFailingURLStringError] as String?, + ), ); } }, didFailProvisionalNavigation: (WKWebView webView, NSError error) { if (weakThis.target?._onWebResourceError != null) { weakThis.target!._onWebResourceError!( - WebKitWebResourceError._(error, isForMainFrame: true), + WebKitWebResourceError._( + error, + isForMainFrame: true, + url: error.userInfo[NSErrorUserInfoKey + .NSURLErrorFailingURLStringError] as String?, + ), ); } }, @@ -785,9 +798,9 @@ class WebKitNavigationDelegate extends PlatformNavigationDelegate { code: WKErrorCode.webContentProcessTerminated, // Value from https://developer.apple.com/documentation/webkit/wkerrordomain?language=objc. domain: 'WKErrorDomain', - localizedDescription: '', ), isForMainFrame: true, + url: null, ), ); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart index 16248e88de54..dea5080e556a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart @@ -299,7 +299,7 @@ class WKFrameInfoData { class NSErrorData { late int code; late String domain; - late String localizedDescription; + late Map? userInfo; } /// Mirror of WKScriptMessage. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 9c313c01ef36..a88d74d61da4 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_wkwebview description: A Flutter plugin that provides a WebView widget based on Apple's WKWebView control. repository: https://github.com/flutter/packages/tree/main/packages/webview_flutter/webview_flutter_wkwebview issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 3.6.3 +version: 3.7.0 environment: sdk: ">=2.18.0 <4.0.0" @@ -20,7 +20,7 @@ dependencies: flutter: sdk: flutter path: ^1.8.0 - webview_flutter_platform_interface: ^2.3.0 + webview_flutter_platform_interface: ^2.4.0 dev_dependencies: build_runner: ^2.1.5 diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/legacy/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/legacy/web_kit_webview_widget_test.dart index 9388bddd5583..2033d39c9855 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/legacy/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/legacy/web_kit_webview_widget_test.dart @@ -777,7 +777,6 @@ void main() { details: const NSError( code: WKErrorCode.javaScriptResultTypeIsUnsupported, domain: '', - localizedDescription: '', ), )); expect( @@ -1049,7 +1048,9 @@ void main() { const NSError( code: WKErrorCode.webViewInvalidated, domain: 'domain', - localizedDescription: 'my desc', + userInfo: { + NSErrorUserInfoKey.NSLocalizedDescription: 'my desc', + }, ), ); @@ -1086,7 +1087,9 @@ void main() { const NSError( code: WKErrorCode.webContentProcessTerminated, domain: 'domain', - localizedDescription: 'my desc', + userInfo: { + NSErrorUserInfoKey.NSLocalizedDescription: 'my desc', + }, ), ); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart index 3dbaea08ede2..ea2e37e2cdad 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart @@ -252,33 +252,41 @@ void main() { const NSError( code: 0, domain: 'domain', - localizedDescription: 'desc', + userInfo: { + NSErrorUserInfoKey.NSLocalizedDescription: 'desc', + }, ).toString(), - 'desc (domain:0)', + 'desc (domain:0:{NSLocalizedDescription: desc})', ); expect( const NSError( code: 0, domain: 'domain', - localizedDescription: '', + userInfo: { + NSErrorUserInfoKey.NSLocalizedDescription: '', + }, ).toString(), - 'Error domain:0', + 'Error domain:0:{NSLocalizedDescription: }', ); expect( const NSError( code: 255, domain: 'bar', - localizedDescription: 'baz', + userInfo: { + NSErrorUserInfoKey.NSLocalizedDescription: 'baz', + }, ).toString(), - 'baz (bar:255)', + 'baz (bar:255:{NSLocalizedDescription: baz})', ); expect( const NSError( code: 255, domain: 'bar', - localizedDescription: '', + userInfo: { + NSErrorUserInfoKey.NSLocalizedDescription: '', + }, ).toString(), - 'Error bar:255', + 'Error bar:255:{NSLocalizedDescription: }', ); }); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart index 856ca55e7c60..055f342ab786 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart @@ -614,7 +614,9 @@ void main() { NSErrorData( code: 23, domain: 'Hello', - localizedDescription: 'localiziedDescription', + userInfo: { + NSErrorUserInfoKey.NSLocalizedDescription: 'my desc', + }, ), ); @@ -646,7 +648,9 @@ void main() { NSErrorData( code: 23, domain: 'Hello', - localizedDescription: 'localiziedDescription', + userInfo: { + NSErrorUserInfoKey.NSLocalizedDescription: 'my desc', + }, ), ); @@ -851,7 +855,9 @@ void main() { details: NSErrorData( code: 0, domain: 'domain', - localizedDescription: 'desc', + userInfo: { + NSErrorUserInfoKey.NSLocalizedDescription: 'desc', + }, ), ), ); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_navigation_delegate_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_navigation_delegate_test.dart index 0f754cb7f03e..4581c92b788f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_navigation_delegate_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_navigation_delegate_test.dart @@ -92,12 +92,17 @@ void main() { const NSError( code: WKErrorCode.webViewInvalidated, domain: 'domain', - localizedDescription: 'my desc', + userInfo: { + NSErrorUserInfoKey.NSURLErrorFailingURLStringError: + 'www.flutter.dev', + NSErrorUserInfoKey.NSLocalizedDescription: 'my desc', + }, ), ); expect(callbackError.description, 'my desc'); expect(callbackError.errorCode, WKErrorCode.webViewInvalidated); + expect(callbackError.url, 'www.flutter.dev'); expect(callbackError.domain, 'domain'); expect(callbackError.errorType, WebResourceErrorType.webViewInvalidated); expect(callbackError.isForMainFrame, true); @@ -126,11 +131,16 @@ void main() { const NSError( code: WKErrorCode.webViewInvalidated, domain: 'domain', - localizedDescription: 'my desc', + userInfo: { + NSErrorUserInfoKey.NSURLErrorFailingURLStringError: + 'www.flutter.dev', + NSErrorUserInfoKey.NSLocalizedDescription: 'my desc', + }, ), ); expect(callbackError.description, 'my desc'); + expect(callbackError.url, 'www.flutter.dev'); expect(callbackError.errorCode, WKErrorCode.webViewInvalidated); expect(callbackError.domain, 'domain'); expect(callbackError.errorType, WebResourceErrorType.webViewInvalidated); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_controller_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_controller_test.dart index 89df6340d100..2cd20e0f6889 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_controller_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_controller_test.dart @@ -502,7 +502,6 @@ void main() { details: const NSError( code: WKErrorCode.javaScriptResultTypeIsUnsupported, domain: '', - localizedDescription: '', ), )); expect( diff --git a/packages/xdg_directories/CHANGELOG.md b/packages/xdg_directories/CHANGELOG.md index 5c63413ed555..ff8640df04d1 100644 --- a/packages/xdg_directories/CHANGELOG.md +++ b/packages/xdg_directories/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 1.0.1 +* Removes `process` dependency. * Updates minimum supported SDK version to Flutter 3.3/Dart 2.18. ## 1.0.0 diff --git a/packages/xdg_directories/lib/xdg_directories.dart b/packages/xdg_directories/lib/xdg_directories.dart index bf6de2d31292..7e115a5aab25 100644 --- a/packages/xdg_directories/lib/xdg_directories.dart +++ b/packages/xdg_directories/lib/xdg_directories.dart @@ -9,7 +9,9 @@ import 'dart:io'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as path; -import 'package:process/process.dart'; + +// From errno definitions. +const int _noSuchFileError = 2; /// An override function used by the tests to override the environment variable /// lookups using [xdgEnvironmentOverride]. @@ -36,16 +38,44 @@ EnvironmentAccessor? _xdgEnvironmentOverride; EnvironmentAccessor _getenv = _productionGetEnv; String? _productionGetEnv(String value) => Platform.environment[value]; -/// A testing function that replaces the process manager used to run xdg-user-path -/// with the one supplied. +/// A wrapper around Process.runSync to allow injection of a fake in tests. +@visibleForTesting +abstract class XdgProcessRunner { + /// Runs the given command synchronously. + ProcessResult runSync( + String executable, + List arguments, { + Encoding? stdoutEncoding = systemEncoding, + Encoding? stderrEncoding = systemEncoding, + }); +} + +class _DefaultProcessRunner implements XdgProcessRunner { + const _DefaultProcessRunner(); + + @override + ProcessResult runSync(String executable, List arguments, + {Encoding? stdoutEncoding = systemEncoding, + Encoding? stderrEncoding = systemEncoding}) { + return Process.runSync( + executable, + arguments, + stdoutEncoding: stdoutEncoding, + stderrEncoding: stderrEncoding, + ); + } +} + +/// A testing function that replaces the process runner used to run +/// xdg-user-path with the one supplied. /// /// Only available to tests. @visibleForTesting -set xdgProcessManager(ProcessManager processManager) { - _processManager = processManager; +set xdgProcessRunner(XdgProcessRunner processRunner) { + _processRunner = processRunner; } -ProcessManager _processManager = const LocalProcessManager(); +XdgProcessRunner _processRunner = const _DefaultProcessRunner(); List _directoryListFromEnvironment( String envVar, List fallback) { @@ -152,13 +182,20 @@ Directory? get runtimeDir => _directoryFromEnvironment('XDG_RUNTIME_DIR'); /// /// If the `xdg-user-dir` executable is not present this returns null. Directory? getUserDirectory(String dirName) { - if (!_processManager.canRun('xdg-user-dir')) { - return null; + final ProcessResult result; + try { + result = _processRunner.runSync( + 'xdg-user-dir', + [dirName], + stdoutEncoding: utf8, + ); + } on ProcessException catch (e) { + // Silently return null if it's missing, otherwise pass the exception up. + if (e.errorCode == _noSuchFileError) { + return null; + } + rethrow; } - final ProcessResult result = _processManager.runSync( - ['xdg-user-dir', dirName], - stdoutEncoding: utf8, - ); final String path = (result.stdout as String).split('\n')[0]; return Directory(path); } diff --git a/packages/xdg_directories/pubspec.yaml b/packages/xdg_directories/pubspec.yaml index edf6517739ff..e9c46285da84 100644 --- a/packages/xdg_directories/pubspec.yaml +++ b/packages/xdg_directories/pubspec.yaml @@ -2,7 +2,7 @@ name: xdg_directories description: A Dart package for reading XDG directory configuration information on Linux. repository: https://github.com/flutter/packages/tree/main/packages/xdg_directories issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+xdg_directories%22 -version: 1.0.0 +version: 1.0.1 environment: sdk: ">=2.18.0 <4.0.0" @@ -13,7 +13,6 @@ platforms: dependencies: meta: ^1.3.0 path: ^1.8.0 - process: ^4.0.0 dev_dependencies: test: ^1.16.0 diff --git a/packages/xdg_directories/test/xdg_directories_test.dart b/packages/xdg_directories/test/xdg_directories_test.dart index b3c95d3df7f8..9c99afdaf8d9 100644 --- a/packages/xdg_directories/test/xdg_directories_test.dart +++ b/packages/xdg_directories/test/xdg_directories_test.dart @@ -6,7 +6,6 @@ import 'dart:convert'; import 'dart:io'; import 'package:path/path.dart' as path; -import 'package:process/process.dart'; import 'package:test/fake.dart'; import 'package:test/test.dart'; import 'package:xdg_directories/xdg_directories.dart' as xdg; @@ -27,6 +26,8 @@ void main() { String testPath(String subdir) => path.join(testRootPath(), subdir); setUp(() { + xdg.xdgProcessRunner = + FakeProcessRunner({}, canRunExecutable: false); tmpDir = Directory.systemTemp.createTempSync('xdg_test'); fakeEnv.clear(); fakeEnv['HOME'] = testRootPath(); @@ -103,24 +104,22 @@ XDG_VIDEOS_DIR="$HOME/Videos" 'TEMPLATES': testPath('Templates'), 'VIDEOS': testPath('Videos'), }; - xdg.xdgProcessManager = FakeProcessManager(expected); + xdg.xdgProcessRunner = FakeProcessRunner(expected); final Set userDirs = xdg.getUserDirectoryNames(); expect(userDirs, equals(expected.keys.toSet())); for (final String key in userDirs) { expect(xdg.getUserDirectory(key)!.path, equals(expected[key]), reason: 'Path $key value not correct'); } - xdg.xdgProcessManager = const LocalProcessManager(); }); test('Returns null when xdg-user-dir executable is not present', () { - xdg.xdgProcessManager = FakeProcessManager( + xdg.xdgProcessRunner = FakeProcessRunner( {}, canRunExecutable: false, ); expect(xdg.getUserDirectory('DESKTOP'), isNull, reason: 'Found xdg user directory without access to xdg-user-dir'); - xdg.xdgProcessManager = const LocalProcessManager(); }); test('Throws StateError when HOME not set', () { @@ -131,27 +130,22 @@ XDG_VIDEOS_DIR="$HOME/Videos" }); } -class FakeProcessManager extends Fake implements ProcessManager { - FakeProcessManager(this.expected, {this.canRunExecutable = true}); +class FakeProcessRunner extends Fake implements xdg.XdgProcessRunner { + FakeProcessRunner(this.expected, {this.canRunExecutable = true}); Map expected; final bool canRunExecutable; @override ProcessResult runSync( - List command, { - String? workingDirectory, - Map? environment, - bool includeParentEnvironment = true, - bool runInShell = false, - Encoding stdoutEncoding = systemEncoding, - Encoding stderrEncoding = systemEncoding, + String executable, + List arguments, { + Encoding? stdoutEncoding = systemEncoding, + Encoding? stderrEncoding = systemEncoding, }) { - return ProcessResult(0, 0, expected[command[1]], ''); - } - - @override - bool canRun(dynamic executable, {String? workingDirectory}) { - return canRunExecutable; + if (!canRunExecutable) { + throw ProcessException(executable, arguments, 'No such executable', 2); + } + return ProcessResult(0, 0, expected[arguments.first], ''); } } diff --git a/script/tool/lib/src/fetch_deps_command.dart b/script/tool/lib/src/fetch_deps_command.dart new file mode 100644 index 000000000000..ce70b41524c5 --- /dev/null +++ b/script/tool/lib/src/fetch_deps_command.dart @@ -0,0 +1,74 @@ +// 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 'common/core.dart'; +import 'common/gradle.dart'; +import 'common/output_utils.dart'; +import 'common/package_looping_command.dart'; +import 'common/plugin_utils.dart'; +import 'common/repository_package.dart'; + +/// Download dependencies for the following platforms {android}. +/// +/// Specficially each platform runs: +/// Android: 'gradlew dependencies'. +/// Dart: TBD (flutter/flutter/issues/130279) +/// iOS: TBD (flutter/flutter/issues/130280) +/// +/// See https://docs.gradle.org/6.4/userguide/core_dependency_management.html#sec:dependency-mgmt-in-gradle. +class FetchDepsCommand extends PackageLoopingCommand { + /// Creates an instance of the fetch-deps command. + FetchDepsCommand( + super.packagesDir, { + super.processRunner, + super.platform, + }); + + @override + final String name = 'fetch-deps'; + + @override + final String description = 'Fetches dependencies for plugins.\n' + 'Runs "gradlew dependencies" on Android plugins.\n' + 'Dart see flutter/flutter/issues/130279\n' + 'iOS plugins see flutter/flutter/issues/130280\n' + '\n' + 'Requires the examples to have been built at least once before running.'; + + @override + Future runForPackage(RepositoryPackage package) async { + if (!pluginSupportsPlatform(platformAndroid, package, + requiredMode: PlatformSupport.inline)) { + return PackageResult.skip( + 'Plugin does not have an Android implementation.'); + } + + for (final RepositoryPackage example in package.getExamples()) { + final GradleProject gradleProject = GradleProject(example, + processRunner: processRunner, platform: platform); + + if (!gradleProject.isConfigured()) { + final int exitCode = await processRunner.runAndStream( + flutterCommand, + ['build', 'apk', '--config-only'], + workingDir: example.directory, + ); + if (exitCode != 0) { + printError('Unable to configure Gradle project.'); + return PackageResult.fail(['Unable to configure Gradle.']); + } + } + + final String packageName = package.directory.basename; + + final int exitCode = await gradleProject.runCommand('$packageName:dependencies'); + if (exitCode != 0) { + return PackageResult.fail(); + } + } + + return PackageResult.success(); + } +} diff --git a/script/tool/lib/src/main.dart b/script/tool/lib/src/main.dart index 78fa5a3f0c7b..89ffae268276 100644 --- a/script/tool/lib/src/main.dart +++ b/script/tool/lib/src/main.dart @@ -17,6 +17,7 @@ import 'dart_test_command.dart'; import 'dependabot_check_command.dart'; import 'drive_examples_command.dart'; import 'federation_safety_check_command.dart'; +import 'fetch_deps_command.dart'; import 'firebase_test_lab_command.dart'; import 'fix_command.dart'; import 'format_command.dart'; @@ -65,6 +66,7 @@ void main(List args) { ..addCommand(DependabotCheckCommand(packagesDir)) ..addCommand(DriveExamplesCommand(packagesDir)) ..addCommand(FederationSafetyCheckCommand(packagesDir)) + ..addCommand(FetchDepsCommand(packagesDir)) ..addCommand(FirebaseTestLabCommand(packagesDir)) ..addCommand(FixCommand(packagesDir)) ..addCommand(FormatCommand(packagesDir)) diff --git a/script/tool/test/fetch_deps_command_test.dart b/script/tool/test/fetch_deps_command_test.dart new file mode 100644 index 000000000000..baff6f657d19 --- /dev/null +++ b/script/tool/test/fetch_deps_command_test.dart @@ -0,0 +1,252 @@ +// 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:args/command_runner.dart'; +import 'package:file/file.dart'; +import 'package:file/memory.dart'; +import 'package:flutter_plugin_tools/src/common/core.dart'; +import 'package:flutter_plugin_tools/src/common/plugin_utils.dart'; +import 'package:flutter_plugin_tools/src/fetch_deps_command.dart'; +import 'package:test/test.dart'; + +import 'mocks.dart'; +import 'util.dart'; + +void main() { + group('FetchDepsCommand', () { + FileSystem fileSystem; + late Directory packagesDir; + late CommandRunner runner; + late MockPlatform mockPlatform; + late RecordingProcessRunner processRunner; + + setUp(() { + fileSystem = MemoryFileSystem(); + packagesDir = createPackagesDirectory(fileSystem: fileSystem); + mockPlatform = MockPlatform(); + processRunner = RecordingProcessRunner(); + final FetchDepsCommand command = FetchDepsCommand( + packagesDir, + processRunner: processRunner, + platform: mockPlatform, + ); + + runner = + CommandRunner('fetch_deps_test', 'Test for $FetchDepsCommand'); + runner.addCommand(command); + }); + group('android', () { + test('runs gradlew dependencies', () async { + final RepositoryPackage plugin = + createFakePlugin('plugin1', packagesDir, extraFiles: [ + 'example/android/gradlew', + ], platformSupport: { + platformAndroid: const PlatformDetails(PlatformSupport.inline) + }); + + final Directory androidDir = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android); + + final List output = + await runCapturingPrint(runner, ['fetch-deps']); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall( + androidDir.childFile('gradlew').path, + const ['plugin1:dependencies'], + androidDir.path, + ), + ]), + ); + + expect( + output, + containsAllInOrder([ + contains('Running for plugin1'), + contains('No issues found!'), + ])); + }); + + test('runs on all examples', () async { + final List examples = ['example1', 'example2']; + final RepositoryPackage plugin = createFakePlugin( + 'plugin1', packagesDir, + examples: examples, + extraFiles: [ + 'example/example1/android/gradlew', + 'example/example2/android/gradlew', + ], + platformSupport: { + platformAndroid: const PlatformDetails(PlatformSupport.inline) + }); + + final Iterable exampleAndroidDirs = plugin.getExamples().map( + (RepositoryPackage example) => + example.platformDirectory(FlutterPlatform.android)); + + final List output = + await runCapturingPrint(runner, ['fetch-deps']); + + expect( + processRunner.recordedCalls, + orderedEquals([ + for (final Directory directory in exampleAndroidDirs) + ProcessCall( + directory.childFile('gradlew').path, + const ['plugin1:dependencies'], + directory.path, + ), + ]), + ); + + expect( + output, + containsAllInOrder([ + contains('Running for plugin1'), + contains('No issues found!'), + ])); + }); + + test('runs --config-only build if gradlew is missing', () async { + final RepositoryPackage plugin = createFakePlugin( + 'plugin1', packagesDir, platformSupport: { + platformAndroid: const PlatformDetails(PlatformSupport.inline) + }); + + final Directory androidDir = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android); + + final List output = + await runCapturingPrint(runner, ['fetch-deps']); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall( + getFlutterCommand(mockPlatform), + const ['build', 'apk', '--config-only'], + plugin.getExamples().first.directory.path, + ), + ProcessCall( + androidDir.childFile('gradlew').path, + const ['plugin1:dependencies'], + androidDir.path, + ), + ]), + ); + + expect( + output, + containsAllInOrder([ + contains('Running for plugin1'), + contains('No issues found!'), + ])); + }); + + test('fails if gradlew generation fails', () async { + createFakePlugin('plugin1', packagesDir, + platformSupport: { + platformAndroid: const PlatformDetails(PlatformSupport.inline) + }); + + processRunner + .mockProcessesForExecutable[getFlutterCommand(mockPlatform)] = + [ + FakeProcessInfo(MockProcess(exitCode: 1)), + ]; + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['fetch-deps'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder( + [ + contains('Unable to configure Gradle project'), + ], + )); + }); + + test('fails if dependency download finds issues', () async { + final RepositoryPackage plugin = + createFakePlugin('plugin1', packagesDir, extraFiles: [ + 'example/android/gradlew', + ], platformSupport: { + platformAndroid: const PlatformDetails(PlatformSupport.inline) + }); + + final String gradlewPath = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android) + .childFile('gradlew') + .path; + processRunner.mockProcessesForExecutable[gradlewPath] = + [ + FakeProcessInfo(MockProcess(exitCode: 1)), + ]; + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['fetch-deps'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder( + [ + contains('The following packages had errors:'), + ], + )); + }); + }); + + test('skips non-Android plugins', () async { + createFakePlugin('plugin1', packagesDir); + + final List output = + await runCapturingPrint(runner, ['fetch-deps']); + + expect( + output, + containsAllInOrder( + [ + contains( + 'SKIPPING: Plugin does not have an Android implementation.') + ], + )); + }); + + test('skips non-inline plugins', () async { + createFakePlugin('plugin1', packagesDir, + platformSupport: { + platformAndroid: const PlatformDetails(PlatformSupport.federated) + }); + + final List output = + await runCapturingPrint(runner, ['fetch-deps']); + + expect( + output, + containsAllInOrder( + [ + contains( + 'SKIPPING: Plugin does not have an Android implementation.') + ], + )); + }); + }); +}