From 947e34ce9fedcdd6750b54eb1cc74b854b49ab48 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 20 Feb 2024 07:51:08 -0800 Subject: [PATCH 001/126] [flutter_markdown] Replace deprecated API (#6134) * Internally, removes use of the deprecated framework methods related to `textScaleFactor`, in favor of the newer `textScaler`. * Plumbs that same change through the public API of this package, deprecating the style sheet's `textScaleFactor` and adding a `textScaler`. * Updates the min Flutter SDK to 3.16 where the new APIs were added. * Also updates test code that uses the deprecated `renderViewElement` to use `rootElement` instead. Fixes https://github.com/flutter/flutter/issues/143400 Fixes https://github.com/flutter/flutter/issues/143448 --- packages/flutter_markdown/CHANGELOG.md | 10 +++- .../flutter_markdown/example/pubspec.yaml | 4 +- .../lib/src/_functions_io.dart | 2 +- .../lib/src/_functions_web.dart | 2 +- .../flutter_markdown/lib/src/builder.dart | 4 +- .../flutter_markdown/lib/src/style_sheet.dart | 48 +++++++++++++-- packages/flutter_markdown/pubspec.yaml | 6 +- packages/flutter_markdown/test/all.dart | 4 +- .../test/style_sheet_test.dart | 60 +++++++++++++++++++ ...factor_test.dart => text_scaler_test.dart} | 23 +++---- packages/flutter_markdown/test/utils.dart | 2 +- 11 files changed, 134 insertions(+), 31 deletions(-) rename packages/flutter_markdown/test/{text_scale_factor_test.dart => text_scaler_test.dart} (69%) diff --git a/packages/flutter_markdown/CHANGELOG.md b/packages/flutter_markdown/CHANGELOG.md index 8feeca4bdae2..9d901df3fa72 100644 --- a/packages/flutter_markdown/CHANGELOG.md +++ b/packages/flutter_markdown/CHANGELOG.md @@ -1,6 +1,10 @@ -## NEXT +## 0.6.20 -* Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. +* Adds `textScaler` to `MarkdownStyleSheet`, and deprecates `textScaleFactor`. + * Clients using `textScaleFactor: someFactor` should replace it with + `TextScaler.linear(someFactor)` to preserve behavior. +* Removes use of deprecated Flutter framework `textScaleFactor` methods. +* Updates minimum supported SDK version to Flutter 3.16. ## 0.6.19 @@ -44,7 +48,7 @@ * Introduces a new `MarkdownElementBuilder.visitElementAfterWithContext()` method passing the widget `BuildContext` and the parent text's `TextStyle`. - + ## 0.6.16 * Adds `tableVerticalAlignment` property to allow aligning table cells vertically. diff --git a/packages/flutter_markdown/example/pubspec.yaml b/packages/flutter_markdown/example/pubspec.yaml index a904473786a4..c536ac0ce8d5 100644 --- a/packages/flutter_markdown/example/pubspec.yaml +++ b/packages/flutter_markdown/example/pubspec.yaml @@ -3,8 +3,8 @@ description: Demonstrates how to use the flutter_markdown package. publish_to: none environment: - sdk: ^3.1.0 - flutter: ">=3.13.0" + sdk: ^3.2.0 + flutter: ">=3.16.0" dependencies: flutter: diff --git a/packages/flutter_markdown/lib/src/_functions_io.dart b/packages/flutter_markdown/lib/src/_functions_io.dart index 81fd5b8f22b6..b3020573e165 100644 --- a/packages/flutter_markdown/lib/src/_functions_io.dart +++ b/packages/flutter_markdown/lib/src/_functions_io.dart @@ -64,7 +64,7 @@ final MarkdownStyleSheet Function(BuildContext, MarkdownStyleSheetBaseTheme?) } return result.copyWith( - textScaleFactor: MediaQuery.textScaleFactorOf(context), + textScaler: MediaQuery.textScalerOf(context), ); }; diff --git a/packages/flutter_markdown/lib/src/_functions_web.dart b/packages/flutter_markdown/lib/src/_functions_web.dart index 62692236a21e..7adf81654bed 100644 --- a/packages/flutter_markdown/lib/src/_functions_web.dart +++ b/packages/flutter_markdown/lib/src/_functions_web.dart @@ -66,7 +66,7 @@ final MarkdownStyleSheet Function(BuildContext, MarkdownStyleSheetBaseTheme?) } return result.copyWith( - textScaleFactor: MediaQuery.textScaleFactorOf(context), + textScaler: MediaQuery.textScalerOf(context), ); }; diff --git a/packages/flutter_markdown/lib/src/builder.dart b/packages/flutter_markdown/lib/src/builder.dart index 659df8ae6fc7..5d3cb10a8e43 100644 --- a/packages/flutter_markdown/lib/src/builder.dart +++ b/packages/flutter_markdown/lib/src/builder.dart @@ -867,7 +867,7 @@ class MarkdownBuilder implements md.NodeVisitor { if (selectable) { return SelectableText.rich( text!, - textScaleFactor: styleSheet.textScaleFactor, + textScaler: styleSheet.textScaler, textAlign: textAlign ?? TextAlign.start, onTap: onTapText, key: k, @@ -875,7 +875,7 @@ class MarkdownBuilder implements md.NodeVisitor { } else { return Text.rich( text!, - textScaleFactor: styleSheet.textScaleFactor, + textScaler: styleSheet.textScaler, textAlign: textAlign ?? TextAlign.start, key: k, ); diff --git a/packages/flutter_markdown/lib/src/style_sheet.dart b/packages/flutter_markdown/lib/src/style_sheet.dart index 8d73d318aa9c..7fc16d9881e0 100644 --- a/packages/flutter_markdown/lib/src/style_sheet.dart +++ b/packages/flutter_markdown/lib/src/style_sheet.dart @@ -59,8 +59,19 @@ class MarkdownStyleSheet { this.orderedListAlign = WrapAlignment.start, this.blockquoteAlign = WrapAlignment.start, this.codeblockAlign = WrapAlignment.start, - this.textScaleFactor, - }) : _styles = { + @Deprecated('Use textScaler instead.') this.textScaleFactor, + TextScaler? textScaler, + }) : assert( + textScaler == null || textScaleFactor == null, + 'textScaleFactor is deprecated and cannot be specified when textScaler is specified.', + ), + textScaler = textScaler ?? + // Internally, only textScaler is used, so convert the scale factor + // to a linear scaler. + (textScaleFactor == null + ? null + : TextScaler.linear(textScaleFactor)), + _styles = { 'a': a, 'p': p, 'li': p, @@ -380,8 +391,19 @@ class MarkdownStyleSheet { WrapAlignment? orderedListAlign, WrapAlignment? blockquoteAlign, WrapAlignment? codeblockAlign, - double? textScaleFactor, + @Deprecated('Use textScaler instead.') double? textScaleFactor, + TextScaler? textScaler, }) { + assert( + textScaler == null || textScaleFactor == null, + 'textScaleFactor is deprecated and cannot be specified when textScaler is specified.', + ); + // If either of textScaler or textScaleFactor is non-null, pass null for the + // other instead of the previous value, since only one is allowed. + final TextScaler? newTextScaler = + textScaler ?? (textScaleFactor == null ? this.textScaler : null); + final double? nextTextScaleFactor = + textScaleFactor ?? (textScaler == null ? this.textScaleFactor : null); return MarkdownStyleSheet( a: a ?? this.a, p: p ?? this.p, @@ -435,7 +457,8 @@ class MarkdownStyleSheet { orderedListAlign: orderedListAlign ?? this.orderedListAlign, blockquoteAlign: blockquoteAlign ?? this.blockquoteAlign, codeblockAlign: codeblockAlign ?? this.codeblockAlign, - textScaleFactor: textScaleFactor ?? this.textScaleFactor, + textScaler: newTextScaler, + textScaleFactor: nextTextScaleFactor, ); } @@ -497,6 +520,11 @@ class MarkdownStyleSheet { blockquoteAlign: other.blockquoteAlign, codeblockAlign: other.codeblockAlign, textScaleFactor: other.textScaleFactor, + // Only one of textScaler and textScaleFactor can be passed. If + // other.textScaleFactor is non-null, then the sheet was created with a + // textScaleFactor and the textScaler was derived from that, so should be + // ignored so that the textScaleFactor continues to be set. + textScaler: other.textScaleFactor == null ? other.textScaler : null, ); } @@ -650,7 +678,14 @@ class MarkdownStyleSheet { /// The [WrapAlignment] to use for a code block. Defaults to start. final WrapAlignment codeblockAlign; - /// The text scale factor to use in textual elements + /// The text scaler to use in textual elements. + final TextScaler? textScaler; + + /// The text scale factor to use in textual elements. + /// + /// This will be non-null only if the sheet was created with the deprecated + /// [textScaleFactor] instead of [textScaler]. + @Deprecated('Use textScaler instead.') final double? textScaleFactor; /// A [Map] from element name to the corresponding [TextStyle] object. @@ -717,7 +752,7 @@ class MarkdownStyleSheet { other.orderedListAlign == orderedListAlign && other.blockquoteAlign == blockquoteAlign && other.codeblockAlign == codeblockAlign && - other.textScaleFactor == textScaleFactor; + other.textScaler == textScaler; } @override @@ -774,6 +809,7 @@ class MarkdownStyleSheet { orderedListAlign, blockquoteAlign, codeblockAlign, + textScaler, textScaleFactor, ]); } diff --git a/packages/flutter_markdown/pubspec.yaml b/packages/flutter_markdown/pubspec.yaml index c3732a5c2cb8..0a3c89d24e77 100644 --- a/packages/flutter_markdown/pubspec.yaml +++ b/packages/flutter_markdown/pubspec.yaml @@ -4,11 +4,11 @@ description: A Markdown renderer for Flutter. Create rich text output, formatted with simple Markdown tags. repository: https://github.com/flutter/packages/tree/main/packages/flutter_markdown issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_markdown%22 -version: 0.6.19 +version: 0.6.20 environment: - sdk: ^3.1.0 - flutter: ">=3.13.0" + sdk: ^3.2.0 + flutter: ">=3.16.0" dependencies: flutter: diff --git a/packages/flutter_markdown/test/all.dart b/packages/flutter_markdown/test/all.dart index 44387e0afe9c..28338dbdcf30 100644 --- a/packages/flutter_markdown/test/all.dart +++ b/packages/flutter_markdown/test/all.dart @@ -18,7 +18,7 @@ import 'selection_area_compatibility_test.dart' as selection_area_test; import 'style_sheet_test.dart' as style_sheet_test; import 'table_test.dart' as table_test; import 'text_alignment_test.dart' as text_alignment_test; -import 'text_scale_factor_test.dart' as text_scale_factor; +import 'text_scaler_test.dart' as text_scaler; import 'text_test.dart' as text_test; import 'uri_test.dart' as uri_test; @@ -40,6 +40,6 @@ void main() { table_test.defineTests(); text_test.defineTests(); text_alignment_test.defineTests(); - text_scale_factor.defineTests(); + text_scaler.defineTests(); uri_test.defineTests(); } diff --git a/packages/flutter_markdown/test/style_sheet_test.dart b/packages/flutter_markdown/test/style_sheet_test.dart index ad8c1f0c695b..b48465a415a0 100644 --- a/packages/flutter_markdown/test/style_sheet_test.dart +++ b/packages/flutter_markdown/test/style_sheet_test.dart @@ -398,5 +398,65 @@ void defineTests() { ); }, ); + + testWidgets( + 'deprecated textScaleFactor is converted to linear scaler', + (WidgetTester tester) async { + const double scaleFactor = 2.0; + final MarkdownStyleSheet style = MarkdownStyleSheet( + textScaleFactor: scaleFactor, + ); + + expect(style.textScaler, const TextScaler.linear(scaleFactor)); + expect(style.textScaleFactor, scaleFactor); + }, + ); + + testWidgets( + 'deprecated textScaleFactor is null when a scaler is provided', + (WidgetTester tester) async { + const TextScaler scaler = TextScaler.linear(2.0); + final MarkdownStyleSheet style = MarkdownStyleSheet( + textScaler: scaler, + ); + + expect(style.textScaler, scaler); + expect(style.textScaleFactor, null); + }, + ); + + testWidgets( + 'copyWith textScaler overwrites both textScaler and textScaleFactor', + (WidgetTester tester) async { + final MarkdownStyleSheet original = MarkdownStyleSheet( + textScaleFactor: 2.0, + ); + + const TextScaler newScaler = TextScaler.linear(3.0); + final MarkdownStyleSheet copy = original.copyWith( + textScaler: newScaler, + ); + + expect(copy.textScaler, newScaler); + expect(copy.textScaleFactor, null); + }, + ); + + testWidgets( + 'copyWith textScaleFactor overwrites both textScaler and textScaleFactor', + (WidgetTester tester) async { + final MarkdownStyleSheet original = MarkdownStyleSheet( + textScaleFactor: 2.0, + ); + + const double newScaleFactor = 3.0; + final MarkdownStyleSheet copy = original.copyWith( + textScaleFactor: newScaleFactor, + ); + + expect(copy.textScaler, const TextScaler.linear(newScaleFactor)); + expect(copy.textScaleFactor, newScaleFactor); + }, + ); }); } diff --git a/packages/flutter_markdown/test/text_scale_factor_test.dart b/packages/flutter_markdown/test/text_scaler_test.dart similarity index 69% rename from packages/flutter_markdown/test/text_scale_factor_test.dart rename to packages/flutter_markdown/test/text_scaler_test.dart index 3710b3e0a62e..bc3ff911ef4e 100644 --- a/packages/flutter_markdown/test/text_scale_factor_test.dart +++ b/packages/flutter_markdown/test/text_scaler_test.dart @@ -10,33 +10,35 @@ import 'utils.dart'; void main() => defineTests(); void defineTests() { - group('Text Scale Factor', () { + group('Text Scaler', () { testWidgets( - 'should use style textScaleFactor in RichText', + 'should use style textScaler in RichText', (WidgetTester tester) async { + const TextScaler scaler = TextScaler.linear(2.0); const String data = 'Hello'; await tester.pumpWidget( boilerplate( MarkdownBody( - styleSheet: MarkdownStyleSheet(textScaleFactor: 2.0), + styleSheet: MarkdownStyleSheet(textScaler: scaler), data: data, ), ), ); final RichText richText = tester.widget(find.byType(RichText)); - expect(richText.textScaleFactor, 2.0); + expect(richText.textScaler, scaler); }, ); testWidgets( - 'should use MediaQuery textScaleFactor in RichText', + 'should use MediaQuery textScaler in RichText', (WidgetTester tester) async { + const TextScaler scaler = TextScaler.linear(2.0); const String data = 'Hello'; await tester.pumpWidget( boilerplate( const MediaQuery( - data: MediaQueryData(textScaleFactor: 2.0), + data: MediaQueryData(textScaler: scaler), child: MarkdownBody( data: data, ), @@ -45,18 +47,19 @@ void defineTests() { ); final RichText richText = tester.widget(find.byType(RichText)); - expect(richText.textScaleFactor, 2.0); + expect(richText.textScaler, scaler); }, ); testWidgets( - 'should use MediaQuery textScaleFactor in SelectableText.rich', + 'should use MediaQuery textScaler in SelectableText.rich', (WidgetTester tester) async { + const TextScaler scaler = TextScaler.linear(2.0); const String data = 'Hello'; await tester.pumpWidget( boilerplate( const MediaQuery( - data: MediaQueryData(textScaleFactor: 2.0), + data: MediaQueryData(textScaler: scaler), child: MarkdownBody( data: data, selectable: true, @@ -67,7 +70,7 @@ void defineTests() { final SelectableText selectableText = tester.widget(find.byType(SelectableText)); - expect(selectableText.textScaleFactor, 2.0); + expect(selectableText.textScaler, scaler); }, ); }); diff --git a/packages/flutter_markdown/test/utils.dart b/packages/flutter_markdown/test/utils.dart index 1cd902c30022..2d1a4ff27de5 100644 --- a/packages/flutter_markdown/test/utils.dart +++ b/packages/flutter_markdown/test/utils.dart @@ -169,7 +169,7 @@ void expectLinkTap(MarkdownLink? actual, MarkdownLink expected) { } String dumpRenderView() { - return WidgetsBinding.instance.renderViewElement!.toStringDeep().replaceAll( + return WidgetsBinding.instance.rootElement!.toStringDeep().replaceAll( RegExp(r'SliverChildListDelegate#\d+', multiLine: true), 'SliverChildListDelegate', ); From 8bba41b0c046228339c2b2577c0afcca76629b5f Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 20 Feb 2024 11:20:20 -0500 Subject: [PATCH 002/126] Roll Flutter from 064c340baf0e to 5129806e6c63 (9 revisions) (#6164) https://github.com/flutter/flutter/compare/064c340baf0e...5129806e6c63 2024-02-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from 0abe2b9d6c7c to 781f308c6555 (1 revision) (flutter/flutter#143750) 2024-02-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from 92aad0d0fcee to 0abe2b9d6c7c (1 revision) (flutter/flutter#143745) 2024-02-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from 2335115f08d3 to 92aad0d0fcee (2 revisions) (flutter/flutter#143736) 2024-02-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from e96c18b6c5ee to 2335115f08d3 (1 revision) (flutter/flutter#143731) 2024-02-20 greg@zulip.com Small fixes in TextEditingController docs (flutter/flutter#143717) 2024-02-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from b41494f009f4 to e96c18b6c5ee (1 revision) (flutter/flutter#143722) 2024-02-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from 714215d42e57 to b41494f009f4 (1 revision) (flutter/flutter#143713) 2024-02-19 git@reb0.org Reland (2): "Fix how Gradle resolves Android plugin" (flutter/flutter#142498) 2024-02-19 engine-flutter-autoroll@skia.org Roll Packages from 0af905d779d5 to 84ff11d7ee5a (1 revision) (flutter/flutter#143710) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC dit@google.com,rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 359ddb64593e..1dd06e656959 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -064c340baf0e23790374f5b34ea067c2478e7fd1 +5129806e6c6331870b2a17606d1ddf71421f9fa7 From b21dce5f4964adf1e6c90225377a00f65652c428 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 20 Feb 2024 14:00:12 -0800 Subject: [PATCH 003/126] [web] Updates package:web dependency to ^0.5.0. (#5791) This PR updates the packages that are using `package:web` to version `^0.5.0`. Ancilliary changes: * Bump `environment` to `flutter: ">=3.19.0"` and `sdk: ^3.3.0`. * Bump version to next `Y` * Clean-up code that was kept for compatibility with versions of `web: <0.5.0`. The main exception to this is `package:google_sign_in_web`, which depends on a version of `google_identity_services_web` that has a dependency on package:web that is `<0.5.0`, so that package needs to have a range until `google_identity_services_web` gets published with the new ^0.5.0 dependency. Co-Authored-By: David Iglesias --- packages/cross_file/CHANGELOG.md | 5 +- packages/cross_file/lib/src/types/html.dart | 8 +- packages/cross_file/pubspec.yaml | 6 +- .../cross_file/test/x_file_html_test.dart | 6 +- .../file_selector_web/CHANGELOG.md | 5 +- .../integration_test/dom_helper_test.dart | 35 +- .../file_selector_web/example/pubspec.yaml | 6 +- .../file_selector_web/lib/src/dom_helper.dart | 11 +- .../file_selector_web/pubspec.yaml | 8 +- .../google_identity_services_web/CHANGELOG.md | 5 + .../integration_test/js_interop_id_test.dart | 4 +- .../example/pubspec.yaml | 3 +- .../src/js_interop/google_accounts_id.dart | 4 +- .../src/js_interop/package_web_tweaks.dart | 67 ++- .../lib/src/js_loader.dart | 8 +- .../google_identity_services_web/pubspec.yaml | 6 +- .../google_sign_in_web/CHANGELOG.md | 5 + .../google_sign_in_web/example/pubspec.yaml | 6 +- .../google_sign_in_web/pubspec.yaml | 8 +- .../image_picker_for_web/example/pubspec.yaml | 1 - packages/metrics_center/CHANGELOG.md | 3 +- packages/metrics_center/pubspec.yaml | 4 +- .../test/gcs_lock_test.mocks.dart | 392 +++++++++++++----- .../pointer_interceptor_web/CHANGELOG.md | 5 + .../example/pubspec.yaml | 6 +- .../pointer_interceptor_web/pubspec.yaml | 8 +- .../shared_preferences_web/CHANGELOG.md | 5 + .../shared_preferences_web_test.dart | 3 +- .../example/pubspec.yaml | 3 +- .../shared_preferences_web/pubspec.yaml | 8 +- .../url_launcher_web/CHANGELOG.md | 5 + .../integration_test/link_widget_test.dart | 9 +- .../url_launcher_web_test.dart | 13 +- .../url_launcher_web/example/pubspec.yaml | 6 +- .../url_launcher_web/lib/src/link.dart | 15 +- .../url_launcher_web/pubspec.yaml | 8 +- .../video_player_web/CHANGELOG.md | 4 +- .../example/integration_test/utils.dart | 44 +- .../video_player_web/example/pubspec.yaml | 8 +- .../video_player_web/pubspec.yaml | 6 +- packages/web_benchmarks/CHANGELOG.md | 5 +- packages/web_benchmarks/lib/client.dart | 12 +- packages/web_benchmarks/lib/src/recorder.dart | 2 +- packages/web_benchmarks/pubspec.yaml | 9 +- 44 files changed, 500 insertions(+), 290 deletions(-) diff --git a/packages/cross_file/CHANGELOG.md b/packages/cross_file/CHANGELOG.md index c6113b298146..52b11b97de35 100644 --- a/packages/cross_file/CHANGELOG.md +++ b/packages/cross_file/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 0.3.4 -* Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. +* Updates to web code to package `web: ^0.5.0`. +* Updates SDK version to Dart `^3.3.0`. ## 0.3.3+8 diff --git a/packages/cross_file/lib/src/types/html.dart b/packages/cross_file/lib/src/types/html.dart index 9eb95b448d88..a58dc35278b5 100644 --- a/packages/cross_file/lib/src/types/html.dart +++ b/packages/cross_file/lib/src/types/html.dart @@ -66,9 +66,7 @@ class XFile extends XFileBase { super(path) { if (path == null) { _browserBlob = _createBlobFromBytes(bytes, mimeType); - // TODO(kevmoo): drop ignore when pkg:web constraint excludes v0.3 - // ignore: unnecessary_cast - _path = URL.createObjectURL(_browserBlob! as JSObject); + _path = URL.createObjectURL(_browserBlob!); } else { _path = path; } @@ -131,9 +129,7 @@ class XFile extends XFileBase { // Attempt to re-hydrate the blob from the `path` via a (local) HttpRequest. // Note that safari hangs if the Blob is >=4GB, so bail out in that case. - // TODO(kevmoo): Remove ignore and fix when the MIN Dart SDK is 3.3 - // ignore: unnecessary_non_null_assertion - if (isSafari() && _length != null && _length! >= _fourGigabytes) { + if (isSafari() && _length != null && _length >= _fourGigabytes) { throw Exception('Safari cannot handle XFiles larger than 4GB.'); } diff --git a/packages/cross_file/pubspec.yaml b/packages/cross_file/pubspec.yaml index c2b8f2f53424..b7b7f7af1088 100644 --- a/packages/cross_file/pubspec.yaml +++ b/packages/cross_file/pubspec.yaml @@ -2,14 +2,14 @@ name: cross_file description: An abstraction to allow working with files across multiple platforms. repository: https://github.com/flutter/packages/tree/main/packages/cross_file issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+cross_file%22 -version: 0.3.3+8 +version: 0.3.4 environment: - sdk: ^3.2.0 + sdk: ^3.3.0 dependencies: meta: ^1.3.0 - web: '>=0.3.0 <0.5.0' + web: ^0.5.0 dev_dependencies: path: ^1.8.1 diff --git a/packages/cross_file/test/x_file_html_test.dart b/packages/cross_file/test/x_file_html_test.dart index 4b002cac158b..4b1dabd1583d 100644 --- a/packages/cross_file/test/x_file_html_test.dart +++ b/packages/cross_file/test/x_file_html_test.dart @@ -69,10 +69,10 @@ void main() { test('Stores data as a Blob', () async { // Read the blob from its path 'natively' final html.Response response = - (await html.window.fetch(file.path.toJS).toDart)! as html.Response; + await html.window.fetch(file.path.toJS).toDart; - final JSAny? arrayBuffer = await response.arrayBuffer().toDart; - final ByteBuffer data = (arrayBuffer! as JSArrayBuffer).toDart; + final JSAny arrayBuffer = await response.arrayBuffer().toDart; + final ByteBuffer data = (arrayBuffer as JSArrayBuffer).toDart; expect(data.asUint8List(), equals(bytes)); }); diff --git a/packages/file_selector/file_selector_web/CHANGELOG.md b/packages/file_selector/file_selector_web/CHANGELOG.md index 40b7a853e5b3..e2dd2901aa8e 100644 --- a/packages/file_selector/file_selector_web/CHANGELOG.md +++ b/packages/file_selector/file_selector_web/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 0.9.4 -* Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. +* Updates web code to package `web: ^0.5.0`. +* Updates SDK version to Dart `^3.3.0`. Flutter `^3.16.0`. ## 0.9.3 diff --git a/packages/file_selector/file_selector_web/example/integration_test/dom_helper_test.dart b/packages/file_selector/file_selector_web/example/integration_test/dom_helper_test.dart index 1cec6fc7ad10..c4697a70a5d4 100644 --- a/packages/file_selector/file_selector_web/example/integration_test/dom_helper_test.dart +++ b/packages/file_selector/file_selector_web/example/integration_test/dom_helper_test.dart @@ -18,11 +18,10 @@ void main() { FileList? createFileList(List files) { final DataTransfer dataTransfer = DataTransfer(); + // Tear-offs of external extension type interop member 'add' are disallowed. + // ignore: prefer_foreach for (final File e in files) { - // TODO(srujzs): This is necessary in order to support package:web 0.4.0. - // This was not needed with 0.3.0, hence the lint. - // ignore: unnecessary_cast - dataTransfer.items.add(e as JSAny); + dataTransfer.items.add(e); } return dataTransfer.files; } @@ -46,13 +45,8 @@ void main() { }); group('getFiles', () { - final File mockFile1 = - // TODO(srujzs): Remove once typed JSArrays (JSArray) get to `stable`. - // ignore: always_specify_types - File(['123456'].jsify as JSArray, 'file1.txt'); - // TODO(srujzs): Remove once typed JSArrays (JSArray) get to `stable`. - // ignore: always_specify_types - final File mockFile2 = File([].jsify as JSArray, 'file2.txt'); + final File mockFile1 = File(['123456'.toJS].toJS, 'file1.txt'); + final File mockFile2 = File([].toJS, 'file2.txt'); testWidgets('works', (_) async { final Future> futureFiles = domHelper.getFiles( @@ -114,10 +108,7 @@ void main() { testWidgets('sets the attributes and clicks it', (_) async { const String accept = '.jpg,.png'; const bool multiple = true; - bool wasClicked = false; - - //ignore: unawaited_futures - input.onClick.first.then((_) => wasClicked = true); + final Future wasClicked = input.onClick.first.then((_) => true); final Future> futureFile = domHelper.getFiles( accept: accept, @@ -125,21 +116,19 @@ void main() { input: input, ); - expect(input.matches('body'), true); + expect(input.isConnected, true, + reason: 'input must be injected into the DOM'); expect(input.accept, accept); expect(input.multiple, multiple); - expect( - wasClicked, - true, - reason: - 'The should be clicked otherwise no dialog will be shown', - ); + expect(await wasClicked, true, + reason: + 'The should be clicked otherwise no dialog will be shown'); setFilesAndTriggerChange([]); await futureFile; // It should be already removed from the DOM after the file is resolved. - expect(input.parentElement, isNull); + expect(input.isConnected, isFalse); }); }); }); diff --git a/packages/file_selector/file_selector_web/example/pubspec.yaml b/packages/file_selector/file_selector_web/example/pubspec.yaml index 725ace61b00b..689ab97657e7 100644 --- a/packages/file_selector/file_selector_web/example/pubspec.yaml +++ b/packages/file_selector/file_selector_web/example/pubspec.yaml @@ -2,8 +2,8 @@ name: file_selector_web_integration_tests publish_to: none environment: - sdk: ^3.1.0 - flutter: ">=3.13.0" + sdk: ^3.3.0 + flutter: ">=3.19.0" dependencies: file_selector_platform_interface: ^2.6.0 @@ -11,7 +11,7 @@ dependencies: path: ../ flutter: sdk: flutter - web: '>=0.3.0 <0.5.0' + web: ^0.5.0 dev_dependencies: flutter_test: diff --git a/packages/file_selector/file_selector_web/lib/src/dom_helper.dart b/packages/file_selector/file_selector_web/lib/src/dom_helper.dart index 7684a12286d7..309f0ee432f1 100644 --- a/packages/file_selector/file_selector_web/lib/src/dom_helper.dart +++ b/packages/file_selector/file_selector_web/lib/src/dom_helper.dart @@ -14,11 +14,11 @@ import 'package:web/helpers.dart'; class DomHelper { /// Default constructor, initializes the container DOM element. DomHelper() { - final Element body = querySelector('body')!; + final Element body = document.querySelector('body')!; body.appendChild(_container); } - final Element _container = createElementTag('file-selector'); + final Element _container = document.createElement('file-selector'); /// Sets the attributes and waits for a file to be selected. Future> getFiles({ @@ -28,7 +28,7 @@ class DomHelper { }) { final Completer> completer = Completer>(); final HTMLInputElement inputElement = - input ?? (createElementTag('input') as HTMLInputElement) + input ?? (document.createElement('input') as HTMLInputElement) ..type = 'file'; _container.appendChild( @@ -72,10 +72,7 @@ class DomHelper { } XFile _convertFileToXFile(File file) => XFile( - // TODO(srujzs): This is necessary in order to support package:web 0.4.0. - // This was not needed with 0.3.0, hence the lint. - // ignore: unnecessary_cast - URL.createObjectURL(file as JSObject), + URL.createObjectURL(file), name: file.name, length: file.size, lastModified: DateTime.fromMillisecondsSinceEpoch(file.lastModified), diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index 3c3ca15c3aa9..edc74e3064ea 100644 --- a/packages/file_selector/file_selector_web/pubspec.yaml +++ b/packages/file_selector/file_selector_web/pubspec.yaml @@ -2,11 +2,11 @@ name: file_selector_web description: Web platform implementation of file_selector repository: https://github.com/flutter/packages/tree/main/packages/file_selector/file_selector_web 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.4 environment: - sdk: ^3.2.0 - flutter: ">=3.16.0" + sdk: ^3.3.0 + flutter: ">=3.19.0" flutter: plugin: @@ -22,7 +22,7 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter - web: '>=0.3.0 <0.5.0' + web: ^0.5.0 dev_dependencies: flutter_test: diff --git a/packages/google_identity_services_web/CHANGELOG.md b/packages/google_identity_services_web/CHANGELOG.md index 92c96c07bdce..fe739cab00e5 100644 --- a/packages/google_identity_services_web/CHANGELOG.md +++ b/packages/google_identity_services_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.3.1 + +* Updates web code to package `web: ^0.5.0`. +* Updates SDK version to Dart `^3.3.0`. Flutter `^3.19.0`. + ## 0.3.0+2 * Adds `fedcm_auto` to `CredentialSelectBy` enum. diff --git a/packages/google_identity_services_web/example/integration_test/js_interop_id_test.dart b/packages/google_identity_services_web/example/integration_test/js_interop_id_test.dart index 77914b746740..1d1c5c29ea81 100644 --- a/packages/google_identity_services_web/example/integration_test/js_interop_id_test.dart +++ b/packages/google_identity_services_web/example/integration_test/js_interop_id_test.dart @@ -66,9 +66,7 @@ void main() async { expectConfigValue('login_uri', 'https://www.example.com/login'); expectConfigValue('native_callback', utils.isAJs('function')); expectConfigValue('cancel_on_tap_outside', isFalse); - // TODO(srujzs): Remove once typed JSArrays (JSArray) get to `stable`. - // ignore: always_specify_types - expectConfigValue('allowed_parent_origin', isA()); + expectConfigValue('allowed_parent_origin', isA>()); expectConfigValue('prompt_parent_id', 'some_dom_id'); expectConfigValue('nonce', 's0m3_r4ndOM_vALu3'); expectConfigValue('context', 'signin'); diff --git a/packages/google_identity_services_web/example/pubspec.yaml b/packages/google_identity_services_web/example/pubspec.yaml index e53f85dff1cb..46bdaf47c9e0 100644 --- a/packages/google_identity_services_web/example/pubspec.yaml +++ b/packages/google_identity_services_web/example/pubspec.yaml @@ -1,7 +1,6 @@ name: google_identity_services_web_example description: An example for the google_identity_services_web package, OneTap. publish_to: 'none' -version: 0.0.1 environment: flutter: ">=3.16.0" @@ -13,7 +12,7 @@ dependencies: google_identity_services_web: path: ../ http: ">=0.13.0 <2.0.0" - web: ">=0.3.0 <0.5.0" + web: ^0.5.0 dev_dependencies: build_runner: ^2.1.10 # To extract README excerpts only. diff --git a/packages/google_identity_services_web/lib/src/js_interop/google_accounts_id.dart b/packages/google_identity_services_web/lib/src/js_interop/google_accounts_id.dart index a02197e0cf83..8467854640e8 100644 --- a/packages/google_identity_services_web/lib/src/js_interop/google_accounts_id.dart +++ b/packages/google_identity_services_web/lib/src/js_interop/google_accounts_id.dart @@ -338,9 +338,7 @@ abstract class IdConfiguration { JSString? context, JSString? state_cookie_domain, JSString? ux_mode, - // TODO(srujzs): Remove once typed JSArrays (JSArray) get to `stable`. - // ignore: always_specify_types - JSArray? allowed_parent_origin, + JSArray? allowed_parent_origin, JSFunction? intermediate_iframe_close_callback, JSBoolean? itp_support, JSString? login_hint, diff --git a/packages/google_identity_services_web/lib/src/js_interop/package_web_tweaks.dart b/packages/google_identity_services_web/lib/src/js_interop/package_web_tweaks.dart index 76dc411c103e..b05fbc5a49a2 100644 --- a/packages/google_identity_services_web/lib/src/js_interop/package_web_tweaks.dart +++ b/packages/google_identity_services_web/lib/src/js_interop/package_web_tweaks.dart @@ -2,34 +2,75 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -/// Provides some useful tweaks to `package:web`. -library package_web_tweaks; - import 'dart:js_interop'; + import 'package:web/web.dart' as web; +// TODO(kevmoo): Make this file unnecessary, https://github.com/dart-lang/web/issues/175 + /// This extension gives web.window a nullable getter to the `trustedTypes` /// property, which needs to be used to check for feature support. extension NullableTrustedTypesGetter on web.Window { + /// (Nullable) Bindings to window.trustedTypes. + /// + /// This may be null if the browser doesn't support the Trusted Types API. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API + @JS('trustedTypes') + external TrustedTypePolicyFactory? get nullableTrustedTypes; + + /// Bindings to window.trustedTypes. + /// + /// This will crash if accessed in a browser that doesn't support the + /// Trusted Types API. /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API @JS('trustedTypes') - external web.TrustedTypePolicyFactory? get nullableTrustedTypes; + external TrustedTypePolicyFactory get trustedTypes; } -/// This extension allows a trusted type policy to create a script URL without -/// the `args` parameter (which in Chrome currently fails). -extension CreateScriptUrlWithoutArgs on web.TrustedTypePolicy { +/// This extension allows setting a TrustedScriptURL as the src of a script element, +/// which currently only accepts a string. +extension TrustedTypeSrcAttribute on web.HTMLScriptElement { + @JS('src') + external set trustedSrc(TrustedScriptURL value); +} + +// TODO(kevmoo): drop all of this once `pkg:web` publishes `0.5.1`. + +/// Bindings to a JS TrustedScriptURL. +/// +/// See: https://developer.mozilla.org/en-US/docs/Web/API/TrustedScriptURL +extension type TrustedScriptURL._(JSObject _) implements JSObject {} + +/// Bindings to a JS TrustedTypePolicyFactory. +/// +/// See: https://developer.mozilla.org/en-US/docs/Web/API/TrustedTypePolicyFactory +extension type TrustedTypePolicyFactory._(JSObject _) implements JSObject { + /// + external TrustedTypePolicy createPolicy( + String policyName, [ + TrustedTypePolicyOptions policyOptions, + ]); +} + +/// Bindings to a JS TrustedTypePolicy. +/// +/// See: https://developer.mozilla.org/en-US/docs/Web/API/TrustedTypePolicy +extension type TrustedTypePolicy._(JSObject _) implements JSObject { /// @JS('createScriptURL') - external web.TrustedScriptURL createScriptURLNoArgs( + external TrustedScriptURL createScriptURLNoArgs( String input, ); } -/// This extension allows setting a TrustedScriptURL as the src of a script element, -/// which currently only accepts a string. -extension TrustedTypeSrcAttribute on web.HTMLScriptElement { +/// Bindings to a JS TrustedTypePolicyOptions (anonymous). +/// +/// See: https://developer.mozilla.org/en-US/docs/Web/API/TrustedTypePolicyFactory/createPolicy#policyoptions +extension type TrustedTypePolicyOptions._(JSObject _) implements JSObject { /// - @JS('src') - external set srcTT(web.TrustedScriptURL value); + external factory TrustedTypePolicyOptions({ + JSFunction createScriptURL, + }); } diff --git a/packages/google_identity_services_web/lib/src/js_loader.dart b/packages/google_identity_services_web/lib/src/js_loader.dart index bd876f98c5ce..620ad2129269 100644 --- a/packages/google_identity_services_web/lib/src/js_loader.dart +++ b/packages/google_identity_services_web/lib/src/js_loader.dart @@ -25,15 +25,15 @@ Future loadWebSdk({ onGoogleLibraryLoad = () => completer.complete(); // If TrustedTypes are available, prepare a trusted URL. - web.TrustedScriptURL? trustedUrl; + TrustedScriptURL? trustedUrl; if (web.window.nullableTrustedTypes != null) { web.console.debug( 'TrustedTypes available. Creating policy: $trustedTypePolicyName'.toJS, ); try { - final web.TrustedTypePolicy policy = web.window.trustedTypes.createPolicy( + final TrustedTypePolicy policy = web.window.trustedTypes.createPolicy( trustedTypePolicyName, - web.TrustedTypePolicyOptions( + TrustedTypePolicyOptions( createScriptURL: ((JSString url) => _url).toJS, )); trustedUrl = policy.createScriptURLNoArgs(_url); @@ -47,7 +47,7 @@ Future loadWebSdk({ ..async = true ..defer = true; if (trustedUrl != null) { - script.srcTT = trustedUrl; + script.trustedSrc = trustedUrl; } else { script.src = _url; } diff --git a/packages/google_identity_services_web/pubspec.yaml b/packages/google_identity_services_web/pubspec.yaml index 2f1e5b913901..84502a1f6a49 100644 --- a/packages/google_identity_services_web/pubspec.yaml +++ b/packages/google_identity_services_web/pubspec.yaml @@ -2,14 +2,14 @@ name: google_identity_services_web description: A Dart JS-interop layer for Google Identity Services. Google's new sign-in SDK for Web that supports multiple types of credentials. repository: https://github.com/flutter/packages/tree/main/packages/google_identity_services_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_identiy_services_web%22 -version: 0.3.0+2 +version: 0.3.1 environment: - sdk: ">=3.2.0 <4.0.0" + sdk: ^3.3.0 dependencies: meta: ^1.3.0 - web: ">=0.3.0 <0.5.0" + web: ^0.5.0 dev_dependencies: path: ^1.8.1 diff --git a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md index 73a6b806ac13..2c9c5863c8e2 100644 --- a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.12.3+3 + +* Updates SDK version to Dart `^3.3.0`. Flutter `^3.19.0`. +* Prepares update to package `web: ^0.5.0`. + ## 0.12.3+2 * Fixes new lint warnings. diff --git a/packages/google_sign_in/google_sign_in_web/example/pubspec.yaml b/packages/google_sign_in/google_sign_in_web/example/pubspec.yaml index a7c48aa6d3bd..1c53b0fa93c9 100644 --- a/packages/google_sign_in/google_sign_in_web/example/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_web/example/pubspec.yaml @@ -2,8 +2,8 @@ name: google_sign_in_web_integration_tests publish_to: none environment: - sdk: ">=3.2.0 <4.0.0" - flutter: ">=3.16.0" + sdk: ^3.3.0 + flutter: ">=3.19.0" dependencies: cupertino_icons: ^1.0.2 @@ -22,7 +22,7 @@ dev_dependencies: integration_test: sdk: flutter mockito: 5.4.4 - web: ">=0.3.0 <0.5.0" + web: ">=0.3.0 <0.6.0" flutter: uses-material-design: true diff --git a/packages/google_sign_in/google_sign_in_web/pubspec.yaml b/packages/google_sign_in/google_sign_in_web/pubspec.yaml index 834e666b34ae..82b9cd3cd289 100644 --- a/packages/google_sign_in/google_sign_in_web/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_web/pubspec.yaml @@ -3,11 +3,11 @@ description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android, iOS and Web. repository: https://github.com/flutter/packages/tree/main/packages/google_sign_in/google_sign_in_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 0.12.3+2 +version: 0.12.3+3 environment: - sdk: ">=3.2.0 <4.0.0" - flutter: ">=3.16.0" + sdk: ^3.3.0 + flutter: ">=3.19.0" flutter: plugin: @@ -25,7 +25,7 @@ dependencies: google_identity_services_web: ^0.3.0 google_sign_in_platform_interface: ^2.4.0 http: ">=0.13.0 <2.0.0" - web: ">=0.3.0 <0.5.0" + web: ">=0.3.0 <0.6.0" # because google_identity_services dev_dependencies: flutter_test: diff --git a/packages/image_picker/image_picker_for_web/example/pubspec.yaml b/packages/image_picker/image_picker_for_web/example/pubspec.yaml index 4e1a99bb5530..2efe50fa6375 100644 --- a/packages/image_picker/image_picker_for_web/example/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/example/pubspec.yaml @@ -17,4 +17,3 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - js: ^0.6.3 diff --git a/packages/metrics_center/CHANGELOG.md b/packages/metrics_center/CHANGELOG.md index 0d589218f8ab..a661251dc1d2 100644 --- a/packages/metrics_center/CHANGELOG.md +++ b/packages/metrics_center/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 1.0.13 * Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. +* Updates dependency on `package:googleapis` to `^12.0.0`. ## 1.0.12 diff --git a/packages/metrics_center/pubspec.yaml b/packages/metrics_center/pubspec.yaml index efc8ffcd5d92..581be39e283c 100644 --- a/packages/metrics_center/pubspec.yaml +++ b/packages/metrics_center/pubspec.yaml @@ -1,5 +1,5 @@ name: metrics_center -version: 1.0.12 +version: 1.0.13 description: Support multiple performance metrics sources/formats and destinations. repository: https://github.com/flutter/packages/tree/main/packages/metrics_center @@ -12,7 +12,7 @@ dependencies: _discoveryapis_commons: ^1.0.0 crypto: ^3.0.1 gcloud: ^0.8.2 - googleapis: ^3.0.0 + googleapis: ^12.0.0 googleapis_auth: ^1.1.0 http: ">=0.13.0 <2.0.0" diff --git a/packages/metrics_center/test/gcs_lock_test.mocks.dart b/packages/metrics_center/test/gcs_lock_test.mocks.dart index f28daf64f6e5..d013ac823f25 100644 --- a/packages/metrics_center/test/gcs_lock_test.mocks.dart +++ b/packages/metrics_center/test/gcs_lock_test.mocks.dart @@ -60,9 +60,20 @@ class _FakeStreamedResponse_2 extends _i1.SmartFake ); } -class _FakeBucketAccessControlsResource_3 extends _i1.SmartFake +class _FakeAnywhereCacheResource_3 extends _i1.SmartFake + implements _i4.AnywhereCacheResource { + _FakeAnywhereCacheResource_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeBucketAccessControlsResource_4 extends _i1.SmartFake implements _i4.BucketAccessControlsResource { - _FakeBucketAccessControlsResource_3( + _FakeBucketAccessControlsResource_4( Object parent, Invocation parentInvocation, ) : super( @@ -71,9 +82,9 @@ class _FakeBucketAccessControlsResource_3 extends _i1.SmartFake ); } -class _FakeBucketsResource_4 extends _i1.SmartFake +class _FakeBucketsResource_5 extends _i1.SmartFake implements _i4.BucketsResource { - _FakeBucketsResource_4( + _FakeBucketsResource_5( Object parent, Invocation parentInvocation, ) : super( @@ -82,9 +93,9 @@ class _FakeBucketsResource_4 extends _i1.SmartFake ); } -class _FakeChannelsResource_5 extends _i1.SmartFake +class _FakeChannelsResource_6 extends _i1.SmartFake implements _i4.ChannelsResource { - _FakeChannelsResource_5( + _FakeChannelsResource_6( Object parent, Invocation parentInvocation, ) : super( @@ -93,9 +104,20 @@ class _FakeChannelsResource_5 extends _i1.SmartFake ); } -class _FakeDefaultObjectAccessControlsResource_6 extends _i1.SmartFake +class _FakeDefaultObjectAccessControlsResource_7 extends _i1.SmartFake implements _i4.DefaultObjectAccessControlsResource { - _FakeDefaultObjectAccessControlsResource_6( + _FakeDefaultObjectAccessControlsResource_7( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeManagedFoldersResource_8 extends _i1.SmartFake + implements _i4.ManagedFoldersResource { + _FakeManagedFoldersResource_8( Object parent, Invocation parentInvocation, ) : super( @@ -104,9 +126,9 @@ class _FakeDefaultObjectAccessControlsResource_6 extends _i1.SmartFake ); } -class _FakeNotificationsResource_7 extends _i1.SmartFake +class _FakeNotificationsResource_9 extends _i1.SmartFake implements _i4.NotificationsResource { - _FakeNotificationsResource_7( + _FakeNotificationsResource_9( Object parent, Invocation parentInvocation, ) : super( @@ -115,9 +137,9 @@ class _FakeNotificationsResource_7 extends _i1.SmartFake ); } -class _FakeObjectAccessControlsResource_8 extends _i1.SmartFake +class _FakeObjectAccessControlsResource_10 extends _i1.SmartFake implements _i4.ObjectAccessControlsResource { - _FakeObjectAccessControlsResource_8( + _FakeObjectAccessControlsResource_10( Object parent, Invocation parentInvocation, ) : super( @@ -126,9 +148,9 @@ class _FakeObjectAccessControlsResource_8 extends _i1.SmartFake ); } -class _FakeObjectsResource_9 extends _i1.SmartFake +class _FakeObjectsResource_11 extends _i1.SmartFake implements _i4.ObjectsResource { - _FakeObjectsResource_9( + _FakeObjectsResource_11( Object parent, Invocation parentInvocation, ) : super( @@ -137,9 +159,20 @@ class _FakeObjectsResource_9 extends _i1.SmartFake ); } -class _FakeProjectsResource_10 extends _i1.SmartFake +class _FakeOperationsResource_12 extends _i1.SmartFake + implements _i4.OperationsResource { + _FakeOperationsResource_12( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeProjectsResource_13 extends _i1.SmartFake implements _i4.ProjectsResource { - _FakeProjectsResource_10( + _FakeProjectsResource_13( Object parent, Invocation parentInvocation, ) : super( @@ -148,8 +181,9 @@ class _FakeProjectsResource_10 extends _i1.SmartFake ); } -class _FakeObject_11 extends _i1.SmartFake implements _i4.Object { - _FakeObject_11( +class _FakeGoogleLongrunningOperation_14 extends _i1.SmartFake + implements _i4.GoogleLongrunningOperation { + _FakeGoogleLongrunningOperation_14( Object parent, Invocation parentInvocation, ) : super( @@ -158,8 +192,8 @@ class _FakeObject_11 extends _i1.SmartFake implements _i4.Object { ); } -class _FakeObject_12 extends _i1.SmartFake implements Object { - _FakeObject_12( +class _FakeObject_15 extends _i1.SmartFake implements _i4.Object { + _FakeObject_15( Object parent, Invocation parentInvocation, ) : super( @@ -168,8 +202,8 @@ class _FakeObject_12 extends _i1.SmartFake implements Object { ); } -class _FakePolicy_13 extends _i1.SmartFake implements _i4.Policy { - _FakePolicy_13( +class _FakeObject_16 extends _i1.SmartFake implements Object { + _FakeObject_16( Object parent, Invocation parentInvocation, ) : super( @@ -178,8 +212,8 @@ class _FakePolicy_13 extends _i1.SmartFake implements _i4.Policy { ); } -class _FakeObjects_14 extends _i1.SmartFake implements _i4.Objects { - _FakeObjects_14( +class _FakePolicy_17 extends _i1.SmartFake implements _i4.Policy { + _FakePolicy_17( Object parent, Invocation parentInvocation, ) : super( @@ -188,9 +222,19 @@ class _FakeObjects_14 extends _i1.SmartFake implements _i4.Objects { ); } -class _FakeRewriteResponse_15 extends _i1.SmartFake +class _FakeObjects_18 extends _i1.SmartFake implements _i4.Objects { + _FakeObjects_18( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeRewriteResponse_19 extends _i1.SmartFake implements _i4.RewriteResponse { - _FakeRewriteResponse_15( + _FakeRewriteResponse_19( Object parent, Invocation parentInvocation, ) : super( @@ -199,9 +243,9 @@ class _FakeRewriteResponse_15 extends _i1.SmartFake ); } -class _FakeTestIamPermissionsResponse_16 extends _i1.SmartFake +class _FakeTestIamPermissionsResponse_20 extends _i1.SmartFake implements _i4.TestIamPermissionsResponse { - _FakeTestIamPermissionsResponse_16( + _FakeTestIamPermissionsResponse_20( Object parent, Invocation parentInvocation, ) : super( @@ -210,8 +254,8 @@ class _FakeTestIamPermissionsResponse_16 extends _i1.SmartFake ); } -class _FakeChannel_17 extends _i1.SmartFake implements _i4.Channel { - _FakeChannel_17( +class _FakeChannel_21 extends _i1.SmartFake implements _i4.Channel { + _FakeChannel_21( Object parent, Invocation parentInvocation, ) : super( @@ -473,11 +517,20 @@ class MockStorageApi extends _i1.Mock implements _i4.StorageApi { _i1.throwOnMissingStub(this); } + @override + _i4.AnywhereCacheResource get anywhereCache => (super.noSuchMethod( + Invocation.getter(#anywhereCache), + returnValue: _FakeAnywhereCacheResource_3( + this, + Invocation.getter(#anywhereCache), + ), + ) as _i4.AnywhereCacheResource); + @override _i4.BucketAccessControlsResource get bucketAccessControls => (super.noSuchMethod( Invocation.getter(#bucketAccessControls), - returnValue: _FakeBucketAccessControlsResource_3( + returnValue: _FakeBucketAccessControlsResource_4( this, Invocation.getter(#bucketAccessControls), ), @@ -486,7 +539,7 @@ class MockStorageApi extends _i1.Mock implements _i4.StorageApi { @override _i4.BucketsResource get buckets => (super.noSuchMethod( Invocation.getter(#buckets), - returnValue: _FakeBucketsResource_4( + returnValue: _FakeBucketsResource_5( this, Invocation.getter(#buckets), ), @@ -495,7 +548,7 @@ class MockStorageApi extends _i1.Mock implements _i4.StorageApi { @override _i4.ChannelsResource get channels => (super.noSuchMethod( Invocation.getter(#channels), - returnValue: _FakeChannelsResource_5( + returnValue: _FakeChannelsResource_6( this, Invocation.getter(#channels), ), @@ -505,16 +558,25 @@ class MockStorageApi extends _i1.Mock implements _i4.StorageApi { _i4.DefaultObjectAccessControlsResource get defaultObjectAccessControls => (super.noSuchMethod( Invocation.getter(#defaultObjectAccessControls), - returnValue: _FakeDefaultObjectAccessControlsResource_6( + returnValue: _FakeDefaultObjectAccessControlsResource_7( this, Invocation.getter(#defaultObjectAccessControls), ), ) as _i4.DefaultObjectAccessControlsResource); + @override + _i4.ManagedFoldersResource get managedFolders => (super.noSuchMethod( + Invocation.getter(#managedFolders), + returnValue: _FakeManagedFoldersResource_8( + this, + Invocation.getter(#managedFolders), + ), + ) as _i4.ManagedFoldersResource); + @override _i4.NotificationsResource get notifications => (super.noSuchMethod( Invocation.getter(#notifications), - returnValue: _FakeNotificationsResource_7( + returnValue: _FakeNotificationsResource_9( this, Invocation.getter(#notifications), ), @@ -524,7 +586,7 @@ class MockStorageApi extends _i1.Mock implements _i4.StorageApi { _i4.ObjectAccessControlsResource get objectAccessControls => (super.noSuchMethod( Invocation.getter(#objectAccessControls), - returnValue: _FakeObjectAccessControlsResource_8( + returnValue: _FakeObjectAccessControlsResource_10( this, Invocation.getter(#objectAccessControls), ), @@ -533,16 +595,25 @@ class MockStorageApi extends _i1.Mock implements _i4.StorageApi { @override _i4.ObjectsResource get objects => (super.noSuchMethod( Invocation.getter(#objects), - returnValue: _FakeObjectsResource_9( + returnValue: _FakeObjectsResource_11( this, Invocation.getter(#objects), ), ) as _i4.ObjectsResource); + @override + _i4.OperationsResource get operations => (super.noSuchMethod( + Invocation.getter(#operations), + returnValue: _FakeOperationsResource_12( + this, + Invocation.getter(#operations), + ), + ) as _i4.OperationsResource); + @override _i4.ProjectsResource get projects => (super.noSuchMethod( Invocation.getter(#projects), - returnValue: _FakeProjectsResource_10( + returnValue: _FakeProjectsResource_13( this, Invocation.getter(#projects), ), @@ -553,6 +624,48 @@ class MockStorageApi extends _i1.Mock implements _i4.StorageApi { /// /// See the documentation for Mockito's code generation for more information. class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { + @override + _i6.Future<_i4.GoogleLongrunningOperation> bulkRestore( + _i4.BulkRestoreObjectsRequest? request, + String? bucket, { + String? $fields, + }) => + (super.noSuchMethod( + Invocation.method( + #bulkRestore, + [ + request, + bucket, + ], + {#$fields: $fields}, + ), + returnValue: _i6.Future<_i4.GoogleLongrunningOperation>.value( + _FakeGoogleLongrunningOperation_14( + this, + Invocation.method( + #bulkRestore, + [ + request, + bucket, + ], + {#$fields: $fields}, + ), + )), + returnValueForMissingStub: + _i6.Future<_i4.GoogleLongrunningOperation>.value( + _FakeGoogleLongrunningOperation_14( + this, + Invocation.method( + #bulkRestore, + [ + request, + bucket, + ], + {#$fields: $fields}, + ), + )), + ) as _i6.Future<_i4.GoogleLongrunningOperation>); + @override _i6.Future<_i4.Object> compose( _i4.ComposeRequest? request, @@ -562,7 +675,6 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { String? ifGenerationMatch, String? ifMetagenerationMatch, String? kmsKeyName, - String? provisionalUserProject, String? userProject, String? $fields, }) => @@ -579,12 +691,11 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #ifGenerationMatch: ifGenerationMatch, #ifMetagenerationMatch: ifMetagenerationMatch, #kmsKeyName: kmsKeyName, - #provisionalUserProject: provisionalUserProject, #userProject: userProject, #$fields: $fields, }, ), - returnValue: _i6.Future<_i4.Object>.value(_FakeObject_11( + returnValue: _i6.Future<_i4.Object>.value(_FakeObject_15( this, Invocation.method( #compose, @@ -598,13 +709,12 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #ifGenerationMatch: ifGenerationMatch, #ifMetagenerationMatch: ifMetagenerationMatch, #kmsKeyName: kmsKeyName, - #provisionalUserProject: provisionalUserProject, #userProject: userProject, #$fields: $fields, }, ), )), - returnValueForMissingStub: _i6.Future<_i4.Object>.value(_FakeObject_11( + returnValueForMissingStub: _i6.Future<_i4.Object>.value(_FakeObject_15( this, Invocation.method( #compose, @@ -618,7 +728,6 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #ifGenerationMatch: ifGenerationMatch, #ifMetagenerationMatch: ifMetagenerationMatch, #kmsKeyName: kmsKeyName, - #provisionalUserProject: provisionalUserProject, #userProject: userProject, #$fields: $fields, }, @@ -644,7 +753,6 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { String? ifSourceMetagenerationMatch, String? ifSourceMetagenerationNotMatch, String? projection, - String? provisionalUserProject, String? sourceGeneration, String? userProject, String? $fields, @@ -671,13 +779,12 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #ifSourceMetagenerationMatch: ifSourceMetagenerationMatch, #ifSourceMetagenerationNotMatch: ifSourceMetagenerationNotMatch, #projection: projection, - #provisionalUserProject: provisionalUserProject, #sourceGeneration: sourceGeneration, #userProject: userProject, #$fields: $fields, }, ), - returnValue: _i6.Future<_i4.Object>.value(_FakeObject_11( + returnValue: _i6.Future<_i4.Object>.value(_FakeObject_15( this, Invocation.method( #copy, @@ -700,14 +807,13 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #ifSourceMetagenerationMatch: ifSourceMetagenerationMatch, #ifSourceMetagenerationNotMatch: ifSourceMetagenerationNotMatch, #projection: projection, - #provisionalUserProject: provisionalUserProject, #sourceGeneration: sourceGeneration, #userProject: userProject, #$fields: $fields, }, ), )), - returnValueForMissingStub: _i6.Future<_i4.Object>.value(_FakeObject_11( + returnValueForMissingStub: _i6.Future<_i4.Object>.value(_FakeObject_15( this, Invocation.method( #copy, @@ -730,7 +836,6 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #ifSourceMetagenerationMatch: ifSourceMetagenerationMatch, #ifSourceMetagenerationNotMatch: ifSourceMetagenerationNotMatch, #projection: projection, - #provisionalUserProject: provisionalUserProject, #sourceGeneration: sourceGeneration, #userProject: userProject, #$fields: $fields, @@ -748,7 +853,6 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { String? ifGenerationNotMatch, String? ifMetagenerationMatch, String? ifMetagenerationNotMatch, - String? provisionalUserProject, String? userProject, String? $fields, }) => @@ -765,7 +869,6 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #ifGenerationNotMatch: ifGenerationNotMatch, #ifMetagenerationMatch: ifMetagenerationMatch, #ifMetagenerationNotMatch: ifMetagenerationNotMatch, - #provisionalUserProject: provisionalUserProject, #userProject: userProject, #$fields: $fields, }, @@ -784,7 +887,7 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { String? ifMetagenerationMatch, String? ifMetagenerationNotMatch, String? projection, - String? provisionalUserProject, + bool? softDeleted, String? userProject, String? $fields, _i10.DownloadOptions? downloadOptions = _i10.DownloadOptions.metadata, @@ -803,13 +906,13 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #ifMetagenerationMatch: ifMetagenerationMatch, #ifMetagenerationNotMatch: ifMetagenerationNotMatch, #projection: projection, - #provisionalUserProject: provisionalUserProject, + #softDeleted: softDeleted, #userProject: userProject, #$fields: $fields, #downloadOptions: downloadOptions, }, ), - returnValue: _i6.Future.value(_FakeObject_12( + returnValue: _i6.Future.value(_FakeObject_16( this, Invocation.method( #get, @@ -824,14 +927,14 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #ifMetagenerationMatch: ifMetagenerationMatch, #ifMetagenerationNotMatch: ifMetagenerationNotMatch, #projection: projection, - #provisionalUserProject: provisionalUserProject, + #softDeleted: softDeleted, #userProject: userProject, #$fields: $fields, #downloadOptions: downloadOptions, }, ), )), - returnValueForMissingStub: _i6.Future.value(_FakeObject_12( + returnValueForMissingStub: _i6.Future.value(_FakeObject_16( this, Invocation.method( #get, @@ -846,7 +949,7 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #ifMetagenerationMatch: ifMetagenerationMatch, #ifMetagenerationNotMatch: ifMetagenerationNotMatch, #projection: projection, - #provisionalUserProject: provisionalUserProject, + #softDeleted: softDeleted, #userProject: userProject, #$fields: $fields, #downloadOptions: downloadOptions, @@ -860,7 +963,6 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { String? bucket, String? object, { String? generation, - String? provisionalUserProject, String? userProject, String? $fields, }) => @@ -873,12 +975,11 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { ], { #generation: generation, - #provisionalUserProject: provisionalUserProject, #userProject: userProject, #$fields: $fields, }, ), - returnValue: _i6.Future<_i4.Policy>.value(_FakePolicy_13( + returnValue: _i6.Future<_i4.Policy>.value(_FakePolicy_17( this, Invocation.method( #getIamPolicy, @@ -888,13 +989,12 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { ], { #generation: generation, - #provisionalUserProject: provisionalUserProject, #userProject: userProject, #$fields: $fields, }, ), )), - returnValueForMissingStub: _i6.Future<_i4.Policy>.value(_FakePolicy_13( + returnValueForMissingStub: _i6.Future<_i4.Policy>.value(_FakePolicy_17( this, Invocation.method( #getIamPolicy, @@ -904,7 +1004,6 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { ], { #generation: generation, - #provisionalUserProject: provisionalUserProject, #userProject: userProject, #$fields: $fields, }, @@ -925,7 +1024,6 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { String? name, String? predefinedAcl, String? projection, - String? provisionalUserProject, String? userProject, String? $fields, _i10.UploadOptions? uploadOptions = _i10.UploadOptions.defaultOptions, @@ -948,14 +1046,13 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #name: name, #predefinedAcl: predefinedAcl, #projection: projection, - #provisionalUserProject: provisionalUserProject, #userProject: userProject, #$fields: $fields, #uploadOptions: uploadOptions, #uploadMedia: uploadMedia, }, ), - returnValue: _i6.Future<_i4.Object>.value(_FakeObject_11( + returnValue: _i6.Future<_i4.Object>.value(_FakeObject_15( this, Invocation.method( #insert, @@ -973,7 +1070,6 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #name: name, #predefinedAcl: predefinedAcl, #projection: projection, - #provisionalUserProject: provisionalUserProject, #userProject: userProject, #$fields: $fields, #uploadOptions: uploadOptions, @@ -981,7 +1077,7 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { }, ), )), - returnValueForMissingStub: _i6.Future<_i4.Object>.value(_FakeObject_11( + returnValueForMissingStub: _i6.Future<_i4.Object>.value(_FakeObject_15( this, Invocation.method( #insert, @@ -999,7 +1095,6 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #name: name, #predefinedAcl: predefinedAcl, #projection: projection, - #provisionalUserProject: provisionalUserProject, #userProject: userProject, #$fields: $fields, #uploadOptions: uploadOptions, @@ -1014,12 +1109,14 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { String? bucket, { String? delimiter, String? endOffset, + bool? includeFoldersAsPrefixes, bool? includeTrailingDelimiter, + String? matchGlob, int? maxResults, String? pageToken, String? prefix, String? projection, - String? provisionalUserProject, + bool? softDeleted, String? startOffset, String? userProject, bool? versions, @@ -1032,19 +1129,21 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { { #delimiter: delimiter, #endOffset: endOffset, + #includeFoldersAsPrefixes: includeFoldersAsPrefixes, #includeTrailingDelimiter: includeTrailingDelimiter, + #matchGlob: matchGlob, #maxResults: maxResults, #pageToken: pageToken, #prefix: prefix, #projection: projection, - #provisionalUserProject: provisionalUserProject, + #softDeleted: softDeleted, #startOffset: startOffset, #userProject: userProject, #versions: versions, #$fields: $fields, }, ), - returnValue: _i6.Future<_i4.Objects>.value(_FakeObjects_14( + returnValue: _i6.Future<_i4.Objects>.value(_FakeObjects_18( this, Invocation.method( #list, @@ -1052,12 +1151,14 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { { #delimiter: delimiter, #endOffset: endOffset, + #includeFoldersAsPrefixes: includeFoldersAsPrefixes, #includeTrailingDelimiter: includeTrailingDelimiter, + #matchGlob: matchGlob, #maxResults: maxResults, #pageToken: pageToken, #prefix: prefix, #projection: projection, - #provisionalUserProject: provisionalUserProject, + #softDeleted: softDeleted, #startOffset: startOffset, #userProject: userProject, #versions: versions, @@ -1066,7 +1167,7 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { ), )), returnValueForMissingStub: - _i6.Future<_i4.Objects>.value(_FakeObjects_14( + _i6.Future<_i4.Objects>.value(_FakeObjects_18( this, Invocation.method( #list, @@ -1074,12 +1175,14 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { { #delimiter: delimiter, #endOffset: endOffset, + #includeFoldersAsPrefixes: includeFoldersAsPrefixes, #includeTrailingDelimiter: includeTrailingDelimiter, + #matchGlob: matchGlob, #maxResults: maxResults, #pageToken: pageToken, #prefix: prefix, #projection: projection, - #provisionalUserProject: provisionalUserProject, + #softDeleted: softDeleted, #startOffset: startOffset, #userProject: userProject, #versions: versions, @@ -1099,9 +1202,9 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { String? ifGenerationNotMatch, String? ifMetagenerationMatch, String? ifMetagenerationNotMatch, + bool? overrideUnlockedRetention, String? predefinedAcl, String? projection, - String? provisionalUserProject, String? userProject, String? $fields, }) => @@ -1119,14 +1222,14 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #ifGenerationNotMatch: ifGenerationNotMatch, #ifMetagenerationMatch: ifMetagenerationMatch, #ifMetagenerationNotMatch: ifMetagenerationNotMatch, + #overrideUnlockedRetention: overrideUnlockedRetention, #predefinedAcl: predefinedAcl, #projection: projection, - #provisionalUserProject: provisionalUserProject, #userProject: userProject, #$fields: $fields, }, ), - returnValue: _i6.Future<_i4.Object>.value(_FakeObject_11( + returnValue: _i6.Future<_i4.Object>.value(_FakeObject_15( this, Invocation.method( #patch, @@ -1141,15 +1244,15 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #ifGenerationNotMatch: ifGenerationNotMatch, #ifMetagenerationMatch: ifMetagenerationMatch, #ifMetagenerationNotMatch: ifMetagenerationNotMatch, + #overrideUnlockedRetention: overrideUnlockedRetention, #predefinedAcl: predefinedAcl, #projection: projection, - #provisionalUserProject: provisionalUserProject, #userProject: userProject, #$fields: $fields, }, ), )), - returnValueForMissingStub: _i6.Future<_i4.Object>.value(_FakeObject_11( + returnValueForMissingStub: _i6.Future<_i4.Object>.value(_FakeObject_15( this, Invocation.method( #patch, @@ -1164,9 +1267,90 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #ifGenerationNotMatch: ifGenerationNotMatch, #ifMetagenerationMatch: ifMetagenerationMatch, #ifMetagenerationNotMatch: ifMetagenerationNotMatch, + #overrideUnlockedRetention: overrideUnlockedRetention, #predefinedAcl: predefinedAcl, #projection: projection, - #provisionalUserProject: provisionalUserProject, + #userProject: userProject, + #$fields: $fields, + }, + ), + )), + ) as _i6.Future<_i4.Object>); + + @override + _i6.Future<_i4.Object> restore( + _i4.Object? request, + String? bucket, + String? object, + String? generation, { + bool? copySourceAcl, + String? ifGenerationMatch, + String? ifGenerationNotMatch, + String? ifMetagenerationMatch, + String? ifMetagenerationNotMatch, + String? projection, + String? userProject, + String? $fields, + }) => + (super.noSuchMethod( + Invocation.method( + #restore, + [ + request, + bucket, + object, + generation, + ], + { + #copySourceAcl: copySourceAcl, + #ifGenerationMatch: ifGenerationMatch, + #ifGenerationNotMatch: ifGenerationNotMatch, + #ifMetagenerationMatch: ifMetagenerationMatch, + #ifMetagenerationNotMatch: ifMetagenerationNotMatch, + #projection: projection, + #userProject: userProject, + #$fields: $fields, + }, + ), + returnValue: _i6.Future<_i4.Object>.value(_FakeObject_15( + this, + Invocation.method( + #restore, + [ + request, + bucket, + object, + generation, + ], + { + #copySourceAcl: copySourceAcl, + #ifGenerationMatch: ifGenerationMatch, + #ifGenerationNotMatch: ifGenerationNotMatch, + #ifMetagenerationMatch: ifMetagenerationMatch, + #ifMetagenerationNotMatch: ifMetagenerationNotMatch, + #projection: projection, + #userProject: userProject, + #$fields: $fields, + }, + ), + )), + returnValueForMissingStub: _i6.Future<_i4.Object>.value(_FakeObject_15( + this, + Invocation.method( + #restore, + [ + request, + bucket, + object, + generation, + ], + { + #copySourceAcl: copySourceAcl, + #ifGenerationMatch: ifGenerationMatch, + #ifGenerationNotMatch: ifGenerationNotMatch, + #ifMetagenerationMatch: ifMetagenerationMatch, + #ifMetagenerationNotMatch: ifMetagenerationNotMatch, + #projection: projection, #userProject: userProject, #$fields: $fields, }, @@ -1193,7 +1377,6 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { String? ifSourceMetagenerationNotMatch, String? maxBytesRewrittenPerCall, String? projection, - String? provisionalUserProject, String? rewriteToken, String? sourceGeneration, String? userProject, @@ -1222,7 +1405,6 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #ifSourceMetagenerationNotMatch: ifSourceMetagenerationNotMatch, #maxBytesRewrittenPerCall: maxBytesRewrittenPerCall, #projection: projection, - #provisionalUserProject: provisionalUserProject, #rewriteToken: rewriteToken, #sourceGeneration: sourceGeneration, #userProject: userProject, @@ -1230,7 +1412,7 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { }, ), returnValue: - _i6.Future<_i4.RewriteResponse>.value(_FakeRewriteResponse_15( + _i6.Future<_i4.RewriteResponse>.value(_FakeRewriteResponse_19( this, Invocation.method( #rewrite, @@ -1254,7 +1436,6 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #ifSourceMetagenerationNotMatch: ifSourceMetagenerationNotMatch, #maxBytesRewrittenPerCall: maxBytesRewrittenPerCall, #projection: projection, - #provisionalUserProject: provisionalUserProject, #rewriteToken: rewriteToken, #sourceGeneration: sourceGeneration, #userProject: userProject, @@ -1263,7 +1444,7 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { ), )), returnValueForMissingStub: - _i6.Future<_i4.RewriteResponse>.value(_FakeRewriteResponse_15( + _i6.Future<_i4.RewriteResponse>.value(_FakeRewriteResponse_19( this, Invocation.method( #rewrite, @@ -1287,7 +1468,6 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #ifSourceMetagenerationNotMatch: ifSourceMetagenerationNotMatch, #maxBytesRewrittenPerCall: maxBytesRewrittenPerCall, #projection: projection, - #provisionalUserProject: provisionalUserProject, #rewriteToken: rewriteToken, #sourceGeneration: sourceGeneration, #userProject: userProject, @@ -1303,7 +1483,6 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { String? bucket, String? object, { String? generation, - String? provisionalUserProject, String? userProject, String? $fields, }) => @@ -1317,12 +1496,11 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { ], { #generation: generation, - #provisionalUserProject: provisionalUserProject, #userProject: userProject, #$fields: $fields, }, ), - returnValue: _i6.Future<_i4.Policy>.value(_FakePolicy_13( + returnValue: _i6.Future<_i4.Policy>.value(_FakePolicy_17( this, Invocation.method( #setIamPolicy, @@ -1333,13 +1511,12 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { ], { #generation: generation, - #provisionalUserProject: provisionalUserProject, #userProject: userProject, #$fields: $fields, }, ), )), - returnValueForMissingStub: _i6.Future<_i4.Policy>.value(_FakePolicy_13( + returnValueForMissingStub: _i6.Future<_i4.Policy>.value(_FakePolicy_17( this, Invocation.method( #setIamPolicy, @@ -1350,7 +1527,6 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { ], { #generation: generation, - #provisionalUserProject: provisionalUserProject, #userProject: userProject, #$fields: $fields, }, @@ -1364,7 +1540,6 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { String? object, List? permissions, { String? generation, - String? provisionalUserProject, String? userProject, String? $fields, }) => @@ -1378,13 +1553,12 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { ], { #generation: generation, - #provisionalUserProject: provisionalUserProject, #userProject: userProject, #$fields: $fields, }, ), returnValue: _i6.Future<_i4.TestIamPermissionsResponse>.value( - _FakeTestIamPermissionsResponse_16( + _FakeTestIamPermissionsResponse_20( this, Invocation.method( #testIamPermissions, @@ -1395,7 +1569,6 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { ], { #generation: generation, - #provisionalUserProject: provisionalUserProject, #userProject: userProject, #$fields: $fields, }, @@ -1403,7 +1576,7 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { )), returnValueForMissingStub: _i6.Future<_i4.TestIamPermissionsResponse>.value( - _FakeTestIamPermissionsResponse_16( + _FakeTestIamPermissionsResponse_20( this, Invocation.method( #testIamPermissions, @@ -1414,7 +1587,6 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { ], { #generation: generation, - #provisionalUserProject: provisionalUserProject, #userProject: userProject, #$fields: $fields, }, @@ -1432,9 +1604,9 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { String? ifGenerationNotMatch, String? ifMetagenerationMatch, String? ifMetagenerationNotMatch, + bool? overrideUnlockedRetention, String? predefinedAcl, String? projection, - String? provisionalUserProject, String? userProject, String? $fields, }) => @@ -1452,14 +1624,14 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #ifGenerationNotMatch: ifGenerationNotMatch, #ifMetagenerationMatch: ifMetagenerationMatch, #ifMetagenerationNotMatch: ifMetagenerationNotMatch, + #overrideUnlockedRetention: overrideUnlockedRetention, #predefinedAcl: predefinedAcl, #projection: projection, - #provisionalUserProject: provisionalUserProject, #userProject: userProject, #$fields: $fields, }, ), - returnValue: _i6.Future<_i4.Object>.value(_FakeObject_11( + returnValue: _i6.Future<_i4.Object>.value(_FakeObject_15( this, Invocation.method( #update, @@ -1474,15 +1646,15 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #ifGenerationNotMatch: ifGenerationNotMatch, #ifMetagenerationMatch: ifMetagenerationMatch, #ifMetagenerationNotMatch: ifMetagenerationNotMatch, + #overrideUnlockedRetention: overrideUnlockedRetention, #predefinedAcl: predefinedAcl, #projection: projection, - #provisionalUserProject: provisionalUserProject, #userProject: userProject, #$fields: $fields, }, ), )), - returnValueForMissingStub: _i6.Future<_i4.Object>.value(_FakeObject_11( + returnValueForMissingStub: _i6.Future<_i4.Object>.value(_FakeObject_15( this, Invocation.method( #update, @@ -1497,9 +1669,9 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #ifGenerationNotMatch: ifGenerationNotMatch, #ifMetagenerationMatch: ifMetagenerationMatch, #ifMetagenerationNotMatch: ifMetagenerationNotMatch, + #overrideUnlockedRetention: overrideUnlockedRetention, #predefinedAcl: predefinedAcl, #projection: projection, - #provisionalUserProject: provisionalUserProject, #userProject: userProject, #$fields: $fields, }, @@ -1518,7 +1690,6 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { String? pageToken, String? prefix, String? projection, - String? provisionalUserProject, String? startOffset, String? userProject, bool? versions, @@ -1539,14 +1710,13 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #pageToken: pageToken, #prefix: prefix, #projection: projection, - #provisionalUserProject: provisionalUserProject, #startOffset: startOffset, #userProject: userProject, #versions: versions, #$fields: $fields, }, ), - returnValue: _i6.Future<_i4.Channel>.value(_FakeChannel_17( + returnValue: _i6.Future<_i4.Channel>.value(_FakeChannel_21( this, Invocation.method( #watchAll, @@ -1562,7 +1732,6 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #pageToken: pageToken, #prefix: prefix, #projection: projection, - #provisionalUserProject: provisionalUserProject, #startOffset: startOffset, #userProject: userProject, #versions: versions, @@ -1571,7 +1740,7 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { ), )), returnValueForMissingStub: - _i6.Future<_i4.Channel>.value(_FakeChannel_17( + _i6.Future<_i4.Channel>.value(_FakeChannel_21( this, Invocation.method( #watchAll, @@ -1587,7 +1756,6 @@ class MockObjectsResource extends _i1.Mock implements _i4.ObjectsResource { #pageToken: pageToken, #prefix: prefix, #projection: projection, - #provisionalUserProject: provisionalUserProject, #startOffset: startOffset, #userProject: userProject, #versions: versions, diff --git a/packages/pointer_interceptor/pointer_interceptor_web/CHANGELOG.md b/packages/pointer_interceptor/pointer_interceptor_web/CHANGELOG.md index 28955127a78e..58a970e78f7d 100644 --- a/packages/pointer_interceptor/pointer_interceptor_web/CHANGELOG.md +++ b/packages/pointer_interceptor/pointer_interceptor_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.10.2 + +* Updates web code to package `web: ^0.5.0`. +* Updates SDK version to Dart `^3.3.0`. Flutter `^3.19.0`. + ## 0.10.1+2 * Fixes "width and height missing" warning on web. diff --git a/packages/pointer_interceptor/pointer_interceptor_web/example/pubspec.yaml b/packages/pointer_interceptor/pointer_interceptor_web/example/pubspec.yaml index 2132641ca791..06625c07fbb7 100644 --- a/packages/pointer_interceptor/pointer_interceptor_web/example/pubspec.yaml +++ b/packages/pointer_interceptor/pointer_interceptor_web/example/pubspec.yaml @@ -3,8 +3,8 @@ description: "Demonstrates how to use the pointer_interceptor_web plugin." publish_to: 'none' environment: - sdk: ^3.2.0 - flutter: '>=3.16.0' + sdk: ^3.3.0 + flutter: ">=3.19.0" dependencies: cupertino_icons: ^1.0.2 @@ -13,7 +13,7 @@ dependencies: pointer_interceptor_platform_interface: ^0.10.0 pointer_interceptor_web: path: ../../pointer_interceptor_web - web: '>=0.3.0 <0.5.0' + web: ^0.5.0 dev_dependencies: flutter_test: diff --git a/packages/pointer_interceptor/pointer_interceptor_web/pubspec.yaml b/packages/pointer_interceptor/pointer_interceptor_web/pubspec.yaml index ff1f0747d9d5..83e41de9e23e 100644 --- a/packages/pointer_interceptor/pointer_interceptor_web/pubspec.yaml +++ b/packages/pointer_interceptor/pointer_interceptor_web/pubspec.yaml @@ -2,11 +2,11 @@ name: pointer_interceptor_web description: Web implementation of the pointer_interceptor plugin. repository: https://github.com/flutter/packages/tree/main/packages/pointer_interceptor/pointer_interceptor_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3Apointer_interceptor -version: 0.10.1+2 +version: 0.10.2 environment: - sdk: ^3.2.0 - flutter: '>=3.16.0' + sdk: ^3.3.0 + flutter: ">=3.19.0" flutter: plugin: @@ -23,7 +23,7 @@ dependencies: sdk: flutter plugin_platform_interface: ^2.1.7 pointer_interceptor_platform_interface: ^0.10.0 - web: '>=0.3.0 <0.5.0' + web: ^0.5.0 dev_dependencies: flutter_test: diff --git a/packages/shared_preferences/shared_preferences_web/CHANGELOG.md b/packages/shared_preferences/shared_preferences_web/CHANGELOG.md index 5f5d8c246cd6..5cb8dc3b6120 100644 --- a/packages/shared_preferences/shared_preferences_web/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.3.0 + +* Updates web code to package `web: ^0.5.0`. +* Updates SDK version to Dart `^3.3.0`. Flutter `^3.19.0`. + ## 2.2.2 * Updates minimum supported SDK version to Dart 3.2. diff --git a/packages/shared_preferences/shared_preferences_web/example/integration_test/shared_preferences_web_test.dart b/packages/shared_preferences/shared_preferences_web/example/integration_test/shared_preferences_web_test.dart index 9f0faae41367..0238ba578a45 100644 --- a/packages/shared_preferences/shared_preferences_web/example/integration_test/shared_preferences_web_test.dart +++ b/packages/shared_preferences/shared_preferences_web/example/integration_test/shared_preferences_web_test.dart @@ -9,8 +9,7 @@ import 'package:shared_preferences_platform_interface/shared_preferences_platfor import 'package:shared_preferences_platform_interface/types.dart'; import 'package:shared_preferences_web/shared_preferences_web.dart'; import 'package:shared_preferences_web/src/keys_extension.dart'; - -import 'package:web/helpers.dart' as html; +import 'package:web/web.dart' as html; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); diff --git a/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml index bf6ca2a883df..44672c4e6425 100644 --- a/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml @@ -11,11 +11,10 @@ dependencies: shared_preferences_platform_interface: ^2.3.0 shared_preferences_web: path: ../ - web: '>=0.3.0 <0.5.0' + web: ^0.5.0 dev_dependencies: flutter_test: sdk: flutter integration_test: sdk: flutter - js: ^0.6.3 diff --git a/packages/shared_preferences/shared_preferences_web/pubspec.yaml b/packages/shared_preferences/shared_preferences_web/pubspec.yaml index ceb1dfebcf7a..1278a54a3e51 100644 --- a/packages/shared_preferences/shared_preferences_web/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_web/pubspec.yaml @@ -2,11 +2,11 @@ name: shared_preferences_web description: Web platform implementation of shared_preferences repository: https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.2.2 +version: 2.3.0 environment: - sdk: ">=3.2.0 <4.0.0" - flutter: ">=3.16.0" + sdk: ^3.3.0 + flutter: ">=3.19.0" flutter: plugin: @@ -22,7 +22,7 @@ dependencies: flutter_web_plugins: sdk: flutter shared_preferences_platform_interface: ^2.3.0 - web: '>=0.3.0 <0.5.0' + web: ^0.5.0 dev_dependencies: flutter_test: diff --git a/packages/url_launcher/url_launcher_web/CHANGELOG.md b/packages/url_launcher/url_launcher_web/CHANGELOG.md index 641b48d27cf2..5e3d6d42b35e 100644 --- a/packages/url_launcher/url_launcher_web/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.3.0 + +* Updates web code to package `web: ^0.5.0`. +* Updates SDK version to Dart `^3.3.0`. Flutter `^3.19.0`. + ## 2.2.3 * Fixes new lint warnings. diff --git a/packages/url_launcher/url_launcher_web/example/integration_test/link_widget_test.dart b/packages/url_launcher/url_launcher_web/example/integration_test/link_widget_test.dart index 528cff32c0e5..9cd76d03c80c 100644 --- a/packages/url_launcher/url_launcher_web/example/integration_test/link_widget_test.dart +++ b/packages/url_launcher/url_launcher_web/example/integration_test/link_widget_test.dart @@ -2,7 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:js_util'; +import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; import 'dart:ui_web' as ui_web; import 'package:flutter/widgets.dart'; @@ -10,7 +11,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:url_launcher_platform_interface/link.dart'; import 'package:url_launcher_web/src/link.dart'; -import 'package:web/helpers.dart' as html; +import 'package:web/web.dart' as html; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -177,7 +178,7 @@ html.Element _findSingleAnchor() { html.NodeList anchors = html.document.querySelectorAll('a'); for (int i = 0; i < anchors.length; i++) { final html.Element anchor = anchors.item(i)! as html.Element; - if (hasProperty(anchor, linkViewIdProperty)) { + if (anchor.hasProperty(linkViewIdProperty.toJS).toDart) { foundAnchors.add(anchor); } } @@ -189,7 +190,7 @@ html.Element _findSingleAnchor() { anchors = shadowRoot.querySelectorAll('a'); for (int i = 0; i < anchors.length; i++) { final html.Element anchor = anchors.item(i)! as html.Element; - if (hasProperty(anchor, linkViewIdProperty)) { + if (anchor.hasProperty(linkViewIdProperty.toJS).toDart) { foundAnchors.add(anchor); } } diff --git a/packages/url_launcher/url_launcher_web/example/integration_test/url_launcher_web_test.dart b/packages/url_launcher/url_launcher_web/example/integration_test/url_launcher_web_test.dart index ec01d28163aa..b75903873467 100644 --- a/packages/url_launcher/url_launcher_web/example/integration_test/url_launcher_web_test.dart +++ b/packages/url_launcher/url_launcher_web/example/integration_test/url_launcher_web_test.dart @@ -3,14 +3,13 @@ // found in the LICENSE file. import 'dart:js_interop'; -import 'dart:js_util'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:mockito/mockito.dart' show Mock, any, verify, when; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; import 'package:url_launcher_web/url_launcher_web.dart'; -import 'package:web/helpers.dart' as html; +import 'package:web/web.dart' as html; abstract class MyWindow { html.Window? open(Object? a, Object? b, Object? c); @@ -33,6 +32,7 @@ void main() { group('UrlLauncherPlugin', () { late MockWindow mockWindow; late MockNavigator mockNavigator; + late html.Window jsMockWindow; late UrlLauncherPlugin plugin; @@ -40,10 +40,9 @@ void main() { mockWindow = MockWindow(); mockNavigator = MockNavigator(); - final html.Window jsMockWindow = - createDartExport(mockWindow) as html.Window; + jsMockWindow = createJSInteropWrapper(mockWindow) as html.Window; final html.Navigator jsMockNavigator = - createDartExport(mockNavigator) as html.Navigator; + createJSInteropWrapper(mockNavigator) as html.Navigator; when(mockWindow.navigator).thenReturn(jsMockNavigator); @@ -53,7 +52,7 @@ void main() { when(mockNavigator.userAgent).thenReturn( 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'); - plugin = UrlLauncherPlugin(debugWindow: mockWindow as html.Window); + plugin = UrlLauncherPlugin(debugWindow: jsMockWindow); }); group('canLaunch', () { @@ -185,7 +184,7 @@ void main() { when(mockNavigator.userAgent).thenReturn( 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5.1 Safari/605.1.15'); // Recreate the plugin, so it grabs the overrides from this group - plugin = UrlLauncherPlugin(debugWindow: mockWindow as html.Window); + plugin = UrlLauncherPlugin(debugWindow: jsMockWindow); }); testWidgets('http urls should be launched in a new window', diff --git a/packages/url_launcher/url_launcher_web/example/pubspec.yaml b/packages/url_launcher/url_launcher_web/example/pubspec.yaml index eca281ef0a2f..c87511dd84b8 100644 --- a/packages/url_launcher/url_launcher_web/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/example/pubspec.yaml @@ -2,8 +2,8 @@ name: regular_integration_tests publish_to: none environment: - sdk: ^3.2.0 - flutter: ">=3.16.0" + sdk: ^3.3.0 + flutter: ">=3.19.0" dependencies: flutter: @@ -18,4 +18,4 @@ dev_dependencies: url_launcher_platform_interface: ^2.2.0 url_launcher_web: path: ../ - web: '>=0.3.0 <0.5.0' + web: ^0.5.0 diff --git a/packages/url_launcher/url_launcher_web/lib/src/link.dart b/packages/url_launcher/url_launcher_web/lib/src/link.dart index b2217fafb805..558f3a135a27 100644 --- a/packages/url_launcher/url_launcher_web/lib/src/link.dart +++ b/packages/url_launcher/url_launcher_web/lib/src/link.dart @@ -3,7 +3,8 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:js_util'; +import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; import 'dart:ui_web' as ui_web; import 'package:flutter/foundation.dart'; @@ -12,7 +13,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:url_launcher_platform_interface/link.dart'; -import 'package:web/helpers.dart' as html; +import 'package:web/web.dart' as html; /// The unique identifier for the view type to be used for link platform views. const String linkViewType = '__url_launcher::link'; @@ -172,7 +173,7 @@ class LinkViewController extends PlatformViewController { Future _initialize() async { _element = html.document.createElement('a') as html.HTMLElement; - setProperty(_element, linkViewIdProperty, viewId); + _element[linkViewIdProperty] = viewId.toJS; _element.style ..opacity = '0' ..display = 'block' @@ -283,11 +284,7 @@ class LinkViewController extends PlatformViewController { int? getViewIdFromTarget(html.Event event) { final html.Element? linkElement = getLinkElementFromTarget(event); if (linkElement != null) { - // TODO(stuartmorgan): Remove this ignore (and change to getProperty) - // once the templated version is available on stable. On master (2.8) this - // is already not necessary. - // ignore: return_of_invalid_type - return getProperty(linkElement, linkViewIdProperty); + return linkElement.getProperty(linkViewIdProperty.toJS).toDartInt; } return null; } @@ -316,5 +313,5 @@ html.Element? getLinkElementFromTarget(html.Event event) { bool isLinkElement(html.Element? element) { return element != null && element.tagName == 'A' && - hasProperty(element, linkViewIdProperty); + element.hasProperty(linkViewIdProperty.toJS).toDart; } diff --git a/packages/url_launcher/url_launcher_web/pubspec.yaml b/packages/url_launcher/url_launcher_web/pubspec.yaml index 6c89bba58091..35154b5a6d2b 100644 --- a/packages/url_launcher/url_launcher_web/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/pubspec.yaml @@ -2,11 +2,11 @@ name: url_launcher_web description: Web platform implementation of url_launcher repository: https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 2.2.3 +version: 2.3.0 environment: - sdk: ^3.2.0 - flutter: ">=3.16.0" + sdk: ^3.3.0 + flutter: ">=3.19.0" flutter: plugin: @@ -22,7 +22,7 @@ dependencies: flutter_web_plugins: sdk: flutter url_launcher_platform_interface: ^2.2.0 - web: '>=0.3.0 <0.5.0' + web: ^0.5.0 dev_dependencies: flutter_test: diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index 4ce1c96de5d8..8e9e9f21f736 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,6 +1,6 @@ -## NEXT +## 2.2.0 -* Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. +* Updates SDK version to Dart `^3.3.0`. Flutter `^3.19.0`. ## 2.1.3 diff --git a/packages/video_player/video_player_web/example/integration_test/utils.dart b/packages/video_player/video_player_web/example/integration_test/utils.dart index 6be5b5abcc75..75af525d4103 100644 --- a/packages/video_player/video_player_web/example/integration_test/utils.dart +++ b/packages/video_player/video_player_web/example/integration_test/utils.dart @@ -2,12 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@JS() -library integration_test_utils; - -import 'dart:html'; - -import 'package:js/js.dart'; +import 'dart:js_interop'; +import 'dart:js_interop_unsafe'; +import 'package:web/web.dart' as web; // Returns the URL to load an asset from this example app as a network source. // @@ -22,35 +19,40 @@ String getUrlForAssetAsNetworkSource(String assetKey) { '?raw=true'; } -@JS() -@anonymous -class _Descriptor { +extension type Descriptor._(JSObject _) implements JSObject { // May also contain "configurable" and "enumerable" bools. // See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#description - external factory _Descriptor({ + external factory Descriptor({ // bool configurable, // bool enumerable, - bool writable, - Object value, + JSBoolean writable, + JSAny value, }); } -@JS('Object.defineProperty') -external void _defineProperty( +void _defineProperty( Object object, String property, - _Descriptor description, -); + Descriptor description, +) { + (globalContext['Object'] as JSObject?)?.callMethod( + 'defineProperty'.toJS, + object as JSObject, + property.toJS, + description, + ); +} /// Forces a VideoElement to report "Infinity" duration. /// /// Uses JS Object.defineProperty to set the value of a readonly property. -void setInfinityDuration(VideoElement element) { +void setInfinityDuration(Object videoElement) { + assert(videoElement is web.HTMLVideoElement); _defineProperty( - element, + videoElement, 'duration', - _Descriptor( - writable: true, - value: double.infinity, + Descriptor( + writable: true.toJS, + value: double.infinity.toJS, )); } diff --git a/packages/video_player/video_player_web/example/pubspec.yaml b/packages/video_player/video_player_web/example/pubspec.yaml index 866d089f8687..bd2f3ba48788 100644 --- a/packages/video_player/video_player_web/example/pubspec.yaml +++ b/packages/video_player/video_player_web/example/pubspec.yaml @@ -2,16 +2,16 @@ name: video_player_for_web_integration_tests publish_to: none environment: - sdk: ^3.1.0 - flutter: ">=3.13.0" + sdk: ^3.3.0 + flutter: ">=3.19.0" dependencies: flutter: sdk: flutter - js: ^0.6.0 - video_player_platform_interface: ">=6.1.0 <7.0.0" + video_player_platform_interface: ^6.1.0 video_player_web: path: ../ + web: ^0.5.0 dev_dependencies: flutter_test: diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index 81ad0e6d1bf7..0cb69f88b743 100644 --- a/packages/video_player/video_player_web/pubspec.yaml +++ b/packages/video_player/video_player_web/pubspec.yaml @@ -2,11 +2,11 @@ name: video_player_web description: Web platform implementation of video_player. repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.1.3 +version: 2.2.0 environment: - sdk: ">=3.1.0 <4.0.0" - flutter: ">=3.13.0" + sdk: ^3.3.0 + flutter: ">=3.19.0" flutter: plugin: diff --git a/packages/web_benchmarks/CHANGELOG.md b/packages/web_benchmarks/CHANGELOG.md index 98e9f675b1fd..c4b3cf125ad2 100644 --- a/packages/web_benchmarks/CHANGELOG.md +++ b/packages/web_benchmarks/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 1.2.0 -* Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. +* Updates to web code to package `web: ^0.5.0`. +* Updates SDK version to Dart `^3.3.0`. Flutter `^3.19.0`. ## 1.1.1 diff --git a/packages/web_benchmarks/lib/client.dart b/packages/web_benchmarks/lib/client.dart index baaab84fe504..d1c52d775fb8 100644 --- a/packages/web_benchmarks/lib/client.dart +++ b/packages/web_benchmarks/lib/client.dart @@ -7,7 +7,7 @@ import 'dart:convert' show json; import 'dart:js_interop'; import 'dart:math' as math; -import 'package:web/helpers.dart'; +import 'package:web/web.dart'; import 'src/common.dart'; import 'src/recorder.dart'; @@ -157,9 +157,7 @@ void _printResultsToScreen(Profile profile) { profile.scoreData.forEach((String scoreKey, Timeseries timeseries) { body.appendHtml('

$scoreKey

'); body.appendHtml('
${timeseries.computeStats()}
'); - // TODO(kevmoo): remove `NodeGlue` cast when we no longer need to support - // pkg:web 0.3.0 - NodeGlue(body).append(TimeseriesVisualization(timeseries).render()); + body.appendChild(TimeseriesVisualization(timeseries).render()); }); } @@ -168,7 +166,7 @@ class TimeseriesVisualization { /// Creates a visualization for a [Timeseries]. TimeseriesVisualization(this._timeseries) { _stats = _timeseries.computeStats(); - _canvas = CanvasElement(); + _canvas = HTMLCanvasElement(); _screenWidth = window.screen.width; _canvas.width = _screenWidth; _canvas.height = (_kCanvasHeight * window.devicePixelRatio).round(); @@ -192,7 +190,7 @@ class TimeseriesVisualization { final Timeseries _timeseries; late TimeseriesStats _stats; - late CanvasElement _canvas; + late HTMLCanvasElement _canvas; late CanvasRenderingContext2D _ctx; late int _screenWidth; @@ -215,7 +213,7 @@ class TimeseriesVisualization { } /// Renders the timeseries into a `` and returns the canvas element. - CanvasElement render() { + HTMLCanvasElement render() { _ctx.translate(0, _kCanvasHeight * window.devicePixelRatio); _ctx.scale(1, -window.devicePixelRatio); diff --git a/packages/web_benchmarks/lib/src/recorder.dart b/packages/web_benchmarks/lib/src/recorder.dart index 49707b8c4b63..7488c44c2c41 100644 --- a/packages/web_benchmarks/lib/src/recorder.dart +++ b/packages/web_benchmarks/lib/src/recorder.dart @@ -15,7 +15,7 @@ import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:meta/meta.dart'; -import 'package:web/helpers.dart' as html; +import 'package:web/web.dart' as html; import 'common.dart'; diff --git a/packages/web_benchmarks/pubspec.yaml b/packages/web_benchmarks/pubspec.yaml index aa89736869cb..584e69706ee3 100644 --- a/packages/web_benchmarks/pubspec.yaml +++ b/packages/web_benchmarks/pubspec.yaml @@ -2,11 +2,11 @@ name: web_benchmarks description: A benchmark harness for performance-testing Flutter apps in Chrome. repository: https://github.com/flutter/packages/tree/main/packages/web_benchmarks issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+web_benchmarks%22 -version: 1.1.1 +version: 1.2.0 environment: - sdk: ">=3.2.0 <4.0.0" - flutter: ">=3.16.0" + sdk: ^3.3.0 + flutter: ">=3.19.0" dependencies: collection: ^1.18.0 @@ -14,6 +14,7 @@ dependencies: sdk: flutter flutter_test: sdk: flutter + http: ^1.0.0 logging: ^1.0.2 meta: ^1.7.0 path: ^1.8.0 @@ -21,7 +22,7 @@ dependencies: shelf: ^1.2.0 shelf_static: ^1.1.0 test: ^1.19.5 - web: '>=0.3.0 <0.5.0' + web: ^0.5.0 webkit_inspection_protocol: ^1.0.0 topics: From 48048f6bc779d86394397bcdceb1ce345210e27c Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Tue, 20 Feb 2024 15:46:50 -0800 Subject: [PATCH 004/126] [google_sign_in_web] Updates package:web dependency to 0.5.0. (#6167) This PR updates `google_sign_in_web` to use package `web: ^0.5.0`. ## Testing Manually verified using the `example/lib/button_tester.dart` app. This should be text exempt? This is a refactor with no semantic change (also, I wouldn't know how to test we're using `package:web` the expected way!) I removed a couple of `//ignore` at least! ## Issues * Fast follow of: https://github.com/flutter/packages/pull/5791 --- .../google_sign_in_web/CHANGELOG.md | 4 ++++ .../google_sign_in_web/example/pubspec.yaml | 4 ++-- .../src/flexible_size_html_element_view.dart | 17 +++++------------ .../google_sign_in_web/pubspec.yaml | 6 +++--- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md index 2c9c5863c8e2..49d02d368bae 100644 --- a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.12.4 + +* Updates dependencies to `web: ^0.5.0` and `google_identity_services_web: ^0.3.1`. + ## 0.12.3+3 * Updates SDK version to Dart `^3.3.0`. Flutter `^3.19.0`. diff --git a/packages/google_sign_in/google_sign_in_web/example/pubspec.yaml b/packages/google_sign_in/google_sign_in_web/example/pubspec.yaml index 1c53b0fa93c9..e260814e73bb 100644 --- a/packages/google_sign_in/google_sign_in_web/example/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_web/example/pubspec.yaml @@ -9,7 +9,7 @@ dependencies: cupertino_icons: ^1.0.2 flutter: sdk: flutter - google_identity_services_web: ^0.3.0 + google_identity_services_web: ^0.3.1 google_sign_in_platform_interface: ^2.4.0 google_sign_in_web: path: ../ @@ -22,7 +22,7 @@ dev_dependencies: integration_test: sdk: flutter mockito: 5.4.4 - web: ">=0.3.0 <0.6.0" + web: ^0.5.0 flutter: uses-material-design: true diff --git a/packages/google_sign_in/google_sign_in_web/lib/src/flexible_size_html_element_view.dart b/packages/google_sign_in/google_sign_in_web/lib/src/flexible_size_html_element_view.dart index 63acfb84836e..6fa019ed909a 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/src/flexible_size_html_element_view.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/src/flexible_size_html_element_view.dart @@ -73,13 +73,10 @@ class _FlexHtmlElementView extends State { /// The function called whenever an observed resize occurs. void _onResizeEntries( - // TODO(srujzs): Remove once typed JSArrays (JSArray) get to `stable`. - // ignore: always_specify_types - JSArray resizes, + JSArray resizes, web.ResizeObserver observer, ) { - final web.DOMRectReadOnly rect = - resizes.toDart.cast().last.contentRect; + final web.DOMRectReadOnly rect = resizes.toDart.last.contentRect; if (rect.width > 0 && rect.height > 0) { _doResize(Size(rect.width.toDouble(), rect.height.toDouble())); } @@ -90,14 +87,10 @@ class _FlexHtmlElementView extends State { /// When mutations are received, this function attaches a Resize Observer to /// the first child of the mutation, which will drive void _onMutationRecords( - // TODO(srujzs): Remove once typed JSArrays (JSArray) get to `stable`. - // ignore: always_specify_types - JSArray mutations, + JSArray mutations, web.MutationObserver observer, ) { - mutations.toDart - .cast() - .forEach((web.MutationRecord mutation) { + for (final web.MutationRecord mutation in mutations.toDart) { if (mutation.addedNodes.length > 0) { final web.Element? element = _locateSizeProvider(mutation.addedNodes); if (element != null) { @@ -108,7 +101,7 @@ class _FlexHtmlElementView extends State { return; } } - }); + } } /// Registers a MutationObserver on the root element of the HtmlElementView. diff --git a/packages/google_sign_in/google_sign_in_web/pubspec.yaml b/packages/google_sign_in/google_sign_in_web/pubspec.yaml index 82b9cd3cd289..b7ed804cc930 100644 --- a/packages/google_sign_in/google_sign_in_web/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_web/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android, iOS and Web. repository: https://github.com/flutter/packages/tree/main/packages/google_sign_in/google_sign_in_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 0.12.3+3 +version: 0.12.4 environment: sdk: ^3.3.0 @@ -22,10 +22,10 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter - google_identity_services_web: ^0.3.0 + google_identity_services_web: ^0.3.1 google_sign_in_platform_interface: ^2.4.0 http: ">=0.13.0 <2.0.0" - web: ">=0.3.0 <0.6.0" # because google_identity_services + web: ^0.5.0 dev_dependencies: flutter_test: From cd5d7d88d277d081af35017c1c8356e085e92d5e Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 21 Feb 2024 07:39:20 -0800 Subject: [PATCH 005/126] [local_auth] Switch iOS endorsement to `local_auth_darwin` (#6107) Updates to use `local_auth_darwin` instead of the discontinued `local_auth_ios`. --- packages/local_auth/local_auth/CHANGELOG.md | 9 +++++---- packages/local_auth/local_auth/README.md | 4 ++-- .../local_auth/example/lib/readme_excerpts.dart | 2 +- packages/local_auth/local_auth/example/pubspec.yaml | 6 +++--- packages/local_auth/local_auth/lib/src/local_auth.dart | 2 +- packages/local_auth/local_auth/pubspec.yaml | 10 +++++----- .../local_auth/local_auth/test/local_auth_test.dart | 2 +- script/configs/allowed_unpinned_deps.yaml | 4 ---- 8 files changed, 18 insertions(+), 21 deletions(-) diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index 475c3f5a3417..b7b2c263da5f 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,9 +1,10 @@ -## NEXT +## 2.2.0 -* Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. +* Switches endorsed iOS implementation to `local_auth_darwin`. + * Clients directly importing `local_auth_ios` for auth strings should switch + dependencies and imports to `local_auth_darwin`. No other change is necessary. * Updates support matrix in README to indicate that iOS 11 is no longer supported. -* Clients on versions of Flutter that still support iOS 11 can continue to use this - package with iOS 11, but will not receive any further updates to the iOS implementation. +* Updates minimum supported SDK version to Flutter 3.16.6. ## 2.1.8 diff --git a/packages/local_auth/local_auth/README.md b/packages/local_auth/local_auth/README.md index 8c86785d21f3..78c0b2038a19 100644 --- a/packages/local_auth/local_auth/README.md +++ b/packages/local_auth/local_auth/README.md @@ -136,7 +136,7 @@ instance, to customize Android and iOS: ```dart import 'package:local_auth_android/local_auth_android.dart'; -import 'package:local_auth_ios/local_auth_ios.dart'; +import 'package:local_auth_darwin/local_auth_darwin.dart'; // ··· final bool didAuthenticate = await auth.authenticate( localizedReason: 'Please authenticate to show account balance', @@ -281,7 +281,7 @@ the Android theme directly in `android/app/src/main/AndroidManifest.xml`: diff --git a/packages/local_auth/local_auth/example/lib/readme_excerpts.dart b/packages/local_auth/local_auth/example/lib/readme_excerpts.dart index 8ae4a3c34178..b0ee0e9daf0d 100644 --- a/packages/local_auth/local_auth/example/lib/readme_excerpts.dart +++ b/packages/local_auth/local_auth/example/lib/readme_excerpts.dart @@ -20,7 +20,7 @@ import 'package:local_auth/local_auth.dart'; // #docregion CustomMessages import 'package:local_auth_android/local_auth_android.dart'; -import 'package:local_auth_ios/local_auth_ios.dart'; +import 'package:local_auth_darwin/local_auth_darwin.dart'; // #enddocregion CustomMessages void main() { diff --git a/packages/local_auth/local_auth/example/pubspec.yaml b/packages/local_auth/local_auth/example/pubspec.yaml index ea71356935a9..73265f90cd33 100644 --- a/packages/local_auth/local_auth/example/pubspec.yaml +++ b/packages/local_auth/local_auth/example/pubspec.yaml @@ -3,8 +3,8 @@ description: Demonstrates how to use the local_auth plugin. publish_to: none environment: - sdk: ^3.1.0 - flutter: ">=3.13.0" + sdk: ^3.2.3 + flutter: ">=3.16.6" dependencies: flutter: @@ -17,7 +17,7 @@ dependencies: # the parent directory to use the current plugin's version. path: ../ local_auth_android: ^1.0.0 - local_auth_ios: ^1.0.1 + local_auth_darwin: ^1.2.1 dev_dependencies: build_runner: ^2.1.10 diff --git a/packages/local_auth/local_auth/lib/src/local_auth.dart b/packages/local_auth/local_auth/lib/src/local_auth.dart index e369f67187a5..9046743dd5d3 100644 --- a/packages/local_auth/local_auth/lib/src/local_auth.dart +++ b/packages/local_auth/local_auth/lib/src/local_auth.dart @@ -12,7 +12,7 @@ import 'dart:async'; import 'package:flutter/services.dart'; import 'package:local_auth_android/local_auth_android.dart'; -import 'package:local_auth_ios/local_auth_ios.dart'; +import 'package:local_auth_darwin/local_auth_darwin.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; import 'package:local_auth_windows/local_auth_windows.dart'; diff --git a/packages/local_auth/local_auth/pubspec.yaml b/packages/local_auth/local_auth/pubspec.yaml index 6573656a4496..f36bc4049def 100644 --- a/packages/local_auth/local_auth/pubspec.yaml +++ b/packages/local_auth/local_auth/pubspec.yaml @@ -3,11 +3,11 @@ description: Flutter plugin for Android and iOS devices to allow local authentication via fingerprint, touch ID, face ID, passcode, pin, or pattern. repository: https://github.com/flutter/packages/tree/main/packages/local_auth/local_auth issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 2.1.8 +version: 2.2.0 environment: - sdk: ^3.1.0 - flutter: ">=3.13.0" + sdk: ^3.2.3 + flutter: ">=3.16.6" flutter: plugin: @@ -15,7 +15,7 @@ flutter: android: default_package: local_auth_android ios: - default_package: local_auth_ios + default_package: local_auth_darwin windows: default_package: local_auth_windows @@ -23,7 +23,7 @@ dependencies: flutter: sdk: flutter local_auth_android: ^1.0.0 - local_auth_ios: ^1.0.1 + local_auth_darwin: ^1.2.1 local_auth_platform_interface: ^1.0.1 local_auth_windows: ^1.0.0 diff --git a/packages/local_auth/local_auth/test/local_auth_test.dart b/packages/local_auth/local_auth/test/local_auth_test.dart index 00196a8b875e..aa9c5cf5e6b6 100644 --- a/packages/local_auth/local_auth/test/local_auth_test.dart +++ b/packages/local_auth/local_auth/test/local_auth_test.dart @@ -6,7 +6,7 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:local_auth/local_auth.dart'; import 'package:local_auth_android/local_auth_android.dart'; -import 'package:local_auth_ios/local_auth_ios.dart'; +import 'package:local_auth_darwin/local_auth_darwin.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; import 'package:local_auth_windows/local_auth_windows.dart'; import 'package:mockito/mockito.dart'; diff --git a/script/configs/allowed_unpinned_deps.yaml b/script/configs/allowed_unpinned_deps.yaml index 7387401c5313..fe4c138b0a63 100644 --- a/script/configs/allowed_unpinned_deps.yaml +++ b/script/configs/allowed_unpinned_deps.yaml @@ -6,10 +6,6 @@ ## Explicit allowances -# Temporary during transition to local_auth_darwin. Can be removed once -# the default endorsement changes. -- local_auth_ios - # Owned by individual Flutter Team members. # Ideally we would not do this, since there's no clear plan for what # would happen if the individuals left the Flutter Team, and the From e88ca69b5d975eb37b4f513070224eb72f629ef7 Mon Sep 17 00:00:00 2001 From: Bas de Vaan Date: Wed, 21 Feb 2024 16:43:20 +0100 Subject: [PATCH 006/126] [camera_android_camerax] Fix typo in readme (#6143) This pull request fixes the typo `pubsepc.yaml` to the correct value `pubspec.yaml` in the `README.md`. https://github.com/flutter/flutter/issues/143572 --- packages/camera/camera_android_camerax/CHANGELOG.md | 3 ++- packages/camera/camera_android_camerax/README.md | 2 +- packages/camera/camera_android_camerax/pubspec.yaml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index ce4475ebf0fc..e97f2a550309 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.5.0+33 +* Fixes typo in `README.md`. * Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. ## 0.5.0+32 diff --git a/packages/camera/camera_android_camerax/README.md b/packages/camera/camera_android_camerax/README.md index 3a2e49d7e60d..13967faa2001 100644 --- a/packages/camera/camera_android_camerax/README.md +++ b/packages/camera/camera_android_camerax/README.md @@ -12,7 +12,7 @@ the title, which will be actively triaged. This package is [non-endorsed][3]; the endorsed Android implementation of `camera` is [`camera_android`][4]. To use this implementation of the plugin instead of -`camera_android`, you will need to specify it in your `pubsepc.yaml` file as a +`camera_android`, you will need to specify it in your `pubspec.yaml` file as a dependency in addition to `camera`: ```yaml diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index d6970d2e559e..fee04bae156f 100644 --- a/packages/camera/camera_android_camerax/pubspec.yaml +++ b/packages/camera/camera_android_camerax/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android_camerax description: Android implementation of the camera plugin using the CameraX library. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android_camerax issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.5.0+32 +version: 0.5.0+33 environment: sdk: ^3.1.0 From 5f44e3db019a6de2f083b188cdcf1904bee3fb4c Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 21 Feb 2024 10:12:07 -0800 Subject: [PATCH 007/126] [url_launcher] Remove `renderView` usage (#6137) Removes calls to the deprecated `renderView` method, replacing them with best-effort lookup of the implicit view. This only affects an API that has been deprecated for almost two years, and only when using a specific optional parameter on that method, so the potential impact here is minimal, and this avoids the need for a breaking change. In the future, when we remove this deprecated API, the workaround will go away as well. Also opportunistically removes `_ambiguate` and `_anonymize` as they were only needed for versions of Flutter we no longer support. Fixes https://github.com/flutter/flutter/issues/143449 --- .../url_launcher/url_launcher/CHANGELOG.md | 4 ++ .../url_launcher/lib/src/legacy_api.dart | 49 ++++++++++++------- .../url_launcher/url_launcher/pubspec.yaml | 6 +-- .../test/src/legacy_api_test.dart | 22 ++++----- 4 files changed, 48 insertions(+), 33 deletions(-) diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 61a568c9aa1c..3659a50c0a6e 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.2.5 + +* Removes use of deprecated `renderView` API. + ## 6.2.4 * Updates support matrix in README to indicate that iOS 11 is no longer supported. diff --git a/packages/url_launcher/url_launcher/lib/src/legacy_api.dart b/packages/url_launcher/url_launcher/lib/src/legacy_api.dart index 9f6d2dca001e..f709a75f758a 100644 --- a/packages/url_launcher/url_launcher/lib/src/legacy_api.dart +++ b/packages/url_launcher/url_launcher/lib/src/legacy_api.dart @@ -3,8 +3,10 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:ui'; import 'package:flutter/foundation.dart'; +import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; @@ -85,15 +87,14 @@ Future launch( /// [true] so that ui is automatically computed if [statusBarBrightness] is set. bool previousAutomaticSystemUiAdjustment = true; - if (statusBarBrightness != null && - defaultTargetPlatform == TargetPlatform.iOS && - _ambiguate(WidgetsBinding.instance) != null) { - previousAutomaticSystemUiAdjustment = _ambiguate(WidgetsBinding.instance)! - .renderView - .automaticSystemUiAdjustment; - _ambiguate(WidgetsBinding.instance)! - .renderView - .automaticSystemUiAdjustment = false; + final RenderView? renderViewToAdjust = + statusBarBrightness != null && defaultTargetPlatform == TargetPlatform.iOS + ? _findImplicitRenderView() + : null; + if (renderViewToAdjust != null) { + previousAutomaticSystemUiAdjustment = + renderViewToAdjust.automaticSystemUiAdjustment; + renderViewToAdjust.automaticSystemUiAdjustment = false; SystemChrome.setSystemUIOverlayStyle(statusBarBrightness == Brightness.light ? SystemUiOverlayStyle.dark : SystemUiOverlayStyle.light); @@ -110,11 +111,9 @@ Future launch( webOnlyWindowName: webOnlyWindowName, ); - if (statusBarBrightness != null && - _ambiguate(WidgetsBinding.instance) != null) { - _ambiguate(WidgetsBinding.instance)! - .renderView - .automaticSystemUiAdjustment = previousAutomaticSystemUiAdjustment; + if (renderViewToAdjust != null) { + renderViewToAdjust.automaticSystemUiAdjustment = + previousAutomaticSystemUiAdjustment; } return result; @@ -146,8 +145,22 @@ Future closeWebView() async { return UrlLauncherPlatform.instance.closeWebView(); } -/// This allows a value of type T or T? to be treated as a value of type T?. +/// Returns the [RenderView] associated with the implicit [FlutterView], if any. /// -/// We use this so that APIs that have become non-nullable can still be used -/// with `!` and `?` on the stable branch. -T? _ambiguate(T? value) => value; +/// [launch] predates multi-window support, and it doesn't have enough context +/// to get the right render view, so this assumes anyone still trying to use +/// the deprecated API with `statusBarBrightness` is in a single-view scenario. +/// This allows a best-effort implementation of the deprecated API for as long +/// as it continues to exist, without depending on deprecated Flutter APIs (and +/// therefore keeping url_launcher forward-compatible with future versions of +/// Flutter for longer). +RenderView? _findImplicitRenderView() { + final FlutterView? implicitFlutterView = + WidgetsBinding.instance.platformDispatcher.implicitView; + if (implicitFlutterView == null) { + return null; + } + return WidgetsBinding.instance.renderViews + .where((RenderView v) => v.flutterView == implicitFlutterView) + .firstOrNull; +} diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index fe3f2990d08a..e59afe4690da 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -3,11 +3,11 @@ description: Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes. repository: https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.2.4 +version: 6.2.5 environment: - sdk: ">=3.1.0 <4.0.0" - flutter: ">=3.13.0" + sdk: ">=3.2.0 <4.0.0" + flutter: ">=3.16.0" flutter: plugin: diff --git a/packages/url_launcher/url_launcher/test/src/legacy_api_test.dart b/packages/url_launcher/url_launcher/test/src/legacy_api_test.dart index 8a694546397c..091f9b7c8fd2 100644 --- a/packages/url_launcher/url_launcher/test/src/legacy_api_test.dart +++ b/packages/url_launcher/url_launcher/test/src/legacy_api_test.dart @@ -235,10 +235,11 @@ void main() { ..setResponse(true); final TestWidgetsFlutterBinding binding = - _anonymize(TestWidgetsFlutterBinding.ensureInitialized())! - as TestWidgetsFlutterBinding; + TestWidgetsFlutterBinding.ensureInitialized(); debugDefaultTargetPlatformOverride = TargetPlatform.iOS; - final RenderView renderView = binding.renderView; + final RenderView renderView = + RenderView(view: binding.platformDispatcher.implicitView!); + binding.addRenderView(renderView); renderView.automaticSystemUiAdjustment = true; final Future launchResult = launch('http://flutter.dev/', statusBarBrightness: Brightness.dark); @@ -248,6 +249,7 @@ void main() { expect(renderView.automaticSystemUiAdjustment, isFalse); await launchResult; expect(renderView.automaticSystemUiAdjustment, isTrue); + binding.removeRenderView(renderView); }); test('sets automaticSystemUiAdjustment to not be null', () async { @@ -265,10 +267,11 @@ void main() { ..setResponse(true); final TestWidgetsFlutterBinding binding = - _anonymize(TestWidgetsFlutterBinding.ensureInitialized())! - as TestWidgetsFlutterBinding; + TestWidgetsFlutterBinding.ensureInitialized(); debugDefaultTargetPlatformOverride = TargetPlatform.android; - final RenderView renderView = binding.renderView; + final RenderView renderView = + RenderView(view: binding.platformDispatcher.implicitView!); + binding.addRenderView(renderView); expect(renderView.automaticSystemUiAdjustment, true); final Future launchResult = launch('http://flutter.dev/', statusBarBrightness: Brightness.dark); @@ -278,6 +281,7 @@ void main() { expect(renderView.automaticSystemUiAdjustment, true); await launchResult; expect(renderView.automaticSystemUiAdjustment, true); + binding.removeRenderView(renderView); }); test('open non-parseable url', () async { @@ -317,9 +321,3 @@ void main() { }); }); } - -/// This removes the type information from a value so that it can be cast -/// to another type even if that cast is redundant. -/// We use this so that APIs whose type have become more descriptive can still -/// be used on the stable branch where they require a cast. -Object? _anonymize(T? value) => value; From 9c15ec5914cc28595965fe5a20bca91673fe78d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Sharma?= <737941+loic-sharma@users.noreply.github.com> Date: Wed, 21 Feb 2024 10:57:04 -0800 Subject: [PATCH 008/126] [ci] Run Windows Arm64 build tests post-submit (#6166) The Flutter try pool does not have any Windows Arm64 machines. Thus, any Windows Arm64 pre-submit tests would fail without `bringup: true` as there's no machines to run them. This change will allow us to promote the Windows Arm64 build tests out of `bringup`. See: https://github.com/flutter/flutter/issues/141986 Part of https://github.com/flutter/flutter/issues/129813 --- .ci.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.ci.yaml b/.ci.yaml index 0b277d28053b..93e3d4035346 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -1472,6 +1472,7 @@ targets: - name: Windows_arm64 windows-build_all_packages master recipe: packages/packages + presubmit: false timeout: 30 bringup: true # https://github.com/flutter/flutter/issues/134083 properties: @@ -1506,6 +1507,7 @@ targets: - name: Windows_arm64 windows-build_all_packages stable recipe: packages/packages + presubmit: false timeout: 30 bringup: true properties: From ed046decc9f202e0553794e1b36fa40b55298792 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 21 Feb 2024 11:20:52 -0800 Subject: [PATCH 009/126] [flutter_markdown] Support wasm (#6168) Drop usage of dart:html --- packages/flutter_markdown/CHANGELOG.md | 4 +++ .../lib/src/_functions_web.dart | 27 +++++++++---------- .../flutter_markdown/lib/src/builder.dart | 5 +--- packages/flutter_markdown/pubspec.yaml | 6 ++--- 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/packages/flutter_markdown/CHANGELOG.md b/packages/flutter_markdown/CHANGELOG.md index 9d901df3fa72..7b19e96b64af 100644 --- a/packages/flutter_markdown/CHANGELOG.md +++ b/packages/flutter_markdown/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.20+1 + +* Updates minimum supported SDK version to Flutter 3.19. + ## 0.6.20 * Adds `textScaler` to `MarkdownStyleSheet`, and deprecates `textScaleFactor`. diff --git a/packages/flutter_markdown/lib/src/_functions_web.dart b/packages/flutter_markdown/lib/src/_functions_web.dart index 7adf81654bed..abf23453225c 100644 --- a/packages/flutter_markdown/lib/src/_functions_web.dart +++ b/packages/flutter_markdown/lib/src/_functions_web.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:html'; // ignore: avoid_web_libraries_in_flutter +import 'dart:js_interop'; import 'package:flutter/cupertino.dart' show CupertinoTheme; import 'package:flutter/material.dart' show Theme; @@ -50,20 +50,14 @@ final MarkdownStyleSheet Function(BuildContext, MarkdownStyleSheetBaseTheme?) BuildContext context, MarkdownStyleSheetBaseTheme? baseTheme, ) { - MarkdownStyleSheet result; - switch (baseTheme) { - case MarkdownStyleSheetBaseTheme.platform: - final String userAgent = window.navigator.userAgent; - result = userAgent.contains('Mac OS X') - ? MarkdownStyleSheet.fromCupertinoTheme(CupertinoTheme.of(context)) - : MarkdownStyleSheet.fromTheme(Theme.of(context)); - case MarkdownStyleSheetBaseTheme.cupertino: - result = - MarkdownStyleSheet.fromCupertinoTheme(CupertinoTheme.of(context)); - case MarkdownStyleSheetBaseTheme.material: - default: // ignore: no_default_cases - result = MarkdownStyleSheet.fromTheme(Theme.of(context)); - } + final MarkdownStyleSheet result = switch (baseTheme) { + MarkdownStyleSheetBaseTheme.platform + when _userAgent.toDart.contains('Mac OS X') => + MarkdownStyleSheet.fromCupertinoTheme(CupertinoTheme.of(context)), + MarkdownStyleSheetBaseTheme.cupertino => + MarkdownStyleSheet.fromCupertinoTheme(CupertinoTheme.of(context)), + _ => MarkdownStyleSheet.fromTheme(Theme.of(context)), + }; return result.copyWith( textScaler: MediaQuery.textScalerOf(context), @@ -84,3 +78,6 @@ Widget _handleDataSchemeUri( } return const SizedBox(); } + +@JS('window.navigator.userAgent') +external JSString get _userAgent; diff --git a/packages/flutter_markdown/lib/src/builder.dart b/packages/flutter_markdown/lib/src/builder.dart index 5d3cb10a8e43..91371dd1c6e2 100644 --- a/packages/flutter_markdown/lib/src/builder.dart +++ b/packages/flutter_markdown/lib/src/builder.dart @@ -2,14 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore: unnecessary_import, see https://github.com/flutter/flutter/pull/138881 -import 'dart:ui'; - import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:markdown/markdown.dart' as md; -import '_functions_io.dart' if (dart.library.html) '_functions_web.dart'; +import '_functions_io.dart' if (dart.library.js_interop) '_functions_web.dart'; import 'style_sheet.dart'; import 'widget.dart'; diff --git a/packages/flutter_markdown/pubspec.yaml b/packages/flutter_markdown/pubspec.yaml index 0a3c89d24e77..c5b8d4706f11 100644 --- a/packages/flutter_markdown/pubspec.yaml +++ b/packages/flutter_markdown/pubspec.yaml @@ -4,11 +4,11 @@ description: A Markdown renderer for Flutter. Create rich text output, formatted with simple Markdown tags. repository: https://github.com/flutter/packages/tree/main/packages/flutter_markdown issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_markdown%22 -version: 0.6.20 +version: 0.6.20+1 environment: - sdk: ^3.2.0 - flutter: ">=3.16.0" + sdk: ^3.3.0 + flutter: ">=3.19.0" dependencies: flutter: From ccb27be4a259704dcd4f103dde3ea453631659b6 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 21 Feb 2024 14:31:22 -0500 Subject: [PATCH 010/126] [various] Commit Windows build migrations (#6175) Commits the changes caused by building all examples for Windows with a recent version of Flutter, to minimize noise in CI logs and local builds. All changes are entirely tool-generated. --- .../example/windows/flutter/CMakeLists.txt | 7 ++++++- .../camera_windows/example/windows/runner/Runner.rc | 10 +++++----- .../example/windows/flutter/CMakeLists.txt | 7 ++++++- .../example/windows/runner/CMakeLists.txt | 7 +++++++ .../dynamic_layouts/example/windows/runner/Runner.rc | 10 +++++----- .../example/windows/flutter/CMakeLists.txt | 7 ++++++- .../example/windows/runner/CMakeLists.txt | 7 +++++++ .../file_selector/example/windows/runner/Runner.rc | 10 +++++----- .../example/windows/flutter/CMakeLists.txt | 7 ++++++- .../example/windows/flutter/CMakeLists.txt | 7 ++++++- .../example/windows/flutter/CMakeLists.txt | 7 ++++++- .../example/windows/flutter/CMakeLists.txt | 7 ++++++- .../example/windows/flutter/CMakeLists.txt | 7 ++++++- .../example/windows/flutter/CMakeLists.txt | 7 ++++++- .../example/windows/runner/Runner.rc | 10 +++++----- .../local_auth/example/windows/flutter/CMakeLists.txt | 7 ++++++- .../local_auth/example/windows/runner/Runner.rc | 10 +++++----- .../example/windows/flutter/CMakeLists.txt | 7 ++++++- .../example/windows/runner/Runner.rc | 10 +++++----- .../example/windows/flutter/CMakeLists.txt | 6 ++++++ .../path_provider/example/windows/runner/Runner.rc | 10 +++++----- .../example/windows/flutter/CMakeLists.txt | 7 ++++++- .../example/windows/runner/Runner.rc | 10 +++++----- .../platform/example/windows/flutter/CMakeLists.txt | 7 ++++++- .../rfw/example/hello/windows/flutter/CMakeLists.txt | 7 ++++++- packages/rfw/example/hello/windows/runner/Runner.rc | 10 +++++----- .../rfw/example/local/windows/flutter/CMakeLists.txt | 7 ++++++- packages/rfw/example/local/windows/runner/Runner.rc | 10 +++++----- .../rfw/example/remote/windows/flutter/CMakeLists.txt | 7 ++++++- packages/rfw/example/remote/windows/runner/Runner.rc | 10 +++++----- .../rfw/example/wasm/windows/flutter/CMakeLists.txt | 7 ++++++- packages/rfw/example/wasm/windows/runner/Runner.rc | 10 +++++----- .../example/windows/flutter/CMakeLists.txt | 6 ++++++ .../example/windows/runner/Runner.rc | 10 +++++----- .../example/windows/flutter/CMakeLists.txt | 6 ++++++ .../example/windows/runner/Runner.rc | 10 +++++----- .../example/windows/flutter/CMakeLists.txt | 7 ++++++- .../example/windows/flutter/CMakeLists.txt | 6 ++++++ .../url_launcher/example/windows/runner/Runner.rc | 10 +++++----- .../example/windows/flutter/CMakeLists.txt | 7 ++++++- .../example/windows/runner/Runner.rc | 10 +++++----- 41 files changed, 232 insertions(+), 99 deletions(-) diff --git a/packages/camera/camera_windows/example/windows/flutter/CMakeLists.txt b/packages/camera/camera_windows/example/windows/flutter/CMakeLists.txt index b2e4bd8d658b..4f2af69bbb09 100644 --- a/packages/camera/camera_windows/example/windows/flutter/CMakeLists.txt +++ b/packages/camera/camera_windows/example/windows/flutter/CMakeLists.txt @@ -9,6 +9,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -91,7 +96,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/packages/camera/camera_windows/example/windows/runner/Runner.rc b/packages/camera/camera_windows/example/windows/runner/Runner.rc index f1cfa4391ebd..06405f8e2355 100644 --- a/packages/camera/camera_windows/example/windows/runner/Runner.rc +++ b/packages/camera/camera_windows/example/windows/runner/Runner.rc @@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico" // Version // -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD #else -#define VERSION_AS_NUMBER 1,0,0 +#define VERSION_AS_NUMBER 1,0,0,0 #endif -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION #else #define VERSION_AS_STRING "1.0.0" #endif diff --git a/packages/dynamic_layouts/example/windows/flutter/CMakeLists.txt b/packages/dynamic_layouts/example/windows/flutter/CMakeLists.txt index 930d2071a324..903f4899d6fc 100644 --- a/packages/dynamic_layouts/example/windows/flutter/CMakeLists.txt +++ b/packages/dynamic_layouts/example/windows/flutter/CMakeLists.txt @@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -92,7 +97,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/packages/dynamic_layouts/example/windows/runner/CMakeLists.txt b/packages/dynamic_layouts/example/windows/runner/CMakeLists.txt index b9e550fba8e1..17411a8ab8eb 100644 --- a/packages/dynamic_layouts/example/windows/runner/CMakeLists.txt +++ b/packages/dynamic_layouts/example/windows/runner/CMakeLists.txt @@ -20,6 +20,13 @@ add_executable(${BINARY_NAME} WIN32 # that need different build settings. apply_standard_settings(${BINARY_NAME}) +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + # Disable Windows macros that collide with C++ standard library functions. target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") diff --git a/packages/dynamic_layouts/example/windows/runner/Runner.rc b/packages/dynamic_layouts/example/windows/runner/Runner.rc index 5fdea291cf19..0f5c0857111f 100644 --- a/packages/dynamic_layouts/example/windows/runner/Runner.rc +++ b/packages/dynamic_layouts/example/windows/runner/Runner.rc @@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico" // Version // -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD #else -#define VERSION_AS_NUMBER 1,0,0 +#define VERSION_AS_NUMBER 1,0,0,0 #endif -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION #else #define VERSION_AS_STRING "1.0.0" #endif diff --git a/packages/file_selector/file_selector/example/windows/flutter/CMakeLists.txt b/packages/file_selector/file_selector/example/windows/flutter/CMakeLists.txt index 930d2071a324..903f4899d6fc 100644 --- a/packages/file_selector/file_selector/example/windows/flutter/CMakeLists.txt +++ b/packages/file_selector/file_selector/example/windows/flutter/CMakeLists.txt @@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -92,7 +97,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/packages/file_selector/file_selector/example/windows/runner/CMakeLists.txt b/packages/file_selector/file_selector/example/windows/runner/CMakeLists.txt index b9e550fba8e1..17411a8ab8eb 100644 --- a/packages/file_selector/file_selector/example/windows/runner/CMakeLists.txt +++ b/packages/file_selector/file_selector/example/windows/runner/CMakeLists.txt @@ -20,6 +20,13 @@ add_executable(${BINARY_NAME} WIN32 # that need different build settings. apply_standard_settings(${BINARY_NAME}) +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + # Disable Windows macros that collide with C++ standard library functions. target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") diff --git a/packages/file_selector/file_selector/example/windows/runner/Runner.rc b/packages/file_selector/file_selector/example/windows/runner/Runner.rc index 5fdea291cf19..0f5c0857111f 100644 --- a/packages/file_selector/file_selector/example/windows/runner/Runner.rc +++ b/packages/file_selector/file_selector/example/windows/runner/Runner.rc @@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico" // Version // -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD #else -#define VERSION_AS_NUMBER 1,0,0 +#define VERSION_AS_NUMBER 1,0,0,0 #endif -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION #else #define VERSION_AS_STRING "1.0.0" #endif diff --git a/packages/file_selector/file_selector_windows/example/windows/flutter/CMakeLists.txt b/packages/file_selector/file_selector_windows/example/windows/flutter/CMakeLists.txt index b2e4bd8d658b..4f2af69bbb09 100644 --- a/packages/file_selector/file_selector_windows/example/windows/flutter/CMakeLists.txt +++ b/packages/file_selector/file_selector_windows/example/windows/flutter/CMakeLists.txt @@ -9,6 +9,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -91,7 +96,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/packages/flutter_adaptive_scaffold/example/windows/flutter/CMakeLists.txt b/packages/flutter_adaptive_scaffold/example/windows/flutter/CMakeLists.txt index 930d2071a324..903f4899d6fc 100644 --- a/packages/flutter_adaptive_scaffold/example/windows/flutter/CMakeLists.txt +++ b/packages/flutter_adaptive_scaffold/example/windows/flutter/CMakeLists.txt @@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -92,7 +97,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/packages/flutter_image/example/windows/flutter/CMakeLists.txt b/packages/flutter_image/example/windows/flutter/CMakeLists.txt index 930d2071a324..903f4899d6fc 100644 --- a/packages/flutter_image/example/windows/flutter/CMakeLists.txt +++ b/packages/flutter_image/example/windows/flutter/CMakeLists.txt @@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -92,7 +97,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/packages/flutter_markdown/example/windows/flutter/CMakeLists.txt b/packages/flutter_markdown/example/windows/flutter/CMakeLists.txt index 744f08a9389b..0a9177722722 100644 --- a/packages/flutter_markdown/example/windows/flutter/CMakeLists.txt +++ b/packages/flutter_markdown/example/windows/flutter/CMakeLists.txt @@ -9,6 +9,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -90,7 +95,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/packages/image_picker/image_picker/example/windows/flutter/CMakeLists.txt b/packages/image_picker/image_picker/example/windows/flutter/CMakeLists.txt index 930d2071a324..903f4899d6fc 100644 --- a/packages/image_picker/image_picker/example/windows/flutter/CMakeLists.txt +++ b/packages/image_picker/image_picker/example/windows/flutter/CMakeLists.txt @@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -92,7 +97,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/packages/image_picker/image_picker_windows/example/windows/flutter/CMakeLists.txt b/packages/image_picker/image_picker_windows/example/windows/flutter/CMakeLists.txt index b2e4bd8d658b..4f2af69bbb09 100644 --- a/packages/image_picker/image_picker_windows/example/windows/flutter/CMakeLists.txt +++ b/packages/image_picker/image_picker_windows/example/windows/flutter/CMakeLists.txt @@ -9,6 +9,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -91,7 +96,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/packages/image_picker/image_picker_windows/example/windows/runner/Runner.rc b/packages/image_picker/image_picker_windows/example/windows/runner/Runner.rc index 5fdea291cf19..0f5c0857111f 100644 --- a/packages/image_picker/image_picker_windows/example/windows/runner/Runner.rc +++ b/packages/image_picker/image_picker_windows/example/windows/runner/Runner.rc @@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico" // Version // -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD #else -#define VERSION_AS_NUMBER 1,0,0 +#define VERSION_AS_NUMBER 1,0,0,0 #endif -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION #else #define VERSION_AS_STRING "1.0.0" #endif diff --git a/packages/local_auth/local_auth/example/windows/flutter/CMakeLists.txt b/packages/local_auth/local_auth/example/windows/flutter/CMakeLists.txt index d83cc95319b6..713648b939df 100644 --- a/packages/local_auth/local_auth/example/windows/flutter/CMakeLists.txt +++ b/packages/local_auth/local_auth/example/windows/flutter/CMakeLists.txt @@ -9,6 +9,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -91,7 +96,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/packages/local_auth/local_auth/example/windows/runner/Runner.rc b/packages/local_auth/local_auth/example/windows/runner/Runner.rc index 7e35b9f56a22..7f035cd3f7ed 100644 --- a/packages/local_auth/local_auth/example/windows/runner/Runner.rc +++ b/packages/local_auth/local_auth/example/windows/runner/Runner.rc @@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico" // Version // -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD #else -#define VERSION_AS_NUMBER 1,0,0 +#define VERSION_AS_NUMBER 1,0,0,0 #endif -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION #else #define VERSION_AS_STRING "1.0.0" #endif diff --git a/packages/local_auth/local_auth_windows/example/windows/flutter/CMakeLists.txt b/packages/local_auth/local_auth_windows/example/windows/flutter/CMakeLists.txt index b2e4bd8d658b..4f2af69bbb09 100644 --- a/packages/local_auth/local_auth_windows/example/windows/flutter/CMakeLists.txt +++ b/packages/local_auth/local_auth_windows/example/windows/flutter/CMakeLists.txt @@ -9,6 +9,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -91,7 +96,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/Runner.rc b/packages/local_auth/local_auth_windows/example/windows/runner/Runner.rc index 5fdea291cf19..0f5c0857111f 100644 --- a/packages/local_auth/local_auth_windows/example/windows/runner/Runner.rc +++ b/packages/local_auth/local_auth_windows/example/windows/runner/Runner.rc @@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico" // Version // -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD #else -#define VERSION_AS_NUMBER 1,0,0 +#define VERSION_AS_NUMBER 1,0,0,0 #endif -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION #else #define VERSION_AS_STRING "1.0.0" #endif diff --git a/packages/path_provider/path_provider/example/windows/flutter/CMakeLists.txt b/packages/path_provider/path_provider/example/windows/flutter/CMakeLists.txt index c7a8c7607d81..c25ffc272ada 100644 --- a/packages/path_provider/path_provider/example/windows/flutter/CMakeLists.txt +++ b/packages/path_provider/path_provider/example/windows/flutter/CMakeLists.txt @@ -9,6 +9,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -91,6 +96,7 @@ add_custom_command( ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" windows-x64 $ + VERBATIM ) add_custom_target(flutter_assemble DEPENDS "${FLUTTER_LIBRARY}" diff --git a/packages/path_provider/path_provider/example/windows/runner/Runner.rc b/packages/path_provider/path_provider/example/windows/runner/Runner.rc index dbda44723259..4d7a2dbcdc20 100644 --- a/packages/path_provider/path_provider/example/windows/runner/Runner.rc +++ b/packages/path_provider/path_provider/example/windows/runner/Runner.rc @@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico" // Version // -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD #else -#define VERSION_AS_NUMBER 1,0,0 +#define VERSION_AS_NUMBER 1,0,0,0 #endif -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION #else #define VERSION_AS_STRING "1.0.0" #endif diff --git a/packages/path_provider/path_provider_windows/example/windows/flutter/CMakeLists.txt b/packages/path_provider/path_provider_windows/example/windows/flutter/CMakeLists.txt index 744f08a9389b..0a9177722722 100644 --- a/packages/path_provider/path_provider_windows/example/windows/flutter/CMakeLists.txt +++ b/packages/path_provider/path_provider_windows/example/windows/flutter/CMakeLists.txt @@ -9,6 +9,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -90,7 +95,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/packages/path_provider/path_provider_windows/example/windows/runner/Runner.rc b/packages/path_provider/path_provider_windows/example/windows/runner/Runner.rc index 944329afc03a..41d1b5cf736b 100644 --- a/packages/path_provider/path_provider_windows/example/windows/runner/Runner.rc +++ b/packages/path_provider/path_provider_windows/example/windows/runner/Runner.rc @@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico" // Version // -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD #else -#define VERSION_AS_NUMBER 1,0,0 +#define VERSION_AS_NUMBER 1,0,0,0 #endif -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION #else #define VERSION_AS_STRING "1.0.0" #endif diff --git a/packages/platform/example/windows/flutter/CMakeLists.txt b/packages/platform/example/windows/flutter/CMakeLists.txt index 930d2071a324..903f4899d6fc 100644 --- a/packages/platform/example/windows/flutter/CMakeLists.txt +++ b/packages/platform/example/windows/flutter/CMakeLists.txt @@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -92,7 +97,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/packages/rfw/example/hello/windows/flutter/CMakeLists.txt b/packages/rfw/example/hello/windows/flutter/CMakeLists.txt index b02c5485c957..86edc67b995f 100644 --- a/packages/rfw/example/hello/windows/flutter/CMakeLists.txt +++ b/packages/rfw/example/hello/windows/flutter/CMakeLists.txt @@ -9,6 +9,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -91,7 +96,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/packages/rfw/example/hello/windows/runner/Runner.rc b/packages/rfw/example/hello/windows/runner/Runner.rc index c9091ea41b5d..a56c2a14c150 100644 --- a/packages/rfw/example/hello/windows/runner/Runner.rc +++ b/packages/rfw/example/hello/windows/runner/Runner.rc @@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico" // Version // -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD #else -#define VERSION_AS_NUMBER 1,0,0 +#define VERSION_AS_NUMBER 1,0,0,0 #endif -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION #else #define VERSION_AS_STRING "1.0.0" #endif diff --git a/packages/rfw/example/local/windows/flutter/CMakeLists.txt b/packages/rfw/example/local/windows/flutter/CMakeLists.txt index b02c5485c957..86edc67b995f 100644 --- a/packages/rfw/example/local/windows/flutter/CMakeLists.txt +++ b/packages/rfw/example/local/windows/flutter/CMakeLists.txt @@ -9,6 +9,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -91,7 +96,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/packages/rfw/example/local/windows/runner/Runner.rc b/packages/rfw/example/local/windows/runner/Runner.rc index 0ca2e22abbca..dfe34f81aae7 100644 --- a/packages/rfw/example/local/windows/runner/Runner.rc +++ b/packages/rfw/example/local/windows/runner/Runner.rc @@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico" // Version // -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD #else -#define VERSION_AS_NUMBER 1,0,0 +#define VERSION_AS_NUMBER 1,0,0,0 #endif -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION #else #define VERSION_AS_STRING "1.0.0" #endif diff --git a/packages/rfw/example/remote/windows/flutter/CMakeLists.txt b/packages/rfw/example/remote/windows/flutter/CMakeLists.txt index b02c5485c957..86edc67b995f 100644 --- a/packages/rfw/example/remote/windows/flutter/CMakeLists.txt +++ b/packages/rfw/example/remote/windows/flutter/CMakeLists.txt @@ -9,6 +9,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -91,7 +96,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/packages/rfw/example/remote/windows/runner/Runner.rc b/packages/rfw/example/remote/windows/runner/Runner.rc index 991f8f5ab578..5f0cfb204f38 100644 --- a/packages/rfw/example/remote/windows/runner/Runner.rc +++ b/packages/rfw/example/remote/windows/runner/Runner.rc @@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico" // Version // -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD #else -#define VERSION_AS_NUMBER 1,0,0 +#define VERSION_AS_NUMBER 1,0,0,0 #endif -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION #else #define VERSION_AS_STRING "1.0.0" #endif diff --git a/packages/rfw/example/wasm/windows/flutter/CMakeLists.txt b/packages/rfw/example/wasm/windows/flutter/CMakeLists.txt index b02c5485c957..86edc67b995f 100644 --- a/packages/rfw/example/wasm/windows/flutter/CMakeLists.txt +++ b/packages/rfw/example/wasm/windows/flutter/CMakeLists.txt @@ -9,6 +9,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -91,7 +96,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/packages/rfw/example/wasm/windows/runner/Runner.rc b/packages/rfw/example/wasm/windows/runner/Runner.rc index 7535ea4587ed..c2d9cad1cb97 100644 --- a/packages/rfw/example/wasm/windows/runner/Runner.rc +++ b/packages/rfw/example/wasm/windows/runner/Runner.rc @@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico" // Version // -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD #else -#define VERSION_AS_NUMBER 1,0,0 +#define VERSION_AS_NUMBER 1,0,0,0 #endif -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION #else #define VERSION_AS_STRING "1.0.0" #endif diff --git a/packages/shared_preferences/shared_preferences/example/windows/flutter/CMakeLists.txt b/packages/shared_preferences/shared_preferences/example/windows/flutter/CMakeLists.txt index c7a8c7607d81..c25ffc272ada 100644 --- a/packages/shared_preferences/shared_preferences/example/windows/flutter/CMakeLists.txt +++ b/packages/shared_preferences/shared_preferences/example/windows/flutter/CMakeLists.txt @@ -9,6 +9,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -91,6 +96,7 @@ add_custom_command( ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" windows-x64 $ + VERBATIM ) add_custom_target(flutter_assemble DEPENDS "${FLUTTER_LIBRARY}" diff --git a/packages/shared_preferences/shared_preferences/example/windows/runner/Runner.rc b/packages/shared_preferences/shared_preferences/example/windows/runner/Runner.rc index dbda44723259..4d7a2dbcdc20 100644 --- a/packages/shared_preferences/shared_preferences/example/windows/runner/Runner.rc +++ b/packages/shared_preferences/shared_preferences/example/windows/runner/Runner.rc @@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico" // Version // -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD #else -#define VERSION_AS_NUMBER 1,0,0 +#define VERSION_AS_NUMBER 1,0,0,0 #endif -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION #else #define VERSION_AS_STRING "1.0.0" #endif diff --git a/packages/shared_preferences/shared_preferences_windows/example/windows/flutter/CMakeLists.txt b/packages/shared_preferences/shared_preferences_windows/example/windows/flutter/CMakeLists.txt index c7a8c7607d81..c25ffc272ada 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/windows/flutter/CMakeLists.txt +++ b/packages/shared_preferences/shared_preferences_windows/example/windows/flutter/CMakeLists.txt @@ -9,6 +9,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -91,6 +96,7 @@ add_custom_command( ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" windows-x64 $ + VERBATIM ) add_custom_target(flutter_assemble DEPENDS "${FLUTTER_LIBRARY}" diff --git a/packages/shared_preferences/shared_preferences_windows/example/windows/runner/Runner.rc b/packages/shared_preferences/shared_preferences_windows/example/windows/runner/Runner.rc index 944329afc03a..41d1b5cf736b 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/windows/runner/Runner.rc +++ b/packages/shared_preferences/shared_preferences_windows/example/windows/runner/Runner.rc @@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico" // Version // -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD #else -#define VERSION_AS_NUMBER 1,0,0 +#define VERSION_AS_NUMBER 1,0,0,0 #endif -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION #else #define VERSION_AS_STRING "1.0.0" #endif diff --git a/packages/two_dimensional_scrollables/example/windows/flutter/CMakeLists.txt b/packages/two_dimensional_scrollables/example/windows/flutter/CMakeLists.txt index 930d2071a324..903f4899d6fc 100644 --- a/packages/two_dimensional_scrollables/example/windows/flutter/CMakeLists.txt +++ b/packages/two_dimensional_scrollables/example/windows/flutter/CMakeLists.txt @@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -92,7 +97,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/packages/url_launcher/url_launcher/example/windows/flutter/CMakeLists.txt b/packages/url_launcher/url_launcher/example/windows/flutter/CMakeLists.txt index c7a8c7607d81..c25ffc272ada 100644 --- a/packages/url_launcher/url_launcher/example/windows/flutter/CMakeLists.txt +++ b/packages/url_launcher/url_launcher/example/windows/flutter/CMakeLists.txt @@ -9,6 +9,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -91,6 +96,7 @@ add_custom_command( ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" windows-x64 $ + VERBATIM ) add_custom_target(flutter_assemble DEPENDS "${FLUTTER_LIBRARY}" diff --git a/packages/url_launcher/url_launcher/example/windows/runner/Runner.rc b/packages/url_launcher/url_launcher/example/windows/runner/Runner.rc index dbda44723259..4d7a2dbcdc20 100644 --- a/packages/url_launcher/url_launcher/example/windows/runner/Runner.rc +++ b/packages/url_launcher/url_launcher/example/windows/runner/Runner.rc @@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico" // Version // -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD #else -#define VERSION_AS_NUMBER 1,0,0 +#define VERSION_AS_NUMBER 1,0,0,0 #endif -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION #else #define VERSION_AS_STRING "1.0.0" #endif diff --git a/packages/url_launcher/url_launcher_windows/example/windows/flutter/CMakeLists.txt b/packages/url_launcher/url_launcher_windows/example/windows/flutter/CMakeLists.txt index 744f08a9389b..0a9177722722 100644 --- a/packages/url_launcher/url_launcher_windows/example/windows/flutter/CMakeLists.txt +++ b/packages/url_launcher/url_launcher_windows/example/windows/flutter/CMakeLists.txt @@ -9,6 +9,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -90,7 +95,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/packages/url_launcher/url_launcher_windows/example/windows/runner/Runner.rc b/packages/url_launcher/url_launcher_windows/example/windows/runner/Runner.rc index 944329afc03a..41d1b5cf736b 100644 --- a/packages/url_launcher/url_launcher_windows/example/windows/runner/Runner.rc +++ b/packages/url_launcher/url_launcher_windows/example/windows/runner/Runner.rc @@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico" // Version // -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD #else -#define VERSION_AS_NUMBER 1,0,0 +#define VERSION_AS_NUMBER 1,0,0,0 #endif -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION #else #define VERSION_AS_STRING "1.0.0" #endif From 83d7fc613e24a068261de934270cd1efd55d2c9e Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Wed, 21 Feb 2024 15:57:37 -0500 Subject: [PATCH 011/126] [camerax] Implements `setFocusPoint`, `setExposurePoint`, `setExposureOffset` (#6059) This PR implements `setFocusPoint`, `setExposurePoint`, `setExposureOffset` and makes some small fixes here and there, each of which I have left a comment about for context. Part of https://github.com/flutter/flutter/issues/120468 & https://github.com/flutter/flutter/issues/120467. ~NOTE: Should land after https://github.com/flutter/packages/pull/6068.~ done :) --- .../camera_android_camerax/CHANGELOG.md | 4 + .../camera/camera_android_camerax/README.md | 9 +- .../camerax/CameraAndroidCameraxPlugin.java | 10 + .../camerax/CameraControlHostApiImpl.java | 24 + .../camerax/GeneratedCameraXLibrary.java | 32 +- .../camerax/MeteringPointHostApiImpl.java | 64 ++- .../CameraAndroidCameraxPluginTest.java | 16 + .../plugins/camerax/CameraControlTest.java | 54 ++ .../plugins/camerax/MeteringPointTest.java | 174 +++++- .../example/lib/main.dart | 30 +- .../lib/src/android_camera_camerax.dart | 187 ++++++- .../lib/src/camera_control.dart | 28 +- .../lib/src/camerax_library.g.dart | 50 +- .../lib/src/camerax_proxy.dart | 25 + .../lib/src/focus_metering_action.dart | 13 +- .../lib/src/metering_point.dart | 17 +- .../pigeons/camerax_library.dart | 11 +- .../camera_android_camerax/pubspec.yaml | 2 +- .../test/android_camera_camerax_test.dart | 524 +++++++++++++++++- .../camera2_camera_control_test.mocks.dart | 63 ++- .../test/camera_control_test.dart | 114 +++- .../test/camera_control_test.mocks.dart | 31 +- .../test/focus_metering_action_test.dart | 3 + .../focus_metering_action_test.mocks.dart | 36 +- .../focus_metering_result_test.mocks.dart | 32 +- .../test/metering_point_test.dart | 22 +- .../test/metering_point_test.mocks.dart | 105 +++- .../test/test_camerax_library.g.dart | 17 +- 28 files changed, 1512 insertions(+), 185 deletions(-) diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index e97f2a550309..4f63c42d1f91 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.5.0+34 + +* Implements `setFocusPoint`, `setExposurePoint`, and `setExposureOffset`. + ## 0.5.0+33 * Fixes typo in `README.md`. diff --git a/packages/camera/camera_android_camerax/README.md b/packages/camera/camera_android_camerax/README.md index 13967faa2001..48674de44cf8 100644 --- a/packages/camera/camera_android_camerax/README.md +++ b/packages/camera/camera_android_camerax/README.md @@ -24,20 +24,19 @@ dependencies: ## Missing features and limitations - ### 240p resolution configuration for video recording 240p resolution configuration for video recording is unsupported by CameraX, and thus, the plugin will fall back to 480p if configured with a `ResolutionPreset`. -### Exposure mode, point, & offset configuration \[[Issue #120468][120468]\] +### Exposure mode configuration \[[Issue #120468][120468]\] -`setExposureMode`, `setExposurePoint`, & `setExposureOffset` are unimplemented. +`setExposureMode`is unimplemented. -### Focus mode & point configuration \[[Issue #120467][120467]\] +### Focus mode configuration \[[Issue #120467][120467]\] -`setFocusMode` & `setFocusPoint` are unimplemented. +`setFocusMode` is unimplemented. ### Setting maximum duration and stream options for video capture diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java index b0e0493cbe2a..ea9f3ed4d06c 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java @@ -27,6 +27,7 @@ public final class CameraAndroidCameraxPlugin implements FlutterPlugin, Activity @VisibleForTesting @Nullable public ImageCaptureHostApiImpl imageCaptureHostApiImpl; @VisibleForTesting @Nullable public CameraControlHostApiImpl cameraControlHostApiImpl; @VisibleForTesting @Nullable public SystemServicesHostApiImpl systemServicesHostApiImpl; + @VisibleForTesting @Nullable public MeteringPointHostApiImpl meteringPointHostApiImpl; @VisibleForTesting public @Nullable DeviceOrientationManagerHostApiImpl deviceOrientationManagerHostApiImpl; @@ -119,6 +120,12 @@ public void setUp( cameraControlHostApiImpl = new CameraControlHostApiImpl(binaryMessenger, instanceManager, context); GeneratedCameraXLibrary.CameraControlHostApi.setup(binaryMessenger, cameraControlHostApiImpl); + GeneratedCameraXLibrary.FocusMeteringActionHostApi.setup( + binaryMessenger, new FocusMeteringActionHostApiImpl(instanceManager)); + GeneratedCameraXLibrary.FocusMeteringResultHostApi.setup( + binaryMessenger, new FocusMeteringResultHostApiImpl(instanceManager)); + meteringPointHostApiImpl = new MeteringPointHostApiImpl(instanceManager); + GeneratedCameraXLibrary.MeteringPointHostApi.setup(binaryMessenger, meteringPointHostApiImpl); } @Override @@ -238,5 +245,8 @@ public void updateActivity(@Nullable Activity activity) { if (deviceOrientationManagerHostApiImpl != null) { deviceOrientationManagerHostApiImpl.setActivity(activity); } + if (meteringPointHostApiImpl != null) { + meteringPointHostApiImpl.setActivity(activity); + } } } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraControlHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraControlHostApiImpl.java index ba26e91c9622..ba4318a8927f 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraControlHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraControlHostApiImpl.java @@ -83,6 +83,12 @@ public void onSuccess(Void voidResult) { } public void onFailure(Throwable t) { + if (t instanceof CameraControl.OperationCanceledException) { + // Operation was canceled due to camera being closed or a new request was submitted, which + // is not actionable and should not block a new value from potentially being submitted. + result.success(null); + return; + } result.error(t); } }, @@ -94,6 +100,9 @@ public void onFailure(Throwable t) { * *

Will trigger an auto focus action and enable auto focus/auto exposure/auto white balance * metering regions. + * + *

Will send a {@link GeneratedCameraXLibrary.Result} with a null result if operation was + * canceled. */ public void startFocusAndMetering( @NonNull CameraControl cameraControl, @@ -117,6 +126,12 @@ public void onSuccess(FocusMeteringResult focusMeteringResult) { } public void onFailure(Throwable t) { + if (t instanceof CameraControl.OperationCanceledException) { + // Operation was canceled due to camera being closed or a new request was submitted, which + // is not actionable and should not block a new value from potentially being submitted. + result.success(null); + return; + } result.error(t); } }, @@ -152,6 +167,9 @@ public void onFailure(Throwable t) { *

The exposure compensation value set on the camera must be within the range of {@code * ExposureState#getExposureCompensationRange()} for the current {@code ExposureState} for the * call to succeed. + * + *

Will send a {@link GeneratedCameraXLibrary.Result} with a null result if operation was + * canceled. */ public void setExposureCompensationIndex( @NonNull CameraControl cameraControl, @NonNull Long index, @NonNull Result result) { @@ -166,6 +184,12 @@ public void onSuccess(Integer integerResult) { } public void onFailure(Throwable t) { + if (t instanceof CameraControl.OperationCanceledException) { + // Operation was canceled due to camera being closed or a new request was submitted, which + // is not actionable and should not block a new value from potentially being submitted. + result.success(null); + return; + } result.error(t); } }, diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java index 08f8ea2da9ff..ac92427314f9 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java @@ -83,14 +83,14 @@ private CameraStateType(final int index) { *

If you need to add another type to support a type S to use a LiveData in this plugin, * ensure the following is done on the Dart side: * - *

* In `../lib/src/live_data.dart`, add new cases for S in + *

* In `camera_android_camerax/lib/src/live_data.dart`, add new cases for S in * `_LiveDataHostApiImpl#getValueFromInstances` to get the current value of type S from a * LiveData instance and in `LiveDataFlutterApiImpl#create` to create the expected type of * LiveData when requested. * *

On the native side, ensure the following is done: * - *

* Update `LiveDataHostApiImpl#getValue` is updated to properly return identifiers for + *

* Make sure `LiveDataHostApiImpl#getValue` is updated to properly return identifiers for * instances of type S. * Update `ObserverFlutterApiWrapper#onChanged` to properly handle * receiving calls with instances of type S if a LiveData instance is observed. */ @@ -146,6 +146,24 @@ private VideoResolutionFallbackRule(final int index) { } } + /** + * The types of capture request options this plugin currently supports. + * + *

If you need to add another option to support, ensure the following is done on the Dart side: + * + *

* In `camera_android_camerax/lib/src/capture_request_options.dart`, add new cases for this + * option in `_CaptureRequestOptionsHostApiImpl#createFromInstances` to create the expected Map + * entry of option key index and value to send to the native side. + * + *

On the native side, ensure the following is done: + * + *

* Update `CaptureRequestOptionsHostApiImpl#create` to set the correct `CaptureRequest` key + * with a valid value type for this option. + * + *

See https://developer.android.com/reference/android/hardware/camera2/CaptureRequest for the + * sorts of capture request options that can be supported via CameraX's interoperability with + * Camera2. + */ public enum CaptureRequestKeySupportedType { CONTROL_AE_LOCK(0); @@ -3899,7 +3917,11 @@ public void create(@NonNull Long identifierArg, @NonNull Reply callback) { public interface MeteringPointHostApi { void create( - @NonNull Long identifier, @NonNull Double x, @NonNull Double y, @Nullable Double size); + @NonNull Long identifier, + @NonNull Double x, + @NonNull Double y, + @Nullable Double size, + @NonNull Long cameraInfoId); @NonNull Double getDefaultPointSize(); @@ -3927,12 +3949,14 @@ static void setup( Double xArg = (Double) args.get(1); Double yArg = (Double) args.get(2); Double sizeArg = (Double) args.get(3); + Number cameraInfoIdArg = (Number) args.get(4); try { api.create( (identifierArg == null) ? null : identifierArg.longValue(), xArg, yArg, - sizeArg); + sizeArg, + (cameraInfoIdArg == null) ? null : cameraInfoIdArg.longValue()); wrapped.add(0, null); } catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/MeteringPointHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/MeteringPointHostApiImpl.java index 56ffcfb2e391..36306253ed13 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/MeteringPointHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/MeteringPointHostApiImpl.java @@ -4,13 +4,20 @@ package io.flutter.plugins.camerax; +import android.app.Activity; +import android.content.Context; +import android.os.Build; +import android.view.Display; +import android.view.WindowManager; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import androidx.camera.core.CameraInfo; +import androidx.camera.core.DisplayOrientedMeteringPointFactory; import androidx.camera.core.MeteringPoint; import androidx.camera.core.MeteringPointFactory; -import androidx.camera.core.SurfaceOrientedMeteringPointFactory; import io.flutter.plugins.camerax.GeneratedCameraXLibrary.MeteringPointHostApi; +import java.util.Objects; /** * Host API implementation for {@link MeteringPoint}. @@ -25,17 +32,32 @@ public class MeteringPointHostApiImpl implements MeteringPointHostApi { /** Proxy for constructor and static methods of {@link MeteringPoint}. */ @VisibleForTesting public static class MeteringPointProxy { + Activity activity; /** * Creates a surface oriented {@link MeteringPoint} with the specified x, y, and size. * - *

A {@link SurfaceOrientedMeteringPointFactory} is used to construct the {@link - * MeteringPoint} because underlying the camera preview that this plugin uses is a Flutter - * texture that is backed by a {@link Surface} created by the Flutter Android embedding. + *

A {@link DisplayOrientedMeteringPointFactory} is used to construct the {@link + * MeteringPoint} because this factory handles the transformation of specified coordinates based + * on camera information and the device orientation automatically. */ @NonNull - public MeteringPoint create(@NonNull Double x, @NonNull Double y, @Nullable Double size) { - SurfaceOrientedMeteringPointFactory factory = getSurfaceOrientedMeteringPointFactory(1f, 1f); + public MeteringPoint create( + @NonNull Double x, + @NonNull Double y, + @Nullable Double size, + @NonNull CameraInfo cameraInfo) { + Display display = null; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + display = activity.getDisplay(); + } else { + display = getDefaultDisplayForAndroidVersionBelowR(activity); + } + + DisplayOrientedMeteringPointFactory factory = + getDisplayOrientedMeteringPointFactory(display, cameraInfo, 1f, 1f); + if (size == null) { return factory.createPoint(x.floatValue(), y.floatValue()); } else { @@ -43,11 +65,18 @@ public MeteringPoint create(@NonNull Double x, @NonNull Double y, @Nullable Doub } } + @NonNull + @SuppressWarnings("deprecation") + private Display getDefaultDisplayForAndroidVersionBelowR(@NonNull Activity activity) { + return ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE)) + .getDefaultDisplay(); + } + @VisibleForTesting @NonNull - public SurfaceOrientedMeteringPointFactory getSurfaceOrientedMeteringPointFactory( - float width, float height) { - return new SurfaceOrientedMeteringPointFactory(width, height); + public DisplayOrientedMeteringPointFactory getDisplayOrientedMeteringPointFactory( + @NonNull Display display, @NonNull CameraInfo cameraInfo, float width, float height) { + return new DisplayOrientedMeteringPointFactory(display, cameraInfo, width, height); } /** @@ -81,10 +110,23 @@ public MeteringPointHostApiImpl(@NonNull InstanceManager instanceManager) { this.proxy = proxy; } + public void setActivity(@NonNull Activity activity) { + this.proxy.activity = activity; + } + @Override public void create( - @NonNull Long identifier, @NonNull Double x, @NonNull Double y, @Nullable Double size) { - MeteringPoint meteringPoint = proxy.create(x, y, size); + @NonNull Long identifier, + @NonNull Double x, + @NonNull Double y, + @Nullable Double size, + @NonNull Long cameraInfoId) { + MeteringPoint meteringPoint = + proxy.create( + x, + y, + size, + (CameraInfo) Objects.requireNonNull(instanceManager.getInstance(cameraInfoId))); instanceManager.addDartCreatedInstance(meteringPoint, identifier); } diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraAndroidCameraxPluginTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraAndroidCameraxPluginTest.java index 8c9daf8e0143..f9f4be6db6e0 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraAndroidCameraxPluginTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraAndroidCameraxPluginTest.java @@ -92,6 +92,8 @@ public void onAttachedToActivity_setsActivityAsNeededAndPermissionsRegistry() { mock(SystemServicesHostApiImpl.class); final DeviceOrientationManagerHostApiImpl mockDeviceOrientationManagerHostApiImpl = mock(DeviceOrientationManagerHostApiImpl.class); + final MeteringPointHostApiImpl mockMeteringPointHostApiImpl = + mock(MeteringPointHostApiImpl.class); final ArgumentCaptor permissionsRegistryCaptor = ArgumentCaptor.forClass(PermissionsRegistry.class); @@ -103,6 +105,7 @@ public void onAttachedToActivity_setsActivityAsNeededAndPermissionsRegistry() { plugin.liveDataHostApiImpl = mock(LiveDataHostApiImpl.class); plugin.systemServicesHostApiImpl = mockSystemServicesHostApiImpl; plugin.deviceOrientationManagerHostApiImpl = mockDeviceOrientationManagerHostApiImpl; + plugin.meteringPointHostApiImpl = mockMeteringPointHostApiImpl; plugin.onAttachedToEngine(flutterPluginBinding); plugin.onAttachedToActivity(activityPluginBinding); @@ -110,6 +113,7 @@ public void onAttachedToActivity_setsActivityAsNeededAndPermissionsRegistry() { // Check Activity references are set. verify(mockSystemServicesHostApiImpl).setActivity(mockActivity); verify(mockDeviceOrientationManagerHostApiImpl).setActivity(mockActivity); + verify(mockMeteringPointHostApiImpl).setActivity(mockActivity); // Check permissions registry reference is set. verify(mockSystemServicesHostApiImpl) @@ -129,11 +133,14 @@ public void onAttachedToActivity_setsActivityAsNeededAndPermissionsRegistry() { mock(SystemServicesHostApiImpl.class); final DeviceOrientationManagerHostApiImpl mockDeviceOrientationManagerHostApiImpl = mock(DeviceOrientationManagerHostApiImpl.class); + final MeteringPointHostApiImpl mockMeteringPointHostApiImpl = + mock(MeteringPointHostApiImpl.class); plugin.processCameraProviderHostApiImpl = mockProcessCameraProviderHostApiImpl; plugin.liveDataHostApiImpl = mockLiveDataHostApiImpl; plugin.systemServicesHostApiImpl = mockSystemServicesHostApiImpl; plugin.deviceOrientationManagerHostApiImpl = mockDeviceOrientationManagerHostApiImpl; + plugin.meteringPointHostApiImpl = mockMeteringPointHostApiImpl; plugin.onAttachedToEngine(flutterPluginBinding); plugin.onDetachedFromActivityForConfigChanges(); @@ -142,6 +149,7 @@ public void onAttachedToActivity_setsActivityAsNeededAndPermissionsRegistry() { verify(mockLiveDataHostApiImpl).setLifecycleOwner(null); verify(mockSystemServicesHostApiImpl).setActivity(null); verify(mockDeviceOrientationManagerHostApiImpl).setActivity(null); + verify(mockMeteringPointHostApiImpl).setActivity(null); } @Test @@ -251,6 +259,8 @@ public void onReattachedToActivityForConfigChanges_setsActivityAndPermissionsReg mock(CameraControlHostApiImpl.class); final DeviceOrientationManagerHostApiImpl mockDeviceOrientationManagerHostApiImpl = mock(DeviceOrientationManagerHostApiImpl.class); + final MeteringPointHostApiImpl mockMeteringPointHostApiImpl = + mock(MeteringPointHostApiImpl.class); final ArgumentCaptor permissionsRegistryCaptor = ArgumentCaptor.forClass(PermissionsRegistry.class); @@ -265,6 +275,7 @@ public void onReattachedToActivityForConfigChanges_setsActivityAndPermissionsReg plugin.imageAnalysisHostApiImpl = mockImageAnalysisHostApiImpl; plugin.cameraControlHostApiImpl = mockCameraControlHostApiImpl; plugin.deviceOrientationManagerHostApiImpl = mockDeviceOrientationManagerHostApiImpl; + plugin.meteringPointHostApiImpl = mockMeteringPointHostApiImpl; plugin.liveDataHostApiImpl = mock(LiveDataHostApiImpl.class); plugin.onAttachedToEngine(flutterPluginBinding); @@ -273,6 +284,7 @@ public void onReattachedToActivityForConfigChanges_setsActivityAndPermissionsReg // Check Activity references are set. verify(mockSystemServicesHostApiImpl).setActivity(mockActivity); verify(mockDeviceOrientationManagerHostApiImpl).setActivity(mockActivity); + verify(mockMeteringPointHostApiImpl).setActivity(mockActivity); // Check Activity as Context references are set. verify(mockProcessCameraProviderHostApiImpl).setContext(mockActivity); @@ -300,11 +312,14 @@ public void onDetachedFromActivity_removesReferencesToActivityPluginBindingAndAc final LiveDataHostApiImpl mockLiveDataHostApiImpl = mock(LiveDataHostApiImpl.class); final DeviceOrientationManagerHostApiImpl mockDeviceOrientationManagerHostApiImpl = mock(DeviceOrientationManagerHostApiImpl.class); + final MeteringPointHostApiImpl mockMeteringPointHostApiImpl = + mock(MeteringPointHostApiImpl.class); plugin.processCameraProviderHostApiImpl = mockProcessCameraProviderHostApiImpl; plugin.liveDataHostApiImpl = mockLiveDataHostApiImpl; plugin.systemServicesHostApiImpl = mockSystemServicesHostApiImpl; plugin.deviceOrientationManagerHostApiImpl = mockDeviceOrientationManagerHostApiImpl; + plugin.meteringPointHostApiImpl = mockMeteringPointHostApiImpl; plugin.onAttachedToEngine(flutterPluginBinding); plugin.onDetachedFromActivityForConfigChanges(); @@ -313,6 +328,7 @@ public void onDetachedFromActivity_removesReferencesToActivityPluginBindingAndAc verify(mockLiveDataHostApiImpl).setLifecycleOwner(null); verify(mockSystemServicesHostApiImpl).setActivity(null); verify(mockDeviceOrientationManagerHostApiImpl).setActivity(null); + verify(mockMeteringPointHostApiImpl).setActivity(null); } @Test diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraControlTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraControlTest.java index 42ace7a51fb5..a0fe8f977ee0 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraControlTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraControlTest.java @@ -149,6 +149,22 @@ public void setZoomRatio_setsZoomAsExpected() { failedSetZoomRatioCallback.onFailure(testThrowable); verify(failedMockResult).error(testThrowable); + + // Test response to canceled operation. + @SuppressWarnings("unchecked") + final GeneratedCameraXLibrary.Result canceledOpResult = + mock(GeneratedCameraXLibrary.Result.class); + final CameraControl.OperationCanceledException canceledOpThrowable = + mock(CameraControl.OperationCanceledException.class); + cameraControlHostApiImpl.setZoomRatio(cameraControlIdentifier, zoomRatio, canceledOpResult); + mockedFutures.verify( + () -> Futures.addCallback(eq(setZoomRatioFuture), futureCallbackCaptor.capture(), any())); + mockedFutures.clearInvocations(); + + FutureCallback canceledOpCallback = futureCallbackCaptor.getValue(); + + canceledOpCallback.onFailure(canceledOpThrowable); + verify(canceledOpResult).success(null); } } @@ -212,6 +228,25 @@ public void startFocusAndMetering_startsFocusAndMeteringAsExpected() { failedCallback.onFailure(testThrowable); verify(failedMockResult).error(testThrowable); + + // Test response to canceled operation. + @SuppressWarnings("unchecked") + final GeneratedCameraXLibrary.Result canceledOpResult = + mock(GeneratedCameraXLibrary.Result.class); + final CameraControl.OperationCanceledException canceledOpThrowable = + mock(CameraControl.OperationCanceledException.class); + cameraControlHostApiImpl.startFocusAndMetering( + cameraControlIdentifier, mockActionId, canceledOpResult); + mockedFutures.verify( + () -> + Futures.addCallback( + eq(startFocusAndMeteringFuture), futureCallbackCaptor.capture(), any())); + mockedFutures.clearInvocations(); + + FutureCallback canceledOpCallback = futureCallbackCaptor.getValue(); + + canceledOpCallback.onFailure(canceledOpThrowable); + verify(canceledOpResult).success(null); } } @@ -326,6 +361,25 @@ public void setExposureCompensationIndex_setsExposureCompensationIndexAsExpected failedCallback.onFailure(testThrowable); verify(failedMockResult).error(testThrowable); + + // Test response to canceled operation. + @SuppressWarnings("unchecked") + final GeneratedCameraXLibrary.Result canceledOpResult = + mock(GeneratedCameraXLibrary.Result.class); + final CameraControl.OperationCanceledException canceledOpThrowable = + mock(CameraControl.OperationCanceledException.class); + cameraControlHostApiImpl.setExposureCompensationIndex( + cameraControlIdentifier, index, canceledOpResult); + mockedFutures.verify( + () -> + Futures.addCallback( + eq(setExposureCompensationIndexFuture), futureCallbackCaptor.capture(), any())); + mockedFutures.clearInvocations(); + + FutureCallback canceledOpCallback = futureCallbackCaptor.getValue(); + + canceledOpCallback.onFailure(canceledOpThrowable); + verify(canceledOpResult).success(null); } } diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/MeteringPointTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/MeteringPointTest.java index f245eb53124e..0734f6ba6a9d 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/MeteringPointTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/MeteringPointTest.java @@ -10,21 +10,30 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.Activity; +import android.content.Context; +import android.view.Display; +import android.view.WindowManager; +import androidx.camera.core.CameraInfo; +import androidx.camera.core.DisplayOrientedMeteringPointFactory; import androidx.camera.core.MeteringPoint; import androidx.camera.core.MeteringPointFactory; -import androidx.camera.core.SurfaceOrientedMeteringPointFactory; import io.flutter.plugin.common.BinaryMessenger; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.mockito.stubbing.Answer; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +@RunWith(RobolectricTestRunner.class) public class MeteringPointTest { @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @@ -44,49 +53,158 @@ public void tearDown() { } @Test - public void hostApiCreate_createsExpectedMeteringPointWithSizeSpecified() { - MeteringPointHostApiImpl.MeteringPointProxy proxySpy = + @Config(sdk = 30) + public void hostApiCreate_createsExpectedMeteringPointWithSizeSpecified_AboveAndroid30() { + final MeteringPointHostApiImpl.MeteringPointProxy proxySpy = spy(new MeteringPointHostApiImpl.MeteringPointProxy()); - MeteringPointHostApiImpl hostApi = new MeteringPointHostApiImpl(testInstanceManager, proxySpy); + final MeteringPointHostApiImpl hostApi = + new MeteringPointHostApiImpl(testInstanceManager, proxySpy); final Long meteringPointIdentifier = 78L; - final Float x = 0.3f; - final Float y = 0.2f; - final Float size = 6f; + final Float x = 0.25f; + final Float y = 0.18f; + final Float size = 0.6f; final Float surfaceWidth = 1f; final Float surfaceHeight = 1f; - SurfaceOrientedMeteringPointFactory mockSurfaceOrientedMeteringPointFactory = - mock(SurfaceOrientedMeteringPointFactory.class); - - when(proxySpy.getSurfaceOrientedMeteringPointFactory(surfaceWidth, surfaceHeight)) - .thenReturn(mockSurfaceOrientedMeteringPointFactory); - when(mockSurfaceOrientedMeteringPointFactory.createPoint(x, y, size)).thenReturn(meteringPoint); - - hostApi.create(meteringPointIdentifier, x.doubleValue(), y.doubleValue(), size.doubleValue()); - - verify(mockSurfaceOrientedMeteringPointFactory).createPoint(x, y, size); + final DisplayOrientedMeteringPointFactory mockDisplayOrientedMeteringPointFactory = + mock(DisplayOrientedMeteringPointFactory.class); + final Activity mockActivity = mock(Activity.class); + final Display mockDisplay = mock(Display.class); + final CameraInfo mockCameraInfo = mock(CameraInfo.class); + final long mockCameraInfoId = 55L; + + hostApi.setActivity(mockActivity); + testInstanceManager.addDartCreatedInstance(mockCameraInfo, mockCameraInfoId); + + when(mockActivity.getDisplay()).thenReturn(mockDisplay); + when(proxySpy.getDisplayOrientedMeteringPointFactory( + mockDisplay, mockCameraInfo, surfaceWidth, surfaceHeight)) + .thenReturn(mockDisplayOrientedMeteringPointFactory); + when(mockDisplayOrientedMeteringPointFactory.createPoint(x, y, size)).thenReturn(meteringPoint); + + hostApi.create( + meteringPointIdentifier, + x.doubleValue(), + y.doubleValue(), + size.doubleValue(), + mockCameraInfoId); + + verify(mockDisplayOrientedMeteringPointFactory).createPoint(x, y, size); assertEquals(testInstanceManager.getInstance(meteringPointIdentifier), meteringPoint); } @Test - public void hostApiCreate_createsExpectedMeteringPointWithoutSizeSpecified() { - MeteringPointHostApiImpl.MeteringPointProxy proxySpy = + @Config(sdk = 29) + @SuppressWarnings("deprecation") + public void hostApiCreate_createsExpectedMeteringPointWithSizeSpecified_BelowAndroid30() { + final MeteringPointHostApiImpl.MeteringPointProxy proxySpy = spy(new MeteringPointHostApiImpl.MeteringPointProxy()); - MeteringPointHostApiImpl hostApi = new MeteringPointHostApiImpl(testInstanceManager, proxySpy); + final MeteringPointHostApiImpl hostApi = + new MeteringPointHostApiImpl(testInstanceManager, proxySpy); final Long meteringPointIdentifier = 78L; final Float x = 0.3f; final Float y = 0.2f; + final Float size = 6f; final Float surfaceWidth = 1f; final Float surfaceHeight = 1f; - SurfaceOrientedMeteringPointFactory mockSurfaceOrientedMeteringPointFactory = - mock(SurfaceOrientedMeteringPointFactory.class); - - when(proxySpy.getSurfaceOrientedMeteringPointFactory(surfaceWidth, surfaceHeight)) - .thenReturn(mockSurfaceOrientedMeteringPointFactory); - when(mockSurfaceOrientedMeteringPointFactory.createPoint(x, y)).thenReturn(meteringPoint); + final DisplayOrientedMeteringPointFactory mockDisplayOrientedMeteringPointFactory = + mock(DisplayOrientedMeteringPointFactory.class); + final Activity mockActivity = mock(Activity.class); + final WindowManager mockWindowManager = mock(WindowManager.class); + final Display mockDisplay = mock(Display.class); + final CameraInfo mockCameraInfo = mock(CameraInfo.class); + final long mockCameraInfoId = 5L; + + hostApi.setActivity(mockActivity); + testInstanceManager.addDartCreatedInstance(mockCameraInfo, mockCameraInfoId); + + when(mockActivity.getSystemService(Context.WINDOW_SERVICE)).thenReturn(mockWindowManager); + when(mockWindowManager.getDefaultDisplay()).thenReturn(mockDisplay); + when(proxySpy.getDisplayOrientedMeteringPointFactory( + mockDisplay, mockCameraInfo, surfaceWidth, surfaceHeight)) + .thenReturn(mockDisplayOrientedMeteringPointFactory); + when(mockDisplayOrientedMeteringPointFactory.createPoint(x, y, size)).thenReturn(meteringPoint); + + hostApi.create( + meteringPointIdentifier, + x.doubleValue(), + y.doubleValue(), + size.doubleValue(), + mockCameraInfoId); + + verify(mockDisplayOrientedMeteringPointFactory).createPoint(x, y, size); + assertEquals(testInstanceManager.getInstance(meteringPointIdentifier), meteringPoint); + } - hostApi.create(meteringPointIdentifier, x.doubleValue(), y.doubleValue(), null); + @Test + @Config(sdk = 30) + public void hostApiCreate_createsExpectedMeteringPointWithoutSizeSpecified_AboveAndroid30() { + final MeteringPointHostApiImpl.MeteringPointProxy proxySpy = + spy(new MeteringPointHostApiImpl.MeteringPointProxy()); + final MeteringPointHostApiImpl hostApi = + new MeteringPointHostApiImpl(testInstanceManager, proxySpy); + final Long meteringPointIdentifier = 78L; + final Float x = 0.23f; + final Float y = 0.32f; + final Float surfaceWidth = 1f; + final Float surfaceHeight = 1f; + final DisplayOrientedMeteringPointFactory mockDisplayOrientedMeteringPointFactory = + mock(DisplayOrientedMeteringPointFactory.class); + final Activity mockActivity = mock(Activity.class); + final Display mockDisplay = mock(Display.class); + final CameraInfo mockCameraInfo = mock(CameraInfo.class); + final long mockCameraInfoId = 6L; + + hostApi.setActivity(mockActivity); + testInstanceManager.addDartCreatedInstance(mockCameraInfo, mockCameraInfoId); + + when(mockActivity.getDisplay()).thenReturn(mockDisplay); + when(proxySpy.getDisplayOrientedMeteringPointFactory( + mockDisplay, mockCameraInfo, surfaceWidth, surfaceHeight)) + .thenReturn(mockDisplayOrientedMeteringPointFactory); + when(mockDisplayOrientedMeteringPointFactory.createPoint(x, y)).thenReturn(meteringPoint); + + hostApi.create( + meteringPointIdentifier, x.doubleValue(), y.doubleValue(), null, mockCameraInfoId); + + verify(mockDisplayOrientedMeteringPointFactory).createPoint(x, y); + assertEquals(testInstanceManager.getInstance(meteringPointIdentifier), meteringPoint); + } - verify(mockSurfaceOrientedMeteringPointFactory).createPoint(x, y); + @Test + @Config(sdk = 29) + @SuppressWarnings("deprecation") + public void hostApiCreate_createsExpectedMeteringPointWithoutSizeSpecified_BelowAndroid30() { + final MeteringPointHostApiImpl.MeteringPointProxy proxySpy = + spy(new MeteringPointHostApiImpl.MeteringPointProxy()); + final MeteringPointHostApiImpl hostApi = + new MeteringPointHostApiImpl(testInstanceManager, proxySpy); + final Long meteringPointIdentifier = 78L; + final Float x = 0.1f; + final Float y = 0.8f; + final Float surfaceWidth = 1f; + final Float surfaceHeight = 1f; + final DisplayOrientedMeteringPointFactory mockDisplayOrientedMeteringPointFactory = + mock(DisplayOrientedMeteringPointFactory.class); + final Activity mockActivity = mock(Activity.class); + final WindowManager mockWindowManager = mock(WindowManager.class); + final Display mockDisplay = mock(Display.class); + final CameraInfo mockCameraInfo = mock(CameraInfo.class); + final long mockCameraInfoId = 7L; + + hostApi.setActivity(mockActivity); + testInstanceManager.addDartCreatedInstance(mockCameraInfo, mockCameraInfoId); + + when(mockActivity.getSystemService(Context.WINDOW_SERVICE)).thenReturn(mockWindowManager); + when(mockWindowManager.getDefaultDisplay()).thenReturn(mockDisplay); + when(proxySpy.getDisplayOrientedMeteringPointFactory( + mockDisplay, mockCameraInfo, surfaceWidth, surfaceHeight)) + .thenReturn(mockDisplayOrientedMeteringPointFactory); + when(mockDisplayOrientedMeteringPointFactory.createPoint(x, y)).thenReturn(meteringPoint); + + hostApi.create( + meteringPointIdentifier, x.doubleValue(), y.doubleValue(), null, mockCameraInfoId); + + verify(mockDisplayOrientedMeteringPointFactory).createPoint(x, y); assertEquals(testInstanceManager.getInstance(meteringPointIdentifier), meteringPoint); } diff --git a/packages/camera/camera_android_camerax/example/lib/main.dart b/packages/camera/camera_android_camerax/example/lib/main.dart index 4484c5dac982..adb868373712 100644 --- a/packages/camera/camera_android_camerax/example/lib/main.dart +++ b/packages/camera/camera_android_camerax/example/lib/main.dart @@ -282,14 +282,15 @@ class _CameraExampleHomeState extends State IconButton( icon: const Icon(Icons.exposure), color: Colors.blue, - onPressed: - () {}, // TODO(camsim99): Add functionality back here. + onPressed: controller != null + ? onExposureModeButtonPressed + : null, ), IconButton( icon: const Icon(Icons.filter_center_focus), color: Colors.blue, onPressed: - () {}, // TODO(camsim99): Add functionality back here. + controller != null ? onFocusModeButtonPressed : null, ) ] : [], @@ -394,8 +395,13 @@ class _CameraExampleHomeState extends State style: styleAuto, onPressed: () {}, // TODO(camsim99): Add functionality back here. - onLongPress: - () {}, // TODO(camsim99): Add functionality back here., + onLongPress: () { + if (controller != null) { + CameraPlatform.instance + .setExposurePoint(controller!.cameraId, null); + showInSnackBar('Resetting exposure point'); + } + }, child: const Text('AUTO'), ), TextButton( @@ -406,8 +412,9 @@ class _CameraExampleHomeState extends State ), TextButton( style: styleLocked, - onPressed: - () {}, // TODO(camsim99): Add functionality back here. + onPressed: controller != null + ? () => controller!.setExposureOffset(0.0) + : null, child: const Text('RESET OFFSET'), ), ], @@ -468,8 +475,13 @@ class _CameraExampleHomeState extends State style: styleAuto, onPressed: () {}, // TODO(camsim99): Add functionality back here. - onLongPress: - () {}, // TODO(camsim99): Add functionality back here. + onLongPress: () { + if (controller != null) { + CameraPlatform.instance + .setFocusPoint(controller!.cameraId, null); + } + showInSnackBar('Resetting focus point'); + }, child: const Text('AUTO'), ), TextButton( diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart index b05788d68a43..deddf4709c92 100644 --- a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart @@ -3,10 +3,12 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:math' show Point; import 'package:async/async.dart'; import 'package:camera_platform_interface/camera_platform_interface.dart'; -import 'package:flutter/services.dart' show DeviceOrientation; +import 'package:flutter/services.dart' + show DeviceOrientation, PlatformException; import 'package:flutter/widgets.dart'; import 'package:stream_transform/stream_transform.dart'; @@ -21,10 +23,12 @@ import 'camerax_proxy.dart'; import 'device_orientation_manager.dart'; import 'exposure_state.dart'; import 'fallback_strategy.dart'; +import 'focus_metering_action.dart'; import 'image_analysis.dart'; import 'image_capture.dart'; import 'image_proxy.dart'; import 'live_data.dart'; +import 'metering_point.dart'; import 'observer.dart'; import 'pending_recording.dart'; import 'plane_proxy.dart'; @@ -69,6 +73,9 @@ class AndroidCameraCameraX extends CameraPlatform { @visibleForTesting CameraInfo? cameraInfo; + /// The [CameraControl] instance that corresponds to the [camera] instance. + late CameraControl cameraControl; + /// The [LiveData] of the [CameraState] that represents the state of the /// [camera] instance. LiveData? liveCameraState; @@ -180,6 +187,16 @@ class AndroidCameraCameraX extends CameraPlatform { /// for an example on how setting target rotations for [UseCase]s works. bool shouldSetDefaultRotation = false; + /// The currently set [FocusMeteringAction] used to enable auto-focus and + /// auto-exposure. + @visibleForTesting + FocusMeteringAction? currentFocusMeteringAction; + + /// Error code indicating that exposure compensation is not supported by + /// CameraX for the device. + static const String exposureCompensationNotSupported = + 'exposureCompensationNotSupported'; + /// Returns list of all available cameras and their descriptions. @override Future> availableCameras() async { @@ -430,6 +447,18 @@ class AndroidCameraCameraX extends CameraPlatform { captureOrientationLocked = false; } + /// Sets the exposure point for automatically determining the exposure values. + /// + /// Supplying `null` for the [point] argument will result in resetting to the + /// original exposure point value. + /// + /// [cameraId] is not used. + @override + Future setExposurePoint(int cameraId, Point? point) async { + await _startFocusAndMeteringFor( + point: point, meteringMode: FocusMeteringAction.flagAe); + } + /// Gets the minimum supported exposure offset for the selected camera in EV units. /// /// [cameraId] not used. @@ -452,13 +481,71 @@ class AndroidCameraCameraX extends CameraPlatform { /// Gets the supported step size for exposure offset for the selected camera in EV units. /// - /// Returns 0 when exposure compensation is not supported. + /// Returns -1 if exposure compensation is not supported for the device. /// /// [cameraId] not used. @override Future getExposureOffsetStepSize(int cameraId) async { final ExposureState exposureState = await cameraInfo!.getExposureState(); - return exposureState.exposureCompensationStep; + final double exposureOffsetStepSize = + exposureState.exposureCompensationStep; + if (exposureOffsetStepSize == 0) { + // CameraX returns a step size of 0 if exposure compensation is not + // supported for the device. + return -1; + } + return exposureOffsetStepSize; + } + + /// Sets the exposure offset for the selected camera. + /// + /// The supplied [offset] value should be in EV units. 1 EV unit represents a + /// doubling in brightness. It should be between the minimum and maximum offsets + /// obtained through `getMinExposureOffset` and `getMaxExposureOffset` respectively. + /// Throws a `CameraException` when trying to set exposure offset on a device + /// that doesn't support exposure compensationan or if setting the offset fails, + /// like in the case that an illegal offset is supplied. + /// + /// When the supplied [offset] value does not align with the step size obtained + /// through `getExposureStepSize`, it will automatically be rounded to the nearest step. + /// + /// Returns the (rounded) offset value that was set. + @override + Future setExposureOffset(int cameraId, double offset) async { + final double exposureOffsetStepSize = + (await cameraInfo!.getExposureState()).exposureCompensationStep; + if (exposureOffsetStepSize == 0) { + throw CameraException(exposureCompensationNotSupported, + 'Exposure compensation not supported'); + } + + // (Exposure compensation index) * (exposure offset step size) = + // (exposure offset). + final int roundedExposureCompensationIndex = + (offset / exposureOffsetStepSize).round(); + + try { + await cameraControl + .setExposureCompensationIndex(roundedExposureCompensationIndex); + } on PlatformException catch (e) { + throw CameraException( + 'setExposureOffsetFailed', + e.message ?? + 'Setting the camera exposure compensation index failed.'); + } + return roundedExposureCompensationIndex * exposureOffsetStepSize; + } + + /// Sets the focus point for automatically determining the focus values. + /// + /// Supplying `null` for the [point] argument will result in resetting to the + /// original focus point value. + /// + /// [cameraId] is not used. + @override + Future setFocusPoint(int cameraId, Point? point) async { + await _startFocusAndMeteringFor( + point: point, meteringMode: FocusMeteringAction.flagAf); } /// Gets the maximum supported zoom level for the selected camera. @@ -502,7 +589,6 @@ class AndroidCameraCameraX extends CameraPlatform { /// Throws a `CameraException` when an illegal zoom level is supplied. @override Future setZoomLevel(int cameraId, double zoom) async { - final CameraControl cameraControl = await camera!.getCameraControl(); await cameraControl.setZoomRatio(zoom); } @@ -587,10 +673,8 @@ class AndroidCameraCameraX extends CameraPlatform { /// respectively. @override Future setFlashMode(int cameraId, FlashMode mode) async { - CameraControl? cameraControl; // Turn off torch mode if it is enabled and not being redundantly set. if (mode != FlashMode.torch && torchEnabled) { - cameraControl = await camera!.getCameraControl(); await cameraControl.enableTorch(false); torchEnabled = false; } @@ -608,7 +692,6 @@ class AndroidCameraCameraX extends CameraPlatform { // Torch mode enabled already. return; } - cameraControl = await camera!.getCameraControl(); await cameraControl.enableTorch(true); torchEnabled = true; } @@ -833,14 +916,15 @@ class AndroidCameraCameraX extends CameraPlatform { // Methods concerning camera state: - /// Updates [cameraInfo] to the information corresponding to [camera] and - /// adds observers to the [LiveData] of the [CameraState] of the current - /// [camera], saved as [liveCameraState]. + /// Updates [cameraInfo] and [cameraControl] to the information corresponding + /// to [camera] and adds observers to the [LiveData] of the [CameraState] of + /// the current [camera], saved as [liveCameraState]. /// /// If a previous [liveCameraState] was stored, existing observers are /// removed, as well. Future _updateCameraInfoAndLiveCameraState(int cameraId) async { cameraInfo = await camera!.getCameraInfo(); + cameraControl = await camera!.getCameraControl(); await liveCameraState?.removeObservers(); liveCameraState = await cameraInfo!.getCameraState(); await liveCameraState!.observe(_createCameraClosingObserver(cameraId)); @@ -981,4 +1065,87 @@ class AndroidCameraCameraX extends CameraPlatform { return proxy.createQualitySelector( videoQuality: videoQuality, fallbackStrategy: fallbackStrategy); } + + // Methods for configuring auto-focus and auto-exposure: + + /// Starts a focus and metering action. + /// + /// This method will modify and start the current action's metering points + /// overriden with the [point] provided for the specified [meteringMode] type + /// only, with all other points of other modes left untouched. Thus, the + /// focus and metering action started will contain only the one most recently + /// set point for each metering mode: AF, AE, AWB. + /// + /// Thus, if [point] is non-null, this action includes: + /// * metering points and their modes previously added to + /// [currentFocusMeteringAction] that do not share a metering mode with + /// [point] and + /// * [point] with the specified [meteringMode]. + /// If [point] is null, this action includes only metering points and + /// their modes previously added to [currentFocusMeteringAction] that do not + /// share a metering mode with [point]. If there are no such metering + /// points, then the previously enabled focus and metering actions will be + /// canceled. + Future _startFocusAndMeteringFor( + {required Point? point, required int meteringMode}) async { + if (point == null) { + // Try to clear any metering point from previous action with the specified + // meteringMode. + if (currentFocusMeteringAction == null) { + // Attempting to clear a metering point from a previous action, but no + // such action exists. + return; + } + + // Remove metering point with specified meteringMode from current focus + // and metering action, as only one focus or exposure point may be set + // at once in this plugin. + final List<(MeteringPoint, int?)> newMeteringPointInfos = + currentFocusMeteringAction!.meteringPointInfos + .where(((MeteringPoint, int?) meteringPointInfo) => + // meteringPointInfo may technically include points without a + // mode specified, but this logic is safe because this plugin + // only uses points that explicitly have mode + // FocusMeteringAction.flagAe or FocusMeteringAction.flagAf. + meteringPointInfo.$2 != meteringMode) + .toList(); + + if (newMeteringPointInfos.isEmpty) { + // If no other metering points were specified, cancel any previously + // started focus and metering actions. + await cameraControl.cancelFocusAndMetering(); + currentFocusMeteringAction = null; + return; + } + currentFocusMeteringAction = + proxy.createFocusMeteringAction(newMeteringPointInfos); + } else if (point.x < 0 || point.x > 1 || point.y < 0 || point.y > 1) { + throw CameraException('pointInvalid', + 'The coordinates of a metering point for an auto-focus or auto-exposure action must be within (0,0) and (1,1), but point $point was provided for metering mode $meteringMode.'); + } else { + // Add new metering point with specified meteringMode, which may involve + // replacing a metering point with the same specified meteringMode from + // the current focus and metering action. + List<(MeteringPoint, int?)> newMeteringPointInfos = + <(MeteringPoint, int?)>[]; + + if (currentFocusMeteringAction != null) { + newMeteringPointInfos = currentFocusMeteringAction!.meteringPointInfos + .where(((MeteringPoint, int?) meteringPointInfo) => + // meteringPointInfo may technically include points without a + // mode specified, but this logic is safe because this plugin + // only uses points that explicitly have mode + // FocusMeteringAction.flagAe or FocusMeteringAction.flagAf. + meteringPointInfo.$2 != meteringMode) + .toList(); + } + final MeteringPoint newMeteringPoint = + proxy.createMeteringPoint(point.x, point.y, cameraInfo!); + newMeteringPointInfos.add((newMeteringPoint, meteringMode)); + currentFocusMeteringAction = + proxy.createFocusMeteringAction(newMeteringPointInfos); + } + + await cameraControl.startFocusAndMetering(currentFocusMeteringAction!); + } } diff --git a/packages/camera/camera_android_camerax/lib/src/camera_control.dart b/packages/camera/camera_android_camerax/lib/src/camera_control.dart index 9c50ffa161fd..a233011975f0 100644 --- a/packages/camera/camera_android_camerax/lib/src/camera_control.dart +++ b/packages/camera/camera_android_camerax/lib/src/camera_control.dart @@ -56,6 +56,10 @@ class CameraControl extends JavaObject { /// Will trigger an auto focus action and enable auto focus/auto exposure/ /// auto white balance metering regions. /// + /// Only one [FocusMeteringAction] is allowed to run at a time; if multiple + /// are executed in a row, only the latest one will work and other actions + /// will be canceled. + /// /// Returns null if focus and metering could not be started. Future startFocusAndMetering( FocusMeteringAction action) { @@ -74,6 +78,10 @@ class CameraControl extends JavaObject { /// of the current [ExposureState]'s `exposureCompensationRange` for the call /// to succeed. /// + /// Only one [setExposureCompensationIndex] is allowed to run at a time; if + /// multiple are executed in a row, only the latest setting will be kept in + /// the camera. + /// /// Returns null if the exposure compensation index failed to be set. Future setExposureCompensationIndex(int index) async { return _api.setExposureCompensationIndexFromInstance(this, index); @@ -133,8 +141,13 @@ class _CameraControlHostApiImpl extends CameraControlHostApi { instanceManager.getIdentifier(instance)!; final int actionIdentifier = instanceManager.getIdentifier(action)!; try { - final int focusMeteringResultId = await startFocusAndMetering( + final int? focusMeteringResultId = await startFocusAndMetering( cameraControlIdentifier, actionIdentifier); + if (focusMeteringResultId == null) { + SystemServices.cameraErrorStreamController.add( + 'Starting focus and metering was canceled due to the camera being closed or a new request being submitted.'); + return Future.value(); + } return instanceManager.getInstanceWithWeakReference( focusMeteringResultId); } on PlatformException catch (e) { @@ -158,11 +171,20 @@ class _CameraControlHostApiImpl extends CameraControlHostApi { CameraControl instance, int index) async { final int identifier = instanceManager.getIdentifier(instance)!; try { - return setExposureCompensationIndex(identifier, index); + final int? exposureCompensationIndex = + await setExposureCompensationIndex(identifier, index); + if (exposureCompensationIndex == null) { + SystemServices.cameraErrorStreamController.add( + 'Setting exposure compensation index was canceled due to the camera being closed or a new request being submitted.'); + return Future.value(); + } + return exposureCompensationIndex; } on PlatformException catch (e) { SystemServices.cameraErrorStreamController.add(e.message ?? 'Setting the camera exposure compensation index failed.'); - return Future.value(); + // Surfacing error to plugin layer to maintain consistency of + // setExposureOffset implementation across platform implementations. + rethrow; } } } diff --git a/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart b/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart index 07515c0c14fd..14239dd01b69 100644 --- a/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart +++ b/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart @@ -27,14 +27,14 @@ enum CameraStateType { /// If you need to add another type to support a type S to use a LiveData in /// this plugin, ensure the following is done on the Dart side: /// -/// * In `../lib/src/live_data.dart`, add new cases for S in +/// * In `camera_android_camerax/lib/src/live_data.dart`, add new cases for S in /// `_LiveDataHostApiImpl#getValueFromInstances` to get the current value of /// type S from a LiveData instance and in `LiveDataFlutterApiImpl#create` /// to create the expected type of LiveData when requested. /// /// On the native side, ensure the following is done: /// -/// * Update `LiveDataHostApiImpl#getValue` is updated to properly return +/// * Make sure `LiveDataHostApiImpl#getValue` is updated to properly return /// identifiers for instances of type S. /// * Update `ObserverFlutterApiWrapper#onChanged` to properly handle receiving /// calls with instances of type S if a LiveData instance is observed. @@ -68,6 +68,24 @@ enum VideoResolutionFallbackRule { lowerQualityThan, } +/// The types of capture request options this plugin currently supports. +/// +/// If you need to add another option to support, ensure the following is done +/// on the Dart side: +/// +/// * In `camera_android_camerax/lib/src/capture_request_options.dart`, add new cases for this +/// option in `_CaptureRequestOptionsHostApiImpl#createFromInstances` +/// to create the expected Map entry of option key index and value to send to +/// the native side. +/// +/// On the native side, ensure the following is done: +/// +/// * Update `CaptureRequestOptionsHostApiImpl#create` to set the correct +/// `CaptureRequest` key with a valid value type for this option. +/// +/// See https://developer.android.com/reference/android/hardware/camera2/CaptureRequest +/// for the sorts of capture request options that can be supported via CameraX's +/// interoperability with Camera2. enum CaptureRequestKeySupportedType { controlAeLock, } @@ -2884,7 +2902,7 @@ class CameraControlHostApi { } } - Future startFocusAndMetering( + Future startFocusAndMetering( int arg_identifier, int arg_focusMeteringActionId) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.CameraControlHostApi.startFocusAndMetering', codec, @@ -2903,13 +2921,8 @@ class CameraControlHostApi { message: replyList[1] as String?, details: replyList[2], ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); } else { - return (replyList[0] as int?)!; + return (replyList[0] as int?); } } @@ -2935,7 +2948,7 @@ class CameraControlHostApi { } } - Future setExposureCompensationIndex( + Future setExposureCompensationIndex( int arg_identifier, int arg_index) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.CameraControlHostApi.setExposureCompensationIndex', @@ -2954,13 +2967,8 @@ class CameraControlHostApi { message: replyList[1] as String?, details: replyList[2], ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); } else { - return (replyList[0] as int?)!; + return (replyList[0] as int?); } } } @@ -3130,14 +3138,14 @@ class MeteringPointHostApi { static const MessageCodec codec = StandardMessageCodec(); - Future create( - int arg_identifier, double arg_x, double arg_y, double? arg_size) async { + Future create(int arg_identifier, double arg_x, double arg_y, + double? arg_size, int arg_cameraInfoId) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.MeteringPointHostApi.create', codec, binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_identifier, arg_x, arg_y, arg_size]) - as List?; + final List? replyList = await channel.send( + [arg_identifier, arg_x, arg_y, arg_size, arg_cameraInfoId]) + as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', diff --git a/packages/camera/camera_android_camerax/lib/src/camerax_proxy.dart b/packages/camera/camera_android_camerax/lib/src/camerax_proxy.dart index 87d3680ff2c3..072a753d8b40 100644 --- a/packages/camera/camera_android_camerax/lib/src/camerax_proxy.dart +++ b/packages/camera/camera_android_camerax/lib/src/camerax_proxy.dart @@ -5,14 +5,17 @@ import 'dart:ui' show Size; import 'analyzer.dart'; +import 'camera_info.dart'; import 'camera_selector.dart'; import 'camera_state.dart'; import 'camerax_library.g.dart'; import 'device_orientation_manager.dart'; import 'fallback_strategy.dart'; +import 'focus_metering_action.dart'; import 'image_analysis.dart'; import 'image_capture.dart'; import 'image_proxy.dart'; +import 'metering_point.dart'; import 'observer.dart'; import 'preview.dart'; import 'process_camera_provider.dart'; @@ -49,6 +52,8 @@ class CameraXProxy { _startListeningForDeviceOrientationChange, this.setPreviewSurfaceProvider = _setPreviewSurfaceProvider, this.getDefaultDisplayRotation = _getDefaultDisplayRotation, + this.createMeteringPoint = _createMeteringPoint, + this.createFocusMeteringAction = _createFocusMeteringAction, }); /// Returns a [ProcessCameraProvider] instance. @@ -137,6 +142,16 @@ class CameraXProxy { /// rotation constants. Future Function() getDefaultDisplayRotation; + /// Returns a [MeteringPoint] with the specified coordinates based on + /// [cameraInfo]. + MeteringPoint Function(double x, double y, CameraInfo cameraInfo) + createMeteringPoint; + + /// Returns a [FocusMeteringAction] based on the specified metering points + /// and their modes. + FocusMeteringAction Function(List<(MeteringPoint, int?)> meteringPointInfos) + createFocusMeteringAction; + static Future _getProcessCameraProvider() { return ProcessCameraProvider.getInstance(); } @@ -239,4 +254,14 @@ class CameraXProxy { static Future _getDefaultDisplayRotation() async { return DeviceOrientationManager.getDefaultDisplayRotation(); } + + static MeteringPoint _createMeteringPoint( + double x, double y, CameraInfo cameraInfo) { + return MeteringPoint(x: x, y: y, cameraInfo: cameraInfo); + } + + static FocusMeteringAction _createFocusMeteringAction( + List<(MeteringPoint, int?)> meteringPointInfos) { + return FocusMeteringAction(meteringPointInfos: meteringPointInfos); + } } diff --git a/packages/camera/camera_android_camerax/lib/src/focus_metering_action.dart b/packages/camera/camera_android_camerax/lib/src/focus_metering_action.dart index 6d9ebd97f015..fe09ebc0443e 100644 --- a/packages/camera/camera_android_camerax/lib/src/focus_metering_action.dart +++ b/packages/camera/camera_android_camerax/lib/src/focus_metering_action.dart @@ -19,8 +19,7 @@ class FocusMeteringAction extends JavaObject { FocusMeteringAction({ BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, - required List<(MeteringPoint meteringPoint, int? meteringMode)> - meteringPointInfos, + required this.meteringPointInfos, }) : super.detached( binaryMessenger: binaryMessenger, instanceManager: instanceManager, @@ -35,6 +34,7 @@ class FocusMeteringAction extends JavaObject { FocusMeteringAction.detached({ BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, + required this.meteringPointInfos, }) : super.detached( binaryMessenger: binaryMessenger, instanceManager: instanceManager, @@ -45,6 +45,11 @@ class FocusMeteringAction extends JavaObject { late final _FocusMeteringActionHostApiImpl _api; + /// The requested [MeteringPoint]s and modes that are relevant to each of those + /// points. + final List<(MeteringPoint meteringPoint, int? meteringMode)> + meteringPointInfos; + /// Flag for metering mode that indicates the auto focus region is enabled. /// /// An autofocus scan is also triggered when [flagAf] is assigned. @@ -97,7 +102,9 @@ class _FocusMeteringActionHostApiImpl extends FocusMeteringActionHostApi { final int identifier = instanceManager.addDartCreatedInstance(instance, onCopy: (FocusMeteringAction original) { return FocusMeteringAction.detached( - binaryMessenger: binaryMessenger, instanceManager: instanceManager); + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + meteringPointInfos: original.meteringPointInfos); }); final List meteringPointInfosWithIds = diff --git a/packages/camera/camera_android_camerax/lib/src/metering_point.dart b/packages/camera/camera_android_camerax/lib/src/metering_point.dart index d699993a8933..c5ee6061de9c 100644 --- a/packages/camera/camera_android_camerax/lib/src/metering_point.dart +++ b/packages/camera/camera_android_camerax/lib/src/metering_point.dart @@ -6,6 +6,7 @@ import 'package:flutter/services.dart' show BinaryMessenger; import 'package:meta/meta.dart' show immutable; import 'android_camera_camerax_flutter_api_impls.dart'; +import 'camera_info.dart'; import 'camerax_library.g.dart'; import 'instance_manager.dart'; import 'java_object.dart'; @@ -23,13 +24,14 @@ class MeteringPoint extends JavaObject { required this.x, required this.y, this.size, + required this.cameraInfo, }) : super.detached( binaryMessenger: binaryMessenger, instanceManager: instanceManager, ) { _api = _MeteringPointHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager); - _api.createFromInstance(this, x, y, size); + _api.createFromInstance(this, x, y, size, cameraInfo); AndroidCameraXCameraFlutterApis.instance.ensureSetUp(); } @@ -41,6 +43,7 @@ class MeteringPoint extends JavaObject { required this.x, required this.y, this.size, + required this.cameraInfo, }) : super.detached( binaryMessenger: binaryMessenger, instanceManager: instanceManager, @@ -63,6 +66,10 @@ class MeteringPoint extends JavaObject { /// region width/height if crop region is set). final double? size; + /// The [CameraInfo] used to construct the metering point with a display- + /// oriented metering point factory. + final CameraInfo cameraInfo; + /// The default size of the [MeteringPoint] width and height (ranging from 0 /// to 1) which is a (normalized) percentage of the sensor width/height (or /// crop region width/height if crop region is set). @@ -100,8 +107,8 @@ class _MeteringPointHostApiImpl extends MeteringPointHostApi { /// Creates a [MeteringPoint] instance with the specified [x] and [y] /// coordinates as well as [size] if non-null. - Future createFromInstance( - MeteringPoint instance, double x, double y, double? size) { + Future createFromInstance(MeteringPoint instance, double x, double y, + double? size, CameraInfo cameraInfo) { int? identifier = instanceManager.getIdentifier(instance); identifier ??= instanceManager.addDartCreatedInstance(instance, onCopy: (MeteringPoint original) { @@ -110,9 +117,11 @@ class _MeteringPointHostApiImpl extends MeteringPointHostApi { instanceManager: instanceManager, x: original.x, y: original.y, + cameraInfo: original.cameraInfo, size: original.size); }); + final int? camInfoId = instanceManager.getIdentifier(cameraInfo); - return create(identifier, x, y, size); + return create(identifier, x, y, size, camInfoId!); } } diff --git a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart index db6164044d30..fc78cae75d24 100644 --- a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart +++ b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart @@ -66,7 +66,7 @@ class CameraStateTypeData { /// If you need to add another type to support a type S to use a LiveData in /// this plugin, ensure the following is done on the Dart side: /// -/// * In `../lib/src/live_data.dart`, add new cases for S in +/// * In `camera_android_camerax/lib/src/live_data.dart`, add new cases for S in /// `_LiveDataHostApiImpl#getValueFromInstances` to get the current value of /// type S from a LiveData instance and in `LiveDataFlutterApiImpl#create` /// to create the expected type of LiveData when requested. @@ -148,7 +148,7 @@ class MeteringPointInfo { /// If you need to add another option to support, ensure the following is done /// on the Dart side: /// -/// * In `../lib/src/capture_request_options.dart`, add new cases for this +/// * In `camera_android_camerax/lib/src/capture_request_options.dart`, add new cases for this /// option in `_CaptureRequestOptionsHostApiImpl#createFromInstances` /// to create the expected Map entry of option key index and value to send to /// the native side. @@ -485,13 +485,13 @@ abstract class CameraControlHostApi { void setZoomRatio(int identifier, double ratio); @async - int startFocusAndMetering(int identifier, int focusMeteringActionId); + int? startFocusAndMetering(int identifier, int focusMeteringActionId); @async void cancelFocusAndMetering(int identifier); @async - int setExposureCompensationIndex(int identifier, int index); + int? setExposureCompensationIndex(int identifier, int index); } @FlutterApi() @@ -516,7 +516,8 @@ abstract class FocusMeteringResultFlutterApi { @HostApi(dartHostTestHandler: 'TestMeteringPointHostApi') abstract class MeteringPointHostApi { - void create(int identifier, double x, double y, double? size); + void create( + int identifier, double x, double y, double? size, int cameraInfoId); double getDefaultPointSize(); } diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index fee04bae156f..3df56f5e1db3 100644 --- a/packages/camera/camera_android_camerax/pubspec.yaml +++ b/packages/camera/camera_android_camerax/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android_camerax description: Android implementation of the camera plugin using the CameraX library. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android_camerax issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.5.0+33 +version: 0.5.0+34 environment: sdk: ^3.1.0 diff --git a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart index bc6a0d1c6a3b..0868916f3bf3 100644 --- a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart +++ b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:math' show Point; import 'package:async/async.dart'; import 'package:camera_android_camerax/camera_android_camerax.dart'; @@ -18,10 +19,12 @@ import 'package:camera_android_camerax/src/camerax_proxy.dart'; import 'package:camera_android_camerax/src/device_orientation_manager.dart'; import 'package:camera_android_camerax/src/exposure_state.dart'; import 'package:camera_android_camerax/src/fallback_strategy.dart'; +import 'package:camera_android_camerax/src/focus_metering_action.dart'; import 'package:camera_android_camerax/src/image_analysis.dart'; import 'package:camera_android_camerax/src/image_capture.dart'; import 'package:camera_android_camerax/src/image_proxy.dart'; import 'package:camera_android_camerax/src/live_data.dart'; +import 'package:camera_android_camerax/src/metering_point.dart'; import 'package:camera_android_camerax/src/observer.dart'; import 'package:camera_android_camerax/src/pending_recording.dart'; import 'package:camera_android_camerax/src/plane_proxy.dart'; @@ -38,7 +41,8 @@ import 'package:camera_android_camerax/src/use_case.dart'; import 'package:camera_android_camerax/src/video_capture.dart'; import 'package:camera_android_camerax/src/zoom_state.dart'; import 'package:camera_platform_interface/camera_platform_interface.dart'; -import 'package:flutter/services.dart' show DeviceOrientation, Uint8List; +import 'package:flutter/services.dart' + show DeviceOrientation, PlatformException, Uint8List; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; @@ -113,6 +117,17 @@ void main() { return cameraClosingEventSent && cameraErrorSent; } + /// CameraXProxy for testing exposure and focus related controls. + /// + /// Modifies the creation of MeteringPoints and FocusMeteringActions to return + /// objects detached from a native object. + CameraXProxy getProxyForExposureAndFocus() => CameraXProxy( + createMeteringPoint: (double x, double y, CameraInfo cameraInfo) => + MeteringPoint.detached(x: x, y: y, cameraInfo: cameraInfo), + createFocusMeteringAction: (List<(MeteringPoint, int?)> + meteringPointInfos) => + FocusMeteringAction.detached(meteringPointInfos: meteringPointInfos)); + test('Should fetch CameraDescription instances for available cameras', () async { // Arrange @@ -331,6 +346,7 @@ void main() { final MockVideoCapture mockVideoCapture = MockVideoCapture(); final MockCamera mockCamera = MockCamera(); final MockCameraInfo mockCameraInfo = MockCameraInfo(); + final MockCameraControl mockCameraControl = MockCameraControl(); // Tell plugin to create mock/detached objects and stub method calls for the // testing of createCamera. @@ -377,6 +393,8 @@ void main() { when(mockCamera.getCameraInfo()).thenAnswer((_) async => mockCameraInfo); when(mockCameraInfo.getCameraState()) .thenAnswer((_) async => MockLiveCameraState()); + when(mockCamera.getCameraControl()) + .thenAnswer((_) async => mockCameraControl); camera.processCameraProvider = mockProcessCameraProvider; await camera.createCamera(testCameraDescription, testResolutionPreset, @@ -389,6 +407,9 @@ void main() { // Verify the camera's CameraInfo instance got updated. expect(camera.cameraInfo, equals(mockCameraInfo)); + // Verify camera's CameraControl instance got updated. + expect(camera.cameraControl, equals(mockCameraControl)); + // Verify preview has been marked as bound to the camera lifecycle by // createCamera. expect(camera.previewInitiallyBound, isTrue); @@ -940,6 +961,7 @@ void main() { MockProcessCameraProvider(); final MockCamera mockCamera = MockCamera(); final MockCameraInfo mockCameraInfo = MockCameraInfo(); + final MockCameraControl mockCameraControl = MockCameraControl(); final MockLiveCameraState mockLiveCameraState = MockLiveCameraState(); // Set directly for test versus calling createCamera. @@ -961,6 +983,8 @@ void main() { when(mockCamera.getCameraInfo()).thenAnswer((_) async => mockCameraInfo); when(mockCameraInfo.getCameraState()) .thenAnswer((_) async => mockLiveCameraState); + when(mockCamera.getCameraControl()) + .thenAnswer((_) async => mockCameraControl); await camera.resumePreview(78); @@ -974,6 +998,7 @@ void main() { as Observer), isTrue); expect(camera.cameraInfo, equals(mockCameraInfo)); + expect(camera.cameraControl, equals(mockCameraControl)); }); test( @@ -1017,6 +1042,7 @@ void main() { final MockCamera mockCamera = MockCamera(); final MockCamera newMockCamera = MockCamera(); final MockCameraInfo mockCameraInfo = MockCameraInfo(); + final MockCameraControl mockCameraControl = MockCameraControl(); final MockLiveCameraState mockLiveCameraState = MockLiveCameraState(); final MockLiveCameraState newMockLiveCameraState = MockLiveCameraState(); final TestSystemServicesHostApi mockSystemServicesApi = @@ -1055,6 +1081,8 @@ void main() { .thenAnswer((_) async => newMockCamera); when(newMockCamera.getCameraInfo()) .thenAnswer((_) async => mockCameraInfo); + when(newMockCamera.getCameraControl()) + .thenAnswer((_) async => mockCameraControl); when(mockCameraInfo.getCameraState()) .thenAnswer((_) async => newMockLiveCameraState); @@ -1066,6 +1094,7 @@ void main() { camera.cameraSelector!, [camera.videoCapture!])); expect(camera.camera, equals(newMockCamera)); expect(camera.cameraInfo, equals(mockCameraInfo)); + expect(camera.cameraControl, equals(mockCameraControl)); verify(mockLiveCameraState.removeObservers()); expect( await testCameraClosingObserver( @@ -1453,18 +1482,14 @@ void main() { () async { final AndroidCameraCameraX camera = AndroidCameraCameraX(); const int cameraId = 77; - final MockCameraControl mockCameraControl = MockCameraControl(); // Set directly for test versus calling createCamera. camera.imageCapture = MockImageCapture(); - camera.camera = MockCamera(); + camera.cameraControl = MockCameraControl(); // Ignore setting target rotation for this test; tested seprately. camera.captureOrientationLocked = true; - when(camera.camera!.getCameraControl()) - .thenAnswer((_) async => mockCameraControl); - await camera.setFlashMode(cameraId, FlashMode.torch); await camera.takePicture(cameraId); verify(camera.imageCapture!.setFlashMode(ImageCapture.flashModeOff)); @@ -1479,14 +1504,11 @@ void main() { // Set directly for test versus calling createCamera. camera.imageCapture = MockImageCapture(); - camera.camera = MockCamera(); + camera.cameraControl = mockCameraControl; // Ignore setting target rotation for this test; tested seprately. camera.captureOrientationLocked = true; - when(camera.camera!.getCameraControl()) - .thenAnswer((_) async => mockCameraControl); - for (final FlashMode flashMode in FlashMode.values) { await camera.setFlashMode(cameraId, flashMode); @@ -1520,10 +1542,7 @@ void main() { final MockCameraControl mockCameraControl = MockCameraControl(); // Set directly for test versus calling createCamera. - camera.camera = MockCamera(); - - when(camera.camera!.getCameraControl()) - .thenAnswer((_) async => mockCameraControl); + camera.cameraControl = mockCameraControl; await camera.setFlashMode(cameraId, FlashMode.torch); @@ -1538,10 +1557,7 @@ void main() { final MockCameraControl mockCameraControl = MockCameraControl(); // Set directly for test versus calling createCamera. - camera.camera = MockCamera(); - - when(camera.camera!.getCameraControl()) - .thenAnswer((_) async => mockCameraControl); + camera.cameraControl = mockCameraControl; for (final FlashMode flashMode in FlashMode.values) { camera.torchEnabled = true; @@ -1614,6 +1630,25 @@ void main() { expect(await camera.getExposureOffsetStepSize(55), 0.2); }); + test( + 'getExposureOffsetStepSize returns -1 when exposure compensation not supported on device', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + final MockCameraInfo mockCameraInfo = MockCameraInfo(); + final ExposureState exposureState = ExposureState.detached( + exposureCompensationRange: + ExposureCompensationRange(minCompensation: 0, maxCompensation: 0), + exposureCompensationStep: 0); + + // Set directly for test versus calling createCamera. + camera.cameraInfo = mockCameraInfo; + + when(mockCameraInfo.getExposureState()) + .thenAnswer((_) async => exposureState); + + expect(await camera.getExposureOffsetStepSize(55), -1); + }); + test('getMaxZoomLevel returns expected exposure offset', () async { final AndroidCameraCameraX camera = AndroidCameraCameraX(); final MockCameraInfo mockCameraInfo = MockCameraInfo(); @@ -1657,10 +1692,7 @@ void main() { final MockCameraControl mockCameraControl = MockCameraControl(); // Set directly for test versus calling createCamera. - camera.camera = MockCamera(); - - when(camera.camera!.getCameraControl()) - .thenAnswer((_) async => mockCameraControl); + camera.cameraControl = mockCameraControl; await camera.setZoomLevel(cameraId, zoomRatio); @@ -1981,4 +2013,452 @@ void main() { await camera.unlockCaptureOrientation(cameraId); expect(camera.captureOrientationLocked, isFalse); }); + + test( + 'setExposurePoint clears current auto-exposure metering point as expected', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 93; + final MockCameraControl mockCameraControl = MockCameraControl(); + final MockCameraInfo mockCameraInfo = MockCameraInfo(); + + // Set directly for test versus calling createCamera. + camera.cameraControl = mockCameraControl; + camera.cameraInfo = mockCameraInfo; + + camera.proxy = getProxyForExposureAndFocus(); + + // Verify nothing happens if no current focus and metering action has been + // enabled. + await camera.setExposurePoint(cameraId, null); + verifyNever(mockCameraControl.startFocusAndMetering(any)); + verifyNever(mockCameraControl.cancelFocusAndMetering()); + + // Verify current auto-exposure metering point is removed if previously set. + final (MeteringPoint, int?) autofocusMeteringPointInfo = ( + MeteringPoint.detached(x: 0.3, y: 0.7, cameraInfo: mockCameraInfo), + FocusMeteringAction.flagAf + ); + List<(MeteringPoint, int?)> meteringPointInfos = <(MeteringPoint, int?)>[ + ( + MeteringPoint.detached(x: 0.2, y: 0.5, cameraInfo: mockCameraInfo), + FocusMeteringAction.flagAe + ), + autofocusMeteringPointInfo + ]; + + camera.currentFocusMeteringAction = + FocusMeteringAction.detached(meteringPointInfos: meteringPointInfos); + + await camera.setExposurePoint(cameraId, null); + + final VerificationResult verificationResult = + verify(mockCameraControl.startFocusAndMetering(captureAny)); + final FocusMeteringAction capturedAction = + verificationResult.captured.single as FocusMeteringAction; + final List<(MeteringPoint, int?)> capturedMeteringPointInfos = + capturedAction.meteringPointInfos; + expect(capturedMeteringPointInfos.length, equals(1)); + expect( + capturedMeteringPointInfos.first, equals(autofocusMeteringPointInfo)); + + // Verify current focus and metering action is cleared if only previously + // set metering point was for auto-exposure. + meteringPointInfos = <(MeteringPoint, int?)>[ + ( + MeteringPoint.detached(x: 0.2, y: 0.5, cameraInfo: mockCameraInfo), + FocusMeteringAction.flagAe + ) + ]; + camera.currentFocusMeteringAction = + FocusMeteringAction.detached(meteringPointInfos: meteringPointInfos); + + await camera.setExposurePoint(cameraId, null); + + verify(mockCameraControl.cancelFocusAndMetering()); + }); + + test('setExposurePoint throws CameraException if invalid point specified', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 23; + final MockCameraControl mockCameraControl = MockCameraControl(); + const Point invalidExposurePoint = Point(3, -1); + + // Set directly for test versus calling createCamera. + camera.cameraControl = mockCameraControl; + + camera.proxy = getProxyForExposureAndFocus(); + + expect(() => camera.setExposurePoint(cameraId, invalidExposurePoint), + throwsA(isA())); + }); + + test( + 'setExposurePoint adds new exposure point to focus metering action to start as expected when previous metering points have been set', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 9; + final MockCameraControl mockCameraControl = MockCameraControl(); + final MockCameraInfo mockCameraInfo = MockCameraInfo(); + + // Set directly for test versus calling createCamera. + camera.cameraControl = mockCameraControl; + camera.cameraInfo = mockCameraInfo; + + camera.proxy = getProxyForExposureAndFocus(); + + // Verify current auto-exposure metering point is removed if previously set. + double exposurePointX = 0.8; + double exposurePointY = 0.1; + Point exposurePoint = Point(exposurePointX, exposurePointY); + final (MeteringPoint, int?) autofocusMeteringPointInfo = ( + MeteringPoint.detached(x: 0.3, y: 0.7, cameraInfo: mockCameraInfo), + FocusMeteringAction.flagAf + ); + List<(MeteringPoint, int?)> meteringPointInfos = <(MeteringPoint, int?)>[ + ( + MeteringPoint.detached(x: 0.2, y: 0.5, cameraInfo: mockCameraInfo), + FocusMeteringAction.flagAe + ), + autofocusMeteringPointInfo + ]; + + camera.currentFocusMeteringAction = + FocusMeteringAction.detached(meteringPointInfos: meteringPointInfos); + + await camera.setExposurePoint(cameraId, exposurePoint); + + VerificationResult verificationResult = + verify(mockCameraControl.startFocusAndMetering(captureAny)); + FocusMeteringAction capturedAction = + verificationResult.captured.single as FocusMeteringAction; + List<(MeteringPoint, int?)> capturedMeteringPointInfos = + capturedAction.meteringPointInfos; + expect(capturedMeteringPointInfos.length, equals(2)); + expect( + capturedMeteringPointInfos.first, equals(autofocusMeteringPointInfo)); + expect(capturedMeteringPointInfos[1].$1.x, equals(exposurePointX)); + expect(capturedMeteringPointInfos[1].$1.y, equals(exposurePointY)); + expect( + capturedMeteringPointInfos[1].$2, equals(FocusMeteringAction.flagAe)); + + // Verify exposure point is set when no auto-exposure metering point + // previously set, but an auto-focus point metering point has been. + exposurePointX = 0.2; + exposurePointY = 0.9; + exposurePoint = Point(exposurePointX, exposurePointY); + meteringPointInfos = <(MeteringPoint, int?)>[autofocusMeteringPointInfo]; + + camera.currentFocusMeteringAction = + FocusMeteringAction.detached(meteringPointInfos: meteringPointInfos); + + await camera.setExposurePoint(cameraId, exposurePoint); + + verificationResult = + verify(mockCameraControl.startFocusAndMetering(captureAny)); + capturedAction = verificationResult.captured.single as FocusMeteringAction; + capturedMeteringPointInfos = capturedAction.meteringPointInfos; + expect(capturedMeteringPointInfos.length, equals(2)); + expect( + capturedMeteringPointInfos.first, equals(autofocusMeteringPointInfo)); + expect(capturedMeteringPointInfos[1].$1.x, equals(exposurePointX)); + expect(capturedMeteringPointInfos[1].$1.y, equals(exposurePointY)); + expect( + capturedMeteringPointInfos[1].$2, equals(FocusMeteringAction.flagAe)); + }); + + test( + 'setExposurePoint adds new exposure point to focus metering action to start as expected when no previous metering points have been set', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 19; + final MockCameraControl mockCameraControl = MockCameraControl(); + const double exposurePointX = 0.8; + const double exposurePointY = 0.1; + const Point exposurePoint = + Point(exposurePointX, exposurePointY); + + // Set directly for test versus calling createCamera. + camera.cameraControl = mockCameraControl; + camera.cameraInfo = MockCameraInfo(); + camera.currentFocusMeteringAction = null; + + camera.proxy = getProxyForExposureAndFocus(); + + await camera.setExposurePoint(cameraId, exposurePoint); + + final VerificationResult verificationResult = + verify(mockCameraControl.startFocusAndMetering(captureAny)); + final FocusMeteringAction capturedAction = + verificationResult.captured.single as FocusMeteringAction; + final List<(MeteringPoint, int?)> capturedMeteringPointInfos = + capturedAction.meteringPointInfos; + expect(capturedMeteringPointInfos.length, equals(1)); + expect(capturedMeteringPointInfos.first.$1.x, equals(exposurePointX)); + expect(capturedMeteringPointInfos.first.$1.y, equals(exposurePointY)); + expect(capturedMeteringPointInfos.first.$2, + equals(FocusMeteringAction.flagAe)); + }); + + test( + 'setExposureOffset throws exception if exposure compensation not supported', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 6; + const double offset = 2; + final MockCameraInfo mockCameraInfo = MockCameraInfo(); + final ExposureState exposureState = ExposureState.detached( + exposureCompensationRange: + ExposureCompensationRange(minCompensation: 3, maxCompensation: 4), + exposureCompensationStep: 0); + + // Set directly for test versus calling createCamera. + camera.cameraInfo = mockCameraInfo; + + when(mockCameraInfo.getExposureState()) + .thenAnswer((_) async => exposureState); + + expect(() => camera.setExposureOffset(cameraId, offset), + throwsA(isA())); + }); + + test( + 'setExposureOffset throws exception if exposure compensation could not be set', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 11; + const double offset = 3; + final MockCameraInfo mockCameraInfo = MockCameraInfo(); + final CameraControl mockCameraControl = MockCameraControl(); + final ExposureState exposureState = ExposureState.detached( + exposureCompensationRange: + ExposureCompensationRange(minCompensation: 3, maxCompensation: 4), + exposureCompensationStep: 0.2); + + // Set directly for test versus calling createCamera. + camera.cameraInfo = mockCameraInfo; + camera.cameraControl = mockCameraControl; + + when(mockCameraInfo.getExposureState()) + .thenAnswer((_) async => exposureState); + when(mockCameraControl.setExposureCompensationIndex(15)).thenThrow( + PlatformException( + code: 'TEST_ERROR', + message: + 'This is a test error message indicating exposure offset could not be set.')); + + expect(() => camera.setExposureOffset(cameraId, offset), + throwsA(isA())); + }); + + test( + 'setExposureOffset behaves as expected to successful attempt to set exposure compensation index', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 11; + const double offset = 3; + final MockCameraInfo mockCameraInfo = MockCameraInfo(); + final CameraControl mockCameraControl = MockCameraControl(); + final ExposureState exposureState = ExposureState.detached( + exposureCompensationRange: + ExposureCompensationRange(minCompensation: 3, maxCompensation: 4), + exposureCompensationStep: 0.2); + + // Set directly for test versus calling createCamera. + camera.cameraInfo = mockCameraInfo; + camera.cameraControl = mockCameraControl; + + when(mockCameraInfo.getExposureState()) + .thenAnswer((_) async => exposureState); + + // Exposure index * exposure offset step size = exposure offset, i.e. + // 15 * 0.2 = 3. + expect(await camera.setExposureOffset(cameraId, offset), equals(3)); + }); + + test('setFocusPoint clears current auto-exposure metering point as expected', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 93; + final MockCameraControl mockCameraControl = MockCameraControl(); + final MockCameraInfo mockCameraInfo = MockCameraInfo(); + + // Set directly for test versus calling createCamera. + camera.cameraControl = mockCameraControl; + camera.cameraInfo = mockCameraInfo; + + camera.proxy = getProxyForExposureAndFocus(); + + // Verify nothing happens if no current focus and metering action has been + // enabled. + await camera.setFocusPoint(cameraId, null); + verifyNever(mockCameraControl.startFocusAndMetering(any)); + verifyNever(mockCameraControl.cancelFocusAndMetering()); + + // Verify current auto-exposure metering point is removed if previously set. + final (MeteringPoint, int?) autoexposureMeteringPointInfo = ( + MeteringPoint.detached(x: 0.3, y: 0.7, cameraInfo: mockCameraInfo), + FocusMeteringAction.flagAe + ); + List<(MeteringPoint, int?)> meteringPointInfos = <(MeteringPoint, int?)>[ + ( + MeteringPoint.detached(x: 0.2, y: 0.5, cameraInfo: mockCameraInfo), + FocusMeteringAction.flagAf + ), + autoexposureMeteringPointInfo + ]; + + camera.currentFocusMeteringAction = + FocusMeteringAction.detached(meteringPointInfos: meteringPointInfos); + + await camera.setFocusPoint(cameraId, null); + + final VerificationResult verificationResult = + verify(mockCameraControl.startFocusAndMetering(captureAny)); + final FocusMeteringAction capturedAction = + verificationResult.captured.single as FocusMeteringAction; + final List<(MeteringPoint, int?)> capturedMeteringPointInfos = + capturedAction.meteringPointInfos; + expect(capturedMeteringPointInfos.length, equals(1)); + expect(capturedMeteringPointInfos.first, + equals(autoexposureMeteringPointInfo)); + + // Verify current focus and metering action is cleared if only previously + // set metering point was for auto-exposure. + meteringPointInfos = <(MeteringPoint, int?)>[ + ( + MeteringPoint.detached(x: 0.2, y: 0.5, cameraInfo: mockCameraInfo), + FocusMeteringAction.flagAf + ) + ]; + camera.currentFocusMeteringAction = + FocusMeteringAction.detached(meteringPointInfos: meteringPointInfos); + + await camera.setFocusPoint(cameraId, null); + + verify(mockCameraControl.cancelFocusAndMetering()); + }); + + test('setFocusPoint throws CameraException if invalid point specified', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 23; + final MockCameraControl mockCameraControl = MockCameraControl(); + const Point invalidFocusPoint = Point(-3, 1); + + // Set directly for test versus calling createCamera. + camera.cameraControl = mockCameraControl; + + camera.proxy = getProxyForExposureAndFocus(); + + expect(() => camera.setFocusPoint(cameraId, invalidFocusPoint), + throwsA(isA())); + }); + + test( + 'setFocusPoint adds new exposure point to focus metering action to start as expected when previous metering points have been set', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 9; + final MockCameraControl mockCameraControl = MockCameraControl(); + final MockCameraInfo mockCameraInfo = MockCameraInfo(); + + // Set directly for test versus calling createCamera. + camera.cameraControl = mockCameraControl; + camera.cameraInfo = mockCameraInfo; + + camera.proxy = getProxyForExposureAndFocus(); + + // Verify current auto-exposure metering point is removed if previously set. + double focusPointX = 0.8; + double focusPointY = 0.1; + Point exposurePoint = Point(focusPointX, focusPointY); + final (MeteringPoint, int?) autoExposureMeteringPointInfo = ( + MeteringPoint.detached(x: 0.3, y: 0.7, cameraInfo: mockCameraInfo), + FocusMeteringAction.flagAe + ); + List<(MeteringPoint, int?)> meteringPointInfos = <(MeteringPoint, int?)>[ + ( + MeteringPoint.detached(x: 0.2, y: 0.5, cameraInfo: mockCameraInfo), + FocusMeteringAction.flagAf + ), + autoExposureMeteringPointInfo + ]; + + camera.currentFocusMeteringAction = + FocusMeteringAction.detached(meteringPointInfos: meteringPointInfos); + + await camera.setFocusPoint(cameraId, exposurePoint); + + VerificationResult verificationResult = + verify(mockCameraControl.startFocusAndMetering(captureAny)); + FocusMeteringAction capturedAction = + verificationResult.captured.single as FocusMeteringAction; + List<(MeteringPoint, int?)> capturedMeteringPointInfos = + capturedAction.meteringPointInfos; + expect(capturedMeteringPointInfos.length, equals(2)); + expect(capturedMeteringPointInfos.first, + equals(autoExposureMeteringPointInfo)); + expect(capturedMeteringPointInfos[1].$1.x, equals(focusPointX)); + expect(capturedMeteringPointInfos[1].$1.y, equals(focusPointY)); + expect( + capturedMeteringPointInfos[1].$2, equals(FocusMeteringAction.flagAf)); + + // Verify exposure point is set when no auto-exposure metering point + // previously set, but an auto-focus point metering point has been. + focusPointX = 0.2; + focusPointY = 0.9; + exposurePoint = Point(focusPointX, focusPointY); + meteringPointInfos = <(MeteringPoint, int?)>[autoExposureMeteringPointInfo]; + + camera.currentFocusMeteringAction = + FocusMeteringAction.detached(meteringPointInfos: meteringPointInfos); + + await camera.setFocusPoint(cameraId, exposurePoint); + + verificationResult = + verify(mockCameraControl.startFocusAndMetering(captureAny)); + capturedAction = verificationResult.captured.single as FocusMeteringAction; + capturedMeteringPointInfos = capturedAction.meteringPointInfos; + expect(capturedMeteringPointInfos.length, equals(2)); + expect(capturedMeteringPointInfos.first, + equals(autoExposureMeteringPointInfo)); + expect(capturedMeteringPointInfos[1].$1.x, equals(focusPointX)); + expect(capturedMeteringPointInfos[1].$1.y, equals(focusPointY)); + expect( + capturedMeteringPointInfos[1].$2, equals(FocusMeteringAction.flagAf)); + }); + + test( + 'setFocusPoint adds new exposure point to focus metering action to start as expected when no previous metering points have been set', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 19; + final MockCameraControl mockCameraControl = MockCameraControl(); + const double focusPointX = 0.8; + const double focusPointY = 0.1; + const Point exposurePoint = Point(focusPointX, focusPointY); + + // Set directly for test versus calling createCamera. + camera.cameraControl = mockCameraControl; + camera.cameraInfo = MockCameraInfo(); + camera.currentFocusMeteringAction = null; + + camera.proxy = getProxyForExposureAndFocus(); + + await camera.setFocusPoint(cameraId, exposurePoint); + + final VerificationResult verificationResult = + verify(mockCameraControl.startFocusAndMetering(captureAny)); + final FocusMeteringAction capturedAction = + verificationResult.captured.single as FocusMeteringAction; + final List<(MeteringPoint, int?)> capturedMeteringPointInfos = + capturedAction.meteringPointInfos; + expect(capturedMeteringPointInfos.length, equals(1)); + expect(capturedMeteringPointInfos.first.$1.x, equals(focusPointX)); + expect(capturedMeteringPointInfos.first.$1.y, equals(focusPointY)); + expect(capturedMeteringPointInfos.first.$2, + equals(FocusMeteringAction.flagAf)); + }); } diff --git a/packages/camera/camera_android_camerax/test/camera2_camera_control_test.mocks.dart b/packages/camera/camera_android_camerax/test/camera2_camera_control_test.mocks.dart index 09eb9436c7ad..58d89c49b20a 100644 --- a/packages/camera/camera_android_camerax/test/camera2_camera_control_test.mocks.dart +++ b/packages/camera/camera_android_camerax/test/camera2_camera_control_test.mocks.dart @@ -6,11 +6,13 @@ import 'dart:async' as _i3; import 'package:camera_android_camerax/src/camera_control.dart' as _i2; -import 'package:camera_android_camerax/src/camerax_library.g.dart' as _i5; -import 'package:camera_android_camerax/src/capture_request_options.dart' as _i4; +import 'package:camera_android_camerax/src/camerax_library.g.dart' as _i7; +import 'package:camera_android_camerax/src/capture_request_options.dart' as _i6; +import 'package:camera_android_camerax/src/focus_metering_action.dart' as _i5; +import 'package:camera_android_camerax/src/focus_metering_result.dart' as _i4; import 'package:mockito/mockito.dart' as _i1; -import 'test_camerax_library.g.dart' as _i6; +import 'test_camerax_library.g.dart' as _i8; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -53,6 +55,37 @@ class MockCameraControl extends _i1.Mock implements _i2.CameraControl { returnValue: _i3.Future.value(), returnValueForMissingStub: _i3.Future.value(), ) as _i3.Future); + + @override + _i3.Future<_i4.FocusMeteringResult?> startFocusAndMetering( + _i5.FocusMeteringAction? action) => + (super.noSuchMethod( + Invocation.method( + #startFocusAndMetering, + [action], + ), + returnValue: _i3.Future<_i4.FocusMeteringResult?>.value(), + ) as _i3.Future<_i4.FocusMeteringResult?>); + + @override + _i3.Future cancelFocusAndMetering() => (super.noSuchMethod( + Invocation.method( + #cancelFocusAndMetering, + [], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future setExposureCompensationIndex(int? index) => + (super.noSuchMethod( + Invocation.method( + #setExposureCompensationIndex, + [index], + ), + returnValue: _i3.Future.value(), + ) as _i3.Future); } /// A class which mocks [CaptureRequestOptions]. @@ -60,36 +93,24 @@ class MockCameraControl extends _i1.Mock implements _i2.CameraControl { /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable class MockCaptureRequestOptions extends _i1.Mock - implements _i4.CaptureRequestOptions { + implements _i6.CaptureRequestOptions { MockCaptureRequestOptions() { _i1.throwOnMissingStub(this); } @override - List<(_i5.CaptureRequestKeySupportedType, dynamic)> get requestedOptions => + List<(_i7.CaptureRequestKeySupportedType, Object?)> get requestedOptions => (super.noSuchMethod( Invocation.getter(#requestedOptions), - returnValue: <(_i5.CaptureRequestKeySupportedType, dynamic)>[], - ) as List<(_i5.CaptureRequestKeySupportedType, dynamic)>); - - @override - set requestedOptions( - List<(_i5.CaptureRequestKeySupportedType, dynamic)>? - _requestedOptions) => - super.noSuchMethod( - Invocation.setter( - #requestedOptions, - _requestedOptions, - ), - returnValueForMissingStub: null, - ); + returnValue: <(_i7.CaptureRequestKeySupportedType, Object?)>[], + ) as List<(_i7.CaptureRequestKeySupportedType, Object?)>); } /// A class which mocks [TestCamera2CameraControlHostApi]. /// /// See the documentation for Mockito's code generation for more information. class MockTestCamera2CameraControlHostApi extends _i1.Mock - implements _i6.TestCamera2CameraControlHostApi { + implements _i8.TestCamera2CameraControlHostApi { MockTestCamera2CameraControlHostApi() { _i1.throwOnMissingStub(this); } @@ -132,7 +153,7 @@ class MockTestCamera2CameraControlHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestInstanceManagerHostApi extends _i1.Mock - implements _i6.TestInstanceManagerHostApi { + implements _i8.TestInstanceManagerHostApi { MockTestInstanceManagerHostApi() { _i1.throwOnMissingStub(this); } diff --git a/packages/camera/camera_android_camerax/test/camera_control_test.dart b/packages/camera/camera_android_camerax/test/camera_control_test.dart index 63c7a7d9aa9b..22ba8aaf2479 100644 --- a/packages/camera/camera_android_camerax/test/camera_control_test.dart +++ b/packages/camera/camera_android_camerax/test/camera_control_test.dart @@ -6,6 +6,7 @@ import 'package:camera_android_camerax/src/camera_control.dart'; import 'package:camera_android_camerax/src/focus_metering_action.dart'; import 'package:camera_android_camerax/src/focus_metering_result.dart'; import 'package:camera_android_camerax/src/instance_manager.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; @@ -13,7 +14,11 @@ import 'package:mockito/mockito.dart'; import 'camera_control_test.mocks.dart'; import 'test_camerax_library.g.dart'; -@GenerateMocks([TestCameraControlHostApi, TestInstanceManagerHostApi]) +@GenerateMocks([ + TestCameraControlHostApi, + TestInstanceManagerHostApi, + FocusMeteringAction +]) void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -90,8 +95,7 @@ void main() { instanceManager: instanceManager, ); const int cameraControlIdentifier = 75; - final FocusMeteringAction action = - FocusMeteringAction.detached(instanceManager: instanceManager); + final FocusMeteringAction action = MockFocusMeteringAction(); const int actionId = 5; final FocusMeteringResult result = FocusMeteringResult.detached(instanceManager: instanceManager); @@ -105,8 +109,7 @@ void main() { instanceManager.addHostCreatedInstance( action, actionId, - onCopy: (_) => - FocusMeteringAction.detached(instanceManager: instanceManager), + onCopy: (_) => MockFocusMeteringAction(), ); instanceManager.addHostCreatedInstance( result, @@ -122,6 +125,41 @@ void main() { verify(mockApi.startFocusAndMetering(cameraControlIdentifier, actionId)); }); + test('startFocusAndMetering returns null result if operation was canceled', + () async { + final MockTestCameraControlHostApi mockApi = + MockTestCameraControlHostApi(); + TestCameraControlHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + + final CameraControl cameraControl = CameraControl.detached( + instanceManager: instanceManager, + ); + const int cameraControlIdentifier = 75; + final FocusMeteringAction action = MockFocusMeteringAction(); + const int actionId = 5; + + instanceManager.addHostCreatedInstance( + cameraControl, + cameraControlIdentifier, + onCopy: (_) => CameraControl.detached(instanceManager: instanceManager), + ); + instanceManager.addHostCreatedInstance( + action, + actionId, + onCopy: (_) => MockFocusMeteringAction(), + ); + + when(mockApi.startFocusAndMetering(cameraControlIdentifier, actionId)) + .thenAnswer((_) => Future.value()); + + expect(await cameraControl.startFocusAndMetering(action), isNull); + verify(mockApi.startFocusAndMetering(cameraControlIdentifier, actionId)); + }); + test( 'cancelFocusAndMetering makes call on Java side to cancel focus and metering', () async { @@ -182,6 +220,72 @@ void main() { mockApi.setExposureCompensationIndex(cameraControlIdentifier, index)); }); + test( + 'setExposureCompensationIndex returns null when operation was canceled', + () async { + final MockTestCameraControlHostApi mockApi = + MockTestCameraControlHostApi(); + TestCameraControlHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + + final CameraControl cameraControl = CameraControl.detached( + instanceManager: instanceManager, + ); + const int cameraControlIdentifier = 40; + + instanceManager.addHostCreatedInstance( + cameraControl, + cameraControlIdentifier, + onCopy: (_) => CameraControl.detached(instanceManager: instanceManager), + ); + + const int index = 2; + when(mockApi.setExposureCompensationIndex(cameraControlIdentifier, index)) + .thenAnswer((_) => Future.value()); + + expect(await cameraControl.setExposureCompensationIndex(index), isNull); + verify( + mockApi.setExposureCompensationIndex(cameraControlIdentifier, index)); + }); + + test( + 'setExposureCompensationIndex throws PlatformException when one is thrown from native side', + () async { + final MockTestCameraControlHostApi mockApi = + MockTestCameraControlHostApi(); + TestCameraControlHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + + final CameraControl cameraControl = CameraControl.detached( + instanceManager: instanceManager, + ); + const int cameraControlIdentifier = 40; + + instanceManager.addHostCreatedInstance( + cameraControl, + cameraControlIdentifier, + onCopy: (_) => CameraControl.detached(instanceManager: instanceManager), + ); + + const int index = 1; + when(mockApi.setExposureCompensationIndex(cameraControlIdentifier, index)) + .thenThrow(PlatformException( + code: 'TEST_ERROR', + details: 'Platform exception thrown from Java side.')); + + expect(() => cameraControl.setExposureCompensationIndex(index), + throwsA(isA())); + + verify( + mockApi.setExposureCompensationIndex(cameraControlIdentifier, index)); + }); + test('flutterApiCreate makes call to add instance to instance manager', () { final InstanceManager instanceManager = InstanceManager( onWeakReferenceRemoved: (_) {}, diff --git a/packages/camera/camera_android_camerax/test/camera_control_test.mocks.dart b/packages/camera/camera_android_camerax/test/camera_control_test.mocks.dart index a11525af9867..57040bcac62b 100644 --- a/packages/camera/camera_android_camerax/test/camera_control_test.mocks.dart +++ b/packages/camera/camera_android_camerax/test/camera_control_test.mocks.dart @@ -5,6 +5,8 @@ // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i3; +import 'package:camera_android_camerax/src/focus_metering_action.dart' as _i4; +import 'package:camera_android_camerax/src/metering_point.dart' as _i5; import 'package:mockito/mockito.dart' as _i1; import 'test_camerax_library.g.dart' as _i2; @@ -66,7 +68,7 @@ class MockTestCameraControlHostApi extends _i1.Mock ) as _i3.Future); @override - _i3.Future startFocusAndMetering( + _i3.Future startFocusAndMetering( int? identifier, int? focusMeteringActionId, ) => @@ -78,8 +80,8 @@ class MockTestCameraControlHostApi extends _i1.Mock focusMeteringActionId, ], ), - returnValue: _i3.Future.value(0), - ) as _i3.Future); + returnValue: _i3.Future.value(), + ) as _i3.Future); @override _i3.Future cancelFocusAndMetering(int? identifier) => @@ -93,7 +95,7 @@ class MockTestCameraControlHostApi extends _i1.Mock ) as _i3.Future); @override - _i3.Future setExposureCompensationIndex( + _i3.Future setExposureCompensationIndex( int? identifier, int? index, ) => @@ -105,8 +107,8 @@ class MockTestCameraControlHostApi extends _i1.Mock index, ], ), - returnValue: _i3.Future.value(0), - ) as _i3.Future); + returnValue: _i3.Future.value(), + ) as _i3.Future); } /// A class which mocks [TestInstanceManagerHostApi]. @@ -127,3 +129,20 @@ class MockTestInstanceManagerHostApi extends _i1.Mock returnValueForMissingStub: null, ); } + +/// A class which mocks [FocusMeteringAction]. +/// +/// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable +class MockFocusMeteringAction extends _i1.Mock + implements _i4.FocusMeteringAction { + MockFocusMeteringAction() { + _i1.throwOnMissingStub(this); + } + + @override + List<(_i5.MeteringPoint, int?)> get meteringPointInfos => (super.noSuchMethod( + Invocation.getter(#meteringPointInfos), + returnValue: <(_i5.MeteringPoint, int?)>[], + ) as List<(_i5.MeteringPoint, int?)>); +} diff --git a/packages/camera/camera_android_camerax/test/focus_metering_action_test.dart b/packages/camera/camera_android_camerax/test/focus_metering_action_test.dart index 657fb1aa3da4..a74338e2735c 100644 --- a/packages/camera/camera_android_camerax/test/focus_metering_action_test.dart +++ b/packages/camera/camera_android_camerax/test/focus_metering_action_test.dart @@ -37,6 +37,9 @@ void main() { ); FocusMeteringAction.detached( + meteringPointInfos: <(MeteringPoint, int?)>[ + (MockMeteringPoint(), FocusMeteringAction.flagAwb) + ], instanceManager: instanceManager, ); diff --git a/packages/camera/camera_android_camerax/test/focus_metering_action_test.mocks.dart b/packages/camera/camera_android_camerax/test/focus_metering_action_test.mocks.dart index 717215ca228e..d1ceef9daadd 100644 --- a/packages/camera/camera_android_camerax/test/focus_metering_action_test.mocks.dart +++ b/packages/camera/camera_android_camerax/test/focus_metering_action_test.mocks.dart @@ -1,13 +1,14 @@ -// Mocks generated by Mockito 5.4.3 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in camera_android_camerax/test/focus_metering_action_test.dart. // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:camera_android_camerax/src/camerax_library.g.dart' as _i4; -import 'package:camera_android_camerax/src/metering_point.dart' as _i2; +import 'package:camera_android_camerax/src/camera_info.dart' as _i2; +import 'package:camera_android_camerax/src/camerax_library.g.dart' as _i5; +import 'package:camera_android_camerax/src/metering_point.dart' as _i3; import 'package:mockito/mockito.dart' as _i1; -import 'test_camerax_library.g.dart' as _i3; +import 'test_camerax_library.g.dart' as _i4; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -22,11 +23,21 @@ import 'test_camerax_library.g.dart' as _i3; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class +class _FakeCameraInfo_0 extends _i1.SmartFake implements _i2.CameraInfo { + _FakeCameraInfo_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + /// A class which mocks [MeteringPoint]. /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockMeteringPoint extends _i1.Mock implements _i2.MeteringPoint { +class MockMeteringPoint extends _i1.Mock implements _i3.MeteringPoint { MockMeteringPoint() { _i1.throwOnMissingStub(this); } @@ -42,13 +53,22 @@ class MockMeteringPoint extends _i1.Mock implements _i2.MeteringPoint { Invocation.getter(#y), returnValue: 0.0, ) as double); + + @override + _i2.CameraInfo get cameraInfo => (super.noSuchMethod( + Invocation.getter(#cameraInfo), + returnValue: _FakeCameraInfo_0( + this, + Invocation.getter(#cameraInfo), + ), + ) as _i2.CameraInfo); } /// A class which mocks [TestFocusMeteringActionHostApi]. /// /// See the documentation for Mockito's code generation for more information. class MockTestFocusMeteringActionHostApi extends _i1.Mock - implements _i3.TestFocusMeteringActionHostApi { + implements _i4.TestFocusMeteringActionHostApi { MockTestFocusMeteringActionHostApi() { _i1.throwOnMissingStub(this); } @@ -56,7 +76,7 @@ class MockTestFocusMeteringActionHostApi extends _i1.Mock @override void create( int? identifier, - List<_i4.MeteringPointInfo?>? meteringPointInfos, + List<_i5.MeteringPointInfo?>? meteringPointInfos, ) => super.noSuchMethod( Invocation.method( @@ -74,7 +94,7 @@ class MockTestFocusMeteringActionHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestInstanceManagerHostApi extends _i1.Mock - implements _i3.TestInstanceManagerHostApi { + implements _i4.TestInstanceManagerHostApi { MockTestInstanceManagerHostApi() { _i1.throwOnMissingStub(this); } diff --git a/packages/camera/camera_android_camerax/test/focus_metering_result_test.mocks.dart b/packages/camera/camera_android_camerax/test/focus_metering_result_test.mocks.dart index d35cdc15efbe..be52b17bda47 100644 --- a/packages/camera/camera_android_camerax/test/focus_metering_result_test.mocks.dart +++ b/packages/camera/camera_android_camerax/test/focus_metering_result_test.mocks.dart @@ -1,12 +1,13 @@ -// Mocks generated by Mockito 5.4.3 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in camera_android_camerax/test/focus_metering_result_test.dart. // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:camera_android_camerax/src/metering_point.dart' as _i2; +import 'package:camera_android_camerax/src/camera_info.dart' as _i2; +import 'package:camera_android_camerax/src/metering_point.dart' as _i3; import 'package:mockito/mockito.dart' as _i1; -import 'test_camerax_library.g.dart' as _i3; +import 'test_camerax_library.g.dart' as _i4; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -21,11 +22,21 @@ import 'test_camerax_library.g.dart' as _i3; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class +class _FakeCameraInfo_0 extends _i1.SmartFake implements _i2.CameraInfo { + _FakeCameraInfo_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + /// A class which mocks [MeteringPoint]. /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockMeteringPoint extends _i1.Mock implements _i2.MeteringPoint { +class MockMeteringPoint extends _i1.Mock implements _i3.MeteringPoint { MockMeteringPoint() { _i1.throwOnMissingStub(this); } @@ -41,13 +52,22 @@ class MockMeteringPoint extends _i1.Mock implements _i2.MeteringPoint { Invocation.getter(#y), returnValue: 0.0, ) as double); + + @override + _i2.CameraInfo get cameraInfo => (super.noSuchMethod( + Invocation.getter(#cameraInfo), + returnValue: _FakeCameraInfo_0( + this, + Invocation.getter(#cameraInfo), + ), + ) as _i2.CameraInfo); } /// A class which mocks [TestFocusMeteringResultHostApi]. /// /// See the documentation for Mockito's code generation for more information. class MockTestFocusMeteringResultHostApi extends _i1.Mock - implements _i3.TestFocusMeteringResultHostApi { + implements _i4.TestFocusMeteringResultHostApi { MockTestFocusMeteringResultHostApi() { _i1.throwOnMissingStub(this); } @@ -66,7 +86,7 @@ class MockTestFocusMeteringResultHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestInstanceManagerHostApi extends _i1.Mock - implements _i3.TestInstanceManagerHostApi { + implements _i4.TestInstanceManagerHostApi { MockTestInstanceManagerHostApi() { _i1.throwOnMissingStub(this); } diff --git a/packages/camera/camera_android_camerax/test/metering_point_test.dart b/packages/camera/camera_android_camerax/test/metering_point_test.dart index ba3daae96b10..5aa921643778 100644 --- a/packages/camera/camera_android_camerax/test/metering_point_test.dart +++ b/packages/camera/camera_android_camerax/test/metering_point_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:camera_android_camerax/src/camera_info.dart'; import 'package:camera_android_camerax/src/instance_manager.dart'; import 'package:camera_android_camerax/src/metering_point.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -11,7 +12,8 @@ import 'package:mockito/mockito.dart'; import 'metering_point_test.mocks.dart'; import 'test_camerax_library.g.dart'; -@GenerateMocks([TestInstanceManagerHostApi, TestMeteringPointHostApi]) +@GenerateMocks( + [TestInstanceManagerHostApi, TestMeteringPointHostApi, CameraInfo]) void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -33,11 +35,12 @@ void main() { x: 0, y: 0.3, size: 4, + cameraInfo: MockCameraInfo(), instanceManager: instanceManager, ); verifyNever(mockApi.create(argThat(isA()), argThat(isA()), - argThat(isA()), argThat(isA()))); + argThat(isA()), argThat(isA()), argThat(isA()))); }); test('create calls create on the Java side', () async { @@ -52,14 +55,27 @@ void main() { const double x = 0.5; const double y = 0.6; const double size = 3; + final CameraInfo mockCameraInfo = MockCameraInfo(); + const int mockCameraInfoId = 4; + + instanceManager.addHostCreatedInstance(mockCameraInfo, mockCameraInfoId, + onCopy: (CameraInfo original) => MockCameraInfo()); + MeteringPoint( x: x, y: y, size: size, + cameraInfo: mockCameraInfo, instanceManager: instanceManager, ); - verify(mockApi.create(argThat(isA()), x, y, size)); + verify(mockApi.create( + argThat(isA()), + x, + y, + size, + mockCameraInfoId, + )); }); test('getDefaultPointSize returns expected size', () async { diff --git a/packages/camera/camera_android_camerax/test/metering_point_test.mocks.dart b/packages/camera/camera_android_camerax/test/metering_point_test.mocks.dart index ba199f66c63f..e7a3d9fadb13 100644 --- a/packages/camera/camera_android_camerax/test/metering_point_test.mocks.dart +++ b/packages/camera/camera_android_camerax/test/metering_point_test.mocks.dart @@ -1,11 +1,18 @@ -// Mocks generated by Mockito 5.4.3 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in camera_android_camerax/test/metering_point_test.dart. // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i6; + +import 'package:camera_android_camerax/src/camera_info.dart' as _i5; +import 'package:camera_android_camerax/src/camera_state.dart' as _i7; +import 'package:camera_android_camerax/src/exposure_state.dart' as _i3; +import 'package:camera_android_camerax/src/live_data.dart' as _i2; +import 'package:camera_android_camerax/src/zoom_state.dart' as _i8; import 'package:mockito/mockito.dart' as _i1; -import 'test_camerax_library.g.dart' as _i2; +import 'test_camerax_library.g.dart' as _i4; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -20,11 +27,32 @@ import 'test_camerax_library.g.dart' as _i2; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class +class _FakeLiveData_0 extends _i1.SmartFake + implements _i2.LiveData { + _FakeLiveData_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeExposureState_1 extends _i1.SmartFake implements _i3.ExposureState { + _FakeExposureState_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + /// A class which mocks [TestInstanceManagerHostApi]. /// /// See the documentation for Mockito's code generation for more information. class MockTestInstanceManagerHostApi extends _i1.Mock - implements _i2.TestInstanceManagerHostApi { + implements _i4.TestInstanceManagerHostApi { MockTestInstanceManagerHostApi() { _i1.throwOnMissingStub(this); } @@ -43,7 +71,7 @@ class MockTestInstanceManagerHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestMeteringPointHostApi extends _i1.Mock - implements _i2.TestMeteringPointHostApi { + implements _i4.TestMeteringPointHostApi { MockTestMeteringPointHostApi() { _i1.throwOnMissingStub(this); } @@ -54,6 +82,7 @@ class MockTestMeteringPointHostApi extends _i1.Mock double? x, double? y, double? size, + int? cameraInfoId, ) => super.noSuchMethod( Invocation.method( @@ -63,6 +92,7 @@ class MockTestMeteringPointHostApi extends _i1.Mock x, y, size, + cameraInfoId, ], ), returnValueForMissingStub: null, @@ -77,3 +107,70 @@ class MockTestMeteringPointHostApi extends _i1.Mock returnValue: 0.0, ) as double); } + +/// A class which mocks [CameraInfo]. +/// +/// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable +class MockCameraInfo extends _i1.Mock implements _i5.CameraInfo { + MockCameraInfo() { + _i1.throwOnMissingStub(this); + } + + @override + _i6.Future getSensorRotationDegrees() => (super.noSuchMethod( + Invocation.method( + #getSensorRotationDegrees, + [], + ), + returnValue: _i6.Future.value(0), + ) as _i6.Future); + + @override + _i6.Future<_i2.LiveData<_i7.CameraState>> getCameraState() => + (super.noSuchMethod( + Invocation.method( + #getCameraState, + [], + ), + returnValue: _i6.Future<_i2.LiveData<_i7.CameraState>>.value( + _FakeLiveData_0<_i7.CameraState>( + this, + Invocation.method( + #getCameraState, + [], + ), + )), + ) as _i6.Future<_i2.LiveData<_i7.CameraState>>); + + @override + _i6.Future<_i3.ExposureState> getExposureState() => (super.noSuchMethod( + Invocation.method( + #getExposureState, + [], + ), + returnValue: _i6.Future<_i3.ExposureState>.value(_FakeExposureState_1( + this, + Invocation.method( + #getExposureState, + [], + ), + )), + ) as _i6.Future<_i3.ExposureState>); + + @override + _i6.Future<_i2.LiveData<_i8.ZoomState>> getZoomState() => (super.noSuchMethod( + Invocation.method( + #getZoomState, + [], + ), + returnValue: _i6.Future<_i2.LiveData<_i8.ZoomState>>.value( + _FakeLiveData_0<_i8.ZoomState>( + this, + Invocation.method( + #getZoomState, + [], + ), + )), + ) as _i6.Future<_i2.LiveData<_i8.ZoomState>>); +} diff --git a/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart b/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart index 4450f97c82d5..c847327cce59 100644 --- a/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart +++ b/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart @@ -1898,11 +1898,11 @@ abstract class TestCameraControlHostApi { Future setZoomRatio(int identifier, double ratio); - Future startFocusAndMetering(int identifier, int focusMeteringActionId); + Future startFocusAndMetering(int identifier, int focusMeteringActionId); Future cancelFocusAndMetering(int identifier); - Future setExposureCompensationIndex(int identifier, int index); + Future setExposureCompensationIndex(int identifier, int index); static void setup(TestCameraControlHostApi? api, {BinaryMessenger? binaryMessenger}) { @@ -1977,7 +1977,7 @@ abstract class TestCameraControlHostApi { final int? arg_focusMeteringActionId = (args[1] as int?); assert(arg_focusMeteringActionId != null, 'Argument for dev.flutter.pigeon.CameraControlHostApi.startFocusAndMetering was null, expected non-null int.'); - final int output = await api.startFocusAndMetering( + final int? output = await api.startFocusAndMetering( arg_identifier!, arg_focusMeteringActionId!); return [output]; }); @@ -2027,7 +2027,7 @@ abstract class TestCameraControlHostApi { final int? arg_index = (args[1] as int?); assert(arg_index != null, 'Argument for dev.flutter.pigeon.CameraControlHostApi.setExposureCompensationIndex was null, expected non-null int.'); - final int output = await api.setExposureCompensationIndex( + final int? output = await api.setExposureCompensationIndex( arg_identifier!, arg_index!); return [output]; }); @@ -2138,7 +2138,8 @@ abstract class TestMeteringPointHostApi { TestDefaultBinaryMessengerBinding.instance; static const MessageCodec codec = StandardMessageCodec(); - void create(int identifier, double x, double y, double? size); + void create( + int identifier, double x, double y, double? size, int cameraInfoId); double getDefaultPointSize(); @@ -2168,7 +2169,11 @@ abstract class TestMeteringPointHostApi { assert(arg_y != null, 'Argument for dev.flutter.pigeon.MeteringPointHostApi.create was null, expected non-null double.'); final double? arg_size = (args[3] as double?); - api.create(arg_identifier!, arg_x!, arg_y!, arg_size); + final int? arg_cameraInfoId = (args[4] as int?); + assert(arg_cameraInfoId != null, + 'Argument for dev.flutter.pigeon.MeteringPointHostApi.create was null, expected non-null int.'); + api.create( + arg_identifier!, arg_x!, arg_y!, arg_size, arg_cameraInfoId!); return []; }); } From 784190cd59f1f6fa6da23ccf289446b0b228c3c9 Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Wed, 21 Feb 2024 18:08:05 -0500 Subject: [PATCH 012/126] [camerax] Modifies initialized camera info to reflect default AF/AE modes and the ability to set focus points for each (#6109) Modifies `CameraInitializedEvent` to 1. Set the initial exposure and focus modes to auto, since CameraX defaults to auto exposure mode and only supports auto focus mode without Camera2 interop 2. Sets the ability to set focus and exposure points to be true, since CameraX supports these by default ~Should land after https://github.com/flutter/packages/pull/6059 so that these values reflect what is actually implemented on our end~ Done :) --- packages/camera/camera_android_camerax/CHANGELOG.md | 5 +++++ .../lib/src/android_camera_camerax.dart | 13 +++++-------- packages/camera/camera_android_camerax/pubspec.yaml | 2 +- .../test/android_camera_camerax_test.dart | 8 ++------ 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index 4f63c42d1f91..5c8002bae00f 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.5.0+35 + +* Modifies `CameraInitializedEvent` that is sent when the camera is initialized to indicate that the initial focus + and exposure modes are auto and that developers may set focus and exposure points. + ## 0.5.0+34 * Implements `setFocusPoint`, `setExposurePoint`, and `setExposureOffset`. diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart index deddf4709c92..ffff56b4d963 100644 --- a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart @@ -347,16 +347,13 @@ class AndroidCameraCameraX extends CameraPlatform { final ResolutionInfo previewResolutionInfo = await preview!.getResolutionInfo(); - // Retrieve exposure and focus mode configurations: - // TODO(camsim99): Implement support for retrieving exposure mode configuration. - // https://github.com/flutter/flutter/issues/120468 + // Mark auto-focus, auto-exposure and setting points for focus & exposure + // as available operations as CameraX does its best across devices to + // support these by default. const ExposureMode exposureMode = ExposureMode.auto; - const bool exposurePointSupported = false; - - // TODO(camsim99): Implement support for retrieving focus mode configuration. - // https://github.com/flutter/flutter/issues/120467 const FocusMode focusMode = FocusMode.auto; - const bool focusPointSupported = false; + const bool exposurePointSupported = true; + const bool focusPointSupported = true; cameraEventStreamController.add(CameraInitializedEvent( cameraId, diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index 3df56f5e1db3..9e151c368ef6 100644 --- a/packages/camera/camera_android_camerax/pubspec.yaml +++ b/packages/camera/camera_android_camerax/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android_camerax description: Android implementation of the camera plugin using the CameraX library. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android_camerax issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.5.0+34 +version: 0.5.0+35 environment: sdk: ^3.1.0 diff --git a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart index 0868916f3bf3..3998860bfb54 100644 --- a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart +++ b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart @@ -755,19 +755,15 @@ void main() { startListeningForDeviceOrientationChange: (_, __) {}, ); - // TODO(camsim99): Modify this when camera configuration is supported and - // default values no longer being used. - // https://github.com/flutter/flutter/issues/120468 - // https://github.com/flutter/flutter/issues/120467 final CameraInitializedEvent testCameraInitializedEvent = CameraInitializedEvent( cameraId, resolutionWidth.toDouble(), resolutionHeight.toDouble(), ExposureMode.auto, - false, + true, FocusMode.auto, - false); + true); // Call createCamera. when(mockPreview.setSurfaceProvider()).thenAnswer((_) async => cameraId); From 1c11001a30b06e650c5a7d2f263e31dc7e92dc7c Mon Sep 17 00:00:00 2001 From: LouiseHsu Date: Wed, 21 Feb 2024 15:11:05 -0800 Subject: [PATCH 013/126] [in_app_purchase] Convert refreshReceipt(), startObservingPaymentQueue(), stopObservingPaymentQueue(), registerPaymentQueueDelegate(), removePaymentQueueDelegate(), showPriceConsentIfNeeded() to Pigeon (#6165) Part 3 of https://github.com/flutter/flutter/issues/117910 --- .../in_app_purchase_storekit/CHANGELOG.md | 5 + .../darwin/Classes/InAppPurchasePlugin.m | 93 +++------ .../darwin/Classes/messages.g.h | 9 +- .../darwin/Classes/messages.g.m | 144 ++++++++++++- .../xcshareddata/xcschemes/Runner.xcscheme | 3 +- .../RunnerTests/InAppPurchasePluginTests.m | 156 ++++---------- .../lib/src/messages.g.dart | 172 +++++++++++++++ .../sk_payment_queue_wrapper.dart | 17 +- .../sk_receipt_manager.dart | 8 +- .../store_kit_wrappers/sk_request_maker.dart | 6 +- .../pigeons/copyright.txt | 3 + .../pigeons/messages.dart | 15 ++ .../in_app_purchase_storekit/pubspec.yaml | 2 +- .../test/fakes/fake_storekit_platform.dart | 65 +++--- ...rchase_storekit_platform_addtion_test.dart | 4 - ...n_app_purchase_storekit_platform_test.dart | 3 - .../sk_methodchannel_apis_test.dart | 94 ++++----- .../sk_payment_queue_delegate_api_test.dart | 30 +-- .../test/test_api.g.dart | 197 ++++++++++++++++++ 19 files changed, 722 insertions(+), 304 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md index c19e4a826dd8..309510b3af50 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.3.12 + +* Converts `refreshReceipt()`, `startObservingPaymentQueue()`, `stopObservingPaymentQueue()`, +`registerPaymentQueueDelegate()`, `removePaymentQueueDelegate()`, `showPriceConsentIfNeeded()` to pigeon. + ## 0.3.11 * Fixes SKError.userInfo not being nullable. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m index 82366537404b..0a8b162bf1a5 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m @@ -87,30 +87,6 @@ - (instancetype)initWithRegistrar:(NSObject *)registrar return self; } -- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { - if ([@"-[InAppPurchasePlugin retrieveReceiptData:result:]" isEqualToString:call.method]) { - [self retrieveReceiptData:call result:result]; - } else if ([@"-[InAppPurchasePlugin refreshReceipt:result:]" isEqualToString:call.method]) { - [self refreshReceipt:call result:result]; - } else if ([@"-[SKPaymentQueue startObservingTransactionQueue]" isEqualToString:call.method]) { - [self startObservingPaymentQueue:result]; - } else if ([@"-[SKPaymentQueue stopObservingTransactionQueue]" isEqualToString:call.method]) { - [self stopObservingPaymentQueue:result]; -#if TARGET_OS_IOS - } else if ([@"-[SKPaymentQueue registerDelegate]" isEqualToString:call.method]) { - [self registerPaymentQueueDelegate:result]; -#endif - } else if ([@"-[SKPaymentQueue removeDelegate]" isEqualToString:call.method]) { - [self removePaymentQueueDelegate:result]; -#if TARGET_OS_IOS - } else if ([@"-[SKPaymentQueue showPriceConsentIfNeeded]" isEqualToString:call.method]) { - [self showPriceConsentIfNeeded:result]; -#endif - } else { - result(FlutterMethodNotImplemented); - } -} - - (nullable NSNumber *)canMakePaymentsWithError: (FlutterError *_Nullable __autoreleasing *_Nonnull)error { return @([SKPaymentQueue canMakePayments]); @@ -270,62 +246,61 @@ - (void)presentCodeRedemptionSheetWithError: #endif } -- (void)retrieveReceiptData:(FlutterMethodCall *)call result:(FlutterResult)result { - FlutterError *error = nil; - NSString *receiptData = [self.receiptManager retrieveReceiptWithError:&error]; - if (error) { - result(error); - return; +- (nullable NSString *)retrieveReceiptDataWithError: + (FlutterError *_Nullable __autoreleasing *_Nonnull)error { + FlutterError *flutterError; + NSString *receiptData = [self.receiptManager retrieveReceiptWithError:&flutterError]; + if (flutterError) { + *error = flutterError; + return nil; } - result(receiptData); + return receiptData; } -- (void)refreshReceipt:(FlutterMethodCall *)call result:(FlutterResult)result { - NSDictionary *arguments = call.arguments; +- (void)refreshReceiptReceiptProperties:(nullable NSDictionary *)receiptProperties + completion:(nonnull void (^)(FlutterError *_Nullable))completion { SKReceiptRefreshRequest *request; - if (arguments) { - if (![arguments isKindOfClass:[NSDictionary class]]) { - result([FlutterError errorWithCode:@"storekit_invalid_argument" - message:@"Argument type of startRequest is not array" - details:call.arguments]); - return; - } + if (receiptProperties) { + // if recieptProperties is not null, this call is for testing. NSMutableDictionary *properties = [NSMutableDictionary new]; - properties[SKReceiptPropertyIsExpired] = arguments[@"isExpired"]; - properties[SKReceiptPropertyIsRevoked] = arguments[@"isRevoked"]; - properties[SKReceiptPropertyIsVolumePurchase] = arguments[@"isVolumePurchase"]; + properties[SKReceiptPropertyIsExpired] = receiptProperties[@"isExpired"]; + properties[SKReceiptPropertyIsRevoked] = receiptProperties[@"isRevoked"]; + properties[SKReceiptPropertyIsVolumePurchase] = receiptProperties[@"isVolumePurchase"]; request = [self getRefreshReceiptRequest:properties]; } else { request = [self getRefreshReceiptRequest:nil]; } + FIAPRequestHandler *handler = [[FIAPRequestHandler alloc] initWithRequest:request]; [self.requestHandlers addObject:handler]; __weak typeof(self) weakSelf = self; [handler startProductRequestWithCompletionHandler:^(SKProductsResponse *_Nullable response, NSError *_Nullable error) { + FlutterError *requestError; if (error) { - result([FlutterError errorWithCode:@"storekit_refreshreceiptrequest_platform_error" - message:error.localizedDescription - details:error.description]); - return; + requestError = [FlutterError errorWithCode:@"storekit_refreshreceiptrequest_platform_error" + message:error.localizedDescription + details:error.description]; + completion(requestError); } - result(nil); + completion(nil); [weakSelf.requestHandlers removeObject:handler]; }]; } -- (void)startObservingPaymentQueue:(FlutterResult)result { +- (void)startObservingPaymentQueueWithError: + (FlutterError *_Nullable __autoreleasing *_Nonnull)error { [_paymentQueueHandler startObservingPaymentQueue]; - result(nil); } -- (void)stopObservingPaymentQueue:(FlutterResult)result { +- (void)stopObservingPaymentQueueWithError: + (FlutterError *_Nullable __autoreleasing *_Nonnull)error { [_paymentQueueHandler stopObservingPaymentQueue]; - result(nil); } +- (void)registerPaymentQueueDelegateWithError: + (FlutterError *_Nullable __autoreleasing *_Nonnull)error { #if TARGET_OS_IOS -- (void)registerPaymentQueueDelegate:(FlutterResult)result { if (@available(iOS 13.0, *)) { _paymentQueueDelegateCallbackChannel = [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/in_app_purchase_payment_queue_delegate" @@ -335,27 +310,25 @@ - (void)registerPaymentQueueDelegate:(FlutterResult)result { initWithMethodChannel:_paymentQueueDelegateCallbackChannel]; _paymentQueueHandler.delegate = _paymentQueueDelegate; } - result(nil); -} #endif +} -- (void)removePaymentQueueDelegate:(FlutterResult)result { +- (void)removePaymentQueueDelegateWithError: + (FlutterError *_Nullable __autoreleasing *_Nonnull)error { if (@available(iOS 13.0, *)) { _paymentQueueHandler.delegate = nil; } _paymentQueueDelegate = nil; _paymentQueueDelegateCallbackChannel = nil; - result(nil); } +- (void)showPriceConsentIfNeededWithError:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { #if TARGET_OS_IOS -- (void)showPriceConsentIfNeeded:(FlutterResult)result { if (@available(iOS 13.4, *)) { [_paymentQueueHandler showPriceConsentIfNeeded]; } - result(nil); -} #endif +} - (id)getNonNullValueFromDictionary:(NSDictionary *)dictionary forKey:(NSString *)key { id value = dictionary[key]; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h index 5d5b3a0c7799..bc315e781cd6 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h @@ -1,7 +1,6 @@ // 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 (v16.0.4), do not edit directly. // See also: https://pub.dev/packages/pigeon @@ -271,6 +270,14 @@ NSObject *InAppPurchaseAPIGetCodec(void); - (void)restoreTransactionsApplicationUserName:(nullable NSString *)applicationUserName error:(FlutterError *_Nullable *_Nonnull)error; - (void)presentCodeRedemptionSheetWithError:(FlutterError *_Nullable *_Nonnull)error; +- (nullable NSString *)retrieveReceiptDataWithError:(FlutterError *_Nullable *_Nonnull)error; +- (void)refreshReceiptReceiptProperties:(nullable NSDictionary *)receiptProperties + completion:(void (^)(FlutterError *_Nullable))completion; +- (void)startObservingPaymentQueueWithError:(FlutterError *_Nullable *_Nonnull)error; +- (void)stopObservingPaymentQueueWithError:(FlutterError *_Nullable *_Nonnull)error; +- (void)registerPaymentQueueDelegateWithError:(FlutterError *_Nullable *_Nonnull)error; +- (void)removePaymentQueueDelegateWithError:(FlutterError *_Nullable *_Nonnull)error; +- (void)showPriceConsentIfNeededWithError:(FlutterError *_Nullable *_Nonnull)error; @end extern void SetUpInAppPurchaseAPI(id binaryMessenger, diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m index bbb5d6b67eca..9588c883b820 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m @@ -1,7 +1,6 @@ // 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 (v16.0.4), do not edit directly. // See also: https://pub.dev/packages/pigeon @@ -752,4 +751,147 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, [channel setMessageHandler:nil]; } } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.retrieveReceiptData" + binaryMessenger:binaryMessenger + codec:InAppPurchaseAPIGetCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(retrieveReceiptDataWithError:)], + @"InAppPurchaseAPI api (%@) doesn't respond to @selector(retrieveReceiptDataWithError:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + NSString *output = [api retrieveReceiptDataWithError:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.refreshReceipt" + binaryMessenger:binaryMessenger + codec:InAppPurchaseAPIGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(refreshReceiptReceiptProperties:completion:)], + @"InAppPurchaseAPI api (%@) doesn't respond to " + @"@selector(refreshReceiptReceiptProperties:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSDictionary *arg_receiptProperties = GetNullableObjectAtIndex(args, 0); + [api refreshReceiptReceiptProperties:arg_receiptProperties + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI." + @"startObservingPaymentQueue" + binaryMessenger:binaryMessenger + codec:InAppPurchaseAPIGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(startObservingPaymentQueueWithError:)], + @"InAppPurchaseAPI api (%@) doesn't respond to " + @"@selector(startObservingPaymentQueueWithError:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + [api startObservingPaymentQueueWithError:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI." + @"stopObservingPaymentQueue" + binaryMessenger:binaryMessenger + codec:InAppPurchaseAPIGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(stopObservingPaymentQueueWithError:)], + @"InAppPurchaseAPI api (%@) doesn't respond to " + @"@selector(stopObservingPaymentQueueWithError:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + [api stopObservingPaymentQueueWithError:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI." + @"registerPaymentQueueDelegate" + binaryMessenger:binaryMessenger + codec:InAppPurchaseAPIGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(registerPaymentQueueDelegateWithError:)], + @"InAppPurchaseAPI api (%@) doesn't respond to " + @"@selector(registerPaymentQueueDelegateWithError:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + [api registerPaymentQueueDelegateWithError:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI." + @"removePaymentQueueDelegate" + binaryMessenger:binaryMessenger + codec:InAppPurchaseAPIGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(removePaymentQueueDelegateWithError:)], + @"InAppPurchaseAPI api (%@) doesn't respond to " + @"@selector(removePaymentQueueDelegateWithError:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + [api removePaymentQueueDelegateWithError:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI." + @"showPriceConsentIfNeeded" + binaryMessenger:binaryMessenger + codec:InAppPurchaseAPIGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(showPriceConsentIfNeededWithError:)], + @"InAppPurchaseAPI api (%@) doesn't respond to " + @"@selector(showPriceConsentIfNeededWithError:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + [api showPriceConsentIfNeededWithError:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 102a865d991a..477131feb699 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -26,7 +26,8 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> retrieveReceiptData() async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.retrieveReceiptData'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as String?); + } + } + + Future refreshReceipt( + {Map? receiptProperties}) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.refreshReceipt'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([receiptProperties]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future startObservingPaymentQueue() async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startObservingPaymentQueue'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future stopObservingPaymentQueue() async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.stopObservingPaymentQueue'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future registerPaymentQueueDelegate() async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.registerPaymentQueueDelegate'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future removePaymentQueueDelegate() async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.removePaymentQueueDelegate'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future showPriceConsentIfNeeded() async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.showPriceConsentIfNeeded'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart index dd5571a7af0a..8f1af1c64049 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart @@ -86,16 +86,16 @@ class SKPaymentQueueWrapper { /// /// Call this method when the first listener is subscribed to the /// [InAppPurchaseStoreKitPlatform.purchaseStream]. - Future startObservingTransactionQueue() => channel - .invokeMethod('-[SKPaymentQueue startObservingTransactionQueue]'); + Future startObservingTransactionQueue() => + _hostApi.startObservingPaymentQueue(); /// Instructs the iOS implementation to remove the transaction observer and /// stop listening to it. /// /// Call this when there are no longer any listeners subscribed to the /// [InAppPurchaseStoreKitPlatform.purchaseStream]. - Future stopObservingTransactionQueue() => channel - .invokeMethod('-[SKPaymentQueue stopObservingTransactionQueue]'); + Future stopObservingTransactionQueue() => + _hostApi.stopObservingPaymentQueue(); /// Sets an implementation of the [SKPaymentQueueDelegateWrapper]. /// @@ -109,10 +109,10 @@ class SKPaymentQueueWrapper { /// default behaviour will apply (see [documentation](https://developer.apple.com/documentation/storekit/skpaymentqueue/3182429-delegate?language=objc)). Future setDelegate(SKPaymentQueueDelegateWrapper? delegate) async { if (delegate == null) { - await channel.invokeMethod('-[SKPaymentQueue removeDelegate]'); + await _hostApi.removePaymentQueueDelegate(); paymentQueueDelegateChannel.setMethodCallHandler(null); } else { - await channel.invokeMethod('-[SKPaymentQueue registerDelegate]'); + await _hostApi.registerPaymentQueueDelegate(); paymentQueueDelegateChannel .setMethodCallHandler(handlePaymentQueueDelegateCallbacks); } @@ -207,8 +207,7 @@ class SKPaymentQueueWrapper { /// /// See documentation of StoreKit's [`-[SKPaymentQueue showPriceConsentIfNeeded]`](https://developer.apple.com/documentation/storekit/skpaymentqueue/3521327-showpriceconsentifneeded?language=objc). Future showPriceConsentIfNeeded() async { - await channel - .invokeMethod('-[SKPaymentQueue showPriceConsentIfNeeded]'); + await _hostApi.showPriceConsentIfNeeded(); } /// Triage a method channel call from the platform and triggers the correct observer method. @@ -354,7 +353,7 @@ class SKError { /// /// Any key of the map must be a valid [NSErrorUserInfoKey](https://developer.apple.com/documentation/foundation/nserroruserinfokey?language=objc). @JsonKey(defaultValue: {}) - final Map userInfo; + final Map? userInfo; @override bool operator ==(Object other) { diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_receipt_manager.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_receipt_manager.dart index b31a3d59c172..f61105aa2136 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_receipt_manager.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_receipt_manager.dart @@ -4,7 +4,9 @@ import 'dart:async'; -import '../channel.dart'; +import '../messages.g.dart'; + +InAppPurchaseAPI _hostApi = InAppPurchaseAPI(); // ignore: avoid_classes_with_only_static_members /// This class contains static methods to manage StoreKit receipts. @@ -17,8 +19,6 @@ class SKReceiptManager { /// For more details on how to validate the receipt data, you can refer to Apple's document about [`About Receipt Validation`](https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Introduction.html#//apple_ref/doc/uid/TP40010573-CH105-SW1). /// If the receipt is invalid or missing, you can use [SKRequestMaker.startRefreshReceiptRequest] to request a new receipt. static Future retrieveReceiptData() async { - return (await channel.invokeMethod( - '-[InAppPurchasePlugin retrieveReceiptData:result:]')) ?? - ''; + return (await _hostApi.retrieveReceiptData()) ?? ''; } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_request_maker.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_request_maker.dart index 2dc2ad7baef2..5a16f261cdd7 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_request_maker.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_request_maker.dart @@ -6,7 +6,6 @@ import 'dart:async'; import 'package:flutter/services.dart'; -import '../channel.dart'; import '../messages.g.dart'; import 'sk_product_wrapper.dart'; @@ -54,9 +53,6 @@ class SKRequestMaker { /// * isVolumePurchase: whether the receipt is a Volume Purchase Plan receipt. Future startRefreshReceiptRequest( {Map? receiptProperties}) { - return channel.invokeMethod( - '-[InAppPurchasePlugin refreshReceipt:result:]', - receiptProperties, - ); + return _hostApi.refreshReceipt(receiptProperties: receiptProperties); } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/copyright.txt b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/copyright.txt index e69de29bb2d1..fb682b1ab965 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/copyright.txt +++ b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/copyright.txt @@ -0,0 +1,3 @@ +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. \ No newline at end of file diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart index c5d352650408..fe1042c8037e 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart @@ -243,4 +243,19 @@ abstract class InAppPurchaseAPI { void restoreTransactions(String? applicationUserName); void presentCodeRedemptionSheet(); + + String? retrieveReceiptData(); + + @async + void refreshReceipt({Map? receiptProperties}); + + void startObservingPaymentQueue(); + + void stopObservingPaymentQueue(); + + void registerPaymentQueueDelegate(); + + void removePaymentQueueDelegate(); + + void showPriceConsentIfNeeded(); } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml index 920873e65273..ee7422b10009 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_storekit description: An implementation for the iOS and macOS platforms of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/packages/tree/main/packages/in_app_purchase/in_app_purchase_storekit issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.3.11 +version: 0.3.12 environment: sdk: ^3.2.3 diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart index 48db0847bf30..8fff5754eb77 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart @@ -5,7 +5,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart'; -import 'package:in_app_purchase_storekit/src/channel.dart'; import 'package:in_app_purchase_storekit/src/messages.g.dart'; import 'package:in_app_purchase_storekit/store_kit_wrappers.dart'; @@ -13,11 +12,6 @@ import '../store_kit_wrappers/sk_test_stub_objects.dart'; import '../test_api.g.dart'; class FakeStoreKitPlatform implements TestInAppPurchaseApi { - FakeStoreKitPlatform() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(channel, onMethodCall); - } - // pre-configured store information String? receiptData; late Set validProductIDs; @@ -32,6 +26,7 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { SKError? testRestoredError; bool queueIsActive = false; Map discountReceived = {}; + bool isPaymentQueueDelegateRegistered = false; void reset() { transactionList = []; @@ -57,6 +52,7 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { testRestoredError = null; queueIsActive = false; discountReceived = {}; + isPaymentQueueDelegateRegistered = false; } SKPaymentTransactionWrapper createPendingTransaction(String id, @@ -120,25 +116,6 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { transactionIdentifier: transactionId); } - Future onMethodCall(MethodCall call) { - switch (call.method) { - case '-[InAppPurchasePlugin retrieveReceiptData:result:]': - if (receiptData != null) { - return Future.value(receiptData!); - } else { - throw PlatformException(code: 'no_receipt_data'); - } - case '-[InAppPurchasePlugin refreshReceipt:result:]': - receiptData = 'refreshed receipt data'; - return Future.sync(() {}); - case '-[SKPaymentQueue startObservingTransactionQueue]': - queueIsActive = true; - case '-[SKPaymentQueue stopObservingTransactionQueue]': - queueIsActive = false; - } - return Future.sync(() {}); - } - @override bool canMakePayments() { return true; @@ -246,4 +223,42 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { return Future.value( SkProductResponseWrapper.convertToPigeon(response)); } + + @override + Future refreshReceipt({Map? receiptProperties}) { + receiptData = 'refreshed receipt data'; + return Future.sync(() {}); + } + + @override + void registerPaymentQueueDelegate() { + isPaymentQueueDelegateRegistered = true; + } + + @override + void removePaymentQueueDelegate() { + isPaymentQueueDelegateRegistered = false; + } + + @override + String retrieveReceiptData() { + if (receiptData != null) { + return receiptData!; + } else { + throw PlatformException(code: 'no_receipt_data'); + } + } + + @override + void showPriceConsentIfNeeded() {} + + @override + void startObservingPaymentQueue() { + queueIsActive = true; + } + + @override + void stopObservingPaymentQueue() { + queueIsActive = false; + } } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_addtion_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_addtion_test.dart index 99fd49d03a32..40068db75a71 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_addtion_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_addtion_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart'; @@ -17,9 +16,6 @@ void main() { setUpAll(() { TestInAppPurchaseApi.setup(fakeStoreKitPlatform); - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler( - SystemChannels.platform, fakeStoreKitPlatform.onMethodCall); }); group('present code redemption sheet', () { diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_test.dart index 278a69184468..e268c23bdaa4 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/in_app_purchase_storekit_platform_test.dart @@ -23,9 +23,6 @@ void main() { setUpAll(() { TestInAppPurchaseApi.setup(fakeStoreKitPlatform); - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler( - SystemChannels.platform, fakeStoreKitPlatform.onMethodCall); }); setUp(() { diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart index 704be83c8e25..82775f6b2e21 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart @@ -4,7 +4,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:in_app_purchase_storekit/src/channel.dart'; import 'package:in_app_purchase_storekit/src/messages.g.dart'; import 'package:in_app_purchase_storekit/store_kit_wrappers.dart'; import '../test_api.g.dart'; @@ -17,9 +16,6 @@ void main() { setUpAll(() { TestInAppPurchaseApi.setup(fakeStoreKitPlatform); - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler( - SystemChannels.platform, fakeStoreKitPlatform.onMethodCall); }); setUp(() {}); @@ -76,10 +72,10 @@ void main() { }); test('refreshed receipt', () async { - final int receiptCountBefore = fakeStoreKitPlatform.refreshReceipt; + final int receiptCountBefore = fakeStoreKitPlatform.refreshReceiptCount; await SKRequestMaker().startRefreshReceiptRequest( receiptProperties: {'isExpired': true}); - expect(fakeStoreKitPlatform.refreshReceipt, receiptCountBefore + 1); + expect(fakeStoreKitPlatform.refreshReceiptCount, receiptCountBefore + 1); expect(fakeStoreKitPlatform.refreshReceiptParam, {'isExpired': true}); }); @@ -175,9 +171,9 @@ void main() { }); test('showPriceConsentIfNeeded should call methodChannel', () async { - expect(fakeStoreKitPlatform.showPriceConsentIfNeeded, false); + expect(fakeStoreKitPlatform.showPriceConsent, false); await SKPaymentQueueWrapper().showPriceConsentIfNeeded(); - expect(fakeStoreKitPlatform.showPriceConsentIfNeeded, true); + expect(fakeStoreKitPlatform.showPriceConsent, true); }); }); @@ -192,10 +188,6 @@ void main() { } class FakeStoreKitPlatform implements TestInAppPurchaseApi { - FakeStoreKitPlatform() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(channel, onMethodCall); - } // get product request List startProductRequestParam = []; bool getProductRequestFailTest = false; @@ -205,7 +197,7 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { bool getReceiptFailTest = false; // refresh receipt request - int refreshReceipt = 0; + int refreshReceiptCount = 0; late Map refreshReceiptParam; // payment queue @@ -217,7 +209,7 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { bool presentCodeRedemption = false; // show price consent sheet - bool showPriceConsentIfNeeded = false; + bool showPriceConsent = false; // indicate if the payment queue delegate is registered bool isPaymentQueueDelegateRegistered = false; @@ -225,39 +217,6 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { // Listen to purchase updates bool? queueIsActive; - Future onMethodCall(MethodCall call) { - switch (call.method) { - // request makers - case '-[InAppPurchasePlugin refreshReceipt:result:]': - refreshReceipt++; - refreshReceiptParam = Map.castFrom( - call.arguments as Map); - return Future.sync(() {}); - // receipt manager - case '-[InAppPurchasePlugin retrieveReceiptData:result:]': - if (getReceiptFailTest) { - throw Exception('some arbitrary error'); - } - return Future.value('receipt data'); - case '-[SKPaymentQueue startObservingTransactionQueue]': - queueIsActive = true; - return Future.sync(() {}); - case '-[SKPaymentQueue stopObservingTransactionQueue]': - queueIsActive = false; - return Future.sync(() {}); - case '-[SKPaymentQueue registerDelegate]': - isPaymentQueueDelegateRegistered = true; - return Future.sync(() {}); - case '-[SKPaymentQueue removeDelegate]': - isPaymentQueueDelegateRegistered = false; - return Future.sync(() {}); - case '-[SKPaymentQueue showPriceConsentIfNeeded]': - showPriceConsentIfNeeded = true; - return Future.sync(() {}); - } - return Future.error('method not mocked'); - } - @override void addPayment(Map paymentMap) { payments @@ -304,6 +263,47 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { } return Future.value(dummyProductResponseMessage); } + + @override + void registerPaymentQueueDelegate() { + isPaymentQueueDelegateRegistered = true; + } + + @override + void removePaymentQueueDelegate() { + isPaymentQueueDelegateRegistered = false; + } + + @override + void startObservingPaymentQueue() { + queueIsActive = true; + } + + @override + void stopObservingPaymentQueue() { + queueIsActive = false; + } + + @override + String retrieveReceiptData() { + if (getReceiptFailTest) { + throw Exception('some arbitrary error'); + } + return 'receipt data'; + } + + @override + Future refreshReceipt({Map? receiptProperties}) { + refreshReceiptCount++; + refreshReceiptParam = + Map.castFrom(receiptProperties!); + return Future.sync(() {}); + } + + @override + void showPriceConsentIfNeeded() { + showPriceConsent = true; + } } class TestPaymentQueueDelegate extends SKPaymentQueueDelegateWrapper {} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_payment_queue_delegate_api_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_payment_queue_delegate_api_test.dart index 6c23da9cb495..03c9fda381cb 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_payment_queue_delegate_api_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_payment_queue_delegate_api_test.dart @@ -4,18 +4,18 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:in_app_purchase_storekit/src/channel.dart'; import 'package:in_app_purchase_storekit/store_kit_wrappers.dart'; +import '../fakes/fake_storekit_platform.dart'; +import '../test_api.g.dart'; + void main() { TestWidgetsFlutterBinding.ensureInitialized(); final FakeStoreKitPlatform fakeStoreKitPlatform = FakeStoreKitPlatform(); setUpAll(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler( - SystemChannels.platform, fakeStoreKitPlatform.onMethodCall); + TestInAppPurchaseApi.setup(fakeStoreKitPlatform); }); test( @@ -146,25 +146,3 @@ class TestPaymentQueueDelegate extends SKPaymentQueueDelegateWrapper { return false; } } - -class FakeStoreKitPlatform { - FakeStoreKitPlatform() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(channel, onMethodCall); - } - - // indicate if the payment queue delegate is registered - bool isPaymentQueueDelegateRegistered = false; - - Future onMethodCall(MethodCall call) { - switch (call.method) { - case '-[SKPaymentQueue registerDelegate]': - isPaymentQueueDelegateRegistered = true; - return Future.sync(() {}); - case '-[SKPaymentQueue removeDelegate]': - isPaymentQueueDelegateRegistered = false; - return Future.sync(() {}); - } - return Future.error('method not mocked'); - } -} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart index 80debcebb79d..65cd6e0bcc2d 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart @@ -1,3 +1,6 @@ +// 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 (v16.0.4), 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, no_leading_underscores_for_local_identifiers @@ -102,6 +105,20 @@ abstract class TestInAppPurchaseApi { void presentCodeRedemptionSheet(); + String? retrieveReceiptData(); + + Future refreshReceipt({Map? receiptProperties}); + + void startObservingPaymentQueue(); + + void stopObservingPaymentQueue(); + + void registerPaymentQueueDelegate(); + + void removePaymentQueueDelegate(); + + void showPriceConsentIfNeeded(); + static void setup(TestInAppPurchaseApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -331,5 +348,185 @@ abstract class TestInAppPurchaseApi { }); } } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.retrieveReceiptData', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + try { + final String? output = api.retrieveReceiptData(); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.refreshReceipt', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.refreshReceipt was null.'); + final List args = (message as List?)!; + final Map? arg_receiptProperties = + (args[0] as Map?)?.cast(); + try { + await api.refreshReceipt(receiptProperties: arg_receiptProperties); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.startObservingPaymentQueue', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + try { + api.startObservingPaymentQueue(); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.stopObservingPaymentQueue', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + try { + api.stopObservingPaymentQueue(); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.registerPaymentQueueDelegate', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + try { + api.registerPaymentQueueDelegate(); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.removePaymentQueueDelegate', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + try { + api.removePaymentQueueDelegate(); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.showPriceConsentIfNeeded', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + try { + api.showPriceConsentIfNeeded(); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } } } From 291d9d97fcac34001c5a2fac3135b135102652b7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 21 Feb 2024 23:24:52 -0500 Subject: [PATCH 014/126] Manual roll Flutter (stable) from bae5e49bc2a8 to abb292a07e20 (1 revision) (#6179) Manual roll requested by dit@google.com https://github.com/flutter/flutter/compare/bae5e49bc2a8...abb292a07e20 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-stable-packages Please CC dit@google.com,rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter (stable): https://github.com/flutter/flutter/issues/new/choose To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_stable.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_stable.version b/.ci/flutter_stable.version index 845fae768cba..1d282c387537 100644 --- a/.ci/flutter_stable.version +++ b/.ci/flutter_stable.version @@ -1 +1 @@ -bae5e49bc2a867403c43b2aae2de8f8c33b037e4 +abb292a07e20d696c4568099f918f6c5f330e6b0 From 078c2a304a01b69c587e4d99a28812a907796b0f Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 21 Feb 2024 23:38:21 -0500 Subject: [PATCH 015/126] Manual roll Flutter from 5129806e6c63 to efee280cfa93 (47 revisions) (#6180) Manual roll Flutter from 5129806e6c63 to efee280cfa93 (47 revisions) Manual roll requested by dit@google.com https://github.com/flutter/flutter/compare/5129806e6c63...efee280cfa93 2024-02-22 engine-flutter-autoroll@skia.org Roll Flutter Engine from bf5c003085fd to 7eeb697687d5 (16 revisions) (flutter/flutter#143911) 2024-02-22 katelovett@google.com Update PR template for dart fix (flutter/flutter#143879) 2024-02-22 katelovett@google.com Re-use methods to calculate leading and trailing garbage in RenderSliverMultiBoxAdaptor (flutter/flutter#143884) 2024-02-21 98614782+auto-submit[bot]@users.noreply.github.com Reverts "[Impeller] Make impeller goldens test blocking. (#143864)" (flutter/flutter#143896) 2024-02-21 jonahwilliams@google.com [Impeller] Make impeller goldens test blocking. (flutter/flutter#143864) 2024-02-21 jonahwilliams@google.com Disable color filter sepia test for Impeller. (flutter/flutter#143861) 2024-02-21 engine-flutter-autoroll@skia.org Roll Flutter Engine from 52ffcaadea41 to bf5c003085fd (12 revisions) (flutter/flutter#143875) 2024-02-21 engine-flutter-autoroll@skia.org Roll Flutter Engine from 4128895d79a1 to 52ffcaadea41 (1 revision) (flutter/flutter#143862) 2024-02-21 katelovett@google.com Deprecate redundant itemExtent in RenderSliverFixedExtentBoxAdaptor methods (flutter/flutter#143412) 2024-02-21 reidbaker@google.com Add aab as alias for appbundle (flutter/flutter#143855) 2024-02-21 engine-flutter-autoroll@skia.org Roll Flutter Engine from e16f43eeaaa4 to 4128895d79a1 (1 revision) (flutter/flutter#143856) 2024-02-21 engine-flutter-autoroll@skia.org Roll Packages from 8bba41b0c046 to 48048f6bc779 (2 revisions) (flutter/flutter#143853) 2024-02-21 tessertaha@gmail.com Update `hourMinuteTextStyle` defaults for Material 3 Time Picker (flutter/flutter#143749) 2024-02-21 engine-flutter-autoroll@skia.org Roll Flutter Engine from 93063f61943a to e16f43eeaaa4 (2 revisions) (flutter/flutter#143827) 2024-02-21 engine-flutter-autoroll@skia.org Roll Flutter Engine from ed49634486e9 to 93063f61943a (1 revision) (flutter/flutter#143826) 2024-02-21 tessertaha@gmail.com `CalendarDatePicker` doesn't announce selected date on desktop (flutter/flutter#143583) 2024-02-21 engine-flutter-autoroll@skia.org Roll Flutter Engine from 9100d326475a to ed49634486e9 (2 revisions) (flutter/flutter#143824) 2024-02-21 tessertaha@gmail.com Add `timeSelectorSeparatorColor` and `timeSelectorSeparatorTextStyle` for Material 3 Time Picker (flutter/flutter#143739) 2024-02-21 engine-flutter-autoroll@skia.org Roll Flutter Engine from efc69946cb1e to 9100d326475a (2 revisions) (flutter/flutter#143820) 2024-02-21 engine-flutter-autoroll@skia.org Roll Flutter Engine from 700250436e3f to efc69946cb1e (2 revisions) (flutter/flutter#143816) 2024-02-21 engine-flutter-autoroll@skia.org Roll Flutter Engine from 3557277c575c to 700250436e3f (1 revision) (flutter/flutter#143814) 2024-02-21 jonahwilliams@google.com more fixes to unstable impeller goldens. (flutter/flutter#143811) 2024-02-21 engine-flutter-autoroll@skia.org Roll Flutter Engine from cb12a8cc97a1 to 3557277c575c (2 revisions) (flutter/flutter#143807) 2024-02-21 kevmoo@users.noreply.github.com [flutter_tools] enable wasm compile on beta channel (flutter/flutter#143779) 2024-02-21 simonfv@gmail.com Fix initialization of time in repeat on AnimationController (flutter/flutter#142887) 2024-02-21 jonahwilliams@google.com Disable debug banner to stabilize impeller goldens. (flutter/flutter#143794) 2024-02-21 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Changing `TextPainter.getOffsetForCaret` implementation to remove the logarithmic search (#143281)" (flutter/flutter#143801) 2024-02-20 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Add UI Benchmarks (#143542)" (flutter/flutter#143798) 2024-02-20 31859944+LongCatIsLooong@users.noreply.github.com Avoid applying partial dartfixes on CI (flutter/flutter#143551) 2024-02-20 bernaferrari2@gmail.com Add UI Benchmarks (flutter/flutter#143542) 2024-02-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from 1ae2c10e8071 to cb12a8cc97a1 (1 revision) (flutter/flutter#143791) 2024-02-20 nate.w5687@gmail.com Implement `_suspendedNode` fix (flutter/flutter#143556) 2024-02-20 xubaolin@oppo.com Change `ItemExtentBuilder`'s return value nullable (flutter/flutter#142428) 2024-02-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from 27828054f07a to 1ae2c10e8071 (6 revisions) (flutter/flutter#143783) 2024-02-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from e16a260265ad to 27828054f07a (1 revision) (flutter/flutter#143769) 2024-02-20 jonahwilliams@google.com [gold] Always provide host ABI to gold config (flutter/flutter#143621) 2024-02-20 andrewrkolos@gmail.com instead of exiting the tool, print a warning when using --flavor with an incompatible device (flutter/flutter#143735) 2024-02-20 nate.w5687@gmail.com Implementing `switch` expressions: everything in `flutter/lib/src/` (flutter/flutter#143634) 2024-02-20 31859944+LongCatIsLooong@users.noreply.github.com Changing `TextPainter.getOffsetForCaret` implementation to remove the logarithmic search (flutter/flutter#143281) 2024-02-20 34871572+gmackall@users.noreply.github.com Delete local.properties that shouldn't have been pushed (flutter/flutter#143774) 2024-02-20 polinach@google.com Clean leaks. (flutter/flutter#142818) 2024-02-20 36861262+QuncCccccc@users.noreply.github.com Introduce tone-based surfaces and accent color add-ons - Part 2 (flutter/flutter#138521) 2024-02-20 greg@zulip.com Explain when and why to use CrossAxisAlignment.baseline (flutter/flutter#143632) 2024-02-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from a41da3701923 to e16a260265ad (2 revisions) (flutter/flutter#143763) ... --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 1dd06e656959..9054d03b0bae 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -5129806e6c6331870b2a17606d1ddf71421f9fa7 +efee280cfa93314eb32550197b08cda32cee196d From ff587b7c9552a47e716a822eca3a1eaa39154a65 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 22 Feb 2024 10:45:26 -0500 Subject: [PATCH 016/126] Roll Flutter from efee280cfa93 to 41581c9a1b90 (1 revision) (#6181) https://github.com/flutter/flutter/compare/efee280cfa93...41581c9a1b90 2024-02-22 engine-flutter-autoroll@skia.org Roll Flutter Engine from 7eeb697687d5 to 7777480eb94d (1 revision) (flutter/flutter#143913) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC dit@google.com,rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 9054d03b0bae..b7983d7d8244 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -efee280cfa93314eb32550197b08cda32cee196d +41581c9a1b909e481c13a2cee7a6e133f7e2ab1e From 67470fc95e54e28a5e16f107277687a3e63987fa Mon Sep 17 00:00:00 2001 From: Kate Lovett Date: Thu, 22 Feb 2024 16:10:06 -0600 Subject: [PATCH 017/126] [two_dimensional_scrollables] Fix layout offset of merged pinned cells (#6178) Fixes https://github.com/flutter/flutter/issues/143526 This fixes the layout offset computation when there are merged cells within pinned rows and/or columns in TableView. --- .../two_dimensional_scrollables/CHANGELOG.md | 4 + .../lib/src/table_view/table.dart | 30 +- .../two_dimensional_scrollables/pubspec.yaml | 2 +- .../test/table_view/table_test.dart | 476 ++++++++++++++++++ 4 files changed, 509 insertions(+), 3 deletions(-) diff --git a/packages/two_dimensional_scrollables/CHANGELOG.md b/packages/two_dimensional_scrollables/CHANGELOG.md index 52ea3b087a7f..64d1a0a6a552 100644 --- a/packages/two_dimensional_scrollables/CHANGELOG.md +++ b/packages/two_dimensional_scrollables/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.1 + +* Fixes a layout issue when pinned cells are merged. + ## 0.1.0 * [Breaking change] Adds support for merged cells in the TableView. diff --git a/packages/two_dimensional_scrollables/lib/src/table_view/table.dart b/packages/two_dimensional_scrollables/lib/src/table_view/table.dart index 5d1575b1f5af..917881a61c29 100644 --- a/packages/two_dimensional_scrollables/lib/src/table_view/table.dart +++ b/packages/two_dimensional_scrollables/lib/src/table_view/table.dart @@ -801,7 +801,20 @@ class RenderTableViewport extends RenderTwoDimensionalViewport { // | <--------- extent of merged cell ---------> | // Compute height and layout offset for merged rows. - mergedRowOffset = -verticalOffset.pixels + + final bool rowIsInPinnedColumn = _lastPinnedColumn != null && + vicinity.column <= _lastPinnedColumn!; + final bool rowIsPinned = + _lastPinnedRow != null && firstRow <= _lastPinnedRow!; + final double baseRowOffset = + switch ((rowIsInPinnedColumn, rowIsPinned)) { + // Both row and column are pinned at this cell, or just pinned row. + (true, true) || (false, true) => 0.0, + // Cell is within a pinned column + (true, false) => _pinnedRowsExtent - verticalOffset.pixels, + // Cell is within a pinned row, or no pinned portion. + (false, false) => -verticalOffset.pixels, + }; + mergedRowOffset = baseRowOffset + _rowMetrics[firstRow]!.leadingOffset + _rowMetrics[firstRow]!.configuration.padding.leading; mergedRowHeight = _rowMetrics[lastRow]!.trailingOffset - @@ -809,7 +822,20 @@ class RenderTableViewport extends RenderTwoDimensionalViewport { _rowMetrics[lastRow]!.configuration.padding.trailing - _rowMetrics[firstRow]!.configuration.padding.leading; // Compute width and layout offset for merged columns. - mergedColumnOffset = -horizontalOffset.pixels + + final bool columnIsInPinnedRow = + _lastPinnedRow != null && vicinity.row <= _lastPinnedRow!; + final bool columnIsPinned = + _lastPinnedColumn != null && firstColumn <= _lastPinnedColumn!; + final double baseColumnOffset = + switch ((columnIsInPinnedRow, columnIsPinned)) { + // Both row and column are pinned at this cell, or just pinned column. + (true, true) || (false, true) => 0.0, + // Cell is within a pinned row. + (true, false) => _pinnedColumnsExtent - horizontalOffset.pixels, + // No pinned portion. + (false, false) => -horizontalOffset.pixels, + }; + mergedColumnOffset = baseColumnOffset + _columnMetrics[firstColumn]!.leadingOffset + _columnMetrics[firstColumn]!.configuration.padding.leading; mergedColumnWidth = _columnMetrics[lastColumn]!.trailingOffset - diff --git a/packages/two_dimensional_scrollables/pubspec.yaml b/packages/two_dimensional_scrollables/pubspec.yaml index e67277843dee..8d04d42f6455 100644 --- a/packages/two_dimensional_scrollables/pubspec.yaml +++ b/packages/two_dimensional_scrollables/pubspec.yaml @@ -1,6 +1,6 @@ name: two_dimensional_scrollables description: Widgets that scroll using the two dimensional scrolling foundation. -version: 0.1.0 +version: 0.1.1 repository: https://github.com/flutter/packages/tree/main/packages/two_dimensional_scrollables issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+two_dimensional_scrollables%22+ diff --git a/packages/two_dimensional_scrollables/test/table_view/table_test.dart b/packages/two_dimensional_scrollables/test/table_view/table_test.dart index 7d59ad8aa573..c24d1927405e 100644 --- a/packages/two_dimensional_scrollables/test/table_view/table_test.dart +++ b/packages/two_dimensional_scrollables/test/table_view/table_test.dart @@ -1730,6 +1730,482 @@ void main() { SystemMouseCursors.basic, ); }); + + group('Merged pinned cells layout', () { + // Regression tests for https://github.com/flutter/flutter/issues/143526 + // These tests all use the same collection of merged pinned cells in a + // variety of combinations. + final Map bothMerged = + { + TableVicinity.zero: (start: 0, span: 2), + const TableVicinity(row: 1, column: 0): (start: 0, span: 2), + const TableVicinity(row: 0, column: 1): (start: 0, span: 2), + const TableVicinity(row: 1, column: 1): (start: 0, span: 2), + }; + + final Map rowMerged = + { + const TableVicinity(row: 2, column: 0): (start: 2, span: 2), + const TableVicinity(row: 3, column: 0): (start: 2, span: 2), + const TableVicinity(row: 4, column: 1): (start: 4, span: 3), + const TableVicinity(row: 5, column: 1): (start: 4, span: 3), + const TableVicinity(row: 6, column: 1): (start: 4, span: 3), + }; + + final Map columnMerged = + { + const TableVicinity(row: 0, column: 2): (start: 2, span: 2), + const TableVicinity(row: 0, column: 3): (start: 2, span: 2), + const TableVicinity(row: 1, column: 4): (start: 4, span: 3), + const TableVicinity(row: 1, column: 5): (start: 4, span: 3), + const TableVicinity(row: 1, column: 6): (start: 4, span: 3), + }; + const TableSpan span = TableSpan(extent: FixedTableSpanExtent(75)); + + testWidgets('Normal axes', (WidgetTester tester) async { + final ScrollController verticalController = ScrollController(); + final ScrollController horizontalController = ScrollController(); + final TableView tableView = TableView.builder( + verticalDetails: ScrollableDetails.vertical( + controller: verticalController, + ), + horizontalDetails: ScrollableDetails.horizontal( + controller: horizontalController, + ), + columnCount: 20, + rowCount: 20, + pinnedRowCount: 2, + pinnedColumnCount: 2, + columnBuilder: (_) => span, + rowBuilder: (_) => span, + cellBuilder: (_, TableVicinity vicinity) { + return TableViewCell( + columnMergeStart: + bothMerged[vicinity]?.start ?? columnMerged[vicinity]?.start, + columnMergeSpan: + bothMerged[vicinity]?.span ?? columnMerged[vicinity]?.span, + rowMergeStart: + bothMerged[vicinity]?.start ?? rowMerged[vicinity]?.start, + rowMergeSpan: + bothMerged[vicinity]?.span ?? rowMerged[vicinity]?.span, + child: Text( + 'R${bothMerged[vicinity]?.start ?? rowMerged[vicinity]?.start ?? vicinity.row}:' + 'C${bothMerged[vicinity]?.start ?? columnMerged[vicinity]?.start ?? vicinity.column}', + ), + ); + }, + ); + await tester.pumpWidget(MaterialApp(home: tableView)); + await tester.pumpAndSettle(); + + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTWH(0.0, 0.0, 150.0, 150.0), + ); + expect( + tester.getRect(find.text('R0:C2')), + const Rect.fromLTWH(150.0, 0.0, 150.0, 75.0), + ); + expect( + tester.getRect(find.text('R1:C4')), + const Rect.fromLTWH(300.0, 75.0, 225.0, 75.0), + ); + expect( + tester.getRect(find.text('R2:C0')), + const Rect.fromLTWH(0.0, 150.0, 75.0, 150.0), + ); + expect( + tester.getRect(find.text('R4:C1')), + const Rect.fromLTWH(75.0, 300.0, 75.0, 225.0), + ); + + verticalController.jumpTo(10.0); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 10.0); + expect(horizontalController.position.pixels, 0.0); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTWH(0.0, 0.0, 150.0, 150.0), + ); + expect( + tester.getRect(find.text('R0:C2')), + const Rect.fromLTWH(150.0, 0.0, 150.0, 75.0), + ); + expect( + tester.getRect(find.text('R1:C4')), + const Rect.fromLTWH(300.0, 75.0, 225.0, 75.0), + ); + expect( + tester.getRect(find.text('R2:C0')), + const Rect.fromLTWH(0.0, 140.0, 75.0, 150.0), + ); + expect( + tester.getRect(find.text('R4:C1')), + const Rect.fromLTWH(75.0, 290.0, 75.0, 225.0), + ); + + horizontalController.jumpTo(10.0); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 10.0); + expect(horizontalController.position.pixels, 10.0); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTWH(0.0, 0.0, 150.0, 150.0), + ); + expect( + tester.getRect(find.text('R0:C2')), + const Rect.fromLTWH(140.0, 0.0, 150.0, 75.0), + ); + expect( + tester.getRect(find.text('R1:C4')), + const Rect.fromLTWH(290.0, 75.0, 225.0, 75.0), + ); + expect( + tester.getRect(find.text('R2:C0')), + const Rect.fromLTWH(0.0, 140.0, 75.0, 150.0), + ); + expect( + tester.getRect(find.text('R4:C1')), + const Rect.fromLTWH(75.0, 290.0, 75.0, 225.0), + ); + }); + + testWidgets('Vertical reversed', (WidgetTester tester) async { + final ScrollController verticalController = ScrollController(); + final ScrollController horizontalController = ScrollController(); + final TableView tableView = TableView.builder( + verticalDetails: ScrollableDetails.vertical( + reverse: true, + controller: verticalController, + ), + horizontalDetails: ScrollableDetails.horizontal( + controller: horizontalController, + ), + columnCount: 20, + rowCount: 20, + pinnedRowCount: 2, + pinnedColumnCount: 2, + columnBuilder: (_) => span, + rowBuilder: (_) => span, + cellBuilder: (_, TableVicinity vicinity) { + return TableViewCell( + columnMergeStart: + bothMerged[vicinity]?.start ?? columnMerged[vicinity]?.start, + columnMergeSpan: + bothMerged[vicinity]?.span ?? columnMerged[vicinity]?.span, + rowMergeStart: + bothMerged[vicinity]?.start ?? rowMerged[vicinity]?.start, + rowMergeSpan: + bothMerged[vicinity]?.span ?? rowMerged[vicinity]?.span, + child: Text( + 'R${bothMerged[vicinity]?.start ?? rowMerged[vicinity]?.start ?? vicinity.row}:' + 'C${bothMerged[vicinity]?.start ?? columnMerged[vicinity]?.start ?? vicinity.column}', + ), + ); + }, + ); + await tester.pumpWidget(MaterialApp(home: tableView)); + await tester.pumpAndSettle(); + + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTWH(0.0, 450.0, 150.0, 150.0), + ); + expect( + tester.getRect(find.text('R0:C2')), + const Rect.fromLTWH(150.0, 525.0, 150.0, 75.0), + ); + expect( + tester.getRect(find.text('R1:C4')), + const Rect.fromLTWH(300.0, 450.0, 225.0, 75.0), + ); + expect( + tester.getRect(find.text('R2:C0')), + const Rect.fromLTWH(0.0, 300.0, 75.0, 150.0), + ); + expect( + tester.getRect(find.text('R4:C1')), + const Rect.fromLTWH(75.0, 75.0, 75.0, 225.0), + ); + + verticalController.jumpTo(10.0); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 10.0); + expect(horizontalController.position.pixels, 0.0); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTWH(0.0, 450.0, 150.0, 150.0), + ); + expect( + tester.getRect(find.text('R0:C2')), + const Rect.fromLTWH(150.0, 525.0, 150.0, 75.0), + ); + expect( + tester.getRect(find.text('R1:C4')), + const Rect.fromLTWH(300.0, 450.0, 225.0, 75.0), + ); + expect( + tester.getRect(find.text('R2:C0')), + const Rect.fromLTWH(0.0, 310.0, 75.0, 150.0), + ); + expect( + tester.getRect(find.text('R4:C1')), + const Rect.fromLTWH(75.0, 85.0, 75.0, 225.0), + ); + + horizontalController.jumpTo(10.0); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 10.0); + expect(horizontalController.position.pixels, 10.0); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTWH(0.0, 450.0, 150.0, 150.0), + ); + expect( + tester.getRect(find.text('R0:C2')), + const Rect.fromLTWH(140.0, 525.0, 150.0, 75.0), + ); + expect( + tester.getRect(find.text('R1:C4')), + const Rect.fromLTWH(290.0, 450.0, 225.0, 75.0), + ); + expect( + tester.getRect(find.text('R2:C0')), + const Rect.fromLTWH(0.0, 310.0, 75.0, 150.0), + ); + expect( + tester.getRect(find.text('R4:C1')), + const Rect.fromLTWH(75.0, 85.0, 75.0, 225.0), + ); + }); + + testWidgets('Horizontal reversed', (WidgetTester tester) async { + final ScrollController verticalController = ScrollController(); + final ScrollController horizontalController = ScrollController(); + final TableView tableView = TableView.builder( + verticalDetails: ScrollableDetails.vertical( + controller: verticalController, + ), + horizontalDetails: ScrollableDetails.horizontal( + reverse: true, + controller: horizontalController, + ), + columnCount: 20, + rowCount: 20, + pinnedRowCount: 2, + pinnedColumnCount: 2, + columnBuilder: (_) => span, + rowBuilder: (_) => span, + cellBuilder: (_, TableVicinity vicinity) { + return TableViewCell( + columnMergeStart: + bothMerged[vicinity]?.start ?? columnMerged[vicinity]?.start, + columnMergeSpan: + bothMerged[vicinity]?.span ?? columnMerged[vicinity]?.span, + rowMergeStart: + bothMerged[vicinity]?.start ?? rowMerged[vicinity]?.start, + rowMergeSpan: + bothMerged[vicinity]?.span ?? rowMerged[vicinity]?.span, + child: Text( + 'R${bothMerged[vicinity]?.start ?? rowMerged[vicinity]?.start ?? vicinity.row}:' + 'C${bothMerged[vicinity]?.start ?? columnMerged[vicinity]?.start ?? vicinity.column}', + ), + ); + }, + ); + await tester.pumpWidget(MaterialApp(home: tableView)); + await tester.pumpAndSettle(); + + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTWH(650.0, 0.0, 150.0, 150.0), + ); + expect( + tester.getRect(find.text('R0:C2')), + const Rect.fromLTWH(500.0, 0.0, 150.0, 75.0), + ); + expect( + tester.getRect(find.text('R1:C4')), + const Rect.fromLTWH(275.0, 75.0, 225.0, 75.0), + ); + expect( + tester.getRect(find.text('R2:C0')), + const Rect.fromLTWH(725.0, 150.0, 75.0, 150.0), + ); + expect( + tester.getRect(find.text('R4:C1')), + const Rect.fromLTWH(650.0, 300.0, 75.0, 225.0), + ); + + verticalController.jumpTo(10.0); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 10.0); + expect(horizontalController.position.pixels, 0.0); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTWH(650.0, 0.0, 150.0, 150.0), + ); + expect( + tester.getRect(find.text('R0:C2')), + const Rect.fromLTWH(500.0, 0.0, 150.0, 75.0), + ); + expect( + tester.getRect(find.text('R1:C4')), + const Rect.fromLTWH(275.0, 75.0, 225.0, 75.0), + ); + expect( + tester.getRect(find.text('R2:C0')), + const Rect.fromLTWH(725.0, 140.0, 75.0, 150.0), + ); + expect( + tester.getRect(find.text('R4:C1')), + const Rect.fromLTWH(650.0, 290.0, 75.0, 225.0), + ); + + horizontalController.jumpTo(10.0); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 10.0); + expect(horizontalController.position.pixels, 10.0); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTWH(650.0, 0.0, 150.0, 150.0), + ); + expect( + tester.getRect(find.text('R0:C2')), + const Rect.fromLTWH(510.0, 0.0, 150.0, 75.0), + ); + expect( + tester.getRect(find.text('R1:C4')), + const Rect.fromLTWH(285.0, 75.0, 225.0, 75.0), + ); + expect( + tester.getRect(find.text('R2:C0')), + const Rect.fromLTWH(725.0, 140.0, 75.0, 150.0), + ); + expect( + tester.getRect(find.text('R4:C1')), + const Rect.fromLTWH(650.0, 290.0, 75.0, 225.0), + ); + }); + + testWidgets('Both reversed', (WidgetTester tester) async { + final ScrollController verticalController = ScrollController(); + final ScrollController horizontalController = ScrollController(); + final TableView tableView = TableView.builder( + verticalDetails: ScrollableDetails.vertical( + reverse: true, + controller: verticalController, + ), + horizontalDetails: ScrollableDetails.horizontal( + reverse: true, + controller: horizontalController, + ), + columnCount: 20, + rowCount: 20, + pinnedRowCount: 2, + pinnedColumnCount: 2, + columnBuilder: (_) => span, + rowBuilder: (_) => span, + cellBuilder: (_, TableVicinity vicinity) { + return TableViewCell( + columnMergeStart: + bothMerged[vicinity]?.start ?? columnMerged[vicinity]?.start, + columnMergeSpan: + bothMerged[vicinity]?.span ?? columnMerged[vicinity]?.span, + rowMergeStart: + bothMerged[vicinity]?.start ?? rowMerged[vicinity]?.start, + rowMergeSpan: + bothMerged[vicinity]?.span ?? rowMerged[vicinity]?.span, + child: Text( + 'R${bothMerged[vicinity]?.start ?? rowMerged[vicinity]?.start ?? vicinity.row}:' + 'C${bothMerged[vicinity]?.start ?? columnMerged[vicinity]?.start ?? vicinity.column}', + ), + ); + }, + ); + await tester.pumpWidget(MaterialApp(home: tableView)); + await tester.pumpAndSettle(); + + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTWH(650.0, 450.0, 150.0, 150.0), + ); + expect( + tester.getRect(find.text('R0:C2')), + const Rect.fromLTWH(500.0, 525.0, 150.0, 75.0), + ); + expect( + tester.getRect(find.text('R1:C4')), + const Rect.fromLTWH(275.0, 450.0, 225.0, 75.0), + ); + expect( + tester.getRect(find.text('R2:C0')), + const Rect.fromLTWH(725.0, 300.0, 75.0, 150.0), + ); + expect( + tester.getRect(find.text('R4:C1')), + const Rect.fromLTWH(650.0, 75.0, 75.0, 225.0), + ); + + verticalController.jumpTo(10.0); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 10.0); + expect(horizontalController.position.pixels, 0.0); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTWH(650.0, 450.0, 150.0, 150.0), + ); + expect( + tester.getRect(find.text('R0:C2')), + const Rect.fromLTWH(500.0, 525.0, 150.0, 75.0), + ); + expect( + tester.getRect(find.text('R1:C4')), + const Rect.fromLTWH(275.0, 450.0, 225.0, 75.0), + ); + expect( + tester.getRect(find.text('R2:C0')), + const Rect.fromLTWH(725.0, 310.0, 75.0, 150.0), + ); + expect( + tester.getRect(find.text('R4:C1')), + const Rect.fromLTWH(650.0, 85.0, 75.0, 225.0), + ); + + horizontalController.jumpTo(10.0); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 10.0); + expect(horizontalController.position.pixels, 10.0); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTWH(650.0, 450.0, 150.0, 150.0), + ); + expect( + tester.getRect(find.text('R0:C2')), + const Rect.fromLTWH(510.0, 525.0, 150.0, 75.0), + ); + expect( + tester.getRect(find.text('R1:C4')), + const Rect.fromLTWH(285.0, 450.0, 225.0, 75.0), + ); + expect( + tester.getRect(find.text('R2:C0')), + const Rect.fromLTWH(725.0, 310.0, 75.0, 150.0), + ); + expect( + tester.getRect(find.text('R4:C1')), + const Rect.fromLTWH(650.0, 85.0, 75.0, 225.0), + ); + }); + }); }); } From 7df2085c80e22a990b790891f1b8199f4a7c48ab Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Thu, 22 Feb 2024 14:31:42 -0800 Subject: [PATCH 018/126] [web] Removes a few deprecated API usages. (#6177) This PR removes a bunch of deprecated APIs from the following packages: * `web_benchmarks` * `file_selector_web` * `cross_file` * `image_picker_platform_interface` ## Issues * Fixes https://github.com/flutter/flutter/issues/143113 * Fixes https://github.com/flutter/flutter/issues/143399 * Fixes https://github.com/flutter/flutter/issues/143878 * Fixes https://github.com/flutter/flutter/issues/143892 * Closes https://github.com/flutter/packages/pull/5248 --- packages/cross_file/CHANGELOG.md | 4 +++ packages/cross_file/lib/src/types/html.dart | 36 ++++++++++--------- .../lib/src/web_helpers/web_helpers.dart | 6 ++-- packages/cross_file/pubspec.yaml | 2 +- .../cross_file/test/x_file_html_test.dart | 6 ++-- .../file_selector_web/CHANGELOG.md | 4 +++ .../integration_test/dom_helper_test.dart | 5 +-- .../file_selector_web_test.dart | 2 +- .../file_selector_web/lib/src/dom_helper.dart | 2 +- .../file_selector_web/pubspec.yaml | 2 +- .../CHANGELOG.md | 5 +-- .../lib/src/types/picked_file/html.dart | 2 +- .../pubspec.yaml | 6 ++-- packages/web_benchmarks/CHANGELOG.md | 4 +++ packages/web_benchmarks/lib/client.dart | 16 +++++---- packages/web_benchmarks/lib/src/recorder.dart | 5 ++- packages/web_benchmarks/pubspec.yaml | 3 +- 17 files changed, 67 insertions(+), 43 deletions(-) diff --git a/packages/cross_file/CHANGELOG.md b/packages/cross_file/CHANGELOG.md index 52b11b97de35..2c4e4939615e 100644 --- a/packages/cross_file/CHANGELOG.md +++ b/packages/cross_file/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.4+1 + +* Removes a few deprecated API usages. + ## 0.3.4 * Updates to web code to package `web: ^0.5.0`. diff --git a/packages/cross_file/lib/src/types/html.dart b/packages/cross_file/lib/src/types/html.dart index a58dc35278b5..8bc5361bbfb2 100644 --- a/packages/cross_file/lib/src/types/html.dart +++ b/packages/cross_file/lib/src/types/html.dart @@ -8,7 +8,7 @@ import 'dart:js_interop'; import 'dart:typed_data'; import 'package:meta/meta.dart'; -import 'package:web/helpers.dart'; +import 'package:web/web.dart'; import '../web_helpers/web_helpers.dart'; import 'base.dart'; @@ -133,22 +133,26 @@ class XFile extends XFileBase { throw Exception('Safari cannot handle XFiles larger than 4GB.'); } - late XMLHttpRequest request; - try { - request = await HttpRequest.request(path, responseType: 'blob'); - } on ProgressEvent catch (e) { - if (e.type == 'error') { - throw Exception( - 'Could not load Blob from its URL. Has it been revoked?'); - } - rethrow; - } - - _browserBlob = request.response as Blob?; + final Completer blobCompleter = Completer(); - assert(_browserBlob != null, 'The Blob backing this XFile cannot be null!'); - - return _browserBlob!; + late XMLHttpRequest request; + request = XMLHttpRequest() + ..open('get', path, true) + ..responseType = 'blob' + ..onLoad.listen((ProgressEvent e) { + assert(request.response != null, + 'The Blob backing this XFile cannot be null!'); + blobCompleter.complete(request.response! as Blob); + }) + ..onError.listen((ProgressEvent e) { + if (e.type == 'error') { + blobCompleter.completeError(Exception( + 'Could not load Blob from its URL. Has it been revoked?')); + } + }) + ..send(); + + return blobCompleter.future; } @override diff --git a/packages/cross_file/lib/src/web_helpers/web_helpers.dart b/packages/cross_file/lib/src/web_helpers/web_helpers.dart index ec9a2e86e7d7..bd50fc989502 100644 --- a/packages/cross_file/lib/src/web_helpers/web_helpers.dart +++ b/packages/cross_file/lib/src/web_helpers/web_helpers.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:web/helpers.dart'; +import 'package:web/web.dart'; /// Create anchor element with download attribute HTMLAnchorElement createAnchorElement(String href, String? suggestedName) => @@ -20,11 +20,11 @@ void addElementToContainerAndClick(Element container, HTMLElement element) { /// Initializes a DOM container where elements can be injected. Element ensureInitialized(String id) { - Element? target = querySelector('#$id'); + Element? target = document.querySelector('#$id'); if (target == null) { final Element targetElement = document.createElement('flt-x-file')..id = id; - querySelector('body')!.appendChild(targetElement); + document.body!.appendChild(targetElement); target = targetElement; } return target; diff --git a/packages/cross_file/pubspec.yaml b/packages/cross_file/pubspec.yaml index b7b7f7af1088..cf8f9ca7d447 100644 --- a/packages/cross_file/pubspec.yaml +++ b/packages/cross_file/pubspec.yaml @@ -2,7 +2,7 @@ name: cross_file description: An abstraction to allow working with files across multiple platforms. repository: https://github.com/flutter/packages/tree/main/packages/cross_file issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+cross_file%22 -version: 0.3.4 +version: 0.3.4+1 environment: sdk: ^3.3.0 diff --git a/packages/cross_file/test/x_file_html_test.dart b/packages/cross_file/test/x_file_html_test.dart index 4b1dabd1583d..dedd8806f01a 100644 --- a/packages/cross_file/test/x_file_html_test.dart +++ b/packages/cross_file/test/x_file_html_test.dart @@ -11,7 +11,7 @@ import 'dart:typed_data'; import 'package:cross_file/cross_file.dart'; import 'package:test/test.dart'; -import 'package:web/helpers.dart' as html; +import 'package:web/web.dart' as html; const String expectedStringContents = 'Hello, world! I ❤ ñ! 空手'; final Uint8List bytes = Uint8List.fromList(utf8.encode(expectedStringContents)); @@ -95,7 +95,7 @@ void main() { await file.saveTo(''); final html.Element? container = - html.querySelector('#$crossFileDomElementId'); + html.document.querySelector('#$crossFileDomElementId'); expect(container, isNotNull); }); @@ -106,7 +106,7 @@ void main() { await file.saveTo('path'); final html.Element container = - html.querySelector('#$crossFileDomElementId')!; + html.document.querySelector('#$crossFileDomElementId')!; late html.HTMLAnchorElement element; for (int i = 0; i < container.childNodes.length; i++) { diff --git a/packages/file_selector/file_selector_web/CHANGELOG.md b/packages/file_selector/file_selector_web/CHANGELOG.md index e2dd2901aa8e..692d1da7589a 100644 --- a/packages/file_selector/file_selector_web/CHANGELOG.md +++ b/packages/file_selector/file_selector_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.4+1 + +* Removes a few deprecated API usages. + ## 0.9.4 * Updates web code to package `web: ^0.5.0`. diff --git a/packages/file_selector/file_selector_web/example/integration_test/dom_helper_test.dart b/packages/file_selector/file_selector_web/example/integration_test/dom_helper_test.dart index c4697a70a5d4..aa9dcdba187f 100644 --- a/packages/file_selector/file_selector_web/example/integration_test/dom_helper_test.dart +++ b/packages/file_selector/file_selector_web/example/integration_test/dom_helper_test.dart @@ -8,7 +8,7 @@ import 'package:file_selector_platform_interface/file_selector_platform_interfac import 'package:file_selector_web/src/dom_helper.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; -import 'package:web/helpers.dart'; +import 'package:web/web.dart'; void main() { group('dom_helper', () { @@ -41,7 +41,8 @@ void main() { setUp(() { domHelper = DomHelper(); - input = (createElementTag('input') as HTMLInputElement)..type = 'file'; + input = (document.createElement('input') as HTMLInputElement) + ..type = 'file'; }); group('getFiles', () { diff --git a/packages/file_selector/file_selector_web/example/integration_test/file_selector_web_test.dart b/packages/file_selector/file_selector_web/example/integration_test/file_selector_web_test.dart index f3d4c1ebf3e7..80376c5449fc 100644 --- a/packages/file_selector/file_selector_web/example/integration_test/file_selector_web_test.dart +++ b/packages/file_selector/file_selector_web/example/integration_test/file_selector_web_test.dart @@ -9,7 +9,7 @@ import 'package:file_selector_web/file_selector_web.dart'; import 'package:file_selector_web/src/dom_helper.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; -import 'package:web/helpers.dart'; +import 'package:web/web.dart'; void main() { group('FileSelectorWeb', () { diff --git a/packages/file_selector/file_selector_web/lib/src/dom_helper.dart b/packages/file_selector/file_selector_web/lib/src/dom_helper.dart index 309f0ee432f1..c19748b92443 100644 --- a/packages/file_selector/file_selector_web/lib/src/dom_helper.dart +++ b/packages/file_selector/file_selector_web/lib/src/dom_helper.dart @@ -8,7 +8,7 @@ import 'dart:js_interop'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/services.dart'; -import 'package:web/helpers.dart'; +import 'package:web/web.dart'; /// Class to manipulate the DOM with the intention of reading files from it. class DomHelper { diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index edc74e3064ea..2b4dc5cd7d9d 100644 --- a/packages/file_selector/file_selector_web/pubspec.yaml +++ b/packages/file_selector/file_selector_web/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_web description: Web platform implementation of file_selector repository: https://github.com/flutter/packages/tree/main/packages/file_selector/file_selector_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.9.4 +version: 0.9.4+1 environment: sdk: ^3.3.0 diff --git a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md index 854437f5d232..ef63d598bb83 100644 --- a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md +++ b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 2.9.4 -* Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. +* Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. +* Removes a few deprecated API usages. ## 2.9.3 diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart index 7d9761a57602..e296ceb9d80d 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart @@ -27,7 +27,7 @@ class PickedFile extends PickedFileBase { Future get _bytes async { if (_initBytes != null) { - return Future.value(UnmodifiableUint8ListView(_initBytes!)); + return _initBytes.asUnmodifiableView(); } return http.readBytes(Uri.parse(path)); } diff --git a/packages/image_picker/image_picker_platform_interface/pubspec.yaml b/packages/image_picker/image_picker_platform_interface/pubspec.yaml index 66b17ed51f47..b38945c6dac7 100644 --- a/packages/image_picker/image_picker_platform_interface/pubspec.yaml +++ b/packages/image_picker/image_picker_platform_interface/pubspec.yaml @@ -4,11 +4,11 @@ repository: https://github.com/flutter/packages/tree/main/packages/image_picker/ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%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.9.3 +version: 2.9.4 environment: - sdk: ^3.1.0 - flutter: ">=3.13.0" + sdk: ^3.3.0 + flutter: ">=3.19.0" dependencies: cross_file: ^0.3.1+1 diff --git a/packages/web_benchmarks/CHANGELOG.md b/packages/web_benchmarks/CHANGELOG.md index c4b3cf125ad2..02e11be9976d 100644 --- a/packages/web_benchmarks/CHANGELOG.md +++ b/packages/web_benchmarks/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.2.1 + +* Removes a few deprecated API usages. + ## 1.2.0 * Updates to web code to package `web: ^0.5.0`. diff --git a/packages/web_benchmarks/lib/client.dart b/packages/web_benchmarks/lib/client.dart index d1c52d775fb8..d800e97a7589 100644 --- a/packages/web_benchmarks/lib/client.dart +++ b/packages/web_benchmarks/lib/client.dart @@ -318,7 +318,7 @@ class LocalBenchmarkServerClient { /// DevTools Protocol. Future startPerformanceTracing(String? benchmarkName) async { _checkNotManualMode(); - await HttpRequest.request( + await _requestXhr( '/start-performance-tracing?label=$benchmarkName', method: 'POST', mimeType: 'application/json', @@ -328,7 +328,7 @@ class LocalBenchmarkServerClient { /// Stops the performance tracing session started by [startPerformanceTracing]. Future stopPerformanceTracing() async { _checkNotManualMode(); - await HttpRequest.request( + await _requestXhr( '/stop-performance-tracing', method: 'POST', mimeType: 'application/json', @@ -356,7 +356,7 @@ class LocalBenchmarkServerClient { /// The server will halt the devicelab task and log the error. Future reportError(dynamic error, StackTrace stackTrace) async { _checkNotManualMode(); - await HttpRequest.request( + await _requestXhr( '/on-error', method: 'POST', mimeType: 'application/json', @@ -370,7 +370,7 @@ class LocalBenchmarkServerClient { /// Reports a message about the demo to the benchmark server. Future printToConsole(String report) async { _checkNotManualMode(); - await HttpRequest.request( + await _requestXhr( '/print-to-console', method: 'POST', mimeType: 'text/plain', @@ -384,7 +384,7 @@ class LocalBenchmarkServerClient { String url, { required String method, required String mimeType, - required String sendData, + String? sendData, }) { final Completer completer = Completer(); final XMLHttpRequest xhr = XMLHttpRequest(); @@ -394,7 +394,11 @@ class LocalBenchmarkServerClient { completer.complete(xhr); }); xhr.onError.listen(completer.completeError); - xhr.send(sendData.toJS); + if (sendData != null) { + xhr.send(sendData.toJS); + } else { + xhr.send(); + } return completer.future; } } diff --git a/packages/web_benchmarks/lib/src/recorder.dart b/packages/web_benchmarks/lib/src/recorder.dart index 7488c44c2c41..85d841f1f8cd 100644 --- a/packages/web_benchmarks/lib/src/recorder.dart +++ b/packages/web_benchmarks/lib/src/recorder.dart @@ -248,6 +248,7 @@ abstract class SceneBuilderRecorder extends Recorder { } }; PlatformDispatcher.instance.onDrawFrame = () { + final FlutterView? view = PlatformDispatcher.instance.implicitView; try { _profile.record('drawFrameDuration', () { final SceneBuilder sceneBuilder = SceneBuilder(); @@ -255,7 +256,9 @@ abstract class SceneBuilderRecorder extends Recorder { _profile.record('sceneBuildDuration', () { final Scene scene = sceneBuilder.build(); _profile.record('windowRenderDuration', () { - window.render(scene); + assert(view != null, + 'Cannot profile windowRenderDuration on a null View.'); + view!.render(scene); }, reported: false); }, reported: false); }, reported: true); diff --git a/packages/web_benchmarks/pubspec.yaml b/packages/web_benchmarks/pubspec.yaml index 584e69706ee3..af66a69c9088 100644 --- a/packages/web_benchmarks/pubspec.yaml +++ b/packages/web_benchmarks/pubspec.yaml @@ -2,7 +2,7 @@ name: web_benchmarks description: A benchmark harness for performance-testing Flutter apps in Chrome. repository: https://github.com/flutter/packages/tree/main/packages/web_benchmarks issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+web_benchmarks%22 -version: 1.2.0 +version: 1.2.1 environment: sdk: ^3.3.0 @@ -14,7 +14,6 @@ dependencies: sdk: flutter flutter_test: sdk: flutter - http: ^1.0.0 logging: ^1.0.2 meta: ^1.7.0 path: ^1.8.0 From f191932f9e807af1c343663659d5078c74384ea8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Feb 2024 17:34:48 +0000 Subject: [PATCH 019/126] Bump github/codeql-action from 3.24.3 to 3.24.4 (#6184) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.24.3 to 3.24.4.

Changelog

Sourced from github/codeql-action's changelog.

CodeQL Action Changelog

See the releases page for the relevant changes to the CodeQL CLI and language packs.

Note that the only difference between v2 and v3 of the CodeQL Action is the node version they support, with v3 running on node 20 while we continue to release v2 to support running on node 16. For example 3.22.11 was the first v3 release and is functionally identical to 2.22.11. This approach ensures an easy way to track exactly which features are included in different versions, indicated by the minor and patch version numbers.

[UNRELEASED]

No user facing changes.

3.24.4 - 21 Feb 2024

  • Fix an issue where an existing, but empty, /sys/fs/cgroup/cpuset.cpus file always resulted in a single-threaded run. #2151
  • Update default CodeQL bundle version to 2.16.3. #2156

3.24.3 - 15 Feb 2024

  • Fix an issue where the CodeQL Action would fail to load a configuration specified by the config input to the init Action. #2147

3.24.2 - 15 Feb 2024

  • Enable improved multi-threaded performance on larger runners for GitHub Enterprise Server users. This feature is already available to GitHub.com users. #2141

3.24.1 - 13 Feb 2024

  • Update default CodeQL bundle version to 2.16.2. #2124
  • The CodeQL action no longer fails if it can't write to the telemetry api endpoint. #2121

3.24.0 - 02 Feb 2024

  • CodeQL Python analysis will no longer install dependencies on GitHub Enterprise Server, as is already the case for GitHub.com. See release notes for 3.23.0 for more details. #2106

3.23.2 - 26 Jan 2024

  • On Linux, the maximum possible value for the --threads option now respects the CPU count as specified in cgroup files to more accurately reflect the number of available cores when running in containers. #2083
  • Update default CodeQL bundle version to 2.16.1. #2096

3.23.1 - 17 Jan 2024

  • Update default CodeQL bundle version to 2.16.0. #2073
  • Change the retention period for uploaded debug artifacts to 7 days. Previously, this was whatever the repository default was. #2079

3.23.0 - 08 Jan 2024

  • We are rolling out a feature in January 2024 that will disable Python dependency installation by default for all users. This improves the speed of analysis while having only a very minor impact on results. You can override this behavior by setting CODEQL_ACTION_DISABLE_PYTHON_DEPENDENCY_INSTALLATION=false in your workflow, however we plan to remove this ability in future versions of the CodeQL Action. #2031
  • The CodeQL Action now requires CodeQL version 2.11.6 or later. For more information, see the corresponding changelog entry for CodeQL Action version 2.22.7. #2009

3.22.12 - 22 Dec 2023

  • Update default CodeQL bundle version to 2.15.5. #2047

... (truncated)

Commits
  • e2e140a Merge pull request #2157 from github/update-v3.24.4-982d9346a
  • 5b1ae29 Update changelog for v3.24.4
  • 982d934 Merge pull request #2111 from github/nickfyson/first-or-third-party
  • ebda17e respond to review comments
  • cca1dde Merge branch 'main' into nickfyson/first-or-third-party
  • f32f0bf ensure only the upload-sarif action can submit a status report with first_par...
  • bf8c75e Merge pull request #2153 from github/aeisenberg/add-permissions-doc
  • 36c51ac Capitalize Code Scanning
  • a7dc229 add first_party_analysis boolean to all status reports
  • 592977e Merge pull request #2151 from angelapwen/fix-cpu-group-bug
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github/codeql-action&package-manager=github_actions&previous-version=3.24.3&new-version=3.24.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- .github/workflows/scorecards-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 6f3299ad195d..a9d110555e65 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -49,6 +49,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@379614612a29c9e28f31f39a59013eb8012a51f0 # v1.0.26 + uses: github/codeql-action/upload-sarif@e2e140ad1441662206e8f97754b166877dfa1c73 # v1.0.26 with: sarif_file: results.sarif From 74eeb2915438ec98c2c0288bcb1cc12800626bff Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Fri, 23 Feb 2024 13:42:31 -0500 Subject: [PATCH 020/126] [camera] Document -1 return value for `getExposureOffsetStepSize` (#6182) Documents `getExposureOffsetStepSize` to return -1 if the device does not support exposure compensation. Helps account for `camera_android_camerax` since CameraX does not return a step size if exposure compensation is not supported. A follow up to [this discussion](https://github.com/flutter/packages/pull/6059#discussion_r1481839497). --- packages/camera/camera_platform_interface/CHANGELOG.md | 4 +++- .../lib/src/platform_interface/camera_platform.dart | 3 ++- packages/camera/camera_platform_interface/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera_platform_interface/CHANGELOG.md b/packages/camera/camera_platform_interface/CHANGELOG.md index 1db3ab335b91..836b73586df7 100644 --- a/packages/camera/camera_platform_interface/CHANGELOG.md +++ b/packages/camera/camera_platform_interface/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.7.4 * Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. +* Documents `getExposureOffsetStepSize` to return -1 if the device does not support + exposure compensation. ## 2.7.3 diff --git a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart index 6707962cbc1f..c791d030ca2c 100644 --- a/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart +++ b/packages/camera/camera_platform_interface/lib/src/platform_interface/camera_platform.dart @@ -221,7 +221,8 @@ abstract class CameraPlatform extends PlatformInterface { /// Gets the supported step size for exposure offset for the selected camera in EV units. /// - /// Returns 0 when the camera supports using a free value without stepping. + /// Returns 0 when the camera supports using a free value without stepping and + /// returns -1 when exposure compensation is not supported. Future getExposureOffsetStepSize(int cameraId) { throw UnimplementedError('getMinExposureOffset() is not implemented.'); } diff --git a/packages/camera/camera_platform_interface/pubspec.yaml b/packages/camera/camera_platform_interface/pubspec.yaml index de4de97acff1..e139e2062db1 100644 --- a/packages/camera/camera_platform_interface/pubspec.yaml +++ b/packages/camera/camera_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/packages/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%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.7.3 +version: 2.7.4 environment: sdk: ^3.1.0 From fe6111ba54651ea793879070f90b8df9a10feb58 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 23 Feb 2024 14:24:03 -0500 Subject: [PATCH 021/126] Roll Flutter from 41581c9a1b90 to 39585e66c11f (31 revisions) (#6194) https://github.com/flutter/flutter/compare/41581c9a1b90...39585e66c11f 2024-02-23 jonahwilliams@google.com Re-enable Impeller goldens blocking. (flutter/flutter#144023) 2024-02-23 engine-flutter-autoroll@skia.org Roll Flutter Engine from 0f3ad23b84ed to 5f99a6c3289e (16 revisions) (flutter/flutter#144028) 2024-02-23 engine-flutter-autoroll@skia.org Roll Packages from 078c2a304a01 to 7df2085c80e2 (3 revisions) (flutter/flutter#144030) 2024-02-23 15619084+vashworth@users.noreply.github.com Run tests on iOS 17 exclusively (flutter/flutter#144022) 2024-02-23 15619084+vashworth@users.noreply.github.com Retry button tap in [FlutterUITests testFlutterViewWarm] (flutter/flutter#143967) 2024-02-23 31859944+LongCatIsLooong@users.noreply.github.com Remove deprecated `InteractiveViewer.alignPanAxis` (flutter/flutter#142500) 2024-02-23 31859944+LongCatIsLooong@users.noreply.github.com Remove deprecated `KeepAliveHandle.release` (flutter/flutter#143961) 2024-02-23 goderbauer@google.com Remove deprecated AnimatedListItemBuilder, AnimatedListRemovedItemBuilder (flutter/flutter#143974) 2024-02-23 engine-flutter-autoroll@skia.org Manual roll Flutter Engine from 06448ee8165f to 0f3ad23b84ed (16 revisions) (flutter/flutter#143989) 2024-02-23 katelovett@google.com Remove deprecated TimelineSummary.writeSummaryToFile (flutter/flutter#143983) 2024-02-23 54558023+keyonghan@users.noreply.github.com Remove unused drone_dimension field (flutter/flutter#143984) 2024-02-23 goderbauer@google.com Remove deprecated MediaQuery.boldTextOverride (flutter/flutter#143960) 2024-02-22 goderbauer@google.com Revert engine back to 06448ee8165f4ff6579ef9510ac69322eda896ac (flutter/flutter#143986) 2024-02-22 katelovett@google.com Remove deprecated FlutterDriver.enableAccessibility (flutter/flutter#143979) 2024-02-22 engine-flutter-autoroll@skia.org Roll Flutter Engine from cb6115d3a6ab to c5d2e011652b (3 revisions) (flutter/flutter#143980) 2024-02-22 36861262+QuncCccccc@users.noreply.github.com Revert "Introduce tone-based surfaces and accent color add-ons - Part 2" (flutter/flutter#143973) 2024-02-22 110420204+dsanagustin@users.noreply.github.com Add CloseButtonTooltip to the 'X' button on a SnackBar (flutter/flutter#143934) 2024-02-22 49699333+dependabot[bot]@users.noreply.github.com Bump github/codeql-action from 3.24.3 to 3.24.4 (flutter/flutter#143971) 2024-02-22 engine-flutter-autoroll@skia.org Roll Flutter Engine from 06448ee8165f to cb6115d3a6ab (10 revisions) (flutter/flutter#143972) 2024-02-22 kustermann@google.com Use inlining annotations on important methods for all targets (flutter/flutter#143923) 2024-02-22 31859944+LongCatIsLooong@users.noreply.github.com Add missing `TextPainter.strutStyle` to paragraph style (flutter/flutter#143771) 2024-02-22 zanderso@users.noreply.github.com Shift Moto G4 staging tests to A02s (flutter/flutter#143957) 2024-02-22 sokolovskyi.konstantin@gmail.com Add CurvedAnimation disposals in some widgets (flutter/flutter#143790) 2024-02-22 andrew@divineflame.com Fix documentation typo in basic.dart (flutter/flutter#143887) 2024-02-22 pateltirth454@gmail.com Fixed Small Typo in Emulators Test Name (flutter/flutter#143578) 2024-02-22 derekx@google.com Support using lightweight Flutter Engines to run tests (flutter/flutter#141726) 2024-02-22 engine-flutter-autoroll@skia.org Roll Flutter Engine from 9250bfdb38c5 to 06448ee8165f (1 revision) (flutter/flutter#143943) 2024-02-22 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Reland Add UI Benchmarks (#143542) (#143799)" (flutter/flutter#143946) 2024-02-22 engine-flutter-autoroll@skia.org Roll Packages from 48048f6bc779 to 078c2a304a01 (11 revisions) (flutter/flutter#143935) 2024-02-22 engine-flutter-autoroll@skia.org Roll Flutter Engine from 7777480eb94d to 9250bfdb38c5 (8 revisions) (flutter/flutter#143933) 2024-02-22 bernaferrari2@gmail.com Reland Add UI Benchmarks (#143542) (flutter/flutter#143799) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC dit@google.com,rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index b7983d7d8244..17a30297da50 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -41581c9a1b909e481c13a2cee7a6e133f7e2ab1e +39585e66c11f0dccbf27089155f2bdc4b28f82d2 From 41f5a16e13f4c9327e75d431d338c33f2fb35870 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 24 Feb 2024 10:45:23 -0500 Subject: [PATCH 022/126] Roll Flutter from 39585e66c11f to f6c10820fa30 (20 revisions) (#6200) https://github.com/flutter/flutter/compare/39585e66c11f...f6c10820fa30 2024-02-24 engine-flutter-autoroll@skia.org Roll Flutter Engine from 81035b7d56ef to 1d7d5e613d7e (1 revision) (flutter/flutter#144085) 2024-02-24 engine-flutter-autoroll@skia.org Roll Flutter Engine from a148bdb63740 to 81035b7d56ef (2 revisions) (flutter/flutter#144083) 2024-02-24 engine-flutter-autoroll@skia.org Roll Flutter Engine from 3c036c081534 to a148bdb63740 (1 revision) (flutter/flutter#144077) 2024-02-24 engine-flutter-autoroll@skia.org Roll Flutter Engine from 738042295f97 to 3c036c081534 (2 revisions) (flutter/flutter#144073) 2024-02-24 engine-flutter-autoroll@skia.org Roll Flutter Engine from ca2452074a49 to 738042295f97 (4 revisions) (flutter/flutter#144071) 2024-02-24 engine-flutter-autoroll@skia.org Roll Flutter Engine from 9409b75e8f35 to ca2452074a49 (2 revisions) (flutter/flutter#144068) 2024-02-24 mzhou620@gmail.com Adding support for DDC modules when running Flutter Web in debug mode (flutter/flutter#141423) 2024-02-24 engine-flutter-autoroll@skia.org Roll Flutter Engine from 733163c4e5d7 to 9409b75e8f35 (1 revision) (flutter/flutter#144061) 2024-02-23 andrewrkolos@gmail.com allow optional direct injection of Config instance into DevFS (flutter/flutter#144002) 2024-02-23 andrewrkolos@gmail.com Enable asset transformation for `flutter build` for iOS, Android, Windows, MacOS, Linux, and web (also `flutter run` without hot reload support) (flutter/flutter#143815) 2024-02-23 engine-flutter-autoroll@skia.org Roll Flutter Engine from 5d1c0d4dc327 to 733163c4e5d7 (1 revision) (flutter/flutter#144058) 2024-02-23 49699333+dependabot[bot]@users.noreply.github.com Bump github/codeql-action from 3.24.4 to 3.24.5 (flutter/flutter#144059) 2024-02-23 49699333+dependabot[bot]@users.noreply.github.com Bump codecov/codecov-action from 4.0.1 to 4.0.2 (flutter/flutter#144060) 2024-02-23 dkwingsmt@users.noreply.github.com Render the warm up frame in a proper rendering process (flutter/flutter#143290) 2024-02-23 engine-flutter-autoroll@skia.org Roll Flutter Engine from fbc9b889aee9 to 5d1c0d4dc327 (2 revisions) (flutter/flutter#144049) 2024-02-23 engine-flutter-autoroll@skia.org Roll Flutter Engine from b5bebfe43d29 to fbc9b889aee9 (3 revisions) (flutter/flutter#144041) 2024-02-23 jonahwilliams@google.com disable debug banner in m3 page test apps. (flutter/flutter#143857) 2024-02-23 31859944+LongCatIsLooong@users.noreply.github.com Relands "Changing `TextPainter.getOffsetForCaret` implementation to remove the logarithmic search (#143281)" (reverted in #143801) (flutter/flutter#143954) 2024-02-23 engine-flutter-autoroll@skia.org Roll Flutter Engine from 5f99a6c3289e to b5bebfe43d29 (1 revision) (flutter/flutter#144035) 2024-02-23 nate.w5687@gmail.com Implementing null-aware operators throughout the repository (flutter/flutter#143804) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC dit@google.com,rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 17a30297da50..fd6dd656049e 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -39585e66c11f0dccbf27089155f2bdc4b28f82d2 +f6c10820fa30746281fdc90e5b14f1ce73a67e51 From 7f7b715ad91b5e6953234636ac34768b02f816b4 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 25 Feb 2024 14:01:34 -0500 Subject: [PATCH 023/126] Roll Flutter from f6c10820fa30 to 1e8dd1e4d6d7 (3 revisions) (#6203) https://github.com/flutter/flutter/compare/f6c10820fa30...1e8dd1e4d6d7 2024-02-24 engine-flutter-autoroll@skia.org Roll Flutter Engine from b7f7a841c722 to da3d49c30bcc (1 revision) (flutter/flutter#144092) 2024-02-24 engine-flutter-autoroll@skia.org Roll Flutter Engine from 247971f4d7e6 to b7f7a841c722 (2 revisions) (flutter/flutter#144090) 2024-02-24 engine-flutter-autoroll@skia.org Roll Flutter Engine from 1d7d5e613d7e to 247971f4d7e6 (1 revision) (flutter/flutter#144088) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC dit@google.com,rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index fd6dd656049e..10e84c433db8 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f6c10820fa30746281fdc90e5b14f1ce73a67e51 +1e8dd1e4d6d70c5e06525bea3fb164a03d7a6c1d From 25d9c9db1446cc50969563918ae1e125f29701e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 09:22:04 +0000 Subject: [PATCH 024/126] Bump github/codeql-action from 3.24.4 to 3.24.5 (#6204) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.24.4 to 3.24.5.
Changelog

Sourced from github/codeql-action's changelog.

CodeQL Action Changelog

See the releases page for the relevant changes to the CodeQL CLI and language packs.

Note that the only difference between v2 and v3 of the CodeQL Action is the node version they support, with v3 running on node 20 while we continue to release v2 to support running on node 16. For example 3.22.11 was the first v3 release and is functionally identical to 2.22.11. This approach ensures an easy way to track exactly which features are included in different versions, indicated by the minor and patch version numbers.

[UNRELEASED]

No user facing changes.

3.24.5 - 23 Feb 2024

  • Update default CodeQL bundle version to 2.16.3. #2156

3.24.4 - 21 Feb 2024

  • Fix an issue where an existing, but empty, /sys/fs/cgroup/cpuset.cpus file always resulted in a single-threaded run. #2151

3.24.3 - 15 Feb 2024

  • Fix an issue where the CodeQL Action would fail to load a configuration specified by the config input to the init Action. #2147

3.24.2 - 15 Feb 2024

  • Enable improved multi-threaded performance on larger runners for GitHub Enterprise Server users. This feature is already available to GitHub.com users. #2141

3.24.1 - 13 Feb 2024

  • Update default CodeQL bundle version to 2.16.2. #2124
  • The CodeQL action no longer fails if it can't write to the telemetry api endpoint. #2121

3.24.0 - 02 Feb 2024

  • CodeQL Python analysis will no longer install dependencies on GitHub Enterprise Server, as is already the case for GitHub.com. See release notes for 3.23.0 for more details. #2106

3.23.2 - 26 Jan 2024

  • On Linux, the maximum possible value for the --threads option now respects the CPU count as specified in cgroup files to more accurately reflect the number of available cores when running in containers. #2083
  • Update default CodeQL bundle version to 2.16.1. #2096

3.23.1 - 17 Jan 2024

  • Update default CodeQL bundle version to 2.16.0. #2073
  • Change the retention period for uploaded debug artifacts to 7 days. Previously, this was whatever the repository default was. #2079

3.23.0 - 08 Jan 2024

  • We are rolling out a feature in January 2024 that will disable Python dependency installation by default for all users. This improves the speed of analysis while having only a very minor impact on results. You can override this behavior by setting CODEQL_ACTION_DISABLE_PYTHON_DEPENDENCY_INSTALLATION=false in your workflow, however we plan to remove this ability in future versions of the CodeQL Action. #2031
  • The CodeQL Action now requires CodeQL version 2.11.6 or later. For more information, see the corresponding changelog entry for CodeQL Action version 2.22.7. #2009

... (truncated)

Commits
  • 47b3d88 Merge pull request #2162 from github/update-v3.24.5-a74dcdb05
  • 28c2900 Update changelog for v3.24.5
  • a74dcdb Merge pull request #2160 from github/henrymercer/deptrace-with-build-mode
  • aeb89ef Enable C++ deptrace when using autobuild build mode
  • 2896599 Merge pull request #2060 from github/mbg/go/1.22
  • e3a86ed Add comment justifying why we set cache: false
  • 5d55901 Use Go 1.22 in workflows
  • 908a883 Merge pull request #2158 from github/mergeback/v3.24.4-to-main-e2e140ad
  • 9bce06d Merge branch 'main' into mergeback/v3.24.4-to-main-e2e140ad
  • c9f3eed Update checked-in dependencies
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github/codeql-action&package-manager=github_actions&previous-version=3.24.4&new-version=3.24.5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- .github/workflows/scorecards-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index a9d110555e65..f5a305cde026 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -49,6 +49,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@e2e140ad1441662206e8f97754b166877dfa1c73 # v1.0.26 + uses: github/codeql-action/upload-sarif@47b3d888fe66b639e431abf22ebca059152f1eea # v1.0.26 with: sarif_file: results.sarif From 353086c9a61e31871867ce049bd83d42aadc542c Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 26 Feb 2024 07:40:26 -0500 Subject: [PATCH 025/126] [google_map_flutter] Add style to widget - platform interface (#6197) Platform interface portion of https://github.com/flutter/packages/pull/6192 Adds `style` to `MapConfiguration` and adds new `getStyleError` method. Part of Fixes https://github.com/flutter/flutter/issues/66207 --- .../CHANGELOG.md | 6 ++++- .../google_maps_flutter_platform.dart | 6 +++++ .../lib/src/types/map_configuration.dart | 15 ++++++++++-- .../map_configuration_serialization.dart | 1 + .../pubspec.yaml | 2 +- .../google_maps_flutter_platform_test.dart | 9 +++++++ .../test/types/map_configuration_test.dart | 24 +++++++++++++++++++ 7 files changed, 59 insertions(+), 4 deletions(-) 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 1905f2c44e9f..e062daa45430 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,5 +1,9 @@ -## NEXT +## 2.5.0 +* Adds `style` to the `MapConfiguration` to allow setting style as part of + map creation. +* Adds `getStyleError` to the platform interface, to allow asynchronous access + to style errors that occur during initialization. * Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. ## 2.4.3 diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_flutter_platform.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_flutter_platform.dart index d9d0bb37e117..9e829c496aa4 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_flutter_platform.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_flutter_platform.dart @@ -362,6 +362,12 @@ abstract class GoogleMapsFlutterPlatform extends PlatformInterface { throw UnimplementedError('dispose() has not been implemented.'); } + /// If the last attempt to set the style via [MapConfiguration.style] failed, + /// returns the error information, otherwise returns null. + Future getStyleError({required int mapId}) async { + return null; + } + /// Returns a widget displaying the map view - deprecated, use /// [buildViewWithConfiguration] instead. Widget buildView( 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 3ec973fd7d0a..6abd6c641e86 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 @@ -36,6 +36,7 @@ class MapConfiguration { this.trafficEnabled, this.buildingsEnabled, this.cloudMapId, + this.style, }); /// This setting controls how the API handles gestures on the map. Web only. @@ -113,6 +114,11 @@ class MapConfiguration { /// for more details. final String? cloudMapId; + /// Locally configured JSON style. + /// + /// To clear a previously set style, set this to an empty string. + final String? style; + /// Returns a new options object containing only the values of this instance /// that are different from [other]. MapConfiguration diffFrom(MapConfiguration other) { @@ -174,6 +180,7 @@ class MapConfiguration { buildingsEnabled: buildingsEnabled != other.buildingsEnabled ? buildingsEnabled : null, cloudMapId: cloudMapId != other.cloudMapId ? cloudMapId : null, + style: style != other.style ? style : null, ); } @@ -206,6 +213,7 @@ class MapConfiguration { trafficEnabled: diff.trafficEnabled ?? trafficEnabled, buildingsEnabled: diff.buildingsEnabled ?? buildingsEnabled, cloudMapId: diff.cloudMapId ?? cloudMapId, + style: diff.style ?? style, ); } @@ -231,7 +239,8 @@ class MapConfiguration { indoorViewEnabled == null && trafficEnabled == null && buildingsEnabled == null && - cloudMapId == null; + cloudMapId == null && + style == null; @override bool operator ==(Object other) { @@ -262,7 +271,8 @@ class MapConfiguration { indoorViewEnabled == other.indoorViewEnabled && trafficEnabled == other.trafficEnabled && buildingsEnabled == other.buildingsEnabled && - cloudMapId == other.cloudMapId; + cloudMapId == other.cloudMapId && + style == other.style; } @override @@ -288,5 +298,6 @@ class MapConfiguration { trafficEnabled, buildingsEnabled, cloudMapId, + style, ]); } 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 3c3e0b714cb6..43b25fa642da 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 @@ -59,5 +59,6 @@ Map jsonForMapConfiguration(MapConfiguration config) { if (config.buildingsEnabled != null) 'buildingsEnabled': config.buildingsEnabled!, if (config.cloudMapId != null) 'cloudMapId': config.cloudMapId!, + if (config.style != null) 'style': config.style!, }; } 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 2f30c38978f5..8a712be78a77 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.4.3 +version: 2.5.0 environment: sdk: ^3.1.0 diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/platform_interface/google_maps_flutter_platform_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/platform_interface/google_maps_flutter_platform_test.dart index d1dba2b75b55..798559a68f0a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/platform_interface/google_maps_flutter_platform_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/platform_interface/google_maps_flutter_platform_test.dart @@ -83,6 +83,15 @@ void main() { ); }, ); + + test( + 'default implementation of `getStyleError` returns null', + () async { + final GoogleMapsFlutterPlatform platform = + BuildViewGoogleMapsFlutterPlatform(); + expect(await platform.getStyleError(mapId: 0), null); + }, + ); }); } 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 2a53b8c0bbc6..e34f32676e0a 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 @@ -33,6 +33,7 @@ void main() { indoorViewEnabled: false, trafficEnabled: false, buildingsEnabled: false, + style: 'diff base style', ); test('only include changed fields', () async { @@ -408,6 +409,23 @@ void main() { // The hash code should change. expect(empty.hashCode, isNot(diff.hashCode)); }); + + test('handle style', () async { + const String aStlye = 'a style'; + const MapConfiguration diff = MapConfiguration(style: aStlye); + + 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); + // The diff from empty options should be the diff itself. + expect(diff.diffFrom(empty), diff); + // A diff applied to non-empty options should update that field. + expect(updated.style, aStlye); + // The hash code should change. + expect(empty.hashCode, isNot(diff.hashCode)); + }); }); group('isEmpty', () { @@ -541,5 +559,11 @@ void main() { expect(diff.isEmpty, false); }); + + test('is false with style', () async { + const MapConfiguration diff = MapConfiguration(style: 'a style'); + + expect(diff.isEmpty, false); + }); }); } From f9171d1dea99b82dba5feb3a8072e318da33ce22 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 26 Feb 2024 11:11:07 -0500 Subject: [PATCH 026/126] Roll Flutter from 1e8dd1e4d6d7 to b77560e92a58 (8 revisions) (#6207) https://github.com/flutter/flutter/compare/1e8dd1e4d6d7...b77560e92a58 2024-02-26 engine-flutter-autoroll@skia.org Roll Flutter Engine from 32d7f9f0f546 to a15326b2c439 (1 revision) (flutter/flutter#144143) 2024-02-26 tessertaha@gmail.com Fix `Scrollbar.thickness` property is ignored when the `Scrollbar` is hovered (flutter/flutter#144012) 2024-02-26 engine-flutter-autoroll@skia.org Roll Flutter Engine from 168cc5c0a195 to 32d7f9f0f546 (1 revision) (flutter/flutter#144128) 2024-02-26 engine-flutter-autoroll@skia.org Roll Flutter Engine from 330e743203b0 to 168cc5c0a195 (1 revision) (flutter/flutter#144125) 2024-02-26 engine-flutter-autoroll@skia.org Roll Flutter Engine from fd3136782aae to 330e743203b0 (1 revision) (flutter/flutter#144123) 2024-02-26 engine-flutter-autoroll@skia.org Roll Flutter Engine from f372329c9099 to fd3136782aae (1 revision) (flutter/flutter#144118) 2024-02-26 engine-flutter-autoroll@skia.org Roll Flutter Engine from da3d49c30bcc to f372329c9099 (2 revisions) (flutter/flutter#144117) 2024-02-26 jason-simmons@users.noreply.github.com Revert "Re-enable Impeller goldens blocking. (#144023)" (flutter/flutter#144115) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC bmparr@google.com,rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 10e84c433db8..38a1ae5b7bb5 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -1e8dd1e4d6d70c5e06525bea3fb164a03d7a6c1d +b77560e92a58724eab06490a8c03dfc29eb77271 From 0aff69f74656b0e5e639ef459a409a16aa6a730f Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 26 Feb 2024 13:05:23 -0500 Subject: [PATCH 027/126] [tool] Ignore GeneratedPluginRegistrant.swift for `format` (#6195) This file fails `swift-format lint`, so including it in `format` causes lots of warnings when run locally. --- script/tool/lib/src/format_command.dart | 2 + script/tool/test/format_command_test.dart | 46 +++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/script/tool/lib/src/format_command.dart b/script/tool/lib/src/format_command.dart index f2713666435b..3091cf2e11f3 100644 --- a/script/tool/lib/src/format_command.dart +++ b/script/tool/lib/src/format_command.dart @@ -346,6 +346,8 @@ class FormatCommand extends PackageCommand { pathFragmentForDirectories(['example', 'build'])) && // Ignore files in Pods, which are not part of the repository. !path.contains(pathFragmentForDirectories(['Pods'])) && + // See https://github.com/flutter/flutter/issues/144039 + !path.endsWith('GeneratedPluginRegistrant.swift') && // Ignore .dart_tool/, which can have various intermediate files. !path.contains(pathFragmentForDirectories(['.dart_tool']))) .toList(); diff --git a/script/tool/test/format_command_test.dart b/script/tool/test/format_command_test.dart index cc6019862bae..d05aa14c2de8 100644 --- a/script/tool/test/format_command_test.dart +++ b/script/tool/test/format_command_test.dart @@ -737,6 +737,52 @@ void main() { ])); }); + test('skips GeneratedPluginRegistrant.swift', () async { + const String sourceFile = 'macos/Classes/Foo.swift'; + final RepositoryPackage plugin = createFakePlugin( + 'a_plugin', + packagesDir, + extraFiles: [ + sourceFile, + 'example/macos/Flutter/GeneratedPluginRegistrant.swift', + ], + ); + + await runCapturingPrint(runner, [ + 'format', + '--swift', + '--swift-format-path=/path/to/swift-format' + ]); + + expect( + processRunner.recordedCalls, + orderedEquals([ + const ProcessCall( + '/path/to/swift-format', + ['--version'], + null, + ), + ProcessCall( + '/path/to/swift-format', + [ + '-i', + ...getPackagesDirRelativePaths(plugin, [sourceFile]) + ], + packagesDir.path, + ), + ProcessCall( + '/path/to/swift-format', + [ + 'lint', + '--parallel', + '--strict', + ...getPackagesDirRelativePaths(plugin, [sourceFile]), + ], + packagesDir.path, + ), + ])); + }); + test('fails if files are changed with --fail-on-change', () async { const List files = [ 'linux/foo_plugin.cc', From 6e835062a165c6013b99085ba9aa545dc7ae01b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Sharma?= <737941+loic-sharma@users.noreply.github.com> Date: Mon, 26 Feb 2024 11:02:41 -0800 Subject: [PATCH 028/126] [pigeon] Fix tool hangs on verbose sub-processes (#6198) The Pigeon tool hangs on Windows if you don't have the Java formatter on your path. Repro examples: ``` dart ./tool/generate.dart ``` ``` dart ./tool/test.dart -f windows_integration_tests ``` The root cause is that the tool runs sub-processes without consuming their stdout/stderr output. The sub-process blocks if these pipes get full. See: https://api.dart.dev/stable/3.3.0/dart-io/Process-class.html This change is untested. See: https://github.com/flutter/packages/pull/6198#issuecomment-1962186100 Needed for https://github.com/flutter/packages/pull/6196 Part of https://github.com/flutter/flutter/issues/144042 --- .../pigeon/tool/shared/process_utils.dart | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/pigeon/tool/shared/process_utils.dart b/packages/pigeon/tool/shared/process_utils.dart index 28c816f18b1b..e38a4af777f9 100644 --- a/packages/pigeon/tool/shared/process_utils.dart +++ b/packages/pigeon/tool/shared/process_utils.dart @@ -16,13 +16,29 @@ Future runProcess(String command, List arguments, mode: streamOutput ? ProcessStartMode.inheritStdio : ProcessStartMode.normal, ); + + if (streamOutput) { + return process.exitCode; + } + + final List stdoutBuffer = []; + final List stderrBuffer = []; + final Future stdoutFuture = process.stdout.forEach(stdoutBuffer.addAll); + final Future stderrFuture = process.stderr.forEach(stderrBuffer.addAll); final int exitCode = await process.exitCode; + await Future.wait(>[ + stdoutFuture, + stderrFuture, + ]); + if (exitCode != 0 && logFailure) { // ignore: avoid_print print('$command $arguments failed:'); + stdout.add(stdoutBuffer); + stderr.add(stderrBuffer); await Future.wait(>[ - process.stdout.pipe(stdout), - process.stderr.pipe(stderr), + stdout.flush(), + stderr.flush(), ]); } return exitCode; From 5e03e0fe4ab14c372d1603ad6f6992251af2f9df Mon Sep 17 00:00:00 2001 From: Christian Padilla Date: Mon, 26 Feb 2024 15:06:32 -0500 Subject: [PATCH 029/126] [url_launcher] Add explicit imports of UIKit (#6208) When trying to compile url_launcher_ios as an independent library (for instance, when using Bazel/Blaze) compilation fails due to a missing UIKit import. Presumably this is not a problem when using the Flutter build tools because UIKit is included implicitly. --- packages/url_launcher/url_launcher_ios/CHANGELOG.md | 3 ++- .../url_launcher/url_launcher_ios/ios/Classes/Launcher.swift | 2 ++ .../url_launcher_ios/ios/Classes/URLLauncherPlugin.swift | 1 + packages/url_launcher/url_launcher_ios/pubspec.yaml | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/url_launcher/url_launcher_ios/CHANGELOG.md b/packages/url_launcher/url_launcher_ios/CHANGELOG.md index 7ff9aa669147..b5bb9c81481b 100644 --- a/packages/url_launcher/url_launcher_ios/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_ios/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 6.2.5 +* Adds explicit imports for UIKit. * Updates minimum iOS version to 12.0 and minimum Flutter version to 3.16.6. ## 6.2.4 diff --git a/packages/url_launcher/url_launcher_ios/ios/Classes/Launcher.swift b/packages/url_launcher/url_launcher_ios/ios/Classes/Launcher.swift index f97db9db9c5e..454dd53fbb9c 100644 --- a/packages/url_launcher/url_launcher_ios/ios/Classes/Launcher.swift +++ b/packages/url_launcher/url_launcher_ios/ios/Classes/Launcher.swift @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import UIKit + /// Protocol for UIApplication methods relating to launching URLs. /// /// This protocol exists to allow injecting an alternate implementation for testing. diff --git a/packages/url_launcher/url_launcher_ios/ios/Classes/URLLauncherPlugin.swift b/packages/url_launcher/url_launcher_ios/ios/Classes/URLLauncherPlugin.swift index 18800319218e..44718dee7323 100644 --- a/packages/url_launcher/url_launcher_ios/ios/Classes/URLLauncherPlugin.swift +++ b/packages/url_launcher/url_launcher_ios/ios/Classes/URLLauncherPlugin.swift @@ -3,6 +3,7 @@ // found in the LICENSE file. import Flutter +import UIKit public final class URLLauncherPlugin: NSObject, FlutterPlugin, UrlLauncherApi { diff --git a/packages/url_launcher/url_launcher_ios/pubspec.yaml b/packages/url_launcher/url_launcher_ios/pubspec.yaml index 6be514eb8f0e..fd8c54619c55 100644 --- a/packages/url_launcher/url_launcher_ios/pubspec.yaml +++ b/packages/url_launcher/url_launcher_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_ios description: iOS implementation of the url_launcher plugin. repository: https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.2.4 +version: 6.2.5 environment: sdk: ^3.2.3 From 91d11d696196880fe3cd4817c08362e59d7b5c56 Mon Sep 17 00:00:00 2001 From: Peixin Li Date: Mon, 26 Feb 2024 13:23:06 -0800 Subject: [PATCH 030/126] Add `InkResponse`, `Material` and fix `Opacity` (#6199) Related issue: #141668 --- packages/rfw/CHANGELOG.md | 6 + .../rfw/lib/src/flutter/core_widgets.dart | 1 + .../rfw/lib/src/flutter/material_widgets.dart | 65 +++++++- packages/rfw/pubspec.yaml | 2 +- packages/rfw/test/core_widgets_test.dart | 11 +- .../material_test.ink_response_hover.png | Bin 0 -> 5019 bytes .../material_test.ink_response_tap.png | Bin 0 -> 5287 bytes .../material_test.material_properties.png | Bin 0 -> 21395 bytes packages/rfw/test/material_widgets_test.dart | 148 ++++++++++++++++++ .../rfw/test_coverage/bin/test_coverage.dart | 4 +- 10 files changed, 231 insertions(+), 6 deletions(-) create mode 100644 packages/rfw/test/goldens/material_test.ink_response_hover.png create mode 100644 packages/rfw/test/goldens/material_test.ink_response_tap.png create mode 100644 packages/rfw/test/goldens/material_test.material_properties.png diff --git a/packages/rfw/CHANGELOG.md b/packages/rfw/CHANGELOG.md index 8db008e12bee..b3082c795e66 100644 --- a/packages/rfw/CHANGELOG.md +++ b/packages/rfw/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.0.24 +* Adds `InkResponse` material widget. +* Adds `Material` material widget. +* Adds the `child` to `Opacity` core widget. +* Implements more `InkWell` parameters. + ## 1.0.23 * Replaces usage of deprecated Flutter APIs. diff --git a/packages/rfw/lib/src/flutter/core_widgets.dart b/packages/rfw/lib/src/flutter/core_widgets.dart index 379edfe5aa6c..e8d1d8f74b3b 100644 --- a/packages/rfw/lib/src/flutter/core_widgets.dart +++ b/packages/rfw/lib/src/flutter/core_widgets.dart @@ -499,6 +499,7 @@ Map get _coreWidgetsDefinitions => (['opacity']) ?? 0.0, onEnd: source.voidHandler(['onEnd']), alwaysIncludeSemantics: source.v(['alwaysIncludeSemantics']) ?? true, + child: source.optionalChild(['child']), ); }, diff --git a/packages/rfw/lib/src/flutter/material_widgets.dart b/packages/rfw/lib/src/flutter/material_widgets.dart index cd1e515f2264..1ddd71ad7e65 100644 --- a/packages/rfw/lib/src/flutter/material_widgets.dart +++ b/packages/rfw/lib/src/flutter/material_widgets.dart @@ -30,9 +30,11 @@ import 'runtime.dart'; /// * [DropdownButton] /// * [ElevatedButton] /// * [FloatingActionButton] +/// * [InkResponse] /// * [InkWell] /// * [LinearProgressIndicator] /// * [ListTile] +/// * [Material] /// * [OutlinedButton] /// * [Scaffold] /// * [TextButton] @@ -337,14 +339,58 @@ Map get _materialWidgetsDefinitions => (TapDownDetails details) => trigger()), + onTapUp: source.handler(['onTapUp'], (VoidCallback trigger) => (TapUpDetails details) => trigger()), + onTapCancel: source.voidHandler(['onTapCancel']), + onDoubleTap: source.voidHandler(['onDoubleTap']), + onLongPress: source.voidHandler(['onLongPress']), + onSecondaryTap: source.voidHandler(['onSecondaryTap']), + onSecondaryTapUp: source.handler(['onSecondaryTapUp'], (VoidCallback trigger) => (TapUpDetails details) => trigger()), + onSecondaryTapDown: source.handler(['onSecondaryTapDown'], (VoidCallback trigger) => (TapDownDetails details) => trigger()), + onSecondaryTapCancel: source.voidHandler(['onSecondaryTapCancel']), + onHighlightChanged: source.handler(['onHighlightChanged'], (VoidCallback trigger) => (bool highlighted) => trigger()), + onHover: source.handler(['onHover'], (VoidCallback trigger) => (bool hovered) => trigger()), + containedInkWell: source.v(['containedInkWell']) ?? false, + highlightShape: ArgumentDecoders.enumValue(BoxShape.values, source, ['highlightShape']) ?? BoxShape.circle, + radius: source.v(['radius']), + borderRadius: ArgumentDecoders.borderRadius(source, ['borderRadius'])?.resolve(Directionality.of(context)), + customBorder: ArgumentDecoders.shapeBorder(source, ['customBorder']), + focusColor: ArgumentDecoders.color(source, ['focusColor']), + hoverColor: ArgumentDecoders.color(source, ['hoverColor']), + highlightColor: ArgumentDecoders.color(source, ['highlightColor']), + splashColor: ArgumentDecoders.color(source, ['splashColor']), + enableFeedback: source.v(['enableFeedback']) ?? true, + excludeFromSemantics: source.v(['excludeFromSemantics']) ?? false, + canRequestFocus: source.v(['canRequestFocus']) ?? true, + onFocusChange: source.handler(['onFocusChange'], (VoidCallback trigger) => (bool focus) => trigger()), + autofocus: source.v(['autofocus']) ?? false, + hoverDuration: ArgumentDecoders.duration(source, ['hoverDuration'], context), + child: source.optionalChild(['child']), + ); + }, + 'InkWell': (BuildContext context, DataSource source) { - // not implemented: onHighlightChanged, onHover; mouseCursor; focusColor, hoverColor, highlightColor, overlayColor, splashColor; splashFactory; focusNode, onFocusChange + // not implemented: mouseCursor; overlayColor, splashFactory; focusNode, onFocusChange return InkWell( onTap: source.voidHandler(['onTap']), onDoubleTap: source.voidHandler(['onDoubleTap']), onLongPress: source.voidHandler(['onLongPress']), onTapDown: source.handler(['onTapDown'], (VoidCallback trigger) => (TapDownDetails details) => trigger()), onTapCancel: source.voidHandler(['onTapCancel']), + onSecondaryTap: source.voidHandler(['onSecondaryTap']), + onSecondaryTapUp: source.handler(['onSecondaryTapUp'], (VoidCallback trigger) => (TapUpDetails details) => trigger()), + onSecondaryTapDown: source.handler(['onSecondaryTapDown'], (VoidCallback trigger) => (TapDownDetails details) => trigger()), + onSecondaryTapCancel: source.voidHandler(['onSecondaryTapCancel']), + onHighlightChanged: source.handler(['onHighlightChanged'], (VoidCallback trigger) => (bool highlighted) => trigger()), + onHover: source.handler(['onHover'], (VoidCallback trigger) => (bool hovered) => trigger()), + focusColor: ArgumentDecoders.color(source, ['focusColor']), + hoverColor: ArgumentDecoders.color(source, ['hoverColor']), + highlightColor: ArgumentDecoders.color(source, ['highlightColor']), + splashColor: ArgumentDecoders.color(source, ['splashColor']), radius: source.v(['radius']), borderRadius: ArgumentDecoders.borderRadius(source, ['borderRadius'])?.resolve(Directionality.of(context)), customBorder: ArgumentDecoders.shapeBorder(source, ['customBorder']), @@ -395,6 +441,23 @@ Map get _materialWidgetsDefinitions => (MaterialType.values,source, ['type']) ?? MaterialType.canvas, + elevation: source.v(['elevation']) ?? 0.0, + color: ArgumentDecoders.color(source, ['color']), + shadowColor: ArgumentDecoders.color(source, ['shadowColor']), + surfaceTintColor: ArgumentDecoders.color(source, ['surfaceTintColor']), + textStyle: ArgumentDecoders.textStyle(source, ['textStyle']), + borderRadius: ArgumentDecoders.borderRadius(source, ['borderRadius']), + shape: ArgumentDecoders.shapeBorder(source, ['shape']), + borderOnForeground: source.v(['borderOnForeground']) ?? true, + clipBehavior: ArgumentDecoders.enumValue(Clip.values, source, ['clipBehavior']) ?? Clip.none, + animationDuration: ArgumentDecoders.duration(source, ['animationDuration'], context), + child: source.child(['child']), + ); + }, + 'OutlinedButton': (BuildContext context, DataSource source) { // not implemented: buttonStyle, focusNode return OutlinedButton( diff --git a/packages/rfw/pubspec.yaml b/packages/rfw/pubspec.yaml index 9dd0ddc38c91..6056af37679b 100644 --- a/packages/rfw/pubspec.yaml +++ b/packages/rfw/pubspec.yaml @@ -2,7 +2,7 @@ name: rfw description: "Remote Flutter widgets: a library for rendering declarative widget description files at runtime." repository: https://github.com/flutter/packages/tree/main/packages/rfw issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+rfw%22 -version: 1.0.23 +version: 1.0.24 environment: sdk: ^3.2.0 diff --git a/packages/rfw/test/core_widgets_test.dart b/packages/rfw/test/core_widgets_test.dart index 40521a667455..aaeeacefe50c 100644 --- a/packages/rfw/test/core_widgets_test.dart +++ b/packages/rfw/test/core_widgets_test.dart @@ -106,7 +106,10 @@ void main() { runtime.update(const LibraryName(['test']), parseLibraryFile(''' import core; - widget root = Opacity(onEnd: event 'end' {}); + widget root = Opacity( + onEnd: event 'end' {}, + child: Placeholder(), + ); ''')); await tester.pump(); expect(tester.widget(find.byType(AnimatedOpacity)).onEnd, isNot(isNull)); @@ -226,7 +229,10 @@ void main() { child: FractionallySizedBox( widthFactor: 0.5, heightFactor: 0.8, - child: Text(text: "test"), + child: Text( + text: "test", + textScaleFactor: 3.0, + ), ), ); ''')); @@ -235,6 +241,7 @@ void main() { final Size childSize = tester.getSize(find.text('test')); expect(childSize.width, fractionallySizedBoxSize.width * 0.5); expect(childSize.height, fractionallySizedBoxSize.height * 0.8); + expect(tester.widget(find.text('test')).textScaler, const TextScaler.linear(3)); expect(tester.widget(find.byType(FractionallySizedBox)).alignment, Alignment.center); }); diff --git a/packages/rfw/test/goldens/material_test.ink_response_hover.png b/packages/rfw/test/goldens/material_test.ink_response_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..3156e657805bafa5920dadb2870ae8a331131b99 GIT binary patch literal 5019 zcmeHKYgAKL7CsjOL<)lAR8RzIoeoP|gef8g4I+;MK8Sz{k^osqW#nmz7zhM{+5#e= zi(1PgQHOy*Faauw5?-+aT^0oqxKSZM2g@TD!l+y#2?=vz*Zk|xS!@1q|J<|B-RFE~ ze|w+r+*5Eg$lu&_izxuWJTSmF1OP*O01U#3#;~XS{Drgdh0sI%y@9ZA>nt<~bnn1p zMEGP8WAg#n=ojdF;8=R)y!=3ZCBwGo_4wYsHpBqG7z;Q7hCl?sa{HR=8Ut$#tTC|0!2g%5yRpsZq;Fgt7>Qw<+U3!Pj=Kt=GqwzkoGc8GN9Y;|i0PZEkh zo+(owSJg+z#-}W4B}4p|q@XZ9xnk)}m}N zckkh7IijadLxzJU@hP1QpJGJnOtfS>D!P^3%$P-A&Q{Kyj?X(yB?^UZR-1N?**qre zbV@x8_E_nsYoj{J23o3c?SLvFr1Y;gJ+8vM8jImDUbKLsrj#dwTjtQsZ>35XJYJ>= zVx=s$Guxw5NIX7YEL&p6ssmVO@I!bp(;ssO1U>LX7Nf({T^HLUUgZq|@6My!x}#d!n~N`l z_ePKr8fTp)do%rZ>rqv|JUUGBsem_nHBu{74=73$LY)x8h5JLeZ|@8pWW}yrQy)=P zprL4YuFlIbXj4MR6h%Krx-sk@>Hc`o57v!h=KF=;-cc=&?;U; ze>j6&#O0r=Iwgn$jX|RqjGPTOwRWrDy_*z;K;XoDn9%M=F}5=n&5#B2KiLCAmAA@! ze%nHWOd&!;_Y6HrqjPRnHc&Jah&y3?-{=F{gy(+Ma#=|UpI@IQvMbN5#mzwSRe)GG zy2#5sMmR~N?Nf;iao@SPh<-3(49Q0{>QNbmoUfT0b)%4f@5}XY^*BB)p$p==IP-mI zzvQNfi%7R*5N&C8hR;`7CxZPie<%E&;W{4pKD*X`71=Bsi^_>!&P*HDCp_(}5rIa_ z9?_of{TUKikH>3;nn*|>)d3RdTv*kOZro$up9{=NU|`n$IV*W|#Cadgi`TBS8;WJ+ zGU~FYXu5S83t`m3#R)IwJw^EO{alj3q3x5^Io-)HEFRmuI$oHt(jk|vI=ac8;9^}; z!af>WeJEz#`&dp2h9QdWG=UaQAWGTkO)SU%whzZYWByb#SqaPvA-?6pQgp97V!a8j z%|0sekJ|75wb)2meajzBsn>#kqvliHOIth#vINHTAW*wA%nKNQHOHz-_`(4^{1_Tt zty)=X5ct-Q4b1YbM&7rpDN#{MN(q<{vzS8XbJ-;vk zKEG}HjGZgV%wj~&?Gkebx^&q&oU+O@Bty2VKEJm*+ST*0DBLAXJs27Q;~KiA3$UHD z5OX&$$T?)z{nfMjU2~1n2#n{b4B`4B3lVn%u6eY8X|)wI{JaV+ttX4Q zO}}!ftdK157{DAF+EsX-C<)q@L^}0wp9iZMnkds4VEU?ciHR6g4~_B^uV!Mg9hjnH zmW3}W23_B9H%kv&LX~=jGuu6P>yARjh2m1k(so2teugf+9AlaStZO6?xWr~AHH6ml z^dgUcXCrPMk92Tw=r)mFfq&mFr(rLbvyY|w){ogQr(Xj1=c)XGgeeJQa%36~EqpbrQ0 z^x9+y_04g@O75g^nd~3>6hDaVNQw8KuiZw@rfSqI0S`1jfJ)S8v{h*#d-h8R>2uaU z#lvEhKGrA@4_nnl*H&Nu@(3N0q1UnXySrJ@=#fmzA9}{Q8n3`%jFQXV;%swQm2JK%>LxDWl&g zy*bvL+Clj&Q<$0TQ~Y&YP8>YTaOq<1%aH~7)lPqI*f^lPFk9w=CM){mD733 zoV-k@i*}Q&F%?6yM3fAuRLs&y1w;`mD-BRm2O%zUHm5UvJfF_&dvJ`1)_~9mVRS&gi@VfF)l26eu;X9Q!2z zpmxxp>y2<508W020svnGU;ym3v4`ST;Q^p6hrt18n-85QFi&8fz&wHfHGy(prXBFY z-D8iQqnK*>bt$ZXs3A^DZA^4*{ge->PO&1bzm=IrAQ18&!MxMo-SQxheEm5a*}V8qI~0RiNCBBX3ZG5y28d@@Qqf!ezAmOQ8JL(@RdD=hsde zItb#sGd;*jO$x4>!I#0;DRVu8?~G;QW73gS+&;eA{}qm{yvULoOMUr} zplwo(7698so#KkOLCn5VTFPK7VDV$F`@|CN>L$s!1vJ=aD1hlq@yT!8v6(NTbO?&b zgW;TE(=!PMEJr~if%1k)CGl2juF4g`i5oU>u!us(q!S@+wsHE{HY|>I-vG|!A0ph zv&`dQPsuPdac4_*fIMo1`{E^}JrD-Bx%$v8@8v=7X?+GWv8~8|ZV%4XCM81C2SA9N zhN!Bs=&+MRRHF+q>_b}#F~CrTlr>gqi<(ah>2*dQg@|Gc&aB1*zvRbEX;X#h35G*$ z(At2t!8CAKgj-B)ScbDuRIc6MP$t>d;gEK@4V2hcbbzT?;(T%FZy&;&&3R;d+)|J|uH zVp#cRo{ymnn(9NNI#17&INxxT!ms8)K?VNiX#-K;+a$rFcYms7lK=Y`%0PUWxw9K`iTT!>8LopRXHx%Rs zL~4XijN^-zX^i>gxS2QIkn*=KJP(8@3p;dGrr)D6Sn&2 z0!0>q-MnbiM$ujLpJJjf5td(an(qgiY^kA8N*n&2QXpckTju&HD(}+rt05F~-iTcd z;&59Zn#C%n`?9n}2ll;YtCYDxdB1Bv0g5%5kjyLV-O-ioW~et_xa(BA9bO1kVTtRO zeMeLdeI4??o@5-1mTN7w{jx6l0o*TxyLpCbi8B}ovX;)zEPcX6)VI*=Z)U%_9PMGz zw|^{me_SSMai1frT*1y`5X+iTPvahyL-%F<3|~xvf2explXC|TlG}5lpPpQSeXaMK zK=AwjToL5an#f-K}MKg=@Nv#HP$rzmm*iBE=3NPY>b{G&2f%tjRw@ z>Y0U=B$aDK>fV~j#Qc{Xe=gKf$g3kV(EMxKqIEvH>GjEPzpQSmfC=)f?jrc!MgF6C zk+iX5<358+eeEgF=1PdGEgrJWC1rc&x{*fFBWB_!@`1M=;x%RYcdVV;t(}4QUU0J} zIQ#n%3x%QIF+6!n1ybqAselV;R+CJ4(TY&XN1E%7y7~7~y7Gq7+x~v!zPC>DAlt49 z3IY%^*EgN*9^x>|WM(q?eJ|vsDOM*nJuQ{|6H$_4H9*yWwy@=`a}^UujIq?4%j9M> zvs!)T%2)-VjFcYfxVYOIxekJ`zZoluiNW#>LE;s31zIJz+Tq%#5+8^&v#a;(vt_i`omhri9CniO!hhRiBtG z;#5Qs0=TLMV{1lox6m>$k^`Q62AS;n>;tL}u8x(@UY3{BNO4Y4z}KG0VXM>_mAW)G zGH64TECB&hARtl7S{Dt|F757(9d4AyueG6~IwaQo%J3*`Ppk%KDiQ_5%0sOQa^Akj zsv7L(em`2Qh&)9a@LMxBz&jk=9h>{}73!NoB9GjC_A-K&x&#GGWiFi58prEC;!74v z<_4-sh8)DU@IU`A8@m9j)W}F^9?nU f<_Y}Y2$--Jlj8>qx_YfYAVKi=q1)@X#U1+>C72Or literal 0 HcmV?d00001 diff --git a/packages/rfw/test/goldens/material_test.material_properties.png b/packages/rfw/test/goldens/material_test.material_properties.png new file mode 100644 index 0000000000000000000000000000000000000000..c7de8515b4c60d350523019f5ab761ab1421091b GIT binary patch literal 21395 zcmZ_0cOca9|2Tfb-q|aZL$*lBY#4{gA+t~HQhM@AvoXpL*T%`FcJdd%t>d!$6y!hMNWgfzTs$uAv|hxBvtKGpC{eza&3? z>JI*bdZM(iKngo}|AGfH&nrk1D)0wGWgiNG2tbh6E}QtIE{=HDSew=!ANQ;MrDn36 zWAddwO?D;Q`&zd6QvoqCSU^$#V8GuxH5V?kGO@-=J?GY-LOf!nI(>yU z#-L;G@MEJi;e%>U`-fV`nN5}GL&b{ztv~BN2)U9`$5J{ZYK;6QtPEa8E(}S5U)3!T zoe7$@+vD`{sPwjR8}oBPCMmFva?`Dc{2BAxTd@!vJ9LalCBHh;I9|RczBRT|qwqjV zVwYQDdot7c$Hw6CE!B1GcY+I{fFNnFV&#g&_xOJLL>rUcN=tW!k<%?C(gkO#SMYaL ze*Al{?cl({zNdI!aCl;(Xz5p|)z(|@5d8G*=hHUW-t^}myVq8nRc=3yH;fI5wfg?- zyS?nFSUCcjcM%?ns5P^tBL=XOM{PqJ;|_OAv}T}FOYY}g7ORi`2Pp(Z(> zhMTNCGu8Vxd8@@c`|a%ldSSQY?E$CV_qv`Vl-9MxWS!>Jd&fAui30@_GEKCYD;(DH zm{9!g(892gV%ue7dEpDatwGggHch$=IFAYy&q$&Tk70X#(VmIiagP$=dd+Da*>@M# z8RN3WWU)H)bi1GPYQMo-mI-V0$8z@~pDUL?^;5XTdSQ8t>9$SgMBvNh`!D+K8uvNK z@z+;^whyx`j4RVA3!#Ou!bepJ*#@?d*q%CAHM|-h64lOgw9aqE;yU!WOWq+4lXI># zsMZ=9#!UqxSTt4*Se1ysE0J=0yq?Z{5#7&jzL+aqpWmqxbV0k;pr!lC<6 zO1R&z_di$vpjQ}pwjU&9;Lfmd$?{*UcI=}1zVC)JO?T}v%%Hc1T9a98HK#=GeZjv> zhF(K0P)ATOKp4*2be<=Y|0&TXY$`Xh`8CG-eNpe{BSyb8-_I!CX4@_8rkA<%STJDT zdQD!C0~#niwW)P*1F72tIh!j_mO#OP;Qu=iq?LN<=vx)w$k)^BOlRfWLVp)eebvO- zr)l%78qdP}@gcE#-+W3>shi-Fzk@fc)>yu6lSjd}C@9Jr{ASKoJT6ypxBloIupDfX z@UfdcDE3y-Gb1_5J(gK`AiM)%BRBbC5idr1)fbi$7ZTNob)eiRSsrsY8n1BjOH%lJ zicL3C{t@4z`&~Y*tt#ebRhgd|il-GBchqIyG1O=v(Y)}`Y&4YwEha+6=fS1d8D9Mf z?p<4S3FUow=7E2UM!+wX@uEL=e`sXcFJ!AB=LNx)8!%^B z3!XCCVQ*Vbw!)w1YRIKF<;HI}y|1wE?Z>@?&qC@EQy-hz&LLIhusXVeF;G8fhRsgx z%8L5PE%&y4j-V*Vo38G$C|ZkYC&jr*l?eTc(t0~YOosHDI-%EoL%B-_FVb7>|HO5z-2ViPHnvX{ql9yGBY7p1fxaUeouH(i6WCL9`R~9T``={o__LnsA z2&Y)WFERrGnS#!dlw}F`#wc7?2c5*0*6!(x(nXJJ*!=_pBd-si32fgyT%d;8oGG^d z?WTaY%rGuSY}(k~9SzSf5D8kK<~$d+=Gf`Zo3| z693Y`Rs`0+=^WOpq)qm?QYywP@z}~vy2zmJ)AB*7&vFZ_L~Fy*3(?e@K0{pJkwOv0Mb0Rs$HnfV)*j zwa|7nEoQA8XXn|vV*BLDlT8eL3d>Fp*RFH5kW_d7V$U5Or@5#l5{PTA%f2db+p9 zIoE8he@{+)6TEQ7ohn`EED{g-#>l9GCp7xcqlRmrj1$MmgMWzE-v#^Qm$_x7i61@J zx3P8psv6@m&q5K%Gzpuj)f*XAOe93d*VdFT-@bi&+J?WPMxC~ArA7WM9^sWlxc5i7 zZ{uj8s}vrR#xZAkBOs@xtu6nOS^rbag^jM#eeP?n0>wtZ-9iAsuVCV*6caVPi>j%* zZ?=_B^QzPzL`4Bgdlh|IU*8UM%HI@vE+OD9jGJx?@Oz0sMzWfx5=6=FZC6L4U1|a9|FT-l*b{@&8Ro4B+KF zpMMY)0d85iEqIT-?jVbhueT)>s$w`US8?MOKAW9&AQ995YSf=3$vk89Cql^~EnT!# znShZe))vjKoUc8uiYAOY)JN*#vqz~OT(ef}DPvziENGmx&&ngk@T>rdiAYUMx37;#pbDpAw|Mb9iknFGPos_4#}_h>1FB1Vrv3xiHg?W06{BbqV3OHpR^+ zc?S%^B%Ltq7c(Re@b)pf=9h!W+osri1^tM=?qygOS8@WvUS5Wvc;p?l;X7>p~6P|VQ(bz!OuBKQIg(i2OF zzlF^X)!-nSjt>!j@@3R}!7zIYL_);n+n5S5k2}mT0MZXFOVu=U$ikKyMswqaq9lS_~nXXGe>3v$UEkpYjNUnJ(OFP z*=lSM1cmQOV+Rcg61bOekvKoBwDC%*LLe1p%CERF5&%3O-O~4C>}vgPT6$mbBd7?( z`_zEIbq_o*G!{jLVc$`b`LOI6T|iA~Ll&oCh)O_|*0zf@YEsT-o!xAQMhQ**ATvdt zZa>8YEQlagDrKsH;lxx#mR+|WJwLujCr+Vr%&K$Vqdt0qaC}NJ`e);QX*Se@>>4_O zz)hI`*;srO-Zo9!F74^4gGA@~OSc97E*TD2RaIi!5#TZuey=;P7=_hoXByzCNK9{N zaoC@+(&kn}75dCVe8-TBDu;QPHBII}4B=!#2@oq{yj^iLG4?{I0Em zKswb_@Gb_>V^&3W{X9N)5vIxN^or{d@_6IoY%S^ts8kT&R(wW{5^n&X%3QEG-XxEn z@A*1ICr4ht!YRe)5r3)k@Pp|uZlj+c$mEM|u&Q-jqG>ji#ps~eR4OhXr4O&eHcx-g zv3E&(Etb0kAi>dKVh_siinFT8`H^8Je;gz$T(EG$XY>8wshT@{vK8uUSO^)`A76hV z7B`pxpg5w5fOSx+nQT{T_v4;P1?TdJi0a{c40#NL>I0Z=E_kfxhhww8CA}hDpa?s= zsU2`<+1{6i-)xcIYSdvTCi|w0*Azze8=ILC5mMNL@vLIdi>B(B2u;kX%gSF21LbIFy_<_HPLy#)^&N{5`Ucnpv*!_ z#oeRy0PT_XA_}M@%g*J@yzB&!U-&X}G8~ZaY*%$2k~Ch}=V2+r0f}b@e;?G=&KCF{ zKO;1e4bR`0#{o!uVQnxom=!yTj=WD8tRK05`h-9Vk)ogJk0Ac7&2gpN5}xY4W{Q+- zft+Qd{y=LDXJvF!DUn^=cC5%(%+SFPeoOHFlCk|PJ|lO#=PqH{UM1nZSngX+jqD`C zV}cE?E2vXNhGDDX65|VXdwj^Fu!p+!NePe`{eOu)v(#I~8cd zB=>xvo|n4h#Ah?0W=j?;xo@#N)VxXHSO;*dHw}hoM^z@9_{uZt$98CjQK}&V>EhZv zcTh5(D!8C3&sq^mP+&F{DkX!5k}+{!8Q&%-glBvQF78=$#z^;s2|nAP*!ITJEBDFo zyN=haDKLqI-(&~G7u)XQa&x?MT7o zRY*8DZfT2|kYkK^<}dvWn3S8q-vkxxa$1-89n}sL5Yp{yUI83Y*6_>@R&(Ygom!&D z`r} z3GM1v=T~A%*<@k~s26#7u|`N{2eUat1wE(P&W|Fs#1*ux+kFzRUG9zkJSh2NmXW{R zbB}PHEWywu-h>)}8%5A1AZ*hb90S~8y!&nTLP-4J@W=We>Q-ScRKvQ2IG5h@*3Sz& zn9S)bsLczAuZq%sb%#7hAP+qo7{^~0_4?i6E*9kKW;R8{H$yVFILzS{T;`&q>0dnR z=bVzP@!7cD;i7Slmdos6={^(#xQHsN`^bV0u$7&Y(wb3%!zIvJhS!Td)!XM60QB@c zaUI0x^Fc8j-?Xpr8JNevdbE+YLb6n{phC~yJD>V&k==24qVxOBL zk(?Sm0c-O7V_)&-cg*BV!k;xVtUrPvEh15pX=dtf=$!-d% z+q?JP-*?t)7&RkgPr`o+A$9K^AV7lWd*+X*tqNDC@CRMASSQqIpDF?>jAB*0ZO!mF z_5Mx_!n7rOwRos>SNhxJP4Yq;pP0fPc3!BFA60~~`UK|L{r!~iF9r&PTUNr7did)6lDw$8-z9DoUuMtNpidlRh+KFX8^?zo4@ImzntOdqdj8h(+$-p_?7lmD~d%5e<~tVxDBz3Pfe7DxPN&yGF?Q z_tSs^_xK*|T~=I6Jt&cIWgLGRlo5n#1H{@StHQ2hCm(tI_`3@=1W{4uSRiZEzGkfm z1FSJ6I8T=DQe79oTgPG_e{7zh#n=@po=Msz`Ds}F`@fT`hsqwE~ zaGqEWrXZ&)rt!O?x~bAH4L}xdm@=dvb~SUUe)j@qrXQ(?M~LRy_)FPLaVqc+#6}D; z-$ZPNL$CF6nDbQ7nx7pESiMY~Q12U(yoS&2>+huxJa7-FGab$+T(nn7E*8t}q!Y+1 zBYYyLV?TCn4V49f)c&_1iGV0ktslY2DZ4mPO+yvvgUikN)cN?muaL}nnh1z1MOkQ> zdM(WZHLly17fGy%N5|)^+Yk!Y9(A#lLzUysD*#3oDLHEPzFct*IHfr;wJBtq(FP)d z?2h#fv0caUh1FNz31xp~9Zsy2>0wOfGm7f!@$qomR$(V`kdKJHdw>sQXD7fp2r<1D$YvA%8c0(l7pS3*4NY5Z=9 zG@<_Zf)f&K8#FvVT}Ld*>?0=>+Vh#bpO?YDzXV%|Tk(+>8w_Y)F7A(a1u5Y#WiKyM zon^V7Rdw}}OnL+*%S|CRS?qgmSX*4i=EsMh{ab~L53g^s4}tNmJR*fM% zC2kc36!;uGw3UMsrk^?bxO!+bAuthv^vGACk5dS7pp|QOAu!n2!~?nqds>)~tG_*m z`N0?KCQs6^+`1ey9OL2J(TSQNn7XccEUN>=s1imAYtbC4svZyZ9C0-R=}nc(&_PLw zPwgKLD&Evjh`+UT0%C@Ba|go>TPqkADYbJXqLe-hXKYo!8lHCDcFAN~XJwk3DPFLc z)19&S=I6sT|1~>%?;1eim8z1ot%+Yvo zM$?d2f1H-I3zFFbo5Jz4m+|lGRVde;NkAayv#QIUcV<|wyx>1IaM4tWS&>{%lsp0c z4%VhQWT;M|ww<31OnJ|#jsr4YubV~-w1ju|H8I6!y=35_zo_V7t`yDMLo1`OX2U!M zePHqSoc~Bvcu)?k6=sUK!pj7+lF&fg&v_S*V>a0SI|*dkcSz<8%?GMf zn}MUvmD#HefOxx7L*+SFX#&fMC01zee(d`vFfMo1;=3dQk3S2%ZV8GPr;GA=q(=C+ zYCp`q;KAPdMn#>Eq6FsU$=bSF0I!Q*06FJ2q^8PbFd2blme>>&l>~5>1=-{)T z2X{Psp*-X0(R3IS!GcQ2-g1N(-6d~^mox8|%X_juP+{Y<5qts^J(rLi{pIC#+nyih z&I0PMiG5#9b0d|=^&(j~$q!;PZT*I&Ns?gNe%HAn@u>Q}JUpPDD0dr81rO!^n@Gs( zN}y#3Dp5}_;|2^?KHj&eAjV|&^*z7z42EAzcLWW(Xs^lsc=LR(LpBS`BDe@6ABxQ8V1;x1{t6u~M!a+uTzD+7cLz zy4nn27eOFfKZvVgX3+(%pe!46A)@$lU^3Z2R}5WIz%bu+OLdUdl`8OQdGBVGDt_tuZr;-jm?doFQcBTHKi-Sz{De3jGA{&4feh*=i1x9lM0 z^<6Ll!nh`|w4?>b9Z$HC5U5ea^;Jo0Drk~;jbR6HO)Cp>f#*fn^T z3kPrVao<(2UWlApSGzp?hOA7-BFZwvxR{lrf@h#+=4jZicv+D62mRL>sBW+9V{Vli zKDzKirTX}Y?Hnm&?<`d)nw;SbyBZ{5Zw18B_?IacFRt)>y)IB32;_#F7ur=#9vqY5 zqgM9LWslQ*!G_p8c|ruX(yt4|&QiM{8|Djwe}>kibgbjWew<>e?f3ZFKuItQum@B? zu59fwgET_3U;&nK-8|9~D=1)zC?+G46`Nci0!DoY`5g!$QquM(Ea?+~cK%>GySXD{T{gcIi6+I}W$f~xt z{|6ZFY#Lw#%Qb(N>D(o3ze9S_8`mdCf^cbR}&aRbpnHr-@CKNxf3vvyq-qA82^daG+Iy~O(F)m4V^*{ zrrSXKe0XQ*3~vLMg!T};*I3{8lr1B?bo6?2LarXlOA+?#H|a1)fcsORe~8V_#Z$z_ z<8$bdr-V__HltFo;XBM5p3*<;y#dzgO%9D>tiJmM&f`t!qp*+i2*-F z!%zN7!~MK(&O@?nW40yC=0NgpB|k02+aeI3lqT+F2`q>9zTt)by@oq2I=DNpJaz#D z$l1;B)oDJ-;i$od^5DW3uzpx|-@gmx2Sb1F{+)9^mh1B+=i=A7g>Rjv(#iR5s9HqN zKRySP99{EUaJ>51qNhp$Cy7ePT2q4ka)JetZ{=35oUA8+R@gHh*2t&Uy3J64^b>GD z0!I=a#XUg3#Q7@m~iQPk@W5 zKG2glru_HPnjLH*TS9vpaK)Ic1J02D?@ryfN9Vr%w&#tnLcE=&(h>QltNPZZdgF0SI_LM z8Xs|-2o3|dSJO%`@N&8AZ#t4RR04$G+yfkm&TyQYucSFpO-S9P5d?e)hCF=lkKv82xpK&AF6zDfGz!i z{2Emw`)b_WIazrPT<~JD?wcj+>C_Kpwh$+`RZZ)-=EkKVgcNA0{5|bDCcF~&_0Xyn z^)dmPeT)1e6=@NML<;}xeO406mqreq98D>C(dxz8HnFH4UZuIKl5p28d zIpG~bJk*$r9`d>b$S>Em0`IAMUB%gN2OFM1EAxCD{?1eQeVTt-a&8lLf-AeGC($C3xZuP`JB@n32;cVVc?jSaDCT7vyVTGH ztZI*L*uIpX^y2#aCP3G#2MVywS(GjaFgkZbzJQRmKs#GPv)vBj#F!B=T1$+3x%(7WhWm{hoh{oPzchS4ZARcd> z6G-606>yb0Eu)24z~?D04ND7KDoxKbod;ykB~EaT1jXoT_wM{gYC2tuDDF1MuR%44 zgoUT~5mSy0xVrCgN33SO{w;<9aSwU409?m8{1`nB;&)MV(&h83@}MlIS&&ZstAS3S zK&%)+{PI7{;0kkmv}kCX`||C353ss$S7S-|_j(9nOf9Ak#9rB2``tJhxBDL)sE8AQ z*O$Q_akU>FBZa`XywHmUw&SS+dr18Xs%J0uKWCgPz94gEaAt$lIWq{`zo^V)F6rY(d<2Mm|KB{>bR z;8`>5P`N1S`L*P8rar%`XY<#*J-p66kV2rFaF2?RiFneu8R zP?YJIzu5Z%LHV!#HDfGb>~0n&*|1#LyU6zaD;|aLTt&FfW|Pge6R}VGP$B*2QcRc} z*2Kb=6Ntn;2#^=jub)rjCV;SyRRp2p$+uvI?<>{4IU~|zNonx>wZ@qlZDzU@9_NwB zhy~!UM}cP;1UgeV_M7CaFAWXbxet|T7J%zy;5wz1M}U60p$i(8cxZMWOb?c#HAE)> zB!6@7niaG8B|sikxQ^EnSMC0qEOGAAiPYx3&gE&;YiqWwf-vgmp9AYnIW{AoRUhs5 zgEaP_4G zHLSp`ZF{X1)uT)igAS|5gIs*eg*g#)Y)$OEAKbd2S{t6J7O(37$$eLl<(=~Y=BYAZ z49x$rg5R}K?XY!)ayEOgwi7;0q2LLEu{@(;ZCR{I3^Y4=vN5RenDmqBuy%6HC%Vlj zXf_&_5AUKcpeNyZs>wfln(9OHC)Uj$5R~fbn+B1aAx?K$NSF5j)&$L(og>Lr6Q@R= z$kAC3QV^1B?qTyCf;vwTGyh@*>p)_R*vyrU1UpDBUW0=qUfFfx%i^_ecKmv56E<3< z4=CtzE0rA;f!W+c1439fy}Z6AcNrZCp<$#k@&!nX@*NT(SHO#RO0;|Sg9^Hf#lAvN zlB*moR~JO2NOzKtlvo&rL$j~Y3G^}t_OjL|frB>6Ce`B7a%s+EP1-8vZjqD_)fwg( z`E}o1EC`w{26KjA1$OicKy=>1Bg2UkCvTjPc?SROz4yf_U43&=j*AOy( zi++-9_Hcj-tN(k&96M8B(YURTNxz)~f_ee2BhLv80iL@Ff&z989a_(6jgH zzmvFHN+&RVBGmH9g8#?f`1o5I+O%{OGulH5n06N*U`zL$HzG0%eBd%KBg&c0vV(Y` zqW#HT)#*?Yfn}N;arTNdC^5KB<%vP=kpLt|fTt4$ZJ)dpqbrMj*+$|OcC=?@^r1$!5zR)lpEVI$~*U^!`ujf8q9=&Bw5cRLu~Zl zzNYSS64FlN0BL)OXuoV%^@(WX#Fveg$n!y1@QF+-F5~O2E6B6+>x2P+C>}K5Pm)vV zlT9Uj>zO|12oW{J%&o+~1v613d3q%`>|_o!RWzCwl*RGM?M~+Lcfr#MZGZDZb9v!9 zPlll&Ps;rbE(J@52mG};uSwZEP+JgcOoMqH@L=UJ)(Sx}gWc9}67~-$Vp`8*UxLmQ z>0$Y)sS-uZngn|-kGh055dA}=;qtKUrXFB-*1j6@TsS$y0Gtt*p?BxM?+@F**@#Mb zqxs*rqyP^74|x9oif#-52Ldo&0`=r0c_)CzssDZZ{{qE?53Ym!pW}vzHvtwZ@4$Fs z_B~IUo70>cty5Gal`Y}Y5NiSFo#^j>9@2MeuAV&8P{iQDPC10Hcj3Ytk`^?B3%|EY zy}nD%&P0o~(6I>zXDHKlNv*(SDRQJVqp&ZXZf`6XSH5TimJfZm$v@x6M*ljA9S8h} zM5X`!S^WP6-~Xm?%D;zKHjkkI-_yV}lBf-Okl?(F+W$AS{|D&YC-DGkiu2#j<1WC$ z_?9|z5;Z(%KrV zJvQ_k+eo0fsvNb>Cx*CU5;FO8h?z^Hb<=)hd5=-(#6!@tQO5Z2>w}(EE8B5P{T|Cd zYF%ye2PWhNp!`1-2Qc9O`vK@X+Ok!4!`0$9`J>MSTU)-5^&vtw&$zPUKsbT#Vb|)! z_9sM?){xT)2_g!0XaU>aMU-P5GhL#d0#)1u(crvOqc*Tf)1KTU#%5I>Wqkrq|FMG5 z?Ek+9Wm{8_vSC&^a3}hI4D??DuCx13#(>Fpc)|b-xE`!XYeA4YLDy^EG32j{$wJ!re_X;Z*H=j;qVSqEJQ1|UWu>{@=>LxwR|6GB*3KKAUlO(ej zJE>+;)A=?V_%)`$^A8>yo&+a@pkfn$50V?a6aMAj+fDxGfTR;{73T>Q!-&@Hhs#pt zlU^X1A|=Pjg8P3YeC~S8%}13=U2PW6)%#TqXhY7`33uxoUYQRrJ$_Zk2W01F>1t0V zaW!)yiyK^cieLQEnJNIpN+qOrjpS-Ck-`PsL#XeGYfUQ#$WXMB&&4}Jav4ba|4V~y zI}Ol=%KJ-R7$J;M(p7fCpKem-+=EUCgr?iPdGDnp`wo=yJfg5Bn$4bHA2+}_GJ(!* zYI$V*Woy*-0@2I#=-jOwJ0RYh9~80r*DZqQL2Rj~;&h^czBh**lT$y@^@3{?hO)d* z)JIi(S+0o`NreB*M-)pD-P;d;3);R)TwekB=gDIjKw15$w$BoD`l}F40DB>>Xowto zIXa0xMIUJs4baRV*cb)a<0rB#0kU$UzTTJv5JcnO_nST3GyP9cbz;=nKu!X(oXrz( z_F5soWUZgymdAAwoV&`{(nCNJVMRKyH!_XF@Wbt%OUV)a#tB(_fHKJuV8-V^^4Kf3 zEU5Zm=RL5z@=YN$-c@HjxB#${guiBzAb7Z52nP}{FA3Mt10hrQ>8|RO(Uc5ol6M*= zDy3sb?*J$tamduq6jCySw6%8VVmZfY4MqOqDU-_}YHdwo{F+tMO=1$e=rYepWwGJg zFmO_TWLLG+HJ~w;*^1cVmmthchh|q5hL3uYkz8o(WL;OOXxojICroyxKa@-z*R5lHznqH6YfXX=bGr;Jw0( zSP3F9Nwr7!NroV=Uj$Gw2zcXG7z@@4z1(@4BFpj57_cChx{51h?mhzS>9ZexRfX7$ z5)|ltOi8@gK4RxNLJwrat1Qj+91Xu5E)djZ{B0q*pMYL3fM~H1KtxJ39?5f^0r5W% zDJvE$Cwm{U`f@oq*6 z3YaE(s1q?uhY;l!M@Lcxu5_|1nuDGnNSPD8YYPZ6NdfH7G2nKOKt>q6b19~_@Q3yl zaM~r!Lkoez&SwjqW+qm0F48QuLr)m{!BC9(%ai_r5IG(vt^PF@Y)w@Ln&V)FW4 zHfrMsNZ>qZZi-VsU>pYND;TG^c7vpE*^f;F_j<`>UG;9^_gYTH^gWa#K5QR(vz{6Z z_%vt*GGsF>@U9x7dTzXW;X~8dzfuZ|(qY~b0HJ#f`$XP5q~jU<_~l6z=Ykz9$-4C!s~paYefm~qZVDvct0^w{gb&s4)el>{JFQ-V-)_$r|EivknxQiqZxb_<=r3Ys}eUrY^%M3o<_z5A~f)wV}W_+so}eKc=9}^P4MA;KzQsqRb z$v?5}n~x|c^)SG7_CpOD2N(GUF4SEkIX=|IT7{MBuYf<-Ni{nmLP{4q$?KuS&)vqT zu~b~J6GESb>r{VBf*+h2ycsC<7Od@RwLcf6p;QXc4o-V@5mely>qAFR%bjL8FrGvK zRQA9az#0?t_Btp$0q?zpxGU@K{JC9@bZ$JGAy<7+1SQB7X8;K^x--4KQ>l1%20@X7 z?el2k6C0uB9`N2ScyIQ~cu;b+1@?Gb{ck?WxPIpdNZ2+-*FvmQL2c?4G<(FphWtQm z@bMp|;tg@91(MxzoOT?aa^xsIx??w{{R+!oB`T>}p_Uoc~ zn-!}|aaICptTh?ou14%i-Zw15cb267D^Vj?F5I~YhR`H7(H}Ow?RjIX5b_y<0!!j` zw$AZ63rim*f#P<=&awtEy01PN0$qVL@@0yPMU}_n6kwE;>0klaY@zR0Mnx=ECd?O9 z?PJv<2A_fhB_w-gp)j160!24gFx%QAR)Xi@*DC2Db;LqQ*u>G?| z5DvtLYbWLHX6y0(t}@l4HQOryk*H0+j{UP9uC+rmfLbeH_`CfUqc9llb`Vwb`B~tt zjLnZxzWlG^U!v8JXZS^2FNf5w@Ot<5c6<3~WX9ntr7Q!e|NjEu%VDH_`=3#RGBP@2 z*UM-JvSSAlS_aTG&>X*Ho0z=cSwQ7{NP92(L}TV9{-@(|TeJB#Xg>IYrb$#>mbV#3 z>i9aC(n6K^&-818ZeBLvrwUla6C?@GLGuAbcckXSo(G&u88pzQ-N~{`6ls8`Y&8np zn5qU6UJ3dJ7jV^BN%XQZX(9RT(OirDC!MitE^Vh`I;PG6!PdD6M(iTtV2^u4U?GIbpG36TM|tXdtBB&h2AkkKI505ZWoMuw znnS$gKyD9B0qTfGZAVYg1e;dAqe}I`s{5ip-G@U%AO9DB9DsdcFWuqUooh+@`>~G_ z8)=Oh{S9t-w0qDdtvS@JA5xTtF9UY7XRvO)J(CC}YBq*|7Oj*!t<0N~9_DDHOu^$4 zQcc_&_#ge>WE7I2r3Q$*Ii%Jd=ur--fpXddse?t@lS-WA-}-tJRyCOWgGscB5e3t< z>@r0Fzst}uYG6I@q(R>IY1S092sI04@v9o3zjVvs%dfMbI-`v}Bkuwx5U;-@JiYv{ z?#vf3uv`oi5GgWeCa98fsRBXlKNpz|I||mPJyAAH+A_q}C<7 zIn0OV{fJ+`%M;R&WbfGHZBtt>+1rNy>62O=QLLujPZ?SR{l@LRz3l~;YCrtM)fF%% zfF=)yM8n1VKUYc?A&|a9Xu}4-mA1Enk1!cYAVwDlZkV!|=fU3bs2+ z`qF(6N|jb$rX43uIzA6!$O9eICRtZ2j$kzV++6}i#UoFj753Fc;In3Fn=H|9hX7BwkCVRt``#PvMM#VL4XIdm6Ki81JevK zV46Xh%NVzlx9)H|m_Ja7)BzDK;18@Mv8#vpW)!fimcu+1b9s-ZYb&iNgy*p?X@*9Z zG^Ijna~Q%G@aL87G|_gTjlV5B3W*YbpnjO-oAUjRLN` zz$Hz4qAqgHD8{k`C(q&fTY3}J%jT;ia^Ijd? zSaeMBPRr_X)tNC7Yrx0CDZ@67jFu6~)MPL&a0<+%9K%-N%MoSmyyPkRI51jk1uo;R zkmm~l73=q}KK5w@jE~=Xk&jAoC%bl-xXL&_+f`;HH&N60CSTbFq;*cVU|wsRUy_C& zqJ9x`Uw}@4`+X6NQPZl~@F|O-fmI|pE{Vl3*u_;E?a#T>2}U;P!DJ4cV}PuJmeabs zZ-jiMo?JNcq~SfE<>DG*CBoWnn?zX?{lS+0eUDxg_Wro+yBnXyZ<=|lvE}=4f-b%3 zm-nxBd-5m$c8hSU^-xhT$+wE$kl}yS-ZI`|b8_c=N_SKz!1OvS;3dHHJiycg<_z72 zWo0h$9|>Cf===g~L`h@*FuH$vqgfz_qi0Wt3)3e-uSK%$NVxlcUg~{o+qgeb$=2Nm zoPSsWQ3VWRC-|1my)eXk+-t@6jHW()+uXWiGoE*U2vyWzq5YPmX}#Xdr#8U{0l|#& z;G@ZQcQU3xk)bK{&f@XA0?^xSIvnb>D|ZgJ%z^V8nnzxG4kVBlcZk)?a*@;iQdT}K z@&O3W!v~M}hf9U%(#$r#rrTSmeL{FqfMFS4u0V>87iG2ny1uJC>A)krd$YbK=^d^f z9_YMtqXdj%>Ss`f}gO^?}!Pmjy^Vr_Wk}RAvPW9i6 z*+cr!Wa&UbWlkUA!Mee?UFyHLMv!g5S>1i)HJee|G%~?LP zc749$FGw1e^ojugIQ|fsLW_xs-~yUX=}D9Diw484SX+DuCY%o%PsUTLl@|{%`}=o7 z;yeQK9wNYK9&IWuM&bmSCmbrD14ziAwN<4r9ZPa)=SU!BvFB+e9vf8wAJ^TR7MQ>u z63z8zL5+@I7Yt%QwAY7+ariBIDy8~7JQ=SQ8Qa%7vfxf!F?eN^WRQLDAx%OnnRIe(>`E^t_~*{O zaKuG^_Ynevu=HR&5VLzGh#xXpUud!Q7Q@}gTqGh1CZ!WTeqC^H8Ud4np&tb_9J!SOti_o~# z>+@otLMyK5nB4Gqc|NoGzP0R&SN3=GW$)=v;`JvvPbYqrMALDa3kL2yJTzrt@abFsxo# zek^n!YlKjea!Zf}vuSOtb7VhYTWF$x`D{GHAD9dT!XiZae*qAs_d?X^0*_F2eu6d> z6!<>_bC*}h227u zuM0mawf30gy?l3QeP;CG4Se?er_Ftj>XG4zyKFMU#5V~)AejjTAD5nZzv4-JUHw6) z6a??6eg{pJndwniZ$Yrt%y>~(4A>z_o8k2BA*HdgV~dK}`*Xz?Yk~RDH4Z6VH7`Fd z+0{y$cwC)FM!{qn{9GL8>{%B`oO179y-{-JWZ%{HV4n!(omEVhl*q))XG?r?W6Doc zC7R#q-UsG8tt)Rj9!MhdU@r6H+cvn5Q|pmcjEc$6V^C(O5}$;3t*5rn^V6%ud;S<$ zCFZYW_W{G};I&y985GS$SPf=0Wrl}}={2AyX}+y1wWNBJryj5O04JL(Ayo_MsFE5B z&B2{Q_^@P)g(ceN%92;Xy1#F}zJe_&3R>vUEdWt~?u=D^uy{wC!X&vZC$+foyp9+b zIMd8leDc>iAAOxozwjudY`}v&7Z6BF+-+&uR~x)g16P&d;nZ#T#9EC9J*>=D_5q3{ z>I(;o8}#z-Kdx1*d@~sWKp>-P7=`)b+{{|~v1bqx$dBT=pbnac!MVIRlkNR%rDzEv zm_wMN%ptK+Q)y>)=jHlvXQ?loQ@>%7bTlvr^GpNjoL9=+h=**q#*4Mbe;*pV05yBM zcr2mv&>S=Pq;ZsjBDPqjsWAH!n6C9D^M!YRW4od@$I$rhi@5*E=9;}aSap7IIPO&uRvPKuhL@%<5XEEi=1VL19!EJD;2p7At?@f(L4JF}Z2hy~D` z8YZ{quj(}Rx47<}QNj4s@B#=VPH*X`2zyz^JAb`%XU9jG1b`g&{T||nP;gAJH`U+I zG|R`b5y`1&D?H+G^X_M3_Rz~s;2v2@?9-}L3Hr3t+45-40vXr;Lp=Z42!Qke>jYV&`MngLW2?q z+aeER7aYcKFh5*w5;tXmhgv2Q5(tfiI;9qCzl*7U@?`aVAg9xTS^K-*P25#8q~mWh zp4V+nbu>r&!_u&#grBK*moM=d)X}4da{9nWNnxL@D5qK)K6=mcY?8^~pQrk*>h^wv zWcG>vMjwp@AO=`mfDcvsa-e9 zSr2dWh9MPd$p6!Pr{c6?AMs5euoQigZx|H0Dnw_KT-Y0XPOUx!ITQE8#S!8+yVbS) z;X-nQ(MOB1mXh$$mrOc}rz>P;leS9TXQ|yM41XRwyIOQ}4cuc>Zz9&Z56(5ZG6NTZv8atEY_DBQ14@?`{8mS%nvS3cZHbdCf>< z`YAG-WVYA9!H*>$dP|}l`P?RhiX!jU!5AiM2C5wsvmNy)SALC;Dfi4O_9OqF-F(wA zvq=Vvebq!uD|~ju(mrPRuUOzt5_HjVCx#o$hCD=?)9{i#DI6Mch3czQHS6mXfX|1u zn)R_F!Od(j!^;qckwZGE#v3MUtzNP z;8Qvkf^Q46!vHK{rxeMOsQ!!|q3sM6R~eEnN|b_{zk|#J?qPq42c?Ly_YQ1{ofz>C*coPpiM7>6_V|ur-1!IeyxGEMUE`Ty7 zD?2-ynjZM-ZmW*sgn@aNP&_!hy~jZlQ`hoMG%^yysfc@CL#_Es{UP}^beIo8>7|dS z@3KQwtnv7+|C{5*&zIs5@}|eXJvuu0)nG(p+lq;+Xl^r&>=~K}vSqS|(w-mwQcbxA zQQtk0z31my#``=XmmZeVab9g`6!j7^6(VicO@b}b|G7Qmo_`yOenm5O$NOS&A~;D6 zETX7Ci@0|0cRp!p=cW@Gqgda|{*{0b4N+U4Y~4_kzIbs>YuuU{Z9J!&=Ua%?d5gcH z*MKCvikTIV_hGO3tdQ#Ak1jys)ux>lzE&y*?z|^AI6L7S%$WPg8XDVrrkK%P`lt9x zgFEs{rCi(`6fWvT?l=bpXRZkwALBE<85)N9a#{aXp3~B;$_f>RLz{;p67G_wM3j$%l#~bGx*Ij8i|z$*|6(e4S)|qPRCbLvoA?VK58=2v6q);37-^cjq_y)!$iu)z z1)~DnkjLo!btk{3Yb*e*qqe4a^i8>M$yX@XY469gA%m!DhM95-#f<0q;MiZnfOt;2 zREUFQpJ%>Dk+UO(X85q}uC1!bkZ2BEEj>SKk4^x^&O8HyV>6(YMWypK%mIGFKtCS8 zO1bHcH+k~-70+zzLT9L_qbmvG2yua?^sKR!w&DX$Lg~T1(FOJ4@`X~kprd%*r&0x)dsqOk$vfo%~*2r?TZ4`XT zegA3(md8-~QP2+exH6(*tn%e~=`VQ8r_Xy4spJu4Ypy86S#^vh{0JU_r(THzPkk zOeD@d6-ez9KqtL;cb;M%VvbfzR61GFMv&Vf_h3(atvpiqddj7pnRt&V9@X5K<5g{2 zM&6BQTRtz-xU?;J?lbbXyW2Se9kA?CSAe;M%j|+*R0XCV}0uo|Yv^ zY8?Wn5qQ6#L-dkvf$#ojp#9qHPcSUYvOZnyY6IW-Wng)iLW9R*0;gs@4NH>L`UJWY zxT(MccHphg1;+j%kkn;=!eLpKmDI&K>8;NdI*Zj2xPicySx>)`n$DZhrD_lu4BP|- zUW)MCesK1+@uwtZVaQCl;yl>zxkCTP>%ce?o+)Ejk|e2Mff@oA5LjK<-w~6*ACC|8 zo|pY!K`qN_J=J?&;Ld+4bj2eOSViD`0ySArvyvpKA%V{lxMkC&3XQ*N+XXJUAkbxb z_9rHmWmRu=TN1eZ+i}h*?6r}=%>+K1^|UHUk_r*%N8s^-|HinYbKvLS3v9hO`x6z* zvZ|-HUL3gW{5Z!H{+ddlU)Iy8BuQ$Dzz6~_8$>M2O0}96JZ2LZmi4qLNs^i?(1y5e z?*fB}Wm#pdrUj4r1WqBiIgTVr)g{n@zz+y4D=LUwbYWo2rP-g@SeDgtt7*aGLjvC? z&>`#TQIaH;Q+$5NRRq=-e44OyOM{4IS^rpDE)D$0kBbc+>j+#)pnKNSp(II4Q(z|o ze=6dn!60H;R;-VL$I>l}nLz@9y9j(P>*-IDBxNbE4}nP(x^-_LuxyLKFa9miYeDuW zLY8HvSG^X*`@@!PQB3HlBXBo?{#j3Vk|e1m3GBJ)uakubk#(B|es@Zs-|Xy9j4aD4 zU-g?ExcSuhoiF^gk-(h<_RM;ElO#zkSzr$Ww-$L>Mc~gP1BX1C{fUxgS*5E(9u3_8 zrDA7;uxSq2E$it_k|b4|62Ldnb6FA|HK-&%3pFCNXReZJG5WmAO>{D#$ zSWn<40==`Iz9dOfB@668;CkZDHHE+4-ZAhmR|Yz*%>IPRvaH6d)5^d%u8QB?;$F1` zenVh~tfwnUl2lCs+Yz{$z}litO_p>E{PahG9`9v;f@N9OKURX;0gk(i#k*dQLwdafr`!Uk-3&XwQSASI z2e<~fhJ+7G-<%{#QXv901jZ70y2!tx1~#+}+^kk|e2dft~~|Ch&d}PJ9Bh1_e&PA<%h6_9tvHWwqoQIz2wAd`0J` z29K8re3d}^tfvi0)yjGarl|!c1GfSXkg&02JxWp$3?PzPhCo-~ zL}C~j*yPXI*cN6Dj$vf-D0p+HQgtY$r_a3lRgME^K%2VeeA<)1Ex;d0SW%h=C8_BQ zAd*_1KyTo5U>vZ0vp(xDyTa5Fu^vAdR&^>xhf-;ES_KC`9xtyNF%@=uy}6$_AGi&; zk%YIJ-=HLwjsZkc%N0m~Lx`&)#{$hu59`{&v|;suWKO>lG$}Pf5@7F_V%Qk|U|i*B zx30OLx)S&k@e-@YNC?gDQ=?|(dyFd&s^9u-a9gN1Uz&Ee9*M} z;iZCn<~=y#p%^+wOoeS0m*P{G1NQ-U1NW1#wiF*KN#$e!kyLpC?SSFLFft10*6h!$ zYXdKQA%>9$hsXO!Hqyu%l-<;;| zR`qG`0(SxvNtj;B50<1X89*deufXStmuig!4leoUFYFng$Ufu17)V~)+sn49Rn3NY z)A&b${{~Qv3hfOW>Y269QQodHCWJ}uCd7)S;KLx2H5=Tv_8`psa$cJR0DbC=I6`K0b2 z!1jO3UE3{y&DN*Y%}U^DVAkI&VR>4=T#_n*0Ys7-6IV#~A_kI=LdlMq_Vn8B^?~G_ zUNMv`*$S3+tN*#QTf9-e67^gN-Im7MYDxXit>2G<;-he}>tC|&^le}!^?~DM5=y!i zmLydh1BfIwMW82eAoZc-3&3WX_LR%2PM-`W9}g(Yy26@{^|iJ`VgFvaX+fy-O6af_ zIo*UU#`0hQ0000< KMNUMnLSTXyT008> literal 0 HcmV?d00001 diff --git a/packages/rfw/test/material_widgets_test.dart b/packages/rfw/test/material_widgets_test.dart index ac397ec54542..56da9abfd92b 100644 --- a/packages/rfw/test/material_widgets_test.dart +++ b/packages/rfw/test/material_widgets_test.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:rfw/formats.dart' show parseLibraryFile; @@ -438,4 +439,151 @@ void main() { skip: !runGoldens, ); }); + + testWidgets('Implement InkResponse properties', (WidgetTester tester) async { + final Runtime runtime = setupRuntime(); + final DynamicContent data = DynamicContent(); + final List eventLog = []; + await tester.pumpWidget( + MaterialApp( + theme: ThemeData(useMaterial3: false), + home: RemoteWidget( + runtime: runtime, + data: data, + widget: const FullyQualifiedWidgetName(testName, 'root'), + onEvent: (String eventName, DynamicMap eventArguments) { + eventLog.add('$eventName $eventArguments'); + }, + ), + ), + ); + expect( + tester.takeException().toString(), + contains('Could not find remote widget named'), + ); + + runtime.update(testName, parseLibraryFile(''' + import core; + import material; + widget root = Scaffold( + body: Center( + child: InkResponse( + onTap: event 'onTap' {}, + onHover: event 'onHover' {}, + borderRadius: [{x: 8.0, y: 8.0}, {x: 8.0, y: 8.0}, {x: 8.0, y: 8.0}, {x: 8.0, y: 8.0}], + hoverColor: 0xFF00FF00, + splashColor: 0xAA0000FF, + highlightColor: 0xAAFF0000, + containedInkWell: true, + highlightShape: 'circle', + child: Text(text: 'InkResponse'), + ), + ), + ); + ''')); + await tester.pump(); + + expect(find.byType(InkResponse), findsOneWidget); + + // Hover + final Offset center = tester.getCenter(find.byType(InkResponse)); + final TestGesture gesture = + await tester.createGesture(kind: PointerDeviceKind.mouse); + await gesture.addPointer(); + addTearDown(gesture.removePointer); + await gesture.moveTo(center); + await tester.pumpAndSettle(); + + await expectLater( + find.byType(RemoteWidget), + matchesGoldenFile('goldens/material_test.ink_response_hover.png'), + skip: !runGoldens, + ); + expect(eventLog, contains('onHover {}')); + + // Tap + await gesture.down(center); + await tester.pump(); // start gesture + await tester.pump(const Duration( + milliseconds: 200)); // wait for splash to be well under way + + await expectLater( + find.byType(RemoteWidget), + matchesGoldenFile('goldens/material_test.ink_response_tap.png'), + skip: !runGoldens, + ); + await gesture.up(); + await tester.pump(); + + expect(eventLog, contains('onTap {}')); + }); + + testWidgets('Implement Material properties', (WidgetTester tester) async { + final Runtime runtime = setupRuntime(); + final DynamicContent data = DynamicContent(); + final List eventLog = []; + await tester.pumpWidget( + MaterialApp( + theme: ThemeData(useMaterial3: false), + home: RemoteWidget( + runtime: runtime, + data: data, + widget: const FullyQualifiedWidgetName(testName, 'root'), + onEvent: (String eventName, DynamicMap eventArguments) { + eventLog.add('$eventName $eventArguments'); + }, + ), + ), + ); + expect( + tester.takeException().toString(), + contains('Could not find remote widget named'), + ); + + runtime.update(testName, parseLibraryFile(''' + import core; + import material; + widget root = Material( + type: 'circle', + elevation: 6.0, + color: 0xFF0000FF, + shadowColor: 0xFF00FF00, + surfaceTintColor: 0xff0000ff, + animationDuration: 300, + borderOnForeground: false, + child: SizedBox( + width: 20.0, + height: 20.0, + ), + ); + ''')); + await tester.pump(); + + expect(tester.widget(find.byType(Material)).animationDuration, + const Duration(milliseconds: 300)); + expect(tester.widget(find.byType(Material)).borderOnForeground, + false); + await expectLater( + find.byType(RemoteWidget), + matchesGoldenFile('goldens/material_test.material_properties.png'), + skip: !runGoldens, + ); + + runtime.update(testName, parseLibraryFile(''' + import core; + import material; + widget root = Material( + clipBehavior: 'antiAlias', + shape: { type: 'circle', side: { width: 10.0, color: 0xFF0066FF } }, + child: SizedBox( + width: 20.0, + height: 20.0, + ), + ); + ''')); + await tester.pump(); + + expect(tester.widget(find.byType(Material)).clipBehavior, + Clip.antiAlias); + }); } diff --git a/packages/rfw/test_coverage/bin/test_coverage.dart b/packages/rfw/test_coverage/bin/test_coverage.dart index f5f0dcdc9cd4..4781693cb75b 100644 --- a/packages/rfw/test_coverage/bin/test_coverage.dart +++ b/packages/rfw/test_coverage/bin/test_coverage.dart @@ -21,9 +21,9 @@ import 'package:meta/meta.dart'; // Please update these targets when you update this package. // Please ensure that test coverage continues to be 100%. // Don't forget to update the lastUpdate date too! -const int targetLines = 3273; +const int targetLines = 3333; const String targetPercent = '100'; -const String lastUpdate = '2024-01-30'; +const String lastUpdate = '2024-02-26'; @immutable /* final */ class LcovLine { From bc51deab5df81f5ffda0350fe1d947c9bbe2bcb4 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 27 Feb 2024 10:43:26 -0500 Subject: [PATCH 031/126] Roll Flutter from b77560e92a58 to c30f998eb5db (12 revisions) (#6211) https://github.com/flutter/flutter/compare/b77560e92a58...c30f998eb5db 2024-02-27 jiahaog@users.noreply.github.com [flutter_tools] Fix missing stack trace from daemon (flutter/flutter#144113) 2024-02-27 engine-flutter-autoroll@skia.org Roll Flutter Engine from 04ff2868ce80 to 0bc21ea7bc92 (21 revisions) (flutter/flutter#144208) 2024-02-26 polinach@google.com Move debugShowWidgetInspectorOverride (flutter/flutter#144029) 2024-02-26 nate.w5687@gmail.com Allow `Listenable.merge()` to use any iterable (flutter/flutter#143675) 2024-02-26 54558023+keyonghan@users.noreply.github.com Mark firebase_release_smoke_test as `bringup: true` (flutter/flutter#144186) 2024-02-26 git@reb0.org refactor: Differentiate pubspec and resolution errors for plugins (flutter/flutter#142035) 2024-02-26 engine-flutter-autoroll@skia.org Roll Flutter Engine from 34a8b9bdaaac to 04ff2868ce80 (1 revision) (flutter/flutter#144159) 2024-02-26 nate.w5687@gmail.com Implementing `switch` expressions in `rendering/` (flutter/flutter#143812) 2024-02-26 engine-flutter-autoroll@skia.org Roll Flutter Engine from 7a573c21a4ad to 34a8b9bdaaac (1 revision) (flutter/flutter#144147) 2024-02-26 engine-flutter-autoroll@skia.org Roll Flutter Engine from a15326b2c439 to 7a573c21a4ad (1 revision) (flutter/flutter#144145) 2024-02-26 15619084+vashworth@users.noreply.github.com Update copyDirectory to allow links to not be followed (flutter/flutter#144040) 2024-02-26 engine-flutter-autoroll@skia.org Roll Packages from 7df2085c80e2 to 353086c9a61e (7 revisions) (flutter/flutter#144144) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC bmparr@google.com,rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 38a1ae5b7bb5..0e5e495ee63a 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b77560e92a58724eab06490a8c03dfc29eb77271 +c30f998eb5dbea01581ea60aad5b0d6fda18f634 From e07eb50a0fbce46f0de4bcd6f92f486154bed478 Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Tue, 27 Feb 2024 16:45:09 -0500 Subject: [PATCH 032/126] [camerax] Implements `setExposureMode` (#6110) Implements `setExposureMode`. Fixes https://github.com/flutter/flutter/issues/120468. ~To be landed after (1) https://github.com/flutter/packages/pull/6059 then (2) https://github.com/flutter/packages/pull/6109.~ Done :) --- .../camera_android_camerax/CHANGELOG.md | 4 ++ .../camera/camera_android_camerax/README.md | 4 -- .../camerax/CameraAndroidCameraxPlugin.java | 11 ++++ .../CaptureRequestOptionsHostApiImpl.java | 4 +- .../CameraAndroidCameraxPluginTest.java | 12 ++++ .../example/lib/main.dart | 12 ++-- .../lib/src/android_camera_camerax.dart | 23 ++++++++ .../lib/src/camerax_proxy.dart | 24 ++++++++ .../camera_android_camerax/pubspec.yaml | 2 +- .../test/android_camera_camerax_test.dart | 56 +++++++++++++++++++ .../android_camera_camerax_test.mocks.dart | 35 ++++++++++++ 11 files changed, 176 insertions(+), 11 deletions(-) diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index 5c8002bae00f..647a57559951 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.5.0+36 + +* Implements `setExposureMode`. + ## 0.5.0+35 * Modifies `CameraInitializedEvent` that is sent when the camera is initialized to indicate that the initial focus diff --git a/packages/camera/camera_android_camerax/README.md b/packages/camera/camera_android_camerax/README.md index 48674de44cf8..fdc50955de43 100644 --- a/packages/camera/camera_android_camerax/README.md +++ b/packages/camera/camera_android_camerax/README.md @@ -30,10 +30,6 @@ dependencies: and thus, the plugin will fall back to 480p if configured with a `ResolutionPreset`. -### Exposure mode configuration \[[Issue #120468][120468]\] - -`setExposureMode`is unimplemented. - ### Focus mode configuration \[[Issue #120467][120467]\] `setFocusMode` is unimplemented. diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java index ea9f3ed4d06c..6a30074a2f87 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java @@ -29,6 +29,9 @@ public final class CameraAndroidCameraxPlugin implements FlutterPlugin, Activity @VisibleForTesting @Nullable public SystemServicesHostApiImpl systemServicesHostApiImpl; @VisibleForTesting @Nullable public MeteringPointHostApiImpl meteringPointHostApiImpl; + @VisibleForTesting @Nullable + public Camera2CameraControlHostApiImpl camera2CameraControlHostApiImpl; + @VisibleForTesting public @Nullable DeviceOrientationManagerHostApiImpl deviceOrientationManagerHostApiImpl; @@ -120,6 +123,11 @@ public void setUp( cameraControlHostApiImpl = new CameraControlHostApiImpl(binaryMessenger, instanceManager, context); GeneratedCameraXLibrary.CameraControlHostApi.setup(binaryMessenger, cameraControlHostApiImpl); + camera2CameraControlHostApiImpl = new Camera2CameraControlHostApiImpl(instanceManager, context); + GeneratedCameraXLibrary.Camera2CameraControlHostApi.setup( + binaryMessenger, camera2CameraControlHostApiImpl); + GeneratedCameraXLibrary.CaptureRequestOptionsHostApi.setup( + binaryMessenger, new CaptureRequestOptionsHostApiImpl(instanceManager)); GeneratedCameraXLibrary.FocusMeteringActionHostApi.setup( binaryMessenger, new FocusMeteringActionHostApiImpl(instanceManager)); GeneratedCameraXLibrary.FocusMeteringResultHostApi.setup( @@ -217,6 +225,9 @@ public void updateContext(@NonNull Context context) { if (cameraControlHostApiImpl != null) { cameraControlHostApiImpl.setContext(context); } + if (camera2CameraControlHostApiImpl != null) { + camera2CameraControlHostApiImpl.setContext(context); + } } /** Sets {@code LifecycleOwner} that is used to control the lifecycle of the camera by CameraX. */ diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CaptureRequestOptionsHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CaptureRequestOptionsHostApiImpl.java index 6f8d8c91dda6..f76dd5422e72 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CaptureRequestOptionsHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CaptureRequestOptionsHostApiImpl.java @@ -110,8 +110,8 @@ public void create(@NonNull Long identifier, @NonNull Map options) Map decodedOptions = new HashMap(); for (Map.Entry option : options.entrySet()) { - decodedOptions.put( - CaptureRequestKeySupportedType.values()[option.getKey().intValue()], option.getValue()); + Integer index = ((Number) option.getKey()).intValue(); + decodedOptions.put(CaptureRequestKeySupportedType.values()[index], option.getValue()); } instanceManager.addDartCreatedInstance(proxy.create(decodedOptions), identifier); } diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraAndroidCameraxPluginTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraAndroidCameraxPluginTest.java index f9f4be6db6e0..fcca0f8eb0bc 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraAndroidCameraxPluginTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraAndroidCameraxPluginTest.java @@ -169,6 +169,8 @@ public void onAttachedToActivity_setsActivityAsNeededAndPermissionsRegistry() { mock(ImageAnalysisHostApiImpl.class); final CameraControlHostApiImpl mockCameraControlHostApiImpl = mock(CameraControlHostApiImpl.class); + final Camera2CameraControlHostApiImpl mockCamera2CameraControlHostApiImpl = + mock(Camera2CameraControlHostApiImpl.class); when(flutterPluginBinding.getApplicationContext()).thenReturn(mockContext); @@ -180,6 +182,7 @@ public void onAttachedToActivity_setsActivityAsNeededAndPermissionsRegistry() { plugin.imageAnalysisHostApiImpl = mockImageAnalysisHostApiImpl; plugin.cameraControlHostApiImpl = mockCameraControlHostApiImpl; plugin.liveDataHostApiImpl = mock(LiveDataHostApiImpl.class); + plugin.camera2CameraControlHostApiImpl = mockCamera2CameraControlHostApiImpl; plugin.onAttachedToEngine(flutterPluginBinding); plugin.onDetachedFromActivityForConfigChanges(); @@ -191,6 +194,7 @@ public void onAttachedToActivity_setsActivityAsNeededAndPermissionsRegistry() { verify(mockImageCaptureHostApiImpl).setContext(mockContext); verify(mockImageAnalysisHostApiImpl).setContext(mockContext); verify(mockCameraControlHostApiImpl).setContext(mockContext); + verify(mockCamera2CameraControlHostApiImpl).setContext(mockContext); } @Test @@ -259,6 +263,8 @@ public void onReattachedToActivityForConfigChanges_setsActivityAndPermissionsReg mock(CameraControlHostApiImpl.class); final DeviceOrientationManagerHostApiImpl mockDeviceOrientationManagerHostApiImpl = mock(DeviceOrientationManagerHostApiImpl.class); + final Camera2CameraControlHostApiImpl mockCamera2CameraControlHostApiImpl = + mock(Camera2CameraControlHostApiImpl.class); final MeteringPointHostApiImpl mockMeteringPointHostApiImpl = mock(MeteringPointHostApiImpl.class); final ArgumentCaptor permissionsRegistryCaptor = @@ -277,6 +283,7 @@ public void onReattachedToActivityForConfigChanges_setsActivityAndPermissionsReg plugin.deviceOrientationManagerHostApiImpl = mockDeviceOrientationManagerHostApiImpl; plugin.meteringPointHostApiImpl = mockMeteringPointHostApiImpl; plugin.liveDataHostApiImpl = mock(LiveDataHostApiImpl.class); + plugin.camera2CameraControlHostApiImpl = mockCamera2CameraControlHostApiImpl; plugin.onAttachedToEngine(flutterPluginBinding); plugin.onReattachedToActivityForConfigChanges(activityPluginBinding); @@ -294,6 +301,7 @@ public void onReattachedToActivityForConfigChanges_setsActivityAndPermissionsReg verify(mockImageCaptureHostApiImpl).setContext(mockActivity); verify(mockImageAnalysisHostApiImpl).setContext(mockActivity); verify(mockCameraControlHostApiImpl).setContext(mockActivity); + verify(mockCamera2CameraControlHostApiImpl).setContext(mockActivity); // Check permissions registry reference is set. verify(mockSystemServicesHostApiImpl) @@ -347,6 +355,8 @@ public void onDetachedFromActivity_setsContextReferencesBasedOnFlutterPluginBind final ImageCaptureHostApiImpl mockImageCaptureHostApiImpl = mock(ImageCaptureHostApiImpl.class); final CameraControlHostApiImpl mockCameraControlHostApiImpl = mock(CameraControlHostApiImpl.class); + final Camera2CameraControlHostApiImpl mockCamera2CameraControlHostApiImpl = + mock(Camera2CameraControlHostApiImpl.class); final ArgumentCaptor permissionsRegistryCaptor = ArgumentCaptor.forClass(PermissionsRegistry.class); @@ -360,6 +370,7 @@ public void onDetachedFromActivity_setsContextReferencesBasedOnFlutterPluginBind plugin.imageAnalysisHostApiImpl = mockImageAnalysisHostApiImpl; plugin.cameraControlHostApiImpl = mockCameraControlHostApiImpl; plugin.liveDataHostApiImpl = mock(LiveDataHostApiImpl.class); + plugin.camera2CameraControlHostApiImpl = mockCamera2CameraControlHostApiImpl; plugin.onAttachedToEngine(flutterPluginBinding); plugin.onDetachedFromActivity(); @@ -371,5 +382,6 @@ public void onDetachedFromActivity_setsContextReferencesBasedOnFlutterPluginBind verify(mockImageCaptureHostApiImpl).setContext(mockContext); verify(mockImageAnalysisHostApiImpl).setContext(mockContext); verify(mockCameraControlHostApiImpl).setContext(mockContext); + verify(mockCamera2CameraControlHostApiImpl).setContext(mockContext); } } diff --git a/packages/camera/camera_android_camerax/example/lib/main.dart b/packages/camera/camera_android_camerax/example/lib/main.dart index adb868373712..ff1f620e03fa 100644 --- a/packages/camera/camera_android_camerax/example/lib/main.dart +++ b/packages/camera/camera_android_camerax/example/lib/main.dart @@ -393,8 +393,10 @@ class _CameraExampleHomeState extends State children: [ TextButton( style: styleAuto, - onPressed: - () {}, // TODO(camsim99): Add functionality back here. + onPressed: controller != null + ? () => + onSetExposureModeButtonPressed(ExposureMode.auto) + : null, onLongPress: () { if (controller != null) { CameraPlatform.instance @@ -406,8 +408,10 @@ class _CameraExampleHomeState extends State ), TextButton( style: styleLocked, - onPressed: - () {}, // TODO(camsim99): Add functionality back here. + onPressed: controller != null + ? () => + onSetExposureModeButtonPressed(ExposureMode.locked) + : null, child: const Text('LOCKED'), ), TextButton( diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart index ffff56b4d963..261525113d45 100644 --- a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart @@ -14,12 +14,14 @@ import 'package:stream_transform/stream_transform.dart'; import 'analyzer.dart'; import 'camera.dart'; +import 'camera2_camera_control.dart'; import 'camera_control.dart'; import 'camera_info.dart'; import 'camera_selector.dart'; import 'camera_state.dart'; import 'camerax_library.g.dart'; import 'camerax_proxy.dart'; +import 'capture_request_options.dart'; import 'device_orientation_manager.dart'; import 'exposure_state.dart'; import 'fallback_strategy.dart'; @@ -545,6 +547,27 @@ class AndroidCameraCameraX extends CameraPlatform { point: point, meteringMode: FocusMeteringAction.flagAf); } + /// Sets the exposure mode for taking pictures. + /// + /// Setting [ExposureMode.locked] will lock current exposure point until it + /// is unset by setting [ExposureMode.auto]. + /// + /// [cameraId] is not used. + @override + Future setExposureMode(int cameraId, ExposureMode mode) async { + final Camera2CameraControl camera2Control = + proxy.getCamera2CameraControl(cameraControl); + final bool lockExposureMode = mode == ExposureMode.locked; + + final CaptureRequestOptions captureRequestOptions = proxy + .createCaptureRequestOptions(<( + CaptureRequestKeySupportedType, + Object? + )>[(CaptureRequestKeySupportedType.controlAeLock, lockExposureMode)]); + + await camera2Control.addCaptureRequestOptions(captureRequestOptions); + } + /// Gets the maximum supported zoom level for the selected camera. /// /// [cameraId] not used. diff --git a/packages/camera/camera_android_camerax/lib/src/camerax_proxy.dart b/packages/camera/camera_android_camerax/lib/src/camerax_proxy.dart index 072a753d8b40..2ccb353aa66c 100644 --- a/packages/camera/camera_android_camerax/lib/src/camerax_proxy.dart +++ b/packages/camera/camera_android_camerax/lib/src/camerax_proxy.dart @@ -5,10 +5,13 @@ import 'dart:ui' show Size; import 'analyzer.dart'; +import 'camera2_camera_control.dart'; +import 'camera_control.dart'; import 'camera_info.dart'; import 'camera_selector.dart'; import 'camera_state.dart'; import 'camerax_library.g.dart'; +import 'capture_request_options.dart'; import 'device_orientation_manager.dart'; import 'fallback_strategy.dart'; import 'focus_metering_action.dart'; @@ -52,6 +55,8 @@ class CameraXProxy { _startListeningForDeviceOrientationChange, this.setPreviewSurfaceProvider = _setPreviewSurfaceProvider, this.getDefaultDisplayRotation = _getDefaultDisplayRotation, + this.getCamera2CameraControl = _getCamera2CameraControl, + this.createCaptureRequestOptions = _createCaptureRequestOptions, this.createMeteringPoint = _createMeteringPoint, this.createFocusMeteringAction = _createFocusMeteringAction, }); @@ -142,6 +147,15 @@ class CameraXProxy { /// rotation constants. Future Function() getDefaultDisplayRotation; + /// Get [Camera2CameraControl] instance from [cameraControl]. + Camera2CameraControl Function(CameraControl cameraControl) + getCamera2CameraControl; + + /// Create [CapureRequestOptions] with specified options. + CaptureRequestOptions Function( + List<(CaptureRequestKeySupportedType, Object?)> options) + createCaptureRequestOptions; + /// Returns a [MeteringPoint] with the specified coordinates based on /// [cameraInfo]. MeteringPoint Function(double x, double y, CameraInfo cameraInfo) @@ -255,6 +269,16 @@ class CameraXProxy { return DeviceOrientationManager.getDefaultDisplayRotation(); } + static Camera2CameraControl _getCamera2CameraControl( + CameraControl cameraControl) { + return Camera2CameraControl(cameraControl: cameraControl); + } + + static CaptureRequestOptions _createCaptureRequestOptions( + List<(CaptureRequestKeySupportedType, Object?)> options) { + return CaptureRequestOptions(requestedOptions: options); + } + static MeteringPoint _createMeteringPoint( double x, double y, CameraInfo cameraInfo) { return MeteringPoint(x: x, y: y, cameraInfo: cameraInfo); diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index 9e151c368ef6..660997d5945f 100644 --- a/packages/camera/camera_android_camerax/pubspec.yaml +++ b/packages/camera/camera_android_camerax/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android_camerax description: Android implementation of the camera plugin using the CameraX library. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android_camerax issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.5.0+35 +version: 0.5.0+36 environment: sdk: ^3.1.0 diff --git a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart index 3998860bfb54..05ee82abb4ab 100644 --- a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart +++ b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart @@ -9,6 +9,7 @@ import 'package:async/async.dart'; import 'package:camera_android_camerax/camera_android_camerax.dart'; import 'package:camera_android_camerax/src/analyzer.dart'; import 'package:camera_android_camerax/src/camera.dart'; +import 'package:camera_android_camerax/src/camera2_camera_control.dart'; import 'package:camera_android_camerax/src/camera_control.dart'; import 'package:camera_android_camerax/src/camera_info.dart'; import 'package:camera_android_camerax/src/camera_selector.dart'; @@ -16,6 +17,7 @@ import 'package:camera_android_camerax/src/camera_state.dart'; import 'package:camera_android_camerax/src/camera_state_error.dart'; import 'package:camera_android_camerax/src/camerax_library.g.dart'; import 'package:camera_android_camerax/src/camerax_proxy.dart'; +import 'package:camera_android_camerax/src/capture_request_options.dart'; import 'package:camera_android_camerax/src/device_orientation_manager.dart'; import 'package:camera_android_camerax/src/exposure_state.dart'; import 'package:camera_android_camerax/src/fallback_strategy.dart'; @@ -78,6 +80,7 @@ import 'test_camerax_library.g.dart'; MockSpec(), MockSpec(), MockSpec(), + MockSpec(), ]) @GenerateMocks([], customMocks: >[ MockSpec>(as: #MockLiveCameraState), @@ -2010,6 +2013,59 @@ void main() { expect(camera.captureOrientationLocked, isFalse); }); + test('setExposureMode sets expected controlAeLock value via Camera2 interop', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 78; + final MockCameraControl mockCameraControl = MockCameraControl(); + final MockCamera2CameraControl mockCamera2CameraControl = + MockCamera2CameraControl(); + + // Set directly for test versus calling createCamera. + camera.camera = MockCamera(); + camera.cameraControl = mockCameraControl; + + // Tell plugin to create detached Camera2CameraControl and + // CaptureRequestOptions instances for testing. + camera.proxy = CameraXProxy( + getCamera2CameraControl: (CameraControl cameraControl) => + cameraControl == mockCameraControl + ? mockCamera2CameraControl + : Camera2CameraControl.detached(cameraControl: cameraControl), + createCaptureRequestOptions: + (List<(CaptureRequestKeySupportedType, Object?)> options) => + CaptureRequestOptions.detached(requestedOptions: options), + ); + + // Test auto mode. + await camera.setExposureMode(cameraId, ExposureMode.auto); + + VerificationResult verificationResult = + verify(mockCamera2CameraControl.addCaptureRequestOptions(captureAny)); + CaptureRequestOptions capturedCaptureRequestOptions = + verificationResult.captured.single as CaptureRequestOptions; + List<(CaptureRequestKeySupportedType, Object?)> requestedOptions = + capturedCaptureRequestOptions.requestedOptions; + expect(requestedOptions.length, equals(1)); + expect(requestedOptions.first.$1, + equals(CaptureRequestKeySupportedType.controlAeLock)); + expect(requestedOptions.first.$2, equals(false)); + + // Test locked mode. + clearInteractions(mockCamera2CameraControl); + await camera.setExposureMode(cameraId, ExposureMode.locked); + + verificationResult = + verify(mockCamera2CameraControl.addCaptureRequestOptions(captureAny)); + capturedCaptureRequestOptions = + verificationResult.captured.single as CaptureRequestOptions; + requestedOptions = capturedCaptureRequestOptions.requestedOptions; + expect(requestedOptions.length, equals(1)); + expect(requestedOptions.first.$1, + equals(CaptureRequestKeySupportedType.controlAeLock)); + expect(requestedOptions.first.$2, equals(true)); + }); + test( 'setExposurePoint clears current auto-exposure metering point as expected', () async { diff --git a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.mocks.dart b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.mocks.dart index 4c90e940e71c..be2541b8f92b 100644 --- a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.mocks.dart +++ b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.mocks.dart @@ -8,11 +8,14 @@ import 'dart:typed_data' as _i29; import 'package:camera_android_camerax/src/analyzer.dart' as _i15; import 'package:camera_android_camerax/src/camera.dart' as _i9; +import 'package:camera_android_camerax/src/camera2_camera_control.dart' as _i38; import 'package:camera_android_camerax/src/camera_control.dart' as _i3; import 'package:camera_android_camerax/src/camera_info.dart' as _i2; import 'package:camera_android_camerax/src/camera_selector.dart' as _i22; import 'package:camera_android_camerax/src/camera_state.dart' as _i18; import 'package:camera_android_camerax/src/camerax_library.g.dart' as _i7; +import 'package:camera_android_camerax/src/capture_request_options.dart' + as _i39; import 'package:camera_android_camerax/src/exposure_state.dart' as _i5; import 'package:camera_android_camerax/src/fallback_strategy.dart' as _i23; import 'package:camera_android_camerax/src/focus_metering_action.dart' as _i21; @@ -1317,6 +1320,38 @@ class MockZoomState extends _i1.Mock implements _i19.ZoomState { ) as double); } +/// A class which mocks [Camera2CameraControl]. +/// +/// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable +class MockCamera2CameraControl extends _i1.Mock + implements _i38.Camera2CameraControl { + @override + _i3.CameraControl get cameraControl => (super.noSuchMethod( + Invocation.getter(#cameraControl), + returnValue: _FakeCameraControl_1( + this, + Invocation.getter(#cameraControl), + ), + returnValueForMissingStub: _FakeCameraControl_1( + this, + Invocation.getter(#cameraControl), + ), + ) as _i3.CameraControl); + + @override + _i16.Future addCaptureRequestOptions( + _i39.CaptureRequestOptions? captureRequestOptions) => + (super.noSuchMethod( + Invocation.method( + #addCaptureRequestOptions, + [captureRequestOptions], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); +} + /// A class which mocks [LiveData]. /// /// See the documentation for Mockito's code generation for more information. From 679bdd78671c5c7d1518c9512915e58703ab2145 Mon Sep 17 00:00:00 2001 From: Tarrin Neal Date: Tue, 27 Feb 2024 14:05:59 -0800 Subject: [PATCH 033/126] [pigeon] Allows kotlin generator to skip error class generation (#6183) Allows kotlin generator to skip error class generation by creating `includeErrorClass` argument. This may be considered only a partial fix, as it requires another pigeon file with the class generated rather than creating a new file with the class. This solution seems to me to solve the problem well enough as creating another file is a non-trivial task and there is already a working solution - renaming the error class per file. fixes: https://github.com/flutter/flutter/issues/142099 --- packages/pigeon/CHANGELOG.md | 3 +- packages/pigeon/lib/generator_tools.dart | 2 +- packages/pigeon/lib/kotlin_generator.dart | 35 +++-- packages/pigeon/lib/pigeon_lib.dart | 1 + .../com/example/test_plugin/CoreTests.gen.kt | 123 ++++++------------ .../com/example/test_plugin/TestPlugin.kt | 4 +- packages/pigeon/pubspec.yaml | 2 +- packages/pigeon/tool/shared/generation.dart | 16 ++- 8 files changed, 86 insertions(+), 100 deletions(-) diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md index 967426ce7c2a..dd5c01fe48c9 100644 --- a/packages/pigeon/CHANGELOG.md +++ b/packages/pigeon/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 17.1.0 +* [kotlin] Adds `includeErrorClass` to `KotlinOptions`. * Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. ## 17.0.0 diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart index 99da7e0a4d56..54e980eb34a0 100644 --- a/packages/pigeon/lib/generator_tools.dart +++ b/packages/pigeon/lib/generator_tools.dart @@ -13,7 +13,7 @@ import 'ast.dart'; /// The current version of pigeon. /// /// This must match the version in pubspec.yaml. -const String pigeonVersion = '17.0.0'; +const String pigeonVersion = '17.1.0'; /// Read all the content from [stdin] to a String. String readStdin() { diff --git a/packages/pigeon/lib/kotlin_generator.dart b/packages/pigeon/lib/kotlin_generator.dart index 29e3e3662554..39697fa3242d 100644 --- a/packages/pigeon/lib/kotlin_generator.dart +++ b/packages/pigeon/lib/kotlin_generator.dart @@ -32,6 +32,7 @@ class KotlinOptions { this.package, this.copyrightHeader, this.errorClassName, + this.includeErrorClass = true, }); /// The package where the generated class will live. @@ -43,6 +44,12 @@ class KotlinOptions { /// The name of the error class used for passing custom error parameters. final String? errorClassName; + /// Whether to include the error class in generation. + /// + /// This should only ever be set to false if you have another generated + /// Kotlin file in the same directory. + final bool includeErrorClass; + /// Creates a [KotlinOptions] from a Map representation where: /// `x = KotlinOptions.fromMap(x.toMap())`. static KotlinOptions fromMap(Map map) { @@ -50,6 +57,7 @@ class KotlinOptions { package: map['package'] as String?, copyrightHeader: map['copyrightHeader'] as Iterable?, errorClassName: map['errorClassName'] as String?, + includeErrorClass: map['includeErrorClass'] as bool? ?? true, ); } @@ -60,6 +68,7 @@ class KotlinOptions { if (package != null) 'package': package!, if (copyrightHeader != null) 'copyrightHeader': copyrightHeader!, if (errorClassName != null) 'errorClassName': errorClassName!, + 'includeErrorClass': includeErrorClass, }; return result; } @@ -298,7 +307,7 @@ class KotlinGenerator extends StructuredGenerator { addDocumentationComments( indent, field.documentationComments, _docCommentSpec); indent.write( - 'val ${field.name}: ${_nullsafeKotlinTypeForDartType(field.type)}'); + 'val ${field.name}: ${_nullSafeKotlinTypeForDartType(field.type)}'); final String defaultNil = field.type.isNullable ? ' = null' : ''; indent.add(defaultNil); } @@ -361,16 +370,15 @@ class KotlinGenerator extends StructuredGenerator { }); }); - final String errorClassName = _getErrorClassName(generatorOptions); for (final Method method in api.methods) { _writeFlutterMethod( indent, + generatorOptions: generatorOptions, name: method.name, parameters: method.parameters, returnType: method.returnType, channelName: makeChannelName(api, method, dartPackageName), documentationComments: method.documentationComments, - errorClassName: errorClassName, dartPackageName: dartPackageName, ); } @@ -588,7 +596,9 @@ class KotlinGenerator extends StructuredGenerator { if (hasFlutterApi) { _writeCreateConnectionError(generatorOptions, indent); } - _writeErrorClass(generatorOptions, indent); + if (generatorOptions.includeErrorClass) { + _writeErrorClass(generatorOptions, indent); + } } static void _writeMethodDeclaration( @@ -606,7 +616,7 @@ class KotlinGenerator extends StructuredGenerator { final List argSignature = []; if (parameters.isNotEmpty) { final Iterable argTypes = parameters - .map((NamedType e) => _nullsafeKotlinTypeForDartType(e.type)); + .map((NamedType e) => _nullSafeKotlinTypeForDartType(e.type)); final Iterable argNames = indexMap(parameters, getArgumentName); argSignature.addAll( map2( @@ -620,7 +630,7 @@ class KotlinGenerator extends StructuredGenerator { } final String returnTypeString = - returnType.isVoid ? '' : _nullsafeKotlinTypeForDartType(returnType); + returnType.isVoid ? '' : _nullSafeKotlinTypeForDartType(returnType); final String resultType = returnType.isVoid ? 'Unit' : returnTypeString; addDocumentationComments(indent, documentationComments, _docCommentSpec); @@ -700,7 +710,7 @@ class KotlinGenerator extends StructuredGenerator { indent.write('$call '); final String resultType = returnType.isVoid ? 'Unit' - : _nullsafeKotlinTypeForDartType(returnType); + : _nullSafeKotlinTypeForDartType(returnType); indent.addScoped('{ result: Result<$resultType> ->', '}', () { indent.writeln('val error = result.exceptionOrNull()'); indent.writeScoped('if (error != null) {', '}', () { @@ -751,11 +761,11 @@ class KotlinGenerator extends StructuredGenerator { void _writeFlutterMethod( Indent indent, { + required KotlinOptions generatorOptions, required String name, required List parameters, required TypeDeclaration returnType, required String channelName, - required String errorClassName, required String dartPackageName, List documentationComments = const [], int? minApiRequirement, @@ -778,6 +788,7 @@ class KotlinGenerator extends StructuredGenerator { getArgumentName: _getSafeArgumentName, ); + final String errorClassName = _getErrorClassName(generatorOptions); indent.addScoped('{', '}', () { onWriteBody( indent, @@ -910,9 +921,9 @@ String _kotlinTypeForBuiltinGenericDartType(TypeDeclaration type) { } else { switch (type.baseName) { case 'List': - return 'List<${_nullsafeKotlinTypeForDartType(type.typeArguments.first)}>'; + return 'List<${_nullSafeKotlinTypeForDartType(type.typeArguments.first)}>'; case 'Map': - return 'Map<${_nullsafeKotlinTypeForDartType(type.typeArguments.first)}, ${_nullsafeKotlinTypeForDartType(type.typeArguments.last)}>'; + return 'Map<${_nullSafeKotlinTypeForDartType(type.typeArguments.first)}, ${_nullSafeKotlinTypeForDartType(type.typeArguments.last)}>'; default: return '${type.baseName}<${_flattenTypeArguments(type.typeArguments)}>'; } @@ -946,7 +957,7 @@ String _kotlinTypeForDartType(TypeDeclaration type) { return _kotlinTypeForBuiltinDartType(type) ?? type.baseName; } -String _nullsafeKotlinTypeForDartType(TypeDeclaration type) { +String _nullSafeKotlinTypeForDartType(TypeDeclaration type) { final String nullSafe = type.isNullable ? '?' : ''; return '${_kotlinTypeForDartType(type)}$nullSafe'; } @@ -969,7 +980,7 @@ String _cast(Indent indent, String variable, {required TypeDeclaration type}) { } return '${type.baseName}.ofRaw($variable as Int)!!'; } - return '$variable as ${_nullsafeKotlinTypeForDartType(type)}'; + return '$variable as ${_nullSafeKotlinTypeForDartType(type)}'; } String _castInt(bool isNullable) { diff --git a/packages/pigeon/lib/pigeon_lib.dart b/packages/pigeon/lib/pigeon_lib.dart index 187aa3466b2a..8d6fc961c2ec 100644 --- a/packages/pigeon/lib/pigeon_lib.dart +++ b/packages/pigeon/lib/pigeon_lib.dart @@ -776,6 +776,7 @@ class KotlinGeneratorAdapter implements GeneratorAdapter { options.kotlinOptions ?? const KotlinOptions(); kotlinOptions = kotlinOptions.merge(KotlinOptions( errorClassName: kotlinOptions.errorClassName ?? 'FlutterError', + includeErrorClass: kotlinOptions.includeErrorClass, copyrightHeader: options.copyrightHeader != null ? _lineReader( path.posix.join(options.basePath ?? '', options.copyrightHeader)) diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt index c3ec9d98caf6..b97e72c01469 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt @@ -20,7 +20,7 @@ private fun wrapResult(result: Any?): List { } private fun wrapError(exception: Throwable): List { - if (exception is CoreTestsError) { + if (exception is FlutterError) { return listOf(exception.code, exception.message, exception.details) } else { return listOf( @@ -30,24 +30,11 @@ private fun wrapError(exception: Throwable): List { } } -private fun createConnectionError(channelName: String): CoreTestsError { - return CoreTestsError( +private fun createConnectionError(channelName: String): FlutterError { + return FlutterError( "channel-error", "Unable to establish connection on channel: '$channelName'.", "") } -/** - * Error class for passing custom error details to Flutter via a thrown PlatformException. - * - * @property code The error code. - * @property message The error message. - * @property details The error details. Must be a datatype supported by the api codec. - */ -class CoreTestsError( - val code: String, - override val message: String? = null, - val details: Any? = null -) : Throwable() - enum class AnEnum(val raw: Int) { ONE(0), TWO(1), @@ -2399,8 +2386,7 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { channel.send(null) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else { callback(Result.success(Unit)) } @@ -2417,8 +2403,7 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { channel.send(null) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else { val output = it[0] callback(Result.success(output)) @@ -2436,8 +2421,7 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { channel.send(null) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else { callback(Result.success(Unit)) } @@ -2454,12 +2438,11 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { channel.send(listOf(everythingArg)) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else if (it[0] == null) { callback( Result.failure( - CoreTestsError( + FlutterError( "null-error", "Flutter api returned null value for non-null return value.", ""))) @@ -2483,8 +2466,7 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { channel.send(listOf(everythingArg)) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else { val output = it[0] as AllNullableTypes? callback(Result.success(output)) @@ -2511,12 +2493,11 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { channel.send(listOf(aNullableBoolArg, aNullableIntArg, aNullableStringArg)) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else if (it[0] == null) { callback( Result.failure( - CoreTestsError( + FlutterError( "null-error", "Flutter api returned null value for non-null return value.", ""))) @@ -2537,12 +2518,11 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { channel.send(listOf(aBoolArg)) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else if (it[0] == null) { callback( Result.failure( - CoreTestsError( + FlutterError( "null-error", "Flutter api returned null value for non-null return value.", ""))) @@ -2563,12 +2543,11 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { channel.send(listOf(anIntArg)) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else if (it[0] == null) { callback( Result.failure( - CoreTestsError( + FlutterError( "null-error", "Flutter api returned null value for non-null return value.", ""))) @@ -2589,12 +2568,11 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { channel.send(listOf(aDoubleArg)) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else if (it[0] == null) { callback( Result.failure( - CoreTestsError( + FlutterError( "null-error", "Flutter api returned null value for non-null return value.", ""))) @@ -2615,12 +2593,11 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { channel.send(listOf(aStringArg)) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else if (it[0] == null) { callback( Result.failure( - CoreTestsError( + FlutterError( "null-error", "Flutter api returned null value for non-null return value.", ""))) @@ -2641,12 +2618,11 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { channel.send(listOf(aListArg)) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else if (it[0] == null) { callback( Result.failure( - CoreTestsError( + FlutterError( "null-error", "Flutter api returned null value for non-null return value.", ""))) @@ -2667,12 +2643,11 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { channel.send(listOf(aListArg)) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else if (it[0] == null) { callback( Result.failure( - CoreTestsError( + FlutterError( "null-error", "Flutter api returned null value for non-null return value.", ""))) @@ -2693,12 +2668,11 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { channel.send(listOf(aMapArg)) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else if (it[0] == null) { callback( Result.failure( - CoreTestsError( + FlutterError( "null-error", "Flutter api returned null value for non-null return value.", ""))) @@ -2719,12 +2693,11 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { channel.send(listOf(anEnumArg.raw)) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else if (it[0] == null) { callback( Result.failure( - CoreTestsError( + FlutterError( "null-error", "Flutter api returned null value for non-null return value.", ""))) @@ -2745,8 +2718,7 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { channel.send(listOf(aBoolArg)) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else { val output = it[0] as Boolean? callback(Result.success(output)) @@ -2764,8 +2736,7 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { channel.send(listOf(anIntArg)) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else { val output = it[0].let { if (it is Int) it.toLong() else it as Long? } callback(Result.success(output)) @@ -2783,8 +2754,7 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { channel.send(listOf(aDoubleArg)) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else { val output = it[0] as Double? callback(Result.success(output)) @@ -2802,8 +2772,7 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { channel.send(listOf(aStringArg)) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else { val output = it[0] as String? callback(Result.success(output)) @@ -2821,8 +2790,7 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { channel.send(listOf(aListArg)) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else { val output = it[0] as ByteArray? callback(Result.success(output)) @@ -2840,8 +2808,7 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { channel.send(listOf(aListArg)) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else { val output = it[0] as List? callback(Result.success(output)) @@ -2862,8 +2829,7 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { channel.send(listOf(aMapArg)) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else { val output = it[0] as Map? callback(Result.success(output)) @@ -2881,8 +2847,7 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { channel.send(listOf(anEnumArg?.raw)) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else { val output = (it[0] as Int?)?.let { AnEnum.ofRaw(it) } callback(Result.success(output)) @@ -2903,8 +2868,7 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { channel.send(null) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else { callback(Result.success(Unit)) } @@ -2921,12 +2885,11 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { channel.send(listOf(aStringArg)) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else if (it[0] == null) { callback( Result.failure( - CoreTestsError( + FlutterError( "null-error", "Flutter api returned null value for non-null return value.", ""))) @@ -3083,12 +3046,11 @@ class FlutterSmallApi(private val binaryMessenger: BinaryMessenger) { channel.send(listOf(msgArg)) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else if (it[0] == null) { callback( Result.failure( - CoreTestsError( + FlutterError( "null-error", "Flutter api returned null value for non-null return value.", ""))) @@ -3108,12 +3070,11 @@ class FlutterSmallApi(private val binaryMessenger: BinaryMessenger) { channel.send(listOf(aStringArg)) { if (it is List<*>) { if (it.size > 1) { - callback( - Result.failure(CoreTestsError(it[0] as String, it[1] as String, it[2] as String?))) + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else if (it[0] == null) { callback( Result.failure( - CoreTestsError( + FlutterError( "null-error", "Flutter api returned null value for non-null return value.", ""))) diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt index c14f7129e40d..8b4e8bc4fb18 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt @@ -39,7 +39,7 @@ class TestPlugin : FlutterPlugin, HostIntegrationCoreApi { } override fun throwFlutterError(): Any? { - throw CoreTestsError("code", "message", "details") + throw FlutterError("code", "message", "details") } override fun echoInt(anInt: Long): Long { @@ -170,7 +170,7 @@ class TestPlugin : FlutterPlugin, HostIntegrationCoreApi { } override fun throwAsyncFlutterError(callback: (Result) -> Unit) { - callback(Result.failure(CoreTestsError("code", "message", "details"))) + callback(Result.failure(FlutterError("code", "message", "details"))) } override fun echoAsyncAllTypes(everything: AllTypes, callback: (Result) -> Unit) { diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml index 9693bac54588..29c2b26595e3 100644 --- a/packages/pigeon/pubspec.yaml +++ b/packages/pigeon/pubspec.yaml @@ -2,7 +2,7 @@ name: pigeon description: Code generator tool to make communication between Flutter and the host platform type-safe and easier. repository: https://github.com/flutter/packages/tree/main/packages/pigeon issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+pigeon%22 -version: 17.0.0 # This must match the version in lib/generator_tools.dart +version: 17.1.0 # This must match the version in lib/generator_tools.dart environment: sdk: ^3.1.0 diff --git a/packages/pigeon/tool/shared/generation.dart b/packages/pigeon/tool/shared/generation.dart index 9915e26aa57d..68bcc10d6163 100644 --- a/packages/pigeon/tool/shared/generation.dart +++ b/packages/pigeon/tool/shared/generation.dart @@ -81,6 +81,13 @@ Future generateTestPigeons({required String baseDir}) async { final Set skipLanguages = _unsupportedFiles[input] ?? {}; + final bool kotlinErrorClassGenerationTestFiles = + input == 'core_tests' || input == 'background_platform_channels'; + + final String kotlinErrorName = kotlinErrorClassGenerationTestFiles + ? 'FlutterError' + : '${pascalCaseName}Error'; + // Generate the default language test plugin output. int generateCode = await runPigeon( input: './pigeons/$input.dart', @@ -90,7 +97,8 @@ Future generateTestPigeons({required String baseDir}) async { ? null : '$outputBase/android/src/main/kotlin/com/example/test_plugin/$pascalCaseName.gen.kt', kotlinPackage: 'com.example.test_plugin', - kotlinErrorClassName: '${pascalCaseName}Error', + kotlinErrorClassName: kotlinErrorName, + kotlinIncludeErrorClass: input != 'core_tests', // iOS swiftOut: skipLanguages.contains(GeneratorLanguage.swift) ? null @@ -176,6 +184,7 @@ Future runPigeon({ String? kotlinOut, String? kotlinPackage, String? kotlinErrorClassName, + bool kotlinIncludeErrorClass = true, String? swiftOut, String? cppHeaderOut, String? cppSourceOut, @@ -217,7 +226,10 @@ Future runPigeon({ javaOptions: JavaOptions(package: javaPackage), kotlinOut: kotlinOut, kotlinOptions: KotlinOptions( - package: kotlinPackage, errorClassName: kotlinErrorClassName), + package: kotlinPackage, + errorClassName: kotlinErrorClassName, + includeErrorClass: kotlinIncludeErrorClass, + ), objcHeaderOut: objcHeaderOut, objcSourceOut: objcSourceOut, objcOptions: ObjcOptions(prefix: objcPrefix), From 5ff00b2d611b04aec34be0266a0b3810ec51fc5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Sharma?= <737941+loic-sharma@users.noreply.github.com> Date: Tue, 27 Feb 2024 16:06:06 -0800 Subject: [PATCH 034/126] [pigeon] Remove heap allocation in generated C++ code (#6196) Please let me know if I goofed anything, this is my first time contributing to Pigeon! Addresses https://github.com/flutter/flutter/issues/144042 --- packages/pigeon/CHANGELOG.md | 4 + packages/pigeon/example/app/README.md | 3 +- .../example/app/windows/runner/messages.g.cpp | 30 +- packages/pigeon/lib/cpp_generator.dart | 13 +- packages/pigeon/lib/generator_tools.dart | 2 +- .../example/windows/flutter/CMakeLists.txt | 7 +- .../windows/pigeon/core_tests.gen.cpp | 969 ++++++++---------- packages/pigeon/pubspec.yaml | 2 +- packages/pigeon/test/cpp_generator_test.dart | 47 + 9 files changed, 529 insertions(+), 548 deletions(-) diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md index dd5c01fe48c9..6e7b64d9829f 100644 --- a/packages/pigeon/CHANGELOG.md +++ b/packages/pigeon/CHANGELOG.md @@ -1,3 +1,7 @@ +## 17.1.1 + +* Removes heap allocation in generated C++ code. + ## 17.1.0 * [kotlin] Adds `includeErrorClass` to `KotlinOptions`. diff --git a/packages/pigeon/example/app/README.md b/packages/pigeon/example/app/README.md index 9ac1a473e203..9bc223a17ab0 100644 --- a/packages/pigeon/example/app/README.md +++ b/packages/pigeon/example/app/README.md @@ -5,5 +5,6 @@ application, rather than in a plugin. To update the generated code, run: ```sh -dart run pigeon --input pigeons/messages.dart +cd ../.. +dart tool/generate.dart ``` diff --git a/packages/pigeon/example/app/windows/runner/messages.g.cpp b/packages/pigeon/example/app/windows/runner/messages.g.cpp index 4541b9d4f369..97d11da0bdf8 100644 --- a/packages/pigeon/example/app/windows/runner/messages.g.cpp +++ b/packages/pigeon/example/app/windows/runner/messages.g.cpp @@ -141,13 +141,12 @@ const flutter::StandardMessageCodec& ExampleHostApi::GetCodec() { void ExampleHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, ExampleHostApi* api) { { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_example_package.ExampleHostApi." - "getHostLanguage", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_example_package." + "ExampleHostApi.getHostLanguage", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -164,16 +163,16 @@ void ExampleHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_example_package.ExampleHostApi.add", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -203,16 +202,16 @@ void ExampleHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_example_package.ExampleHostApi.sendMessage", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -239,7 +238,7 @@ void ExampleHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } } @@ -273,12 +272,11 @@ void MessageFlutterApi::FlutterMethod( const std::string channel_name = "dev.flutter.pigeon.pigeon_example_package.MessageFlutterApi." "flutterMethod"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ a_string_arg ? EncodableValue(*a_string_arg) : EncodableValue(), }); - channel->Send( + channel.Send( encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)]( const uint8_t* reply, size_t reply_size) { diff --git a/packages/pigeon/lib/cpp_generator.dart b/packages/pigeon/lib/cpp_generator.dart index 419695b4ed2b..c5e3a1c053af 100644 --- a/packages/pigeon/lib/cpp_generator.dart +++ b/packages/pigeon/lib/cpp_generator.dart @@ -871,11 +871,9 @@ class CppSourceGenerator extends StructuredGenerator { scope: api.name, returnType: _voidType, parameters: parameters, body: () { - const String channel = 'channel'; indent.writeln( 'const std::string channel_name = "${makeChannelName(api, func, dartPackageName)}";'); - indent.writeln( - 'auto channel = std::make_unique>(binary_messenger_, ' + indent.writeln('BasicMessageChannel<> channel(binary_messenger_, ' 'channel_name, &GetCodec());'); // Convert arguments to EncodableValue versions. @@ -894,7 +892,7 @@ class CppSourceGenerator extends StructuredGenerator { }); } - indent.write('$channel->Send($argumentListVariableName, ' + indent.write('channel.Send($argumentListVariableName, ' // ignore: missing_whitespace_between_adjacent_strings '[channel_name, on_success = std::move(on_success), on_error = std::move(on_error)]' '(const uint8_t* reply, size_t reply_size) '); @@ -972,12 +970,11 @@ class CppSourceGenerator extends StructuredGenerator { final String channelName = makeChannelName(api, method, dartPackageName); indent.writeScoped('{', '}', () { - indent.writeln( - 'auto channel = std::make_unique>(binary_messenger, ' + indent.writeln('BasicMessageChannel<> channel(binary_messenger, ' '"$channelName", &GetCodec());'); indent.writeScoped('if (api != nullptr) {', '} else {', () { indent.write( - 'channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) '); + 'channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) '); indent.addScoped('{', '});', () { indent.writeScoped('try {', '}', () { final List methodArgument = []; @@ -1054,7 +1051,7 @@ class CppSourceGenerator extends StructuredGenerator { }); }); indent.addScoped(null, '}', () { - indent.writeln('channel->SetMessageHandler(nullptr);'); + indent.writeln('channel.SetMessageHandler(nullptr);'); }); }); } diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart index 54e980eb34a0..00ce3fede820 100644 --- a/packages/pigeon/lib/generator_tools.dart +++ b/packages/pigeon/lib/generator_tools.dart @@ -13,7 +13,7 @@ import 'ast.dart'; /// The current version of pigeon. /// /// This must match the version in pubspec.yaml. -const String pigeonVersion = '17.1.0'; +const String pigeonVersion = '17.1.1'; /// Read all the content from [stdin] to a String. String readStdin() { diff --git a/packages/pigeon/platform_tests/test_plugin/example/windows/flutter/CMakeLists.txt b/packages/pigeon/platform_tests/test_plugin/example/windows/flutter/CMakeLists.txt index 930d2071a324..903f4899d6fc 100644 --- a/packages/pigeon/platform_tests/test_plugin/example/windows/flutter/CMakeLists.txt +++ b/packages/pigeon/platform_tests/test_plugin/example/windows/flutter/CMakeLists.txt @@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -92,7 +97,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.cpp b/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.cpp index 2ca7340b0ceb..eee8003a7445 100644 --- a/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.cpp +++ b/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.cpp @@ -743,13 +743,12 @@ const flutter::StandardMessageCodec& HostIntegrationCoreApi::GetCodec() { void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, HostIntegrationCoreApi* api) { { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "noop", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.noop", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -766,17 +765,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoAllTypes", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoAllTypes", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -802,17 +800,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "throwError", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.throwError", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -835,17 +832,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "throwErrorFromVoid", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.throwErrorFromVoid", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -862,17 +858,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "throwFlutterError", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.throwFlutterError", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -896,17 +891,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoInt", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoInt", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -930,17 +924,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoDouble", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoDouble", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -965,17 +958,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoBool", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoBool", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -999,17 +991,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoString", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoString", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1034,17 +1025,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoUint8List", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoUint8List", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1070,17 +1060,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoObject", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoObject", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1104,17 +1093,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoList", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoList", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1139,17 +1127,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoMap", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoMap", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1174,17 +1161,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoClassWrapper", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoClassWrapper", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1211,17 +1197,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoEnum", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoEnum", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1247,17 +1232,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "echoNamedDefaultString", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1283,17 +1268,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "echoOptionalDefaultDouble", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1319,17 +1304,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoRequiredInt", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoRequiredInt", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1353,17 +1337,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoAllNullableTypes", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoAllNullableTypes", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1393,17 +1376,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "extractNestedNullableString", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1435,17 +1418,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "createNestedNullableString", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1468,17 +1451,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "sendMultipleNullableTypes", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1514,17 +1497,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoNullableInt", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoNullableInt", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1558,17 +1540,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoNullableDouble", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoNullableDouble", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1596,17 +1577,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoNullableBool", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoNullableBool", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1634,17 +1614,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoNullableString", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoNullableString", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1672,17 +1651,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "echoNullableUint8List", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1711,17 +1690,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoNullableObject", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoNullableObject", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1749,17 +1727,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoNullableList", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoNullableList", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1787,17 +1764,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoNullableMap", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoNullableMap", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1825,17 +1801,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoNullableEnum", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoNullableEnum", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1870,17 +1845,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "echoOptionalNullableInt", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1914,17 +1889,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "echoNamedNullableString", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1952,17 +1927,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "noopAsync", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.noopAsync", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -1980,17 +1954,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoAsyncInt", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoAsyncInt", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2016,17 +1989,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoAsyncDouble", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoAsyncDouble", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2054,17 +2026,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoAsyncBool", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoAsyncBool", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2090,17 +2061,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoAsyncString", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoAsyncString", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2128,17 +2098,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoAsyncUint8List", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoAsyncUint8List", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2167,17 +2136,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoAsyncObject", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoAsyncObject", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2204,17 +2172,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoAsyncList", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoAsyncList", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2242,17 +2209,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoAsyncMap", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoAsyncMap", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2280,17 +2246,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoAsyncEnum", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoAsyncEnum", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2318,17 +2283,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "throwAsyncError", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.throwAsyncError", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2353,17 +2317,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "throwAsyncErrorFromVoid", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2382,17 +2346,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "throwAsyncFlutterError", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2417,17 +2381,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoAsyncAllTypes", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoAsyncAllTypes", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2455,17 +2418,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "echoAsyncNullableAllNullableTypes", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2497,17 +2460,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoAsyncNullableInt", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoAsyncNullableInt", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2541,17 +2503,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "echoAsyncNullableDouble", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2581,17 +2543,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "echoAsyncNullableBool", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2619,17 +2581,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "echoAsyncNullableString", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2659,17 +2621,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "echoAsyncNullableUint8List", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2700,17 +2662,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "echoAsyncNullableObject", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2739,17 +2701,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "echoAsyncNullableList", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2779,17 +2741,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "echoAsyncNullableMap", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.echoAsyncNullableMap", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2819,17 +2780,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "echoAsyncNullableEnum", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2866,17 +2827,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "callFlutterNoop", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.callFlutterNoop", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2895,17 +2855,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "callFlutterThrowError", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2930,17 +2890,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "callFlutterThrowErrorFromVoid", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2959,17 +2919,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "callFlutterEchoAllTypes", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -2997,17 +2957,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "callFlutterEchoAllNullableTypes", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -3039,17 +2999,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "callFlutterSendMultipleNullableTypes", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -3087,17 +3047,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "callFlutterEchoBool", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.callFlutterEchoBool", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -3124,17 +3083,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "callFlutterEchoInt", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.callFlutterEchoInt", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -3161,17 +3119,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "callFlutterEchoDouble", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -3199,17 +3157,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "callFlutterEchoString", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -3237,17 +3195,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "callFlutterEchoUint8List", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -3275,17 +3233,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "callFlutterEchoList", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.callFlutterEchoList", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -3313,17 +3270,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "callFlutterEchoMap", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.callFlutterEchoMap", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -3351,17 +3307,16 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( - binary_messenger, - "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." - "callFlutterEchoEnum", - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.callFlutterEchoEnum", + &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -3389,17 +3344,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "callFlutterEchoNullableBool", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -3427,17 +3382,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "callFlutterEchoNullableInt", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -3471,17 +3426,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "callFlutterEchoNullableDouble", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -3511,17 +3466,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "callFlutterEchoNullableString", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -3551,17 +3506,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "callFlutterEchoNullableUint8List", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -3592,17 +3547,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "callFlutterEchoNullableList", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -3632,17 +3587,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "callFlutterEchoNullableMap", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -3672,17 +3627,17 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "callFlutterEchoNullableEnum", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -3719,7 +3674,7 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } } @@ -3815,10 +3770,9 @@ void FlutterIntegrationCoreApi::Noop( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." "noop"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(); - channel->Send( + channel.Send( encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)]( const uint8_t* reply, size_t reply_size) { @@ -3848,10 +3802,9 @@ void FlutterIntegrationCoreApi::ThrowError( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." "throwError"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(); - channel->Send( + channel.Send( encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)]( const uint8_t* reply, size_t reply_size) { @@ -3882,10 +3835,9 @@ void FlutterIntegrationCoreApi::ThrowErrorFromVoid( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." "throwErrorFromVoid"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(); - channel->Send( + channel.Send( encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)]( const uint8_t* reply, size_t reply_size) { @@ -3916,12 +3868,11 @@ void FlutterIntegrationCoreApi::EchoAllTypes( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." "echoAllTypes"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ CustomEncodableValue(everything_arg), }); - channel->Send( + channel.Send( encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)]( const uint8_t* reply, size_t reply_size) { @@ -3954,12 +3905,11 @@ void FlutterIntegrationCoreApi::EchoAllNullableTypes( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." "echoAllNullableTypes"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ everything_arg ? CustomEncodableValue(*everything_arg) : EncodableValue(), }); - channel->Send( + channel.Send( encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)]( const uint8_t* reply, size_t reply_size) { @@ -3993,8 +3943,7 @@ void FlutterIntegrationCoreApi::SendMultipleNullableTypes( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." "sendMultipleNullableTypes"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ a_nullable_bool_arg ? EncodableValue(*a_nullable_bool_arg) : EncodableValue(), @@ -4003,7 +3952,7 @@ void FlutterIntegrationCoreApi::SendMultipleNullableTypes( a_nullable_string_arg ? EncodableValue(*a_nullable_string_arg) : EncodableValue(), }); - channel->Send( + channel.Send( encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)]( const uint8_t* reply, size_t reply_size) { @@ -4035,12 +3984,11 @@ void FlutterIntegrationCoreApi::EchoBool( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." "echoBool"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ EncodableValue(a_bool_arg), }); - channel->Send( + channel.Send( encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)]( const uint8_t* reply, size_t reply_size) { @@ -4071,12 +4019,11 @@ void FlutterIntegrationCoreApi::EchoInt( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." "echoInt"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ EncodableValue(an_int_arg), }); - channel->Send( + channel.Send( encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)]( const uint8_t* reply, size_t reply_size) { @@ -4107,16 +4054,15 @@ void FlutterIntegrationCoreApi::EchoDouble( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." "echoDouble"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ EncodableValue(a_double_arg), }); - channel->Send(encoded_api_arguments, [channel_name, - on_success = std::move(on_success), - on_error = std::move(on_error)]( - const uint8_t* reply, - size_t reply_size) { + channel.Send(encoded_api_arguments, [channel_name, + on_success = std::move(on_success), + on_error = std::move(on_error)]( + const uint8_t* reply, + size_t reply_size) { std::unique_ptr response = GetCodec().DecodeMessage(reply, reply_size); const auto& encodable_return_value = *response; @@ -4144,12 +4090,11 @@ void FlutterIntegrationCoreApi::EchoString( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." "echoString"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ EncodableValue(a_string_arg), }); - channel->Send( + channel.Send( encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)]( const uint8_t* reply, size_t reply_size) { @@ -4182,12 +4127,11 @@ void FlutterIntegrationCoreApi::EchoUint8List( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." "echoUint8List"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ EncodableValue(a_list_arg), }); - channel->Send( + channel.Send( encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)]( const uint8_t* reply, size_t reply_size) { @@ -4220,12 +4164,11 @@ void FlutterIntegrationCoreApi::EchoList( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." "echoList"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ EncodableValue(a_list_arg), }); - channel->Send( + channel.Send( encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)]( const uint8_t* reply, size_t reply_size) { @@ -4258,12 +4201,11 @@ void FlutterIntegrationCoreApi::EchoMap( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." "echoMap"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ EncodableValue(a_map_arg), }); - channel->Send( + channel.Send( encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)]( const uint8_t* reply, size_t reply_size) { @@ -4295,12 +4237,11 @@ void FlutterIntegrationCoreApi::EchoEnum( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." "echoEnum"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ EncodableValue((int)an_enum_arg), }); - channel->Send( + channel.Send( encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)]( const uint8_t* reply, size_t reply_size) { @@ -4332,16 +4273,15 @@ void FlutterIntegrationCoreApi::EchoNullableBool( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." "echoNullableBool"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ a_bool_arg ? EncodableValue(*a_bool_arg) : EncodableValue(), }); - channel->Send(encoded_api_arguments, [channel_name, - on_success = std::move(on_success), - on_error = std::move(on_error)]( - const uint8_t* reply, - size_t reply_size) { + channel.Send(encoded_api_arguments, [channel_name, + on_success = std::move(on_success), + on_error = std::move(on_error)]( + const uint8_t* reply, + size_t reply_size) { std::unique_ptr response = GetCodec().DecodeMessage(reply, reply_size); const auto& encodable_return_value = *response; @@ -4368,16 +4308,15 @@ void FlutterIntegrationCoreApi::EchoNullableInt( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." "echoNullableInt"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ an_int_arg ? EncodableValue(*an_int_arg) : EncodableValue(), }); - channel->Send(encoded_api_arguments, [channel_name, - on_success = std::move(on_success), - on_error = std::move(on_error)]( - const uint8_t* reply, - size_t reply_size) { + channel.Send(encoded_api_arguments, [channel_name, + on_success = std::move(on_success), + on_error = std::move(on_error)]( + const uint8_t* reply, + size_t reply_size) { std::unique_ptr response = GetCodec().DecodeMessage(reply, reply_size); const auto& encodable_return_value = *response; @@ -4409,12 +4348,11 @@ void FlutterIntegrationCoreApi::EchoNullableDouble( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." "echoNullableDouble"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ a_double_arg ? EncodableValue(*a_double_arg) : EncodableValue(), }); - channel->Send( + channel.Send( encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)]( const uint8_t* reply, size_t reply_size) { @@ -4447,12 +4385,11 @@ void FlutterIntegrationCoreApi::EchoNullableString( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." "echoNullableString"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ a_string_arg ? EncodableValue(*a_string_arg) : EncodableValue(), }); - channel->Send( + channel.Send( encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)]( const uint8_t* reply, size_t reply_size) { @@ -4485,12 +4422,11 @@ void FlutterIntegrationCoreApi::EchoNullableUint8List( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." "echoNullableUint8List"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ a_list_arg ? EncodableValue(*a_list_arg) : EncodableValue(), }); - channel->Send( + channel.Send( encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)]( const uint8_t* reply, size_t reply_size) { @@ -4523,12 +4459,11 @@ void FlutterIntegrationCoreApi::EchoNullableList( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." "echoNullableList"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ a_list_arg ? EncodableValue(*a_list_arg) : EncodableValue(), }); - channel->Send( + channel.Send( encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)]( const uint8_t* reply, size_t reply_size) { @@ -4561,12 +4496,11 @@ void FlutterIntegrationCoreApi::EchoNullableMap( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." "echoNullableMap"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ a_map_arg ? EncodableValue(*a_map_arg) : EncodableValue(), }); - channel->Send( + channel.Send( encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)]( const uint8_t* reply, size_t reply_size) { @@ -4598,16 +4532,15 @@ void FlutterIntegrationCoreApi::EchoNullableEnum( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." "echoNullableEnum"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ an_enum_arg ? EncodableValue((int)(*an_enum_arg)) : EncodableValue(), }); - channel->Send(encoded_api_arguments, [channel_name, - on_success = std::move(on_success), - on_error = std::move(on_error)]( - const uint8_t* reply, - size_t reply_size) { + channel.Send(encoded_api_arguments, [channel_name, + on_success = std::move(on_success), + on_error = std::move(on_error)]( + const uint8_t* reply, + size_t reply_size) { std::unique_ptr response = GetCodec().DecodeMessage(reply, reply_size); const auto& encodable_return_value = *response; @@ -4640,10 +4573,9 @@ void FlutterIntegrationCoreApi::NoopAsync( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." "noopAsync"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(); - channel->Send( + channel.Send( encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)]( const uint8_t* reply, size_t reply_size) { @@ -4674,12 +4606,11 @@ void FlutterIntegrationCoreApi::EchoAsyncString( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." "echoAsyncString"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ EncodableValue(a_string_arg), }); - channel->Send( + channel.Send( encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)]( const uint8_t* reply, size_t reply_size) { @@ -4716,12 +4647,12 @@ const flutter::StandardMessageCodec& HostTrivialApi::GetCodec() { void HostTrivialApi::SetUp(flutter::BinaryMessenger* binary_messenger, HostTrivialApi* api) { { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostTrivialApi.noop", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -4738,7 +4669,7 @@ void HostTrivialApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } } @@ -4766,12 +4697,12 @@ const flutter::StandardMessageCodec& HostSmallApi::GetCodec() { void HostSmallApi::SetUp(flutter::BinaryMessenger* binary_messenger, HostSmallApi* api) { { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostSmallApi.echo", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -4798,16 +4729,16 @@ void HostSmallApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>( + BasicMessageChannel<> channel( binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests.HostSmallApi.voidVoid", &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler( + channel.SetMessageHandler( [api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -4825,7 +4756,7 @@ void HostSmallApi::SetUp(flutter::BinaryMessenger* binary_messenger, } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } } @@ -4888,12 +4819,11 @@ void FlutterSmallApi::EchoWrappedList( const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterSmallApi." "echoWrappedList"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ CustomEncodableValue(msg_arg), }); - channel->Send( + channel.Send( encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)]( const uint8_t* reply, size_t reply_size) { @@ -4925,12 +4855,11 @@ void FlutterSmallApi::EchoString( std::function&& on_error) { const std::string channel_name = "dev.flutter.pigeon.pigeon_integration_tests.FlutterSmallApi.echoString"; - auto channel = std::make_unique>( - binary_messenger_, channel_name, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ EncodableValue(a_string_arg), }); - channel->Send( + channel.Send( encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)]( const uint8_t* reply, size_t reply_size) { diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml index 29c2b26595e3..c6b29262ff35 100644 --- a/packages/pigeon/pubspec.yaml +++ b/packages/pigeon/pubspec.yaml @@ -2,7 +2,7 @@ name: pigeon description: Code generator tool to make communication between Flutter and the host platform type-safe and easier. repository: https://github.com/flutter/packages/tree/main/packages/pigeon issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+pigeon%22 -version: 17.1.0 # This must match the version in lib/generator_tools.dart +version: 17.1.1 # This must match the version in lib/generator_tools.dart environment: sdk: ^3.1.0 diff --git a/packages/pigeon/test/cpp_generator_test.dart b/packages/pigeon/test/cpp_generator_test.dart index 42aa28c5361a..9c1b9cbd091a 100644 --- a/packages/pigeon/test/cpp_generator_test.dart +++ b/packages/pigeon/test/cpp_generator_test.dart @@ -2114,4 +2114,51 @@ void main() { '"Unable to establish connection on channel: \'" + channel_name + "\'."')); expect(code, contains('on_error(CreateConnectionError(channel_name));')); }); + + test('stack allocates the message channel.', () { + final Root root = Root( + apis: [ + AstFlutterApi( + name: 'Api', + methods: [ + Method( + name: 'method', + location: ApiLocation.flutter, + returnType: const TypeDeclaration.voidDeclaration(), + parameters: [ + Parameter( + name: 'field', + type: const TypeDeclaration( + baseName: 'int', + isNullable: true, + ), + ), + ], + ) + ], + ) + ], + classes: [], + enums: [], + ); + final StringBuffer sink = StringBuffer(); + const CppGenerator generator = CppGenerator(); + final OutputFileOptions generatorOptions = + OutputFileOptions( + fileType: FileType.source, + languageOptions: const CppOptions(), + ); + generator.generate( + generatorOptions, + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final String code = sink.toString(); + expect( + code, + contains( + 'BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec());')); + expect(code, contains('channel.Send')); + }); } From 7cdcf30f8f9919dbf02865fe0c665782bf22360a Mon Sep 17 00:00:00 2001 From: Navaron Bracke Date: Wed, 28 Feb 2024 01:13:06 +0100 Subject: [PATCH 035/126] [google_maps_flutter_web] Migrate to package:web (#5254) This PR migrates `google_maps_flutter_web` from `dart:html` to `package:web`. The `google_maps` package does provide a beta version with support for `package:web`, which is currently what this PR is depending on. Before landing this change, we should change the dependency to the stable release version of that package. Fixes https://github.com/flutter/flutter/issues/137374 *If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].* N/A --- .../google_maps_flutter_web/CHANGELOG.md | 5 +- .../google_maps_controller_test.dart | 7 +- .../example/integration_test/marker_test.dart | 9 +- .../integration_test/markers_test.dart | 18 ++-- .../integration_test/overlay_test.dart | 35 ++++---- .../integration_test/overlays_test.dart | 8 +- .../example/integration_test/shapes_test.dart | 5 +- .../example/lib/main.dart | 5 +- .../example/pubspec.yaml | 7 +- .../lib/google_maps_flutter_web.dart | 7 +- .../lib/src/convert.dart | 63 +++++++++---- .../lib/src/dom_window_extension.dart | 89 +++++++++++++++++++ .../lib/src/google_maps_controller.dart | 8 +- .../lib/src/map_styler.dart | 40 +++++++++ .../lib/src/marker.dart | 8 +- .../lib/src/markers.dart | 11 +-- .../lib/src/overlay.dart | 20 +++-- .../lib/src/utils.dart | 10 +++ .../google_maps_flutter_web/pubspec.yaml | 9 +- 19 files changed, 276 insertions(+), 88 deletions(-) create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/lib/src/dom_window_extension.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/lib/src/map_styler.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_web/lib/src/utils.dart diff --git a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md index a8de9d0147e7..8476953537fe 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 0.5.5 -* Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. +* Migrates to `dart:js_interop` and `package:web` APIs. +* Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. ## 0.5.4+3 diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.dart index 51c36a55a478..5c3ccbdc8c76 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.dart @@ -3,13 +3,14 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:html' as html; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps/google_maps.dart' as gmaps; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; +// ignore: implementation_imports +import 'package:google_maps_flutter_web/src/utils.dart'; import 'package:integration_test/integration_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; @@ -230,7 +231,7 @@ void main() { polygons = MockPolygonsController(); polylines = MockPolylinesController(); tileOverlays = MockTileOverlaysController(); - map = gmaps.GMap(html.DivElement()); + map = gmaps.GMap(createDivElement()); }); testWidgets('listens to map events', (WidgetTester tester) async { @@ -471,7 +472,7 @@ void main() { setUp(() { map = gmaps.GMap( - html.DivElement(), + createDivElement(), gmaps.MapOptions() ..zoom = 10 ..center = gmaps.LatLng(0, 0), diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart index 2e2d77b71dec..0b284cac7663 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/marker_test.dart @@ -3,11 +3,12 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:html' as html; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps/google_maps.dart' as gmaps; import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; +// ignore: implementation_imports +import 'package:google_maps_flutter_web/src/utils.dart'; import 'package:integration_test/integration_test.dart'; /// Test Markers @@ -123,7 +124,7 @@ void main() { testWidgets('showInfoWindow', (WidgetTester tester) async { final gmaps.InfoWindow infoWindow = gmaps.InfoWindow(); - final gmaps.GMap map = gmaps.GMap(html.DivElement()); + final gmaps.GMap map = gmaps.GMap(createDivElement()); marker.set('map', map); final MarkerController controller = MarkerController( marker: marker, @@ -138,7 +139,7 @@ void main() { testWidgets('hideInfoWindow', (WidgetTester tester) async { final gmaps.InfoWindow infoWindow = gmaps.InfoWindow(); - final gmaps.GMap map = gmaps.GMap(html.DivElement()); + final gmaps.GMap map = gmaps.GMap(createDivElement()); marker.set('map', map); final MarkerController controller = MarkerController( marker: marker, @@ -156,7 +157,7 @@ void main() { setUp(() { final gmaps.InfoWindow infoWindow = gmaps.InfoWindow(); - final gmaps.GMap map = gmaps.GMap(html.DivElement()); + final gmaps.GMap map = gmaps.GMap(createDivElement()); marker.set('map', map); controller = MarkerController(marker: marker, infoWindow: infoWindow); }); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/markers_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/markers_test.dart index 6a264064a3ca..d965435b3a55 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/markers_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/markers_test.dart @@ -4,7 +4,6 @@ import 'dart:async'; import 'dart:convert'; -import 'dart:html' as html; import 'dart:typed_data'; import 'dart:ui'; @@ -12,8 +11,11 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps/google_maps.dart' as gmaps; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; +// ignore: implementation_imports +import 'package:google_maps_flutter_web/src/utils.dart'; import 'package:http/http.dart' as http; import 'package:integration_test/integration_test.dart'; +import 'package:web/web.dart'; import 'resources/icon_image_base64.dart'; @@ -28,7 +30,7 @@ void main() { setUp(() { events = StreamController>(); controller = MarkersController(stream: events); - map = gmaps.GMap(html.DivElement()); + map = gmaps.GMap(createDivElement()); controller.bindToMap(123, map); }); @@ -274,11 +276,11 @@ void main() { controller.addMarkers(markers); expect(controller.markers.length, 1); - final html.HtmlElement? content = controller.markers[const MarkerId('1')] - ?.infoWindow?.content as html.HtmlElement?; - expect(content?.innerHtml, contains('title for test')); + final HTMLElement? content = controller + .markers[const MarkerId('1')]?.infoWindow?.content as HTMLElement?; + expect(content?.innerHTML, contains('title for test')); expect( - content?.innerHtml, + content?.innerHTML, contains( 'Go to Google >>>', )); @@ -299,8 +301,8 @@ void main() { controller.addMarkers(markers); expect(controller.markers.length, 1); - final html.HtmlElement? content = controller.markers[const MarkerId('1')] - ?.infoWindow?.content as html.HtmlElement?; + final HTMLElement? content = controller + .markers[const MarkerId('1')]?.infoWindow?.content as HTMLElement?; content?.click(); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/overlay_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/overlay_test.dart index 29f902f7f1e0..0724da211275 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/overlay_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/overlay_test.dart @@ -3,13 +3,14 @@ // found in the LICENSE file. import 'dart:convert'; -import 'dart:html' as html; +import 'dart:js_interop'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps/google_maps.dart' as gmaps; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; import 'package:integration_test/integration_test.dart'; +import 'package:web/web.dart'; import 'resources/tile16_base64.dart'; @@ -43,8 +44,10 @@ void main() { final gmaps.Size size = controller.gmMapType.tileSize!; expect(size.width, TileOverlayController.logicalTileSize); expect(size.height, TileOverlayController.logicalTileSize); - expect(controller.gmMapType.getTile!(gmaps.Point(0, 0), 0, html.document), - null); + expect( + controller.gmMapType.getTile!(gmaps.Point(0, 0), 0, document), + null, + ); }); testWidgets('produces image tiles', (WidgetTester tester) async { @@ -55,16 +58,16 @@ void main() { ), ); - final html.ImageElement img = - controller.gmMapType.getTile!(gmaps.Point(0, 0), 0, html.document)! - as html.ImageElement; + final HTMLImageElement img = + controller.gmMapType.getTile!(gmaps.Point(0, 0), 0, document)! + as HTMLImageElement; expect(img.naturalWidth, 0); expect(img.naturalHeight, 0); expect(img.hidden, true); - // Wait until the image is fully loaded and decoded before re-reading its attributes. await img.onLoad.first; - await img.decode(); + + await img.decode().toDart; expect(img.hidden, false); expect(img.naturalWidth, 16); @@ -79,9 +82,9 @@ void main() { ), ); { - final html.ImageElement img = - controller.gmMapType.getTile!(gmaps.Point(0, 0), 0, html.document)! - as html.ImageElement; + final HTMLImageElement img = + controller.gmMapType.getTile!(gmaps.Point(0, 0), 0, document)! + as HTMLImageElement; await null; // let `getTile` `then` complete expect( img.src, @@ -95,10 +98,12 @@ void main() { tileProvider: TestTileProvider(), )); { - final html.ImageElement img = - controller.gmMapType.getTile!(gmaps.Point(0, 0), 0, html.document)! - as html.ImageElement; + final HTMLImageElement img = + controller.gmMapType.getTile!(gmaps.Point(0, 0), 0, document)! + as HTMLImageElement; + await img.onLoad.first; + expect( img.src, isNotEmpty, @@ -109,7 +114,7 @@ void main() { controller.update(const TileOverlay(tileOverlayId: id)); { expect( - controller.gmMapType.getTile!(gmaps.Point(0, 0), 0, html.document), + controller.gmMapType.getTile!(gmaps.Point(0, 0), 0, document), null, reason: 'Setting a null tileProvider should work.', ); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/overlays_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/overlays_test.dart index 8b6b34694f46..c680c97a993a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/overlays_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/overlays_test.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:html' as html; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -11,9 +10,12 @@ import 'package:google_maps/google_maps.dart' as gmaps; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:google_maps_flutter_web/google_maps_flutter_web.dart' hide GoogleMapController; +// ignore: implementation_imports +import 'package:google_maps_flutter_web/src/utils.dart'; import 'package:integration_test/integration_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; +import 'package:web/web.dart'; @GenerateNiceMocks(>[MockSpec()]) import 'overlays_test.mocks.dart'; @@ -38,13 +40,13 @@ void main() { /// 0. void probeTiles() { for (final gmaps.MapType? mapType in map.overlayMapTypes!.array!) { - mapType?.getTile!(gmaps.Point(0, 0), 0, html.document); + mapType?.getTile!(gmaps.Point(0, 0), 0, document); } } setUp(() { controller = TileOverlaysController(); - map = gmaps.GMap(html.DivElement()); + map = gmaps.GMap(createDivElement()); controller.googleMap = map; tileProviders = [ diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/shapes_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/shapes_test.dart index b9bc2d371c9b..ad3d3d4a8a4f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/shapes_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/shapes_test.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:html' as html; import 'dart:ui'; import 'package:flutter_test/flutter_test.dart'; @@ -11,6 +10,8 @@ import 'package:google_maps/google_maps.dart' as gmaps; import 'package:google_maps/google_maps_geometry.dart' as geometry; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; +// ignore: implementation_imports +import 'package:google_maps_flutter_web/src/utils.dart'; import 'package:integration_test/integration_test.dart'; // This value is used when comparing the results of @@ -25,7 +26,7 @@ void main() { late gmaps.GMap map; setUp(() { - map = gmaps.GMap(html.DivElement()); + map = gmaps.GMap(createDivElement()); }); group('CirclesController', () { diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/lib/main.dart index 6bd536109f7a..c32edb4b8ba8 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/lib/main.dart @@ -20,6 +20,9 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { @override Widget build(BuildContext context) { - return const Text('Testing... Look at the console output for results!'); + return const Directionality( + textDirection: TextDirection.ltr, + child: Text('Testing... Look at the console output for results!'), + ); } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/example/pubspec.yaml index 7b2c31b6f537..1a02cfa2a658 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/pubspec.yaml @@ -3,8 +3,8 @@ publish_to: none # Tests require flutter beta or greater to run. environment: - sdk: ^3.1.0 - flutter: ">=3.13.0" + sdk: ^3.3.0 + flutter: ">=3.19.0" dependencies: flutter: @@ -12,12 +12,13 @@ dependencies: google_maps_flutter_platform_interface: ^2.4.0 google_maps_flutter_web: path: ../ + web: ^0.5.0 dev_dependencies: build_runner: ^2.1.1 flutter_test: sdk: flutter - google_maps: ^6.1.0 + google_maps: ^7.1.0 google_maps_flutter: ^2.2.0 # Needed for projection_test.dart http: ">=0.13.0 <2.0.0" integration_test: diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart index d1ef18d3067e..fe44d41aa484 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/google_maps_flutter_web.dart @@ -6,8 +6,7 @@ library google_maps_flutter_web; import 'dart:async'; import 'dart:convert'; -import 'dart:html' hide VoidCallback; -import 'dart:js_util'; +import 'dart:js_interop'; import 'dart:ui_web' as ui_web; import 'package:collection/collection.dart'; @@ -20,10 +19,14 @@ import 'package:google_maps/google_maps.dart' as gmaps; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'package:sanitize_html/sanitize_html.dart'; import 'package:stream_transform/stream_transform.dart'; +import 'package:web/web.dart'; +import 'src/dom_window_extension.dart'; import 'src/google_maps_inspector_web.dart'; +import 'src/map_styler.dart'; import 'src/third_party/to_screen_location/to_screen_location.dart'; import 'src/types.dart'; +import 'src/utils.dart'; part 'src/circle.dart'; part 'src/circles.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart index 8ca6a75559f4..d8d77a7728a8 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart @@ -28,7 +28,7 @@ double _getCssOpacity(Color color) { // mapToolbarEnabled is unused in web, there's no "map toolbar" // myLocationButtonEnabled Widget not available in web yet, it needs to be built on top of the maps widget // See: https://developers.google.com/maps/documentation/javascript/examples/control-custom -// myLocationEnabled needs to be built through dart:html navigator.geolocation +// myLocationEnabled needs to be built through `navigator.geolocation` from package:web. // See: https://api.dart.dev/stable/2.8.4/dart-html/Geolocation-class.html // trafficEnabled is handled when creating the GMap object, since it needs to be added as a layer. // trackCameraPosition is just a boolean value that indicates if the map has an onCameraMove handler. @@ -139,10 +139,11 @@ List _mapStyles(String? mapStyleJson) { styles = (json.decode(mapStyleJson, reviver: (Object? key, Object? value) { if (value is Map && _isJsonMapStyle(value as Map)) { - List stylers = []; + List stylers = []; if (value['stylers'] != null) { stylers = (value['stylers']! as List) - .map((Object? e) => e != null ? jsify(e) : null) + .whereType>() + .map(MapStyler.fromJson) .toList(); } return gmaps.MapTypeStyle() @@ -203,30 +204,44 @@ gmaps.InfoWindowOptions? _infoWindowOptionsFromMarker(Marker marker) { // Add an outer wrapper to the contents of the infowindow, we need it to listen // to click events... - final HtmlElement container = DivElement() + final HTMLElement container = createDivElement() ..id = 'gmaps-marker-${marker.markerId.value}-infowindow'; if (markerTitle.isNotEmpty) { - final HtmlElement title = HeadingElement.h3() - ..className = 'infowindow-title' - ..innerText = markerTitle; - container.children.add(title); + final HTMLHeadingElement title = + (document.createElement('h3') as HTMLHeadingElement) + ..className = 'infowindow-title' + ..innerText = markerTitle; + container.appendChild(title); } if (markerSnippet.isNotEmpty) { - final HtmlElement snippet = DivElement() - ..className = 'infowindow-snippet' + final HTMLElement snippet = createDivElement() + ..className = 'infowindow-snippet'; + + // Firefox and Safari don't support Trusted Types yet. + // See https://developer.mozilla.org/en-US/docs/Web/API/TrustedTypePolicyFactory#browser_compatibility + if (window.nullableTrustedTypes != null) { + final GoogleMapsTrustedTypePolicy trustedTypePolicy = + window.nullableTrustedTypes!.getGoogleMapsTrustedTypesPolicy( + GoogleMapsTrustedTypePolicyOptions( + createHTML: (String html, JSAny? arguments) { + return sanitizeHtml(html); + }.toJS, + ), + ); + + snippet.trustedInnerHTML = + trustedTypePolicy.createHTML(markerSnippet, null); + } else { // `sanitizeHtml` is used to clean the (potential) user input from (potential) // XSS attacks through the contents of the marker InfoWindow. // See: https://pub.dev/documentation/sanitize_html/latest/sanitize_html/sanitizeHtml.html // See: b/159137885, b/159598165 - // The NodeTreeSanitizer.trusted just tells setInnerHtml to leave the output - // of `sanitizeHtml` untouched. // ignore: unsafe_html - ..setInnerHtml( - sanitizeHtml(markerSnippet), - treeSanitizer: NodeTreeSanitizer.trusted, - ); - container.children.add(snippet); + snippet.innerHTML = sanitizeHtml(markerSnippet); + } + + container.appendChild(snippet); } return gmaps.InfoWindowOptions() @@ -274,8 +289,18 @@ gmaps.Icon? _gmIconFromBitmapDescriptor(BitmapDescriptor bitmapDescriptor) { // Grab the bytes, and put them into a blob final List bytes = iconConfig[1]! as List; // Create a Blob from bytes, but let the browser figure out the encoding - final Blob blob = Blob([bytes]); - icon = gmaps.Icon()..url = Url.createObjectUrlFromBlob(blob); + final Blob blob; + + assert( + bytes is Uint8List, + 'The bytes for a BitmapDescriptor icon must be a Uint8List', + ); + + // TODO(ditman): Improve this conversion + // See https://github.com/dart-lang/web/issues/180 + blob = Blob([(bytes as Uint8List).toJS].toJS); + + icon = gmaps.Icon()..url = URL.createObjectURL(blob as JSObject); final gmaps.Size? size = _gmSizeFromIconConfig(iconConfig, 2); if (size != null) { diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/dom_window_extension.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/dom_window_extension.dart new file mode 100644 index 000000000000..f1b9ba34393d --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/dom_window_extension.dart @@ -0,0 +1,89 @@ +// 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 'dart:js_interop'; + +import 'package:web/web.dart' as web; + +/// This extension gives [web.Window] a nullable getter to the `trustedTypes` +/// property, which is used to check for feature support. +extension NullableTrustedTypesGetter on web.Window { + /// (Nullable) Bindings to window.trustedTypes. + /// + /// This may be null if the browser doesn't support the Trusted Types API. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API + @JS('trustedTypes') + external GoogleMapsTrustedTypePolicyFactory? get nullableTrustedTypes; +} + +// TODO(ditman): remove this extension type when we depend on package:web 0.5.1 +/// This extension exists as a stop gap until `package:web 0.5.1` is released. +/// That version provides the `TrustedTypes` API. +@JS('TrustedTypePolicyFactory') +extension type GoogleMapsTrustedTypePolicyFactory._(JSObject _) + implements JSObject { + /// The `TrustedTypePolicy` for Google Maps Flutter. + static GoogleMapsTrustedTypePolicy? _policy; + + @JS('createPolicy') + external GoogleMapsTrustedTypePolicy _createPolicy( + String policyName, [ + GoogleMapsTrustedTypePolicyOptions policyOptions, + ]); + + /// Get a new [GoogleMapsTrustedTypePolicy]. + /// + /// If a policy already exists, it will be returned. + /// Otherwise, a new policy is created. + /// + /// Because of we only cache one _policy, this method + /// specifically hardcoded to the GoogleMaps use case. + GoogleMapsTrustedTypePolicy getGoogleMapsTrustedTypesPolicy( + GoogleMapsTrustedTypePolicyOptions policyOptions, + ) { + const String policyName = 'google_maps_flutter_sanitize'; + _policy ??= _createPolicy(policyName, policyOptions); + + return _policy!; + } +} + +// TODO(ditman): remove this extension type when we depend on package:web 0.5.1 +/// This extension exists as a stop gap until `package:web 0.5.1` is released. +/// That version provides the `TrustedTypes` API. +@JS('TrustedTypePolicy') +extension type GoogleMapsTrustedTypePolicy._(JSObject _) implements JSObject { + /// Create a new `TrustedHTML` instance with the given [input] and [arguments]. + external GoogleMapsTrustedHTML createHTML( + String input, + JSAny? arguments, + ); +} + +// TODO(ditman): remove this extension type when we depend on package:web 0.5.1 +/// This extension exists as a stop gap until `package:web 0.5.1` is released. +/// That version provides the `TrustedTypes` API. +@JS('TrustedTypePolicyOptions') +extension type GoogleMapsTrustedTypePolicyOptions._(JSObject _) + implements JSObject { + /// Create a new `TrustedTypePolicyOptions` instance. + external factory GoogleMapsTrustedTypePolicyOptions({ + JSFunction createHTML, + }); +} + +// TODO(ditman): remove this extension type when we depend on package:web 0.5.1 +/// This extension exists as a stop gap until `package:web 0.5.1` is released. +/// That version provides the `TrustedTypes` API. +@JS('TrustedHTML') +extension type GoogleMapsTrustedHTML._(JSObject _) implements JSObject {} + +/// This extension provides a setter for the [web.HTMLElement] `innerHTML` property, +/// that accepts trusted HTML only. +extension TrustedInnerHTML on web.HTMLElement { + /// Set the inner HTML of this element to the given [trustedHTML]. + @JS('innerHTML') + external set trustedInnerHTML(GoogleMapsTrustedHTML trustedHTML); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart index 494029ce02c5..c577ba300214 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart @@ -7,7 +7,7 @@ part of '../google_maps_flutter_web.dart'; /// Type used when passing an override to the _createMap function. @visibleForTesting typedef DebugCreateMapFunction = gmaps.GMap Function( - HtmlElement div, gmaps.MapOptions options); + HTMLElement div, gmaps.MapOptions options); /// Encapsulates a [gmaps.GMap], its events, and where in the DOM it's rendered. class GoogleMapController { @@ -36,7 +36,7 @@ class GoogleMapController { // Register the view factory that will hold the `_div` that holds the map in the DOM. // The `_div` needs to be created outside of the ViewFactory (and cached!) so we can // use it to create the [gmaps.GMap] in the `init()` method of this class. - _div = DivElement() + _div = createDivElement() ..id = _getViewType(mapId) ..style.width = '100%' ..style.height = '100%'; @@ -74,7 +74,7 @@ class GoogleMapController { // The Flutter widget that contains the rendered Map. HtmlElementView? _widget; - late HtmlElement _div; + late HTMLElement _div; /// The Flutter widget that will contain the rendered Map. Used for caching. Widget? get widget { @@ -138,7 +138,7 @@ class GoogleMapController { DebugCreateMapFunction? _overrideCreateMap; - gmaps.GMap _createMap(HtmlElement div, gmaps.MapOptions options) { + gmaps.GMap _createMap(HTMLElement div, gmaps.MapOptions options) { if (_overrideCreateMap != null) { return _overrideCreateMap!(div, options); } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/map_styler.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/map_styler.dart new file mode 100644 index 000000000000..e6a871599cbd --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/map_styler.dart @@ -0,0 +1,40 @@ +// 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 'dart:js_interop'; + +/// The interop type for a Google Maps Map Styler. +/// +/// See: https://developers.google.com/maps/documentation/javascript/style-reference#stylers +@JS() +@staticInterop +@anonymous +extension type MapStyler._(JSObject _) implements JSObject { + /// Create a new [MapStyler] instance. + external factory MapStyler({ + String? hue, + num? lightness, + num? saturation, + num? gamma, + // ignore: non_constant_identifier_names + bool? invert_lightness, + String? visibility, + String? color, + int? weight, + }); + + /// Create a new [MapStyler] instance from the given [json]. + factory MapStyler.fromJson(Map json) { + return MapStyler( + hue: json['hue'] as String?, + lightness: json['lightness'] as num?, + saturation: json['saturation'] as num?, + gamma: json['gamma'] as num?, + invert_lightness: json['invert_lightness'] as bool?, + visibility: json['visibility'] as String?, + color: json['color'] as String?, + weight: json['weight'] as int?, + ); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart index 77258504ef67..c1b0772a1394 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/marker.dart @@ -69,12 +69,12 @@ class MarkerController { /// This cannot be called after [remove]. void update( gmaps.MarkerOptions options, { - HtmlElement? newInfoWindowContent, + HTMLElement? newInfoWindowContent, }) { assert(_marker != null, 'Cannot `update` Marker after calling `remove`.'); _marker!.options = options; if (_infoWindow != null && newInfoWindowContent != null) { - _infoWindow!.content = newInfoWindowContent; + _infoWindow.content = newInfoWindowContent; } } @@ -94,7 +94,7 @@ class MarkerController { void hideInfoWindow() { assert(_marker != null, 'Cannot `hideInfoWindow` on a `remove`d Marker.'); if (_infoWindow != null) { - _infoWindow!.close(); + _infoWindow.close(); _infoWindowShown = false; } } @@ -105,7 +105,7 @@ class MarkerController { void showInfoWindow() { assert(_marker != null, 'Cannot `showInfoWindow` on a `remove`d Marker.'); if (_infoWindow != null) { - _infoWindow!.open(_marker!.map, _marker); + _infoWindow.open(_marker!.map, _marker); _infoWindowShown = true; } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart index 52a659874f45..26cb94f9ad28 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/markers.dart @@ -39,11 +39,12 @@ class MarkersController extends GeometryController { // Google Maps' JS SDK does not have a click event on the InfoWindow, so // we make one... if (infoWindowOptions.content != null && - infoWindowOptions.content is HtmlElement) { - final HtmlElement content = infoWindowOptions.content! as HtmlElement; - content.onClick.listen((_) { + infoWindowOptions.content is HTMLElement) { + final HTMLElement content = infoWindowOptions.content! as HTMLElement; + + content.onclick = (JSAny? _) { _onInfoWindowTap(marker.markerId); - }); + }.toJS; } } @@ -91,7 +92,7 @@ class MarkersController extends GeometryController { _infoWindowOptionsFromMarker(marker); markerController.update( markerOptions, - newInfoWindowContent: infoWindow?.content as HtmlElement?, + newInfoWindowContent: infoWindow?.content as HTMLElement?, ); } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/overlay.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/overlay.dart index 5dd092c67ee2..29823d00b9f3 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/overlay.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/overlay.dart @@ -33,7 +33,7 @@ class TileOverlayController { } /// Renders a Tile for gmaps; delegating to the configured [TileProvider]. - HtmlElement? _getTile( + HTMLElement? _getTile( gmaps.Point? tileCoord, num? zoom, Document? ownerDocument, @@ -42,10 +42,10 @@ class TileOverlayController { return null; } - final ImageElement img = - ownerDocument!.createElement('img') as ImageElement; + final HTMLImageElement img = + ownerDocument!.createElement('img') as HTMLImageElement; img.width = img.height = logicalTileSize; - img.hidden = true; + img.hidden = true.toJS; img.setAttribute('decoding', 'async'); _tileOverlay.tileProvider! @@ -55,12 +55,14 @@ class TileOverlayController { return; } // Using img lets us take advantage of native decoding. - final String src = Url.createObjectUrl(Blob([tile.data])); + final String src = URL.createObjectURL( + Blob([tile.data!.toJS].toJS) as JSObject, + ); img.src = src; - img.addEventListener('load', (_) { - img.hidden = false; - Url.revokeObjectUrl(src); - }); + img.onload = (JSAny? _) { + img.hidden = false.toJS; + URL.revokeObjectURL(src); + }.toJS; }); return img; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/utils.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/utils.dart new file mode 100644 index 000000000000..94bc0b6b90d5 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/utils.dart @@ -0,0 +1,10 @@ +// 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:web/web.dart'; + +/// Convenience method to create a new [HTMLDivElement] element. +HTMLDivElement createDivElement() { + return document.createElement('div') as HTMLDivElement; +} diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml index 2293fe78c14a..f78289d04fce 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -2,11 +2,11 @@ name: google_maps_flutter_web description: Web platform implementation of google_maps_flutter repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 0.5.4+3 +version: 0.5.5 environment: - sdk: ">=3.1.0 <4.0.0" - flutter: ">=3.13.0" + sdk: ^3.3.0 + flutter: ">=3.19.0" flutter: plugin: @@ -22,10 +22,11 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter - google_maps: ^6.1.0 + google_maps: ^7.1.0 google_maps_flutter_platform_interface: ^2.4.0 sanitize_html: ^2.0.0 stream_transform: ^2.0.0 + web: ^0.5.0 dev_dependencies: flutter_test: From 3214a678ee21e7928676149a865fc959f383e6e6 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 28 Feb 2024 07:43:33 -0500 Subject: [PATCH 036/126] [google_map_flutter] Add style to widget - platform impls (#6205) Platform implementations portion of https://github.com/flutter/packages/pull/6192 Adds handling of `MapConfiguration.style` and implements new `getStyleError` method. Part of https://github.com/flutter/flutter/issues/66207 --- .../google_maps_flutter_android/CHANGELOG.md | 4 +- .../flutter/plugins/googlemaps/Convert.java | 4 + .../plugins/googlemaps/GoogleMapBuilder.java | 8 ++ .../googlemaps/GoogleMapController.java | 48 +++++--- .../googlemaps/GoogleMapOptionsSink.java | 3 + .../integration_test/google_maps_tests.dart | 62 +++++++++- .../example/lib/example_google_map.dart | 16 ++- .../example/lib/map_ui.dart | 33 +++--- .../example/pubspec.yaml | 2 +- .../lib/src/google_maps_flutter_android.dart | 6 + .../google_maps_flutter_android/pubspec.yaml | 4 +- .../google_maps_flutter_ios/CHANGELOG.md | 5 + .../integration_test/google_maps_test.dart | 58 +++++++++- .../example/ios12/pubspec.yaml | 2 +- .../example/ios13/pubspec.yaml | 2 +- .../example/ios14/pubspec.yaml | 2 +- .../lib/example_google_map.dart | 16 ++- .../shared/maps_example_dart/lib/map_ui.dart | 33 +++--- .../shared/maps_example_dart/pubspec.yaml | 2 +- .../ios/Classes/GoogleMapController.m | 21 +++- .../lib/src/google_maps_flutter_ios.dart | 7 ++ .../google_maps_flutter_ios/pubspec.yaml | 4 +- .../google_maps_flutter_web/CHANGELOG.md | 6 +- .../google_maps_controller_test.dart | 106 +++++++++++++++++- .../google_maps_plugin_test.mocks.dart | 9 ++ .../example/pubspec.yaml | 2 +- .../lib/src/convert.dart | 1 + .../lib/src/google_maps_controller.dart | 43 ++++++- .../lib/src/google_maps_flutter_web.dart | 5 + .../google_maps_flutter_web/pubspec.yaml | 4 +- 30 files changed, 426 insertions(+), 92 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md index ba2011f39be2..a099bb5ef424 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md @@ -1,5 +1,7 @@ -## NEXT +## 2.7.0 +* Adds support for `MapConfiguration.style`. +* Adds support for `getStyleError`. * Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. * Updates compileSdk version to 34. diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/Convert.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/Convert.java index 78c7dc2428c6..b473f1ea17d8 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/Convert.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/Convert.java @@ -377,6 +377,10 @@ static void interpretGoogleMapOptions(Object o, GoogleMapOptionsSink sink) { if (buildingsEnabled != null) { sink.setBuildingsEnabled(toBoolean(buildingsEnabled)); } + final Object style = data.get("style"); + if (style != null) { + sink.setMapStyle(toString(style)); + } } /** Returns the dartMarkerId of the interpreted marker. */ diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapBuilder.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapBuilder.java index fa82b77743d2..dfbaf02b9d4a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapBuilder.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapBuilder.java @@ -6,6 +6,7 @@ import android.content.Context; import android.graphics.Rect; +import androidx.annotation.Nullable; import com.google.android.gms.maps.GoogleMapOptions; import com.google.android.gms.maps.model.CameraPosition; import com.google.android.gms.maps.model.LatLngBounds; @@ -27,6 +28,7 @@ class GoogleMapBuilder implements GoogleMapOptionsSink { private Object initialCircles; private List> initialTileOverlays; private Rect padding = new Rect(0, 0, 0, 0); + private @Nullable String style; GoogleMapController build( int id, @@ -48,6 +50,7 @@ GoogleMapController build( controller.setInitialCircles(initialCircles); controller.setPadding(padding.top, padding.left, padding.bottom, padding.right); controller.setInitialTileOverlays(initialTileOverlays); + controller.setMapStyle(style); return controller; } @@ -178,4 +181,9 @@ public void setInitialCircles(Object initialCircles) { public void setInitialTileOverlays(List> initialTileOverlays) { this.initialTileOverlays = initialTileOverlays; } + + @Override + public void setMapStyle(@Nullable String style) { + this.style = style; + } } diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java index 917d45218345..b4102471c4b2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java @@ -48,6 +48,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; /** Controller of a single GoogleMaps MapView instance. */ final class GoogleMapController @@ -87,6 +88,9 @@ final class GoogleMapController private List initialPolylines; private List initialCircles; private List> initialTileOverlays; + // Null except between initialization and onMapReady. + private @Nullable String initialMapStyle; + private @Nullable String lastStyleError; @VisibleForTesting List initialPadding; GoogleMapController( @@ -169,6 +173,10 @@ public void onMapReady(GoogleMap googleMap) { initialPadding.get(2), initialPadding.get(3)); } + if (initialMapStyle != null) { + updateMapStyle(initialMapStyle); + initialMapStyle = null; + } } // Returns the first TextureView found in the view hierarchy. @@ -459,26 +467,22 @@ public void onSnapshotReady(Bitmap bitmap) { } case "map#setStyle": { - boolean mapStyleSet; - if (call.arguments instanceof String) { - String mapStyle = (String) call.arguments; - if (mapStyle == null) { - mapStyleSet = googleMap.setMapStyle(null); - } else { - mapStyleSet = googleMap.setMapStyle(new MapStyleOptions(mapStyle)); - } - } else { - mapStyleSet = googleMap.setMapStyle(null); - } + Object arg = call.arguments; + final String style = arg instanceof String ? (String) arg : null; + final boolean mapStyleSet = updateMapStyle(style); ArrayList mapStyleResult = new ArrayList<>(2); mapStyleResult.add(mapStyleSet); if (!mapStyleSet) { - mapStyleResult.add( - "Unable to set the map style. Please check console logs for errors."); + mapStyleResult.add(lastStyleError); } result.success(mapStyleResult); break; } + case "map#getStyleError": + { + result.success(lastStyleError); + break; + } case "tileOverlays#update": { List> tileOverlaysToAdd = call.argument("tileOverlaysToAdd"); @@ -926,4 +930,22 @@ public void setTrafficEnabled(boolean trafficEnabled) { public void setBuildingsEnabled(boolean buildingsEnabled) { this.buildingsEnabled = buildingsEnabled; } + + public void setMapStyle(@Nullable String style) { + if (googleMap == null) { + initialMapStyle = style; + } else { + updateMapStyle(style); + } + } + + private boolean updateMapStyle(String style) { + // Dart passes an empty string to indicate that the style should be cleared. + final MapStyleOptions mapStyleOptions = + style == null || style.isEmpty() ? null : new MapStyleOptions(style); + final boolean set = Objects.requireNonNull(googleMap).setMapStyle(mapStyleOptions); + lastStyleError = + set ? null : "Unable to set the map style. Please check console logs for errors."; + return set; + } } diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapOptionsSink.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapOptionsSink.java index 17f0d970a4ef..95c550c92fd8 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapOptionsSink.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapOptionsSink.java @@ -4,6 +4,7 @@ package io.flutter.plugins.googlemaps; +import androidx.annotation.Nullable; import com.google.android.gms.maps.model.LatLngBounds; import java.util.List; import java.util.Map; @@ -55,4 +56,6 @@ interface GoogleMapOptionsSink { void setInitialCircles(Object initialCircles); void setInitialTileOverlays(List> initialTileOverlays); + + void setMapStyle(@Nullable String style); } diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_tests.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_tests.dart index 97e7dfa0a37e..e03fe8f8851b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_tests.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_tests.dart @@ -722,7 +722,8 @@ void googleMapsTests() { await controllerCompleter.future; const String mapStyle = '[{"elementType":"geometry","stylers":[{"color":"#242f3e"}]}]'; - await controller.setMapStyle(mapStyle); + await GoogleMapsFlutterPlatform.instance + .setMapStyle(mapStyle, mapId: controller.mapId); }); testWidgets('testSetMapStyle invalid Json String', @@ -746,10 +747,12 @@ void googleMapsTests() { await controllerCompleter.future; try { - await controller.setMapStyle('invalid_value'); + await GoogleMapsFlutterPlatform.instance + .setMapStyle('invalid_value', mapId: controller.mapId); fail('expected MapStyleException'); } on MapStyleException catch (e) { expect(e.cause, isNotNull); + expect(await controller.getStyleError(), isNotNull); } }); @@ -771,7 +774,8 @@ void googleMapsTests() { final ExampleGoogleMapController controller = await controllerCompleter.future; - await controller.setMapStyle(null); + await GoogleMapsFlutterPlatform.instance + .setMapStyle(null, mapId: controller.mapId); }); testWidgets('testGetLatLng', (WidgetTester tester) async { @@ -1211,6 +1215,58 @@ void googleMapsTests() { await mapIdCompleter.future; }, ); + + testWidgets('getStyleError reports last error', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer controllerCompleter = + Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + style: '[[[this is an invalid style', + onMapCreated: (ExampleGoogleMapController controller) { + controllerCompleter.complete(controller); + }, + ), + )); + + final ExampleGoogleMapController controller = + await controllerCompleter.future; + String? error = await controller.getStyleError(); + for (int i = 0; i < 1000 && error == null; i++) { + await Future.delayed(const Duration(milliseconds: 10)); + error = await controller.getStyleError(); + } + expect(error, isNotNull); + }); + + testWidgets('getStyleError returns null for a valid style', + (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer controllerCompleter = + Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + // An empty array is the simplest valid style. + style: '[]', + onMapCreated: (ExampleGoogleMapController controller) { + controllerCompleter.complete(controller); + }, + ), + )); + + final ExampleGoogleMapController controller = + await controllerCompleter.future; + final String? error = await controller.getStyleError(); + expect(error, isNull); + }); } class _DebugTileProvider implements TileProvider { diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/example_google_map.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/example_google_map.dart index f176b987e28b..0734731af7c3 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/example_google_map.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/example_google_map.dart @@ -144,12 +144,6 @@ class ExampleGoogleMapController { .moveCamera(cameraUpdate, mapId: mapId); } - /// Sets the styling of the base map. - Future setMapStyle(String? mapStyle) { - return GoogleMapsFlutterPlatform.instance - .setMapStyle(mapStyle, mapId: mapId); - } - /// Return [LatLngBounds] defining the region that is visible in a map. Future getVisibleRegion() { return GoogleMapsFlutterPlatform.instance.getVisibleRegion(mapId: mapId); @@ -195,6 +189,11 @@ class ExampleGoogleMapController { return GoogleMapsFlutterPlatform.instance.takeSnapshot(mapId: mapId); } + /// Returns the last style error, if any. + Future getStyleError() { + return GoogleMapsFlutterPlatform.instance.getStyleError(mapId: mapId); + } + /// Disposes of the platform resources void dispose() { GoogleMapsFlutterPlatform.instance.dispose(mapId: mapId); @@ -245,6 +244,7 @@ class ExampleGoogleMap extends StatefulWidget { this.onTap, this.onLongPress, this.cloudMapId, + this.style, }); /// Callback method for when the map is ready to be used. @@ -353,6 +353,9 @@ class ExampleGoogleMap extends StatefulWidget { /// for more details. final String? cloudMapId; + /// The locally configured style for the map. + final String? style; + /// Creates a [State] for this [ExampleGoogleMap]. @override State createState() => _ExampleGoogleMapState(); @@ -539,5 +542,6 @@ MapConfiguration _configurationFromMapWidget(ExampleGoogleMap map) { trafficEnabled: map.trafficEnabled, buildingsEnabled: map.buildingsEnabled, cloudMapId: map.cloudMapId, + style: map.style, ); } diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_ui.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_ui.dart index 311e2267aa0c..105676da9ed5 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_ui.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_ui.dart @@ -60,6 +60,7 @@ class MapUiBodyState extends State { bool _myLocationButtonEnabled = true; late ExampleGoogleMapController _controller; bool _nightMode = false; + String _mapStyle = ''; @override void initState() { @@ -244,27 +245,16 @@ class MapUiBodyState extends State { return rootBundle.loadString(path); } - void _setMapStyle(String mapStyle) { - setState(() { - _nightMode = true; - _controller.setMapStyle(mapStyle); - }); - } - - // Should only be called if _isMapCreated is true. Widget _nightModeToggler() { - assert(_isMapCreated); return TextButton( child: Text('${_nightMode ? 'disable' : 'enable'} night mode'), - onPressed: () { - if (_nightMode) { - setState(() { - _nightMode = false; - _controller.setMapStyle(null); - }); - } else { - _getFileData('assets/night_mode.json').then(_setMapStyle); - } + onPressed: () async { + _nightMode = !_nightMode; + final String style = + _nightMode ? await _getFileData('assets/night_mode.json') : ''; + setState(() { + _mapStyle = style; + }); }, ); } @@ -279,6 +269,7 @@ class MapUiBodyState extends State { cameraTargetBounds: _cameraTargetBounds, minMaxZoomPreference: _minMaxZoomPreference, mapType: _mapType, + style: _mapStyle, rotateGesturesEnabled: _rotateGesturesEnabled, scrollGesturesEnabled: _scrollGesturesEnabled, tiltGesturesEnabled: _tiltGesturesEnabled, @@ -353,5 +344,11 @@ class MapUiBodyState extends State { _controller = controller; _isMapCreated = true; }); + // Log any style errors to the console for debugging. + _controller.getStyleError().then((String? error) { + if (error != null) { + debugPrint(error); + } + }); } } diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_android/example/pubspec.yaml index f241ddc6c920..533ca53c2914 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/pubspec.yaml @@ -18,7 +18,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: ../ - google_maps_flutter_platform_interface: ^2.4.0 + google_maps_flutter_platform_interface: ^2.5.0 dev_dependencies: build_runner: ^2.1.10 diff --git a/packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_maps_flutter_android.dart b/packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_maps_flutter_android.dart index 4cb87722f375..d40d795d6a32 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_maps_flutter_android.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_maps_flutter_android.dart @@ -484,6 +484,11 @@ class GoogleMapsFlutterAndroid extends GoogleMapsFlutterPlatform { return _channel(mapId).invokeMethod('map#takeSnapshot'); } + @override + Future getStyleError({required int mapId}) { + return _channel(mapId).invokeMethod('map#getStyleError'); + } + /// Set [GoogleMapsFlutterPlatform] to use [AndroidViewSurface] to build the /// Google Maps widget. /// @@ -727,6 +732,7 @@ Map _jsonForMapConfiguration(MapConfiguration config) { if (config.buildingsEnabled != null) 'buildingsEnabled': config.buildingsEnabled!, if (config.cloudMapId != null) 'cloudMapId': config.cloudMapId!, + if (config.style != null) 'style': config.style!, }; } diff --git a/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml index 30b1644e313c..1f2a3be877c1 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_android description: Android implementation of the google_maps_flutter plugin. repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.6.2 +version: 2.7.0 environment: sdk: ^3.1.0 @@ -21,7 +21,7 @@ dependencies: flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.1 - google_maps_flutter_platform_interface: ^2.4.0 + google_maps_flutter_platform_interface: ^2.5.0 stream_transform: ^2.0.0 dev_dependencies: diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md index 4eae3cfc1f0f..6778f3b8e1ee 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.5.0 + +* Adds support for `MapConfiguration.style`. +* Adds support for `getStyleError`. + ## 2.4.2 * Fixes a bug in "takeSnapshot" function that incorrectly returns a blank image on iOS 17. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios12/integration_test/google_maps_test.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios12/integration_test/google_maps_test.dart index 9902456444d6..e49c451d08ee 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios12/integration_test/google_maps_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios12/integration_test/google_maps_test.dart @@ -583,7 +583,8 @@ void main() { await controllerCompleter.future; const String mapStyle = '[{"elementType":"geometry","stylers":[{"color":"#242f3e"}]}]'; - await controller.setMapStyle(mapStyle); + await GoogleMapsFlutterPlatform.instance + .setMapStyle(mapStyle, mapId: controller.mapId); }); testWidgets('testSetMapStyle invalid Json String', @@ -607,10 +608,12 @@ void main() { await controllerCompleter.future; try { - await controller.setMapStyle('invalid_value'); + await GoogleMapsFlutterPlatform.instance + .setMapStyle('invalid_value', mapId: controller.mapId); fail('expected MapStyleException'); } on MapStyleException catch (e) { expect(e.cause, isNotNull); + expect(await controller.getStyleError(), isNotNull); } }); @@ -632,7 +635,8 @@ void main() { final ExampleGoogleMapController controller = await controllerCompleter.future; - await controller.setMapStyle(null); + await GoogleMapsFlutterPlatform.instance + .setMapStyle(null, mapId: controller.mapId); }); testWidgets('testGetLatLng', (WidgetTester tester) async { @@ -1048,6 +1052,54 @@ void main() { ), )); }); + + testWidgets('getStyleError reports last error', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer controllerCompleter = + Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + style: '[[[this is an invalid style', + onMapCreated: (ExampleGoogleMapController controller) { + controllerCompleter.complete(controller); + }, + ), + )); + + final ExampleGoogleMapController controller = + await controllerCompleter.future; + final String? error = await controller.getStyleError(); + expect(error, isNotNull); + }); + + testWidgets('getStyleError returns null for a valid style', + (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer controllerCompleter = + Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + // An empty array is the simplest valid style. + style: '[]', + onMapCreated: (ExampleGoogleMapController controller) { + controllerCompleter.complete(controller); + }, + ), + )); + + final ExampleGoogleMapController controller = + await controllerCompleter.future; + final String? error = await controller.getStyleError(); + expect(error, isNull); + }); } class _DebugTileProvider implements TileProvider { diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios12/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios12/pubspec.yaml index e5e0cd2a4b1e..a0b172a65e1c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios12/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios12/pubspec.yaml @@ -18,7 +18,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: ../../ - google_maps_flutter_platform_interface: ^2.4.0 + google_maps_flutter_platform_interface: ^2.5.0 maps_example_dart: path: ../shared/maps_example_dart/ diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios13/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios13/pubspec.yaml index e5e0cd2a4b1e..a0b172a65e1c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios13/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios13/pubspec.yaml @@ -18,7 +18,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: ../../ - google_maps_flutter_platform_interface: ^2.4.0 + google_maps_flutter_platform_interface: ^2.5.0 maps_example_dart: path: ../shared/maps_example_dart/ diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/pubspec.yaml index e5e0cd2a4b1e..a0b172a65e1c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/pubspec.yaml @@ -18,7 +18,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: ../../ - google_maps_flutter_platform_interface: ^2.4.0 + google_maps_flutter_platform_interface: ^2.5.0 maps_example_dart: path: ../shared/maps_example_dart/ diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/lib/example_google_map.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/lib/example_google_map.dart index f176b987e28b..0734731af7c3 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/lib/example_google_map.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/lib/example_google_map.dart @@ -144,12 +144,6 @@ class ExampleGoogleMapController { .moveCamera(cameraUpdate, mapId: mapId); } - /// Sets the styling of the base map. - Future setMapStyle(String? mapStyle) { - return GoogleMapsFlutterPlatform.instance - .setMapStyle(mapStyle, mapId: mapId); - } - /// Return [LatLngBounds] defining the region that is visible in a map. Future getVisibleRegion() { return GoogleMapsFlutterPlatform.instance.getVisibleRegion(mapId: mapId); @@ -195,6 +189,11 @@ class ExampleGoogleMapController { return GoogleMapsFlutterPlatform.instance.takeSnapshot(mapId: mapId); } + /// Returns the last style error, if any. + Future getStyleError() { + return GoogleMapsFlutterPlatform.instance.getStyleError(mapId: mapId); + } + /// Disposes of the platform resources void dispose() { GoogleMapsFlutterPlatform.instance.dispose(mapId: mapId); @@ -245,6 +244,7 @@ class ExampleGoogleMap extends StatefulWidget { this.onTap, this.onLongPress, this.cloudMapId, + this.style, }); /// Callback method for when the map is ready to be used. @@ -353,6 +353,9 @@ class ExampleGoogleMap extends StatefulWidget { /// for more details. final String? cloudMapId; + /// The locally configured style for the map. + final String? style; + /// Creates a [State] for this [ExampleGoogleMap]. @override State createState() => _ExampleGoogleMapState(); @@ -539,5 +542,6 @@ MapConfiguration _configurationFromMapWidget(ExampleGoogleMap map) { trafficEnabled: map.trafficEnabled, buildingsEnabled: map.buildingsEnabled, cloudMapId: map.cloudMapId, + style: map.style, ); } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/lib/map_ui.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/lib/map_ui.dart index 311e2267aa0c..105676da9ed5 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/lib/map_ui.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/lib/map_ui.dart @@ -60,6 +60,7 @@ class MapUiBodyState extends State { bool _myLocationButtonEnabled = true; late ExampleGoogleMapController _controller; bool _nightMode = false; + String _mapStyle = ''; @override void initState() { @@ -244,27 +245,16 @@ class MapUiBodyState extends State { return rootBundle.loadString(path); } - void _setMapStyle(String mapStyle) { - setState(() { - _nightMode = true; - _controller.setMapStyle(mapStyle); - }); - } - - // Should only be called if _isMapCreated is true. Widget _nightModeToggler() { - assert(_isMapCreated); return TextButton( child: Text('${_nightMode ? 'disable' : 'enable'} night mode'), - onPressed: () { - if (_nightMode) { - setState(() { - _nightMode = false; - _controller.setMapStyle(null); - }); - } else { - _getFileData('assets/night_mode.json').then(_setMapStyle); - } + onPressed: () async { + _nightMode = !_nightMode; + final String style = + _nightMode ? await _getFileData('assets/night_mode.json') : ''; + setState(() { + _mapStyle = style; + }); }, ); } @@ -279,6 +269,7 @@ class MapUiBodyState extends State { cameraTargetBounds: _cameraTargetBounds, minMaxZoomPreference: _minMaxZoomPreference, mapType: _mapType, + style: _mapStyle, rotateGesturesEnabled: _rotateGesturesEnabled, scrollGesturesEnabled: _scrollGesturesEnabled, tiltGesturesEnabled: _tiltGesturesEnabled, @@ -353,5 +344,11 @@ class MapUiBodyState extends State { _controller = controller; _isMapCreated = true; }); + // Log any style errors to the console for debugging. + _controller.getStyleError().then((String? error) { + if (error != null) { + debugPrint(error); + } + }); } } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/pubspec.yaml index 38259978c902..77063ec00061 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/pubspec.yaml @@ -18,7 +18,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: ../../../ - google_maps_flutter_platform_interface: ^2.4.0 + google_maps_flutter_platform_interface: ^2.5.0 dev_dependencies: flutter_test: diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m index 06e7838d9f4d..0b9b09b7748c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m @@ -67,6 +67,11 @@ @interface FLTGoogleMapController () @property(nonatomic, strong) FLTPolylinesController *polylinesController; @property(nonatomic, strong) FLTCirclesController *circlesController; @property(nonatomic, strong) FLTTileOverlaysController *tileOverlaysController; +// The resulting error message, if any, from the last attempt to set the map style. +// This is used to provide access to errors after the fact, since the map style is generally set at +// creation time and there's no mechanism to return non-fatal error details during platform view +// initialization. +@property(nonatomic, copy) NSString *styleError; @end @@ -400,13 +405,15 @@ - (void)onMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { NSNumber *isBuildingsEnabled = @(self.mapView.buildingsEnabled); result(isBuildingsEnabled); } else if ([call.method isEqualToString:@"map#setStyle"]) { - NSString *mapStyle = [call arguments]; - NSString *error = [self setMapStyle:mapStyle]; - if (error == nil) { + id mapStyle = [call arguments]; + self.styleError = [self setMapStyle:(mapStyle == [NSNull null] ? nil : mapStyle)]; + if (self.styleError == nil) { result(@[ @(YES) ]); } else { - result(@[ @(NO), error ]); + result(@[ @(NO), self.styleError ]); } + } else if ([call.method isEqualToString:@"map#getStyleError"]) { + result(self.styleError); } else if ([call.method isEqualToString:@"map#getTileOverlayInfo"]) { NSString *rawTileOverlayId = call.arguments[@"tileOverlayId"]; result([self.tileOverlaysController tileOverlayInfoWithIdentifier:rawTileOverlayId]); @@ -506,7 +513,7 @@ - (void)setMyLocationButtonEnabled:(BOOL)enabled { } - (NSString *)setMapStyle:(NSString *)mapStyle { - if (mapStyle == (id)[NSNull null] || mapStyle.length == 0) { + if (mapStyle.length == 0) { self.mapView.mapStyle = nil; return nil; } @@ -658,6 +665,10 @@ - (void)interpretMapOptions:(NSDictionary *)data { if (myLocationButtonEnabled && myLocationButtonEnabled != (id)[NSNull null]) { [self setMyLocationButtonEnabled:[myLocationButtonEnabled boolValue]]; } + NSString *style = data[@"style"]; + if (style) { + self.styleError = [self setMapStyle:style]; + } } @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart index 642febcdd3b9..56f3cc5a0794 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart @@ -466,6 +466,12 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { return _channel(mapId).invokeMethod('map#takeSnapshot'); } + @override + Future getStyleError({required int mapId}) { + final MethodChannel channel = ensureChannelInitialized(mapId); + return channel.invokeMethod('map#getStyleError'); + } + Widget _buildView( int creationId, PlatformViewCreatedCallback onPlatformViewCreated, { @@ -618,6 +624,7 @@ Map _jsonForMapConfiguration(MapConfiguration config) { if (config.buildingsEnabled != null) 'buildingsEnabled': config.buildingsEnabled!, if (config.cloudMapId != null) 'cloudMapId': config.cloudMapId!, + if (config.style != null) 'style': config.style!, }; } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml index 502db892ac53..a90927475205 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_ios description: iOS implementation of the google_maps_flutter plugin. repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.4.2 +version: 2.5.0 environment: sdk: ^3.2.3 @@ -19,7 +19,7 @@ flutter: dependencies: flutter: sdk: flutter - google_maps_flutter_platform_interface: ^2.4.0 + google_maps_flutter_platform_interface: ^2.5.0 stream_transform: ^2.0.0 dev_dependencies: diff --git a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md index 8476953537fe..49ed230183c5 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md @@ -1,5 +1,9 @@ -## 0.5.5 +## 0.5.6 + +* Adds support for `MapConfiguration.style`. +* Adds support for `getStyleError`. +## 0.5.5 * Migrates to `dart:js_interop` and `package:web` APIs. * Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.dart index 5c3ccbdc8c76..aa7e0ae4fe41 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.dart @@ -357,11 +357,8 @@ void main() { }); group('Initialization options', () { - gmaps.MapOptions? capturedOptions; - setUp(() { - capturedOptions = null; - }); testWidgets('translates initial options', (WidgetTester tester) async { + gmaps.MapOptions? capturedOptions; controller = createController( mapConfiguration: const MapConfiguration( mapType: MapType.satellite, @@ -390,6 +387,7 @@ void main() { testWidgets('translates fortyFiveDegreeImageryEnabled option', (WidgetTester tester) async { + gmaps.MapOptions? capturedOptions; controller = createController( mapConfiguration: const MapConfiguration( scrollGesturesEnabled: false, @@ -410,6 +408,7 @@ void main() { testWidgets('translates webGestureHandling option', (WidgetTester tester) async { + gmaps.MapOptions? capturedOptions; controller = createController( mapConfiguration: const MapConfiguration( zoomGesturesEnabled: false, @@ -429,6 +428,7 @@ void main() { testWidgets('sets initial position when passed', (WidgetTester tester) async { + gmaps.MapOptions? capturedOptions; controller = createController( initialCameraPosition: const CameraPosition( target: LatLng(43.308, -5.6910), @@ -445,6 +445,81 @@ void main() { expect(capturedOptions!.zoom, 12); expect(capturedOptions!.center, isNotNull); }); + + testWidgets('translates style option', (WidgetTester tester) async { + gmaps.MapOptions? capturedOptions; + const String style = ''' +[{ + "featureType": "poi.park", + "elementType": "labels.text.fill", + "stylers": [{"color": "#6b9a76"}] +}]'''; + controller = createController( + mapConfiguration: const MapConfiguration(style: style)); + controller.debugSetOverrides( + createMap: (_, gmaps.MapOptions options) { + capturedOptions = options; + return map; + }); + + controller.init(); + + expect(capturedOptions, isNotNull); + expect(capturedOptions!.styles?.length, 1); + }); + + testWidgets('stores style errors for later querying', + (WidgetTester tester) async { + gmaps.MapOptions? capturedOptions; + controller = createController( + mapConfiguration: const MapConfiguration( + style: '[[invalid style', zoomControlsEnabled: true)); + controller.debugSetOverrides( + createMap: (_, gmaps.MapOptions options) { + capturedOptions = options; + return map; + }); + + controller.init(); + + expect(controller.lastStyleError, isNotNull); + // Style failures should not prevent other options from being set. + expect(capturedOptions, isNotNull); + expect(capturedOptions!.zoomControl, true); + }); + + testWidgets('setting invalid style leaves previous style', + (WidgetTester tester) async { + gmaps.MapOptions? initialCapturedOptions; + gmaps.MapOptions? updatedCapturedOptions; + const String style = ''' +[{ + "featureType": "poi.park", + "elementType": "labels.text.fill", + "stylers": [{"color": "#6b9a76"}] +}]'''; + controller = createController( + mapConfiguration: const MapConfiguration(style: style)); + controller.debugSetOverrides( + createMap: (_, gmaps.MapOptions options) { + initialCapturedOptions = options; + return map; + }, + setOptions: (gmaps.MapOptions options) { + updatedCapturedOptions = options; + }, + ); + + controller.init(); + controller.updateMapConfiguration( + const MapConfiguration(style: '[[invalid style')); + + expect(initialCapturedOptions, isNotNull); + expect(initialCapturedOptions!.styles?.length, 1); + // The styles should not have changed. + expect( + updatedCapturedOptions!.styles, initialCapturedOptions!.styles); + }); }); group('Traffic Layer', () { @@ -482,7 +557,7 @@ void main() { ..init(); }); - group('updateRawOptions', () { + group('updateMapConfiguration', () { testWidgets('can update `options`', (WidgetTester tester) async { controller.updateMapConfiguration(const MapConfiguration( mapType: MapType.satellite, @@ -506,6 +581,27 @@ void main() { expect(controller.trafficLayer, isNull); }); + + testWidgets('can update style', (WidgetTester tester) async { + const String style = ''' +[{ + "featureType": "poi.park", + "elementType": "labels.text.fill", + "stylers": [{"color": "#6b9a76"}] +}]'''; + controller + .updateMapConfiguration(const MapConfiguration(style: style)); + + expect(controller.styles.length, 1); + }); + + testWidgets('can update style', (WidgetTester tester) async { + controller.updateMapConfiguration( + const MapConfiguration(style: '[[[invalid style')); + + expect(controller.styles, isEmpty); + expect(controller.lastStyleError, isNotNull); + }); }); group('viewport getters', () { diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.mocks.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.mocks.dart index 76bf2e672a3e..3f84b40adc88 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.mocks.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.mocks.dart @@ -123,9 +123,17 @@ class MockGoogleMapController extends _i1.Mock returnValueForMissingStub: false, ) as bool); + @override + List<_i5.MapTypeStyle> get styles => (super.noSuchMethod( + Invocation.getter(#styles), + returnValue: <_i5.MapTypeStyle>[], + returnValueForMissingStub: <_i5.MapTypeStyle>[], + ) as List<_i5.MapTypeStyle>); + @override void debugSetOverrides({ _i4.DebugCreateMapFunction? createMap, + _i4.DebugSetOptionsFunction? setOptions, _i4.MarkersController? markers, _i4.CirclesController? circles, _i4.PolygonsController? polygons, @@ -138,6 +146,7 @@ class MockGoogleMapController extends _i1.Mock [], { #createMap: createMap, + #setOptions: setOptions, #markers: markers, #circles: circles, #polygons: polygons, diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/example/pubspec.yaml index 1a02cfa2a658..28381575ea0a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/pubspec.yaml @@ -9,7 +9,7 @@ environment: dependencies: flutter: sdk: flutter - google_maps_flutter_platform_interface: ^2.4.0 + google_maps_flutter_platform_interface: ^2.5.0 google_maps_flutter_web: path: ../ web: ^0.5.0 diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart index d8d77a7728a8..6702d5f520b8 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart @@ -80,6 +80,7 @@ gmaps.MapOptions _configurationAndStyleToGmapsOptions( options.fullscreenControl = false; options.streetViewControl = false; + // See updateMapConfiguration for why this is not using configuration.style. options.styles = styles; options.mapId = configuration.cloudMapId; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart index c577ba300214..c60dd92a0aca 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_controller.dart @@ -9,6 +9,10 @@ part of '../google_maps_flutter_web.dart'; typedef DebugCreateMapFunction = gmaps.GMap Function( HTMLElement div, gmaps.MapOptions options); +/// Type used when passing an override to the _setOptions function. +@visibleForTesting +typedef DebugSetOptionsFunction = void Function(gmaps.MapOptions options); + /// Encapsulates a [gmaps.GMap], its events, and where in the DOM it's rendered. class GoogleMapController { /// Initializes the GMap, and the sub-controllers related to it. Wires events. @@ -32,6 +36,7 @@ class GoogleMapController { _polylinesController = PolylinesController(stream: _streamController); _markersController = MarkersController(stream: _streamController); _tileOverlaysController = TileOverlaysController(); + _updateStylesFromConfiguration(mapConfiguration); // Register the view factory that will hold the `_div` that holds the map in the DOM. // The `_div` needs to be created outside of the ViewFactory (and cached!) so we can @@ -60,6 +65,8 @@ class GoogleMapController { // Caching this allows us to re-create the map faithfully when needed. MapConfiguration _lastMapConfiguration = const MapConfiguration(); List _lastStyles = const []; + // The last error resulting from providing a map style, if any. + String? _lastStyleError; /// Configuration accessor for the [GoogleMapsInspectorWeb]. /// @@ -122,6 +129,7 @@ class GoogleMapController { @visibleForTesting void debugSetOverrides({ DebugCreateMapFunction? createMap, + DebugSetOptionsFunction? setOptions, MarkersController? markers, CirclesController? circles, PolygonsController? polygons, @@ -129,6 +137,7 @@ class GoogleMapController { TileOverlaysController? tileOverlays, }) { _overrideCreateMap = createMap; + _overrideSetOptions = setOptions; _markersController = markers ?? _markersController; _circlesController = circles ?? _circlesController; _polygonsController = polygons ?? _polygonsController; @@ -137,6 +146,7 @@ class GoogleMapController { } DebugCreateMapFunction? _overrideCreateMap; + DebugSetOptionsFunction? _overrideSetOptions; gmaps.GMap _createMap(HTMLElement div, gmaps.MapOptions options) { if (_overrideCreateMap != null) { @@ -279,15 +289,36 @@ class GoogleMapController { return _lastMapConfiguration; } + // TODO(stuartmorgan): Refactor so that _lastMapConfiguration.style is the + // source of truth for style info. Currently it's tracked and handled + // separately since style didn't used to be part of the configuration. + List _updateStylesFromConfiguration( + MapConfiguration update) { + if (update.style != null) { + // Provide async access to the error rather than throwing, to match the + // behavior of other platforms where there's no mechanism to return errors + // from configuration updates. + try { + _lastStyles = _mapStyles(update.style); + _lastStyleError = null; + } on MapStyleException catch (e) { + _lastStyleError = e.cause; + } + } + return _lastStyles; + } + /// Updates the map options from a [MapConfiguration]. /// /// This method converts the map into the proper [gmaps.MapOptions]. void updateMapConfiguration(MapConfiguration update) { assert(_googleMap != null, 'Cannot update options on a null map.'); + final List styles = + _updateStylesFromConfiguration(update); final MapConfiguration newConfiguration = _mergeConfigurations(update); final gmaps.MapOptions newOptions = - _configurationAndStyleToGmapsOptions(newConfiguration, _lastStyles); + _configurationAndStyleToGmapsOptions(newConfiguration, styles); _setOptions(newOptions); _setTrafficLayer(_googleMap!, newConfiguration.trafficEnabled ?? false); @@ -300,9 +331,19 @@ class GoogleMapController { _configurationAndStyleToGmapsOptions(_lastMapConfiguration, styles)); } + /// A getter for the current styles. Only for tests. + @visibleForTesting + List get styles => _lastStyles; + + /// Returns the last error from setting the map's style, if any. + String? get lastStyleError => _lastStyleError; + // Sets new [gmaps.MapOptions] on the wrapped map. // ignore: use_setters_to_change_properties void _setOptions(gmaps.MapOptions options) { + if (_overrideSetOptions != null) { + return _overrideSetOptions!(options); + } _googleMap?.options = options; } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart index 140fd3e1888d..805887f0d7f5 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/google_maps_flutter_web.dart @@ -279,6 +279,11 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { return _events(mapId).whereType(); } + @override + Future getStyleError({required int mapId}) async { + return _map(mapId).lastStyleError; + } + /// Disposes of the current map. It can't be used afterwards! @override void dispose({required int mapId}) { diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml index f78289d04fce..df6988f11c6a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_web description: Web platform implementation of google_maps_flutter repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 0.5.5 +version: 0.5.6 environment: sdk: ^3.3.0 @@ -23,7 +23,7 @@ dependencies: flutter_web_plugins: sdk: flutter google_maps: ^7.1.0 - google_maps_flutter_platform_interface: ^2.4.0 + google_maps_flutter_platform_interface: ^2.5.0 sanitize_html: ^2.0.0 stream_transform: ^2.0.0 web: ^0.5.0 From ccec918fd591e78a2f2c84d5e1607299288f5911 Mon Sep 17 00:00:00 2001 From: Srujan Gaddam <58529443+srujzs@users.noreply.github.com> Date: Wed, 28 Feb 2024 10:04:33 -0800 Subject: [PATCH 037/126] Add library annotations for js interop (#6216) These are needed for 3.3 in order to address issue https://github.com/dart-lang/sdk/issues/54801. This commit also cleans up some unnecessary annotations. Also publishes 0.5.7. https://github.com/dart-lang/sdk/issues/55039 --- .../google_maps_flutter_web/CHANGELOG.md | 5 +++++ .../lib/src/dom_window_extension.dart | 6 ++++++ .../google_maps_flutter_web/lib/src/map_styler.dart | 8 ++++++-- .../google_maps_flutter_web/pubspec.yaml | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md index 49ed230183c5..1df35f0c3c04 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.5.6+1 + +* Fixes an issue where `dart:js_interop` object literal factories did not + compile with dart2js. + ## 0.5.6 * Adds support for `MapConfiguration.style`. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/dom_window_extension.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/dom_window_extension.dart index f1b9ba34393d..9eecc8641da6 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/dom_window_extension.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/dom_window_extension.dart @@ -2,6 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(srujzs): Needed for https://github.com/dart-lang/sdk/issues/54801. Once +// we publish a version with a min SDK constraint that contains this fix, +// remove. +@JS() +library; + import 'dart:js_interop'; import 'package:web/web.dart' as web; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/map_styler.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/map_styler.dart index e6a871599cbd..d724cdd258e5 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/map_styler.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/map_styler.dart @@ -2,14 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(srujzs): Needed for https://github.com/dart-lang/sdk/issues/54801. Once +// we publish a version with a min SDK constraint that contains this fix, +// remove. +@JS() +library; + import 'dart:js_interop'; /// The interop type for a Google Maps Map Styler. /// /// See: https://developers.google.com/maps/documentation/javascript/style-reference#stylers @JS() -@staticInterop -@anonymous extension type MapStyler._(JSObject _) implements JSObject { /// Create a new [MapStyler] instance. external factory MapStyler({ diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml index df6988f11c6a..6f59a0433c75 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_web description: Web platform implementation of google_maps_flutter repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 0.5.6 +version: 0.5.6+1 environment: sdk: ^3.3.0 From 930318a82735042d5dd0d9028a2e66826aaa4589 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 28 Feb 2024 15:06:05 -0500 Subject: [PATCH 038/126] [google_maps_flutter] Add `style` to widget (#6192) Adds `style` to the `GoogleMap` widget construction, and internally to the `MapConfiguration` structure, wiring updates to style through the existing configuration update system instead of needing to be set via a separate channel. This makes things more consistent (style is conceptually similar to many of the other configuration options, and is not something that's likely to require frequent dynamic updates), and more importantly allows setting the style as part of creation instead of asynchronously after the map has likely already been displayed. Because of https://github.com/flutter/flutter/issues/24327, this adds a way to query for style errors after the fact. (It's possible that it's why `setMapStyle` was done that way in the first place; the initial PR doesn't explain.) While this is slightly clunky, it's unlikely that this method would actually be used in production code anyway, since presumably people are defining their styles at build time, not runtime, and thus probably don't need runtime error handling. Long term, the solution to this problem is likely to invert the widget/controller relationship, as was done in webview_flutter, but that's a major breaking change and not something we're ready to take on at this point. Since we don't really want two recommended ways of doing the same thing, this deprecates the existing `setMapStyle` method in favor of the new approach. Fixes https://github.com/flutter/flutter/issues/66207 --- .../google_maps_flutter/CHANGELOG.md | 11 ++++-- .../integration_test/src/maps_controller.dart | 22 ++++++++++++ .../example/lib/map_ui.dart | 34 ++++++++++--------- .../google_maps_flutter/example/pubspec.yaml | 2 +- .../lib/src/controller.dart | 6 ++++ .../lib/src/google_map.dart | 17 ++++++++++ .../google_maps_flutter/pubspec.yaml | 14 ++++---- .../test/google_map_test.dart | 28 +++++++++++++++ 8 files changed, 107 insertions(+), 27 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index 095e04709027..50ca4a9e6894 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,6 +1,11 @@ -## NEXT - -* Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. +## 2.6.0 + +* Adds `style` to the GoogleMap widget constructor. This allows setting the map + style during creation, avoiding the possibility of the default style being + displayed briefly. +* Deprecates `GoogleMapController.setMapStyle` in favor of setting the style via + the new widget `style` parameter. +* Updates minimum supported SDK version to Flutter 3.19. ## 2.5.3 diff --git a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/src/maps_controller.dart b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/src/maps_controller.dart index 01716232814e..3d99369e33be 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/src/maps_controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/src/maps_controller.dart @@ -448,6 +448,28 @@ void runTests() { await mapIdCompleter.future; }, ); + + testWidgets('getStyleError reports last error', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer controllerCompleter = + Completer(); + + await pumpMap( + tester, + GoogleMap( + key: key, + initialCameraPosition: kInitialCameraPosition, + style: '[[[this is an invalid style', + onMapCreated: (GoogleMapController controller) { + controllerCompleter.complete(controller); + }, + ), + ); + + final GoogleMapController controller = await controllerCompleter.future; + final String? error = await controller.getStyleError(); + expect(error, isNotNull); + }); } /// Repeatedly checks an asynchronous value against a test condition. diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_ui.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_ui.dart index 5348c1001af7..26c0b2ab720a 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_ui.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_ui.dart @@ -4,6 +4,7 @@ // ignore_for_file: public_member_api_docs +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart' show rootBundle; import 'package:google_maps_flutter/google_maps_flutter.dart'; @@ -59,6 +60,7 @@ class MapUiBodyState extends State { bool _myLocationButtonEnabled = true; late GoogleMapController _controller; bool _nightMode = false; + String _mapStyle = ''; @override void initState() { @@ -243,27 +245,18 @@ class MapUiBodyState extends State { return rootBundle.loadString(path); } - void _setMapStyle(String mapStyle) { - setState(() { - _nightMode = true; - _controller.setMapStyle(mapStyle); - }); - } - // Should only be called if _isMapCreated is true. Widget _nightModeToggler() { assert(_isMapCreated); return TextButton( child: Text('${_nightMode ? 'disable' : 'enable'} night mode'), - onPressed: () { - if (_nightMode) { - setState(() { - _nightMode = false; - _controller.setMapStyle(null); - }); - } else { - _getFileData('assets/night_mode.json').then(_setMapStyle); - } + onPressed: () async { + _nightMode = !_nightMode; + final String style = + _nightMode ? await _getFileData('assets/night_mode.json') : ''; + setState(() { + _mapStyle = style; + }); }, ); } @@ -278,6 +271,7 @@ class MapUiBodyState extends State { cameraTargetBounds: _cameraTargetBounds, minMaxZoomPreference: _minMaxZoomPreference, mapType: _mapType, + style: _mapStyle, rotateGesturesEnabled: _rotateGesturesEnabled, scrollGesturesEnabled: _scrollGesturesEnabled, tiltGesturesEnabled: _tiltGesturesEnabled, @@ -352,5 +346,13 @@ class MapUiBodyState extends State { _controller = controller; _isMapCreated = true; }); + // Log any style errors to the console for debugging. + if (kDebugMode) { + _controller.getStyleError().then((String? error) { + if (error != null) { + debugPrint(error); + } + }); + } } } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml index efed14ceecbf..ba306fd8bd9e 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml @@ -19,7 +19,7 @@ dependencies: # the parent directory to use the current plugin's version. path: ../ google_maps_flutter_android: ^2.5.0 - google_maps_flutter_platform_interface: ^2.4.0 + google_maps_flutter_platform_interface: ^2.5.0 dev_dependencies: build_runner: ^2.1.10 diff --git a/packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart b/packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart index 095d15867acc..393288a6165f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart +++ b/packages/google_maps_flutter/google_maps_flutter/lib/src/controller.dart @@ -190,11 +190,17 @@ class GoogleMapController { /// Also, refer [iOS](https://developers.google.com/maps/documentation/ios-sdk/style-reference) /// and [Android](https://developers.google.com/maps/documentation/android-sdk/style-reference) /// style reference for more information regarding the supported styles. + @Deprecated('Use GoogleMap.style instead.') Future setMapStyle(String? mapStyle) { return GoogleMapsFlutterPlatform.instance .setMapStyle(mapStyle, mapId: mapId); } + /// Returns the last style error, if any. + Future getStyleError() { + return GoogleMapsFlutterPlatform.instance.getStyleError(mapId: mapId); + } + /// Return [LatLngBounds] defining the region that is visible in a map. Future getVisibleRegion() { return GoogleMapsFlutterPlatform.instance.getVisibleRegion(mapId: mapId); diff --git a/packages/google_maps_flutter/google_maps_flutter/lib/src/google_map.dart b/packages/google_maps_flutter/google_maps_flutter/lib/src/google_map.dart index 3b73e55664dd..c50ffccfa634 100644 --- a/packages/google_maps_flutter/google_maps_flutter/lib/src/google_map.dart +++ b/packages/google_maps_flutter/google_maps_flutter/lib/src/google_map.dart @@ -91,6 +91,7 @@ class GoogleMap extends StatefulWidget { const GoogleMap({ super.key, required this.initialCameraPosition, + this.style, this.onMapCreated, this.gestureRecognizers = const >{}, this.webGestureHandling, @@ -136,6 +137,19 @@ class GoogleMap extends StatefulWidget { /// The initial position of the map's camera. final CameraPosition initialCameraPosition; + /// The style for the map. + /// + /// Set to null to clear any previous custom styling. + /// + /// If problems were detected with the [mapStyle], including un-parsable + /// styling JSON, unrecognized feature type, unrecognized element type, or + /// invalid styler keys, the style is left unchanged, and the error can be + /// retrieved with [GoogleMapController.getStyleError]. + /// + /// The style string can be generated using the + /// [map style tool](https://mapstyle.withgoogle.com/). + final String? style; + /// True if the map should show a compass when rotated. final bool compassEnabled; @@ -556,5 +570,8 @@ MapConfiguration _configurationFromMapWidget(GoogleMap map) { trafficEnabled: map.trafficEnabled, buildingsEnabled: map.buildingsEnabled, cloudMapId: map.cloudMapId, + // A null style in the widget means no style, which is expressed as '' in + // the configuration to distinguish from no change (null). + style: map.style ?? '', ); } diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index 9b53f596fdf6..598ab6f7aeb6 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -2,11 +2,11 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.5.3 +version: 2.6.0 environment: - sdk: ^3.1.0 - flutter: ">=3.13.0" + sdk: ^3.3.0 + flutter: ">=3.19.0" flutter: plugin: @@ -21,10 +21,10 @@ flutter: dependencies: flutter: sdk: flutter - google_maps_flutter_android: ^2.5.0 - google_maps_flutter_ios: ^2.3.0 - google_maps_flutter_platform_interface: ^2.4.0 - google_maps_flutter_web: ^0.5.2 + google_maps_flutter_android: ^2.7.0 + google_maps_flutter_ios: ^2.5.0 + google_maps_flutter_platform_interface: ^2.5.0 + google_maps_flutter_web: ^0.5.6 dev_dependencies: flutter_test: diff --git a/packages/google_maps_flutter/google_maps_flutter/test/google_map_test.dart b/packages/google_maps_flutter/google_maps_flutter/test/google_map_test.dart index 7005a8d3ab60..9276a7dbda30 100644 --- a/packages/google_maps_flutter/google_maps_flutter/test/google_map_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/test/google_map_test.dart @@ -551,4 +551,32 @@ void main() { expect(map.mapConfiguration.buildingsEnabled, true); }); + + testWidgets('Can update style', (WidgetTester tester) async { + const String initialStyle = '[]'; + await tester.pumpWidget( + const Directionality( + textDirection: TextDirection.ltr, + child: GoogleMap( + initialCameraPosition: CameraPosition(target: LatLng(10.0, 15.0)), + style: initialStyle, + ), + ), + ); + + final PlatformMapStateRecorder map = platform.lastCreatedMap; + + expect(map.mapConfiguration.style, initialStyle); + + await tester.pumpWidget( + const Directionality( + textDirection: TextDirection.ltr, + child: GoogleMap( + initialCameraPosition: CameraPosition(target: LatLng(10.0, 15.0)), + ), + ), + ); + + expect(map.mapConfiguration.style, ''); + }); } From 6d02f0359368722826e66cb6660551ee8638d609 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 28 Feb 2024 18:13:17 -0500 Subject: [PATCH 039/126] Manual roll Flutter from c30f998eb5db to d00bfe820eef (32 revisions) (#6222) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Manual roll Flutter from c30f998eb5db to d00bfe820eef (32 revisions) Manual roll requested by bmparr@google.com https://github.com/flutter/flutter/compare/c30f998eb5db...d00bfe820eef 2024-02-28 engine-flutter-autoroll@skia.org Roll Flutter Engine from c9381fb8ef4c to 455c814fe5de (1 revision) (flutter/flutter#144340) 2024-02-28 engine-flutter-autoroll@skia.org Roll Flutter Engine from 91898e397261 to c9381fb8ef4c (11 revisions) (flutter/flutter#144338) 2024-02-28 15619084+vashworth@users.noreply.github.com Reland "Add FlutterMacOS.xcframework artifact (#143244)" (flutter/flutter#144275) 2024-02-28 christopherfujino@gmail.com [flutter_tools] Catch rpc error in render frame with raster stats (flutter/flutter#144190) 2024-02-28 github@alexv525.com 🛡️ Guard Flutter Android app by disallow task affinity by default (flutter/flutter#144018) 2024-02-28 zanderso@users.noreply.github.com Manual roll Flutter Engine 8acc96d405d0 to 91898e397261 (flutter/flutter#144316) 2024-02-28 zanderso@users.noreply.github.com Manual roll Flutter Engine 64a375de9c8f to 8acc96d405d0 (flutter/flutter#144296) 2024-02-28 zanderso@users.noreply.github.com Manual roll Flutter Engine c79117b706e9 to 64a375de9c8f (flutter/flutter#144293) 2024-02-28 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Cache `FocusNode.enclosingScope`, clean up `descendantsAreFocusable` (#144207)" (flutter/flutter#144292) 2024-02-28 zanderso@users.noreply.github.com Manual roll Flutter Engine 2461280c38b7 to c79117b706e9 (flutter/flutter#144290) 2024-02-28 zanderso@users.noreply.github.com Manual roll Flutter Engine 5e0d9ba35dd5 to 2461280c38b7 (flutter/flutter#144288) 2024-02-28 zanderso@users.noreply.github.com Manual roll Flutter Engine fe7ea6d9c34f to 5e0d9ba35dd5 (flutter/flutter#144285) 2024-02-28 zanderso@users.noreply.github.com Manual roll Flutter Engine 0bc21ea7bc92 to fe7ea6d9c34f (flutter/flutter#144283) 2024-02-28 polinach@google.com Use const route for notAnnounced. (flutter/flutter#144050) 2024-02-28 tessertaha@gmail.com Add `tabs_utils.dart` class (flutter/flutter#143937) 2024-02-28 36861262+QuncCccccc@users.noreply.github.com Remove `bottomAppBarColor` from `ThemeData` (flutter/flutter#144080) 2024-02-27 richadlu001@gmail.com fix: unexpected chinese punctuation (flutter/flutter#143678) 2024-02-27 42216813+eliasyishak@users.noreply.github.com Clean up lint ignores (flutter/flutter#144229) 2024-02-27 jhy03261997@gmail.com Reland [a11y] Add isEnabled semantics flag to text field (flutter/flutter#143601) 2024-02-27 31859944+LongCatIsLooong@users.noreply.github.com Remove deprecated `CupertinoContextMenu.previewBuilder` (flutter/flutter#143990) 2024-02-27 chris@bracken.jp [iOS] Fix naming in platform_view example (flutter/flutter#144247) 2024-02-27 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Reland - Introduce tone-based surfaces and accent color add-ons - Part 2 (#144001)" (flutter/flutter#144262) 2024-02-27 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Add FlutterMacOS.xcframework artifact (#143244)" (flutter/flutter#144253) 2024-02-27 kustermann@google.com [web] Make flutter web profile builds always keep wasm symbols (flutter/flutter#144130) 2024-02-27 36861262+QuncCccccc@users.noreply.github.com Reland - Introduce tone-based surfaces and accent color add-ons - Part 2 (flutter/flutter#144001) 2024-02-27 tjrogertj@gmail.com bind missing add icon in platform_view example (flutter/flutter#132028) 2024-02-27 31859944+LongCatIsLooong@users.noreply.github.com Cache `FocusNode.enclosingScope`, clean up `descendantsAreFocusable` (flutter/flutter#144207) 2024-02-27 31859944+LongCatIsLooong@users.noreply.github.com Remove strut migration flag from `TextPainter` (flutter/flutter#144242) 2024-02-27 15619084+vashworth@users.noreply.github.com Remove force Xcode debug workflow (flutter/flutter#144185) 2024-02-27 54558023+keyonghan@users.noreply.github.com Mark two other firebase targets as bringup: true (flutter/flutter#144234) 2024-02-27 15619084+vashworth@users.noreply.github.com Add FlutterMacOS.xcframework artifact (flutter/flutter#143244) 2024-02-27 jonahwilliams@google.com Re-enable Impeller goldens blocking. (flutter/flutter#144210) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC bmparr@google.com,rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 ... --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 0e5e495ee63a..a380109bac53 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c30f998eb5dbea01581ea60aad5b0d6fda18f634 +d00bfe820eefc3602eae6975be583913cc016af1 From a1c5006dccc9cb519674e451a9196caf28029b68 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 29 Feb 2024 10:44:04 -0500 Subject: [PATCH 040/126] Roll Flutter from d00bfe820eef to e92bca3ff5d2 (15 revisions) (#6227) https://github.com/flutter/flutter/compare/d00bfe820eef...e92bca3ff5d2 2024-02-29 leroux_bruno@yahoo.fr [flutter_tools] Update external link in Android manifest template (flutter/flutter#144302) 2024-02-29 engine-flutter-autoroll@skia.org Roll Flutter Engine from 232217f39c3c to d068d980f952 (1 revision) (flutter/flutter#144369) 2024-02-29 engine-flutter-autoroll@skia.org Roll Flutter Engine from 9e1876141be8 to 232217f39c3c (1 revision) (flutter/flutter#144366) 2024-02-29 engine-flutter-autoroll@skia.org Roll Flutter Engine from 61510db94a1c to 9e1876141be8 (1 revision) (flutter/flutter#144362) 2024-02-29 engine-flutter-autoroll@skia.org Roll Flutter Engine from 10331db8f748 to 61510db94a1c (5 revisions) (flutter/flutter#144355) 2024-02-29 jmccandless@google.com Docs on the interaction between Shortcuts and text input (flutter/flutter#144328) 2024-02-29 dnfield@google.com Use robolectric/AndroidJUnit4 for integration test tests (flutter/flutter#144348) 2024-02-29 31859944+LongCatIsLooong@users.noreply.github.com Reland "Cache FocusNode.enclosingScope, clean up descendantsAreFocusable (#144207)" (flutter/flutter#144330) 2024-02-29 engine-flutter-autoroll@skia.org Roll Flutter Engine from 455c814fe5de to 10331db8f748 (7 revisions) (flutter/flutter#144345) 2024-02-29 dkwingsmt@users.noreply.github.com ReportTiming callback should record the sendFrameToEngine when it was scheduled (flutter/flutter#144212) 2024-02-29 katelovett@google.com Disable flaky golden file test (flutter/flutter#144351) 2024-02-28 jmccandless@google.com Mention SelectionArea in SelectableText docs (flutter/flutter#143784) 2024-02-28 49699333+dependabot[bot]@users.noreply.github.com Bump peter-evans/create-pull-request from 6.0.0 to 6.0.1 (flutter/flutter#144344) 2024-02-28 36861262+QuncCccccc@users.noreply.github.com Reland "Reland - Introduce tone-based surfaces and accent color add-ons - Part 2" (flutter/flutter#144273) 2024-02-28 72562119+tgucio@users.noreply.github.com Remove irrelevant comment in TextPainter (flutter/flutter#144308) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC bmparr@google.com,rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index a380109bac53..2630132fc273 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -d00bfe820eefc3602eae6975be583913cc016af1 +e92bca3ff5d27b120ed39411c70d0110c21b4afa From 8e2202d0f3e22486b182cb76828ac9d1bb627192 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 29 Feb 2024 13:05:22 -0500 Subject: [PATCH 041/126] Roll Flutter (stable) from abb292a07e20 to 7482962148e8 (1 revision) (#6230) https://github.com/flutter/flutter/compare/abb292a07e20...7482962148e8 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-stable-packages Please CC bmparr@google.com,rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter (stable): https://github.com/flutter/flutter/issues/new/choose To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_stable.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_stable.version b/.ci/flutter_stable.version index 1d282c387537..67aeb76b7edf 100644 --- a/.ci/flutter_stable.version +++ b/.ci/flutter_stable.version @@ -1 +1 @@ -abb292a07e20d696c4568099f918f6c5f330e6b0 +7482962148e8d758338d8a28f589f317e1e42ba4 From b3f10aaa157eee76a9248df21a8899b0b2461e19 Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Thu, 29 Feb 2024 21:36:03 +0200 Subject: [PATCH 042/126] [google_maps_flutter] Add support for marker clustering - platform interface (#6158) This PR introduces support for marker clustering This is prequel PR for: https://github.com/flutter/packages/pull/4319 Containing only changes to `google_maps_flutter_platform_interface` package. Follow up PR:s will hold the platform and app-facing plugin implementations. Linked issue: https://github.com/flutter/flutter/issues/26863 --- .../AUTHORS | 1 + .../CHANGELOG.md | 4 + .../lib/src/events/map_event.dart | 9 ++ .../method_channel_google_maps_flutter.dart | 50 +++++++++++ .../google_maps_flutter_platform.dart | 19 +++++ .../google_maps_inspector_platform.dart | 6 ++ .../lib/src/types/cluster.dart | 52 ++++++++++++ .../lib/src/types/cluster_manager.dart | 82 +++++++++++++++++++ .../src/types/cluster_manager_updates.dart | 25 ++++++ .../lib/src/types/map_objects.dart | 2 + .../lib/src/types/marker.dart | 12 ++- .../lib/src/types/types.dart | 4 + .../lib/src/types/utils/cluster_manager.dart | 13 +++ .../pubspec.yaml | 2 +- .../google_maps_flutter_platform_test.dart | 30 +++++++ .../test/types/cluster_manager_test.dart | 57 +++++++++++++ .../test/types/cluster_test.dart | 53 ++++++++++++ .../test/types/marker_test.dart | 8 +- .../test/utils/cluster_manager_test.dart | 30 +++++++ 19 files changed, 455 insertions(+), 4 deletions(-) create mode 100644 packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/cluster.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/cluster_manager.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/cluster_manager_updates.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/utils/cluster_manager.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/cluster_manager_test.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/cluster_test.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_platform_interface/test/utils/cluster_manager_test.dart diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/AUTHORS b/packages/google_maps_flutter/google_maps_flutter_platform_interface/AUTHORS index 493a0b4ef9c2..6f33f4aa0511 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/AUTHORS +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/AUTHORS @@ -64,3 +64,4 @@ Aleksandr Yurkovskiy Anton Borries Alex Li Rahul Raj <64.rahulraj@gmail.com> +Joonas Kerttula 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 e062daa45430..668dcb2731b3 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.6.0 + +* Adds support for marker clustering. + ## 2.5.0 * Adds `style` to the `MapConfiguration` to allow setting style as part of diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/events/map_event.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/events/map_event.dart index 0034633b8066..67a026d90557 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/events/map_event.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/events/map_event.dart @@ -167,3 +167,12 @@ class MapLongPressEvent extends _PositionedMapEvent { /// The `position` of this event is the LatLng where the Map was long pressed. MapLongPressEvent(int mapId, LatLng position) : super(mapId, position, null); } + +/// An event fired when a cluster icon managed by [ClusterManager] is tapped. +class ClusterTapEvent extends MapEvent { + /// Build a ClusterTapEvent Event triggered from the map represented by `mapId`. + /// + /// The `value` of this event is a [Cluster] object that represents the tapped + /// cluster icon managed by [ClusterManager]. + ClusterTapEvent(super.mapId, super.cluster); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/method_channel/method_channel_google_maps_flutter.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/method_channel/method_channel_google_maps_flutter.dart index 2c79c0cf995c..dd728cf1349d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/method_channel/method_channel_google_maps_flutter.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/method_channel/method_channel_google_maps_flutter.dart @@ -166,6 +166,11 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { return _events(mapId).whereType(); } + @override + Stream onClusterTap({required int mapId}) { + return _events(mapId).whereType(); + } + Future _handleMethodCall(MethodCall call, int mapId) async { switch (call.method) { case 'camera#onMoveStarted': @@ -258,6 +263,36 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { arguments['zoom'] as int?, ); return tile.toJson(); + case 'cluster#onTap': + final Map arguments = _getArgumentDictionary(call); + final ClusterManagerId clusterManagerId = + ClusterManagerId(arguments['clusterManagerId']! as String); + final LatLng position = LatLng.fromJson(arguments['position'])!; + + final Map> latLngData = + (arguments['bounds']! as Map).map( + (dynamic key, dynamic object) => + MapEntry>( + key as String, object as List)); + + final LatLngBounds bounds = LatLngBounds( + northeast: LatLng.fromJson(latLngData['northeast'])!, + southwest: LatLng.fromJson(latLngData['southwest'])!); + + final List markerIds = + (arguments['markerIds']! as List) + .map((dynamic markerId) => MarkerId(markerId as String)) + .toList(); + + _mapEventStreamController.add(ClusterTapEvent( + mapId, + Cluster( + clusterManagerId, + markerIds, + position: position, + bounds: bounds, + ), + )); default: throw MissingPluginException(); } @@ -347,6 +382,17 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { ); } + @override + Future updateClusterManagers( + ClusterManagerUpdates clusterManagerUpdates, { + required int mapId, + }) { + return channel(mapId).invokeMethod( + 'clusterManagers#update', + clusterManagerUpdates.toJson(), + ); + } + @override Future clearTileCache( TileOverlayId tileOverlayId, { @@ -585,6 +631,7 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { Set polylines = const {}, Set circles = const {}, Set tileOverlays = const {}, + Set clusterManagers = const {}, Set>? gestureRecognizers, Map mapOptions = const {}, }) { @@ -599,6 +646,7 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { polygons: polygons, polylines: polylines, circles: circles, + clusterManagers: clusterManagers, tileOverlays: tileOverlays), mapOptions: mapOptions, ); @@ -614,6 +662,7 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { Set polylines = const {}, Set circles = const {}, Set tileOverlays = const {}, + Set clusterManagers = const {}, Set>? gestureRecognizers, Map mapOptions = const {}, }) { @@ -627,6 +676,7 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { polylines: polylines, circles: circles, tileOverlays: tileOverlays, + clusterManagers: clusterManagers, gestureRecognizers: gestureRecognizers, mapOptions: mapOptions, ); diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_flutter_platform.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_flutter_platform.dart index 9e829c496aa4..648c6a162d68 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_flutter_platform.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_flutter_platform.dart @@ -142,6 +142,20 @@ abstract class GoogleMapsFlutterPlatform extends PlatformInterface { throw UnimplementedError('updateTileOverlays() has not been implemented.'); } + /// Updates cluster manager configuration. + /// + /// Change listeners are notified once the update has been made on the + /// platform side. + /// + /// The returned [Future] completes after listeners have been notified. + Future updateClusterManagers( + ClusterManagerUpdates clusterManagerUpdates, { + required int mapId, + }) { + throw UnimplementedError( + 'updateClusterManagers() has not been implemented.'); + } + /// Clears the tile cache so that all tiles will be requested again from the /// [TileProvider]. /// @@ -357,6 +371,11 @@ abstract class GoogleMapsFlutterPlatform extends PlatformInterface { throw UnimplementedError('onLongPress() has not been implemented.'); } + /// A marker icon managed by [ClusterManager] has been tapped. + Stream onClusterTap({required int mapId}) { + throw UnimplementedError('onClusterTap() has not been implemented.'); + } + /// Dispose of whatever resources the `mapId` is holding on to. void dispose({required int mapId}) { throw UnimplementedError('dispose() has not been implemented.'); diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_inspector_platform.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_inspector_platform.dart index 1e07b97c300d..461d38a61616 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_inspector_platform.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_inspector_platform.dart @@ -115,4 +115,10 @@ abstract class GoogleMapsInspectorPlatform extends PlatformInterface { {required int mapId}) { throw UnimplementedError('getTileOverlayInfo() has not been implemented.'); } + + /// Returns current clusters from [ClusterManager]. + Future> getClusters( + {required int mapId, required ClusterManagerId clusterManagerId}) { + throw UnimplementedError('getClusters() has not been implemented.'); + } } diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/cluster.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/cluster.dart new file mode 100644 index 000000000000..d5ca46a72f55 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/cluster.dart @@ -0,0 +1,52 @@ +// 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/foundation.dart' + show immutable, listEquals, objectRuntimeType; +import 'types.dart'; + +/// A cluster containing multiple markers. +@immutable +class Cluster { + /// Creates a cluster with its location [LatLng], bounds [LatLngBounds], + /// and list of [MarkerId]s in the cluster. + const Cluster( + this.clusterManagerId, + this.markerIds, { + required this.position, + required this.bounds, + }) : assert(markerIds.length > 0); + + /// ID of the [ClusterManager] of the cluster. + final ClusterManagerId clusterManagerId; + + /// Cluster marker location. + final LatLng position; + + /// The bounds containing all cluster markers. + final LatLngBounds bounds; + + /// List of [MarkerId]s in the cluster. + final List markerIds; + + /// Returns the number of markers in the cluster. + int get count => markerIds.length; + + @override + String toString() => + '${objectRuntimeType(this, 'Cluster')}($clusterManagerId, $position, $bounds, $markerIds)'; + + @override + bool operator ==(Object other) { + return other is Cluster && + other.clusterManagerId == clusterManagerId && + other.position == position && + other.bounds == bounds && + listEquals(other.markerIds, markerIds); + } + + @override + int get hashCode => + Object.hash(clusterManagerId, position, bounds, markerIds); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/cluster_manager.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/cluster_manager.dart new file mode 100644 index 000000000000..ec3c1f7e435b --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/cluster_manager.dart @@ -0,0 +1,82 @@ +// 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/foundation.dart' show immutable; +import 'types.dart'; + +/// Uniquely identifies a [ClusterManager] among [GoogleMap] clusters. +/// +/// This does not have to be globally unique, only unique among the list. +@immutable +class ClusterManagerId extends MapsObjectId { + /// Creates an immutable identifier for a [ClusterManager]. + const ClusterManagerId(super.value); +} + +/// [ClusterManager] manages marker clustering for set of [Marker]s that have +/// the same [ClusterManagerId] set. +@immutable +class ClusterManager implements MapsObject { + /// Creates an immutable object for managing clustering for set of markers. + const ClusterManager({ + required this.clusterManagerId, + this.onClusterTap, + }); + + /// Uniquely identifies a [ClusterManager]. + final ClusterManagerId clusterManagerId; + + @override + ClusterManagerId get mapsId => clusterManagerId; + + /// Callback to receive tap events for cluster markers placed on this map. + final ArgumentCallback? onClusterTap; + + /// Creates a new [ClusterManager] object whose values are the same as this instance, + /// unless overwritten by the specified parameters. + ClusterManager copyWith({ + ArgumentCallback? onClusterTapParam, + }) { + return ClusterManager( + clusterManagerId: clusterManagerId, + onClusterTap: onClusterTapParam ?? onClusterTap, + ); + } + + /// Creates a new [ClusterManager] object whose values are the same as this instance. + @override + ClusterManager clone() => copyWith(); + + /// Converts this object to something serializable in JSON. + @override + Object toJson() { + final Map json = {}; + + void addIfPresent(String fieldName, Object? value) { + if (value != null) { + json[fieldName] = value; + } + } + + addIfPresent('clusterManagerId', clusterManagerId.value); + return json; + } + + @override + bool operator ==(Object other) { + if (other.runtimeType != runtimeType) { + return false; + } + return other is ClusterManager && + clusterManagerId == other.clusterManagerId; + } + + @override + int get hashCode => clusterManagerId.hashCode; + + @override + String toString() { + return 'Cluster{clusterManagerId: $clusterManagerId, onClusterTap: $onClusterTap}'; + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/cluster_manager_updates.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/cluster_manager_updates.dart new file mode 100644 index 000000000000..167201906dd8 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/cluster_manager_updates.dart @@ -0,0 +1,25 @@ +// 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 'types.dart'; + +/// [ClusterManager] update events to be applied to the [GoogleMap]. +/// +/// Used in [GoogleMapController] when the map is updated. +// (Do not re-export) +class ClusterManagerUpdates extends MapsObjectUpdates { + /// Computes [ClusterManagerUpdates] given previous and current [ClusterManager]s. + ClusterManagerUpdates.from(super.previous, super.current) + : super.from(objectName: 'clusterManager'); + + /// Set of Clusters to be added in this update. + Set get clusterManagersToAdd => objectsToAdd; + + /// Set of ClusterManagerIds to be removed in this update. + Set get clusterManagerIdsToRemove => + objectIdsToRemove.cast(); + + /// Set of Clusters to be changed in this update. + Set get clusterManagersToChange => objectsToChange; +} diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/map_objects.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/map_objects.dart index 56f80e8312dd..009a6a078268 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/map_objects.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/map_objects.dart @@ -21,6 +21,7 @@ class MapObjects { this.polylines = const {}, this.circles = const {}, this.tileOverlays = const {}, + this.clusterManagers = const {}, }); final Set markers; @@ -28,4 +29,5 @@ class MapObjects { final Set polylines; final Set circles; final Set tileOverlays; + final Set clusterManagers; } diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/marker.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/marker.dart index 0b5b1a507d37..8c4d7b267979 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/marker.dart @@ -151,6 +151,7 @@ class Marker implements MapsObject { this.rotation = 0.0, this.visible = true, this.zIndex = 0.0, + this.clusterManagerId, this.onTap, this.onDrag, this.onDragStart, @@ -163,6 +164,9 @@ class Marker implements MapsObject { @override MarkerId get mapsId => markerId; + /// Marker clustering is managed by [ClusterManager] with [clusterManagerId]. + final ClusterManagerId? clusterManagerId; + /// The opacity of the marker, between 0.0 and 1.0 inclusive. /// /// 0.0 means fully transparent, 1.0 means fully opaque. @@ -241,6 +245,7 @@ class Marker implements MapsObject { ValueChanged? onDragStartParam, ValueChanged? onDragParam, ValueChanged? onDragEndParam, + ClusterManagerId? clusterManagerIdParam, }) { return Marker( markerId: markerId, @@ -259,6 +264,7 @@ class Marker implements MapsObject { onDragStart: onDragStartParam ?? onDragStart, onDrag: onDragParam ?? onDrag, onDragEnd: onDragEndParam ?? onDragEnd, + clusterManagerId: clusterManagerIdParam ?? clusterManagerId, ); } @@ -289,6 +295,7 @@ class Marker implements MapsObject { addIfPresent('rotation', rotation); addIfPresent('visible', visible); addIfPresent('zIndex', zIndex); + addIfPresent('clusterManagerId', clusterManagerId?.value); return json; } @@ -312,7 +319,8 @@ class Marker implements MapsObject { position == other.position && rotation == other.rotation && visible == other.visible && - zIndex == other.zIndex; + zIndex == other.zIndex && + clusterManagerId == other.clusterManagerId; } @override @@ -324,6 +332,6 @@ class Marker implements MapsObject { 'consumeTapEvents: $consumeTapEvents, draggable: $draggable, flat: $flat, ' 'icon: $icon, infoWindow: $infoWindow, position: $position, rotation: $rotation, ' 'visible: $visible, zIndex: $zIndex, onTap: $onTap, onDragStart: $onDragStart, ' - 'onDrag: $onDrag, onDragEnd: $onDragEnd}'; + 'onDrag: $onDrag, onDragEnd: $onDragEnd, clusterManagerId: $clusterManagerId}'; } } diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/types.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/types.dart index 1f1916b1c55e..3ef0e4ab18b5 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/types.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/types.dart @@ -9,6 +9,9 @@ export 'camera.dart'; export 'cap.dart'; export 'circle.dart'; export 'circle_updates.dart'; +export 'cluster.dart'; +export 'cluster_manager.dart'; +export 'cluster_manager_updates.dart'; export 'joint_type.dart'; export 'location.dart'; export 'map_configuration.dart'; @@ -30,6 +33,7 @@ export 'tile_provider.dart'; export 'ui.dart'; // Export the utils used by the Widget export 'utils/circle.dart'; +export 'utils/cluster_manager.dart'; export 'utils/marker.dart'; export 'utils/polygon.dart'; export 'utils/polyline.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/utils/cluster_manager.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/utils/cluster_manager.dart new file mode 100644 index 000000000000..c44578c04d04 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/utils/cluster_manager.dart @@ -0,0 +1,13 @@ +// 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 '../types.dart'; +import 'maps_object.dart'; + +/// Converts an [Iterable] of Cluster Managers in a Map of ClusterManagerId -> Cluster. +Map keyByClusterManagerId( + Iterable clusterManagers) { + return keyByMapsObjectId(clusterManagers) + .cast(); +} 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 8a712be78a77..ec9271aab715 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.5.0 +version: 2.6.0 environment: sdk: ^3.1.0 diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/platform_interface/google_maps_flutter_platform_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/platform_interface/google_maps_flutter_platform_test.dart index 798559a68f0a..b0e48fd474bb 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/platform_interface/google_maps_flutter_platform_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/platform_interface/google_maps_flutter_platform_test.dart @@ -84,6 +84,35 @@ void main() { }, ); + test( + 'updateClusterManagers() throws UnimplementedError', + () { + expect( + () => BuildViewGoogleMapsFlutterPlatform().updateClusterManagers( + ClusterManagerUpdates.from( + { + const ClusterManager( + clusterManagerId: ClusterManagerId('123')) + }, + { + const ClusterManager( + clusterManagerId: ClusterManagerId('456')) + }, + ), + mapId: 0), + throwsUnimplementedError); + }, + ); + + test( + 'onClusterTap() throws UnimplementedError', + () { + expect( + () => BuildViewGoogleMapsFlutterPlatform().onClusterTap(mapId: 0), + throwsUnimplementedError); + }, + ); + test( 'default implementation of `getStyleError` returns null', () async { @@ -115,6 +144,7 @@ class BuildViewGoogleMapsFlutterPlatform extends GoogleMapsFlutterPlatform { Set polylines = const {}, Set circles = const {}, Set tileOverlays = const {}, + Set clusterManagers = const {}, Set>? gestureRecognizers = const >{}, Map mapOptions = const {}, diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/cluster_manager_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/cluster_manager_test.dart new file mode 100644 index 000000000000..073c6c3dbec9 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/cluster_manager_test.dart @@ -0,0 +1,57 @@ +// 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_test/flutter_test.dart'; + +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('$ClusterManager', () { + test('constructor defaults', () { + const ClusterManager manager = + ClusterManager(clusterManagerId: ClusterManagerId('1234')); + + expect(manager.clusterManagerId, const ClusterManagerId('1234')); + }); + + test('toJson', () { + const ClusterManager manager = + ClusterManager(clusterManagerId: ClusterManagerId('1234')); + + final Map json = manager.toJson() as Map; + + expect(json, { + 'clusterManagerId': '1234', + }); + }); + test('clone', () { + const ClusterManager manager = + ClusterManager(clusterManagerId: ClusterManagerId('1234')); + final ClusterManager clone = manager.clone(); + + expect(identical(clone, manager), isFalse); + expect(clone, equals(manager)); + }); + test('copyWith', () { + const ClusterManager manager = + ClusterManager(clusterManagerId: ClusterManagerId('1234')); + final List log = []; + + final ClusterManager copy = manager.copyWith( + onClusterTapParam: (Cluster cluster) { + log.add('onTapParam'); + }, + ); + copy.onClusterTap!(Cluster( + manager.clusterManagerId, const [MarkerId('5678')], + position: const LatLng(11.0, 22.0), + bounds: LatLngBounds( + southwest: const LatLng(22.0, 33.0), + northeast: const LatLng(33.0, 88.0)))); + expect(log, contains('onTapParam')); + }); + }); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/cluster_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/cluster_test.dart new file mode 100644 index 000000000000..26a69ed0ee3b --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/cluster_test.dart @@ -0,0 +1,53 @@ +// 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_test/flutter_test.dart'; + +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('$Cluster', () { + test('constructor', () { + final Cluster cluster = Cluster( + const ClusterManagerId('3456787654'), + const [MarkerId('23456')], + position: const LatLng(55.0, 66.0), + bounds: LatLngBounds( + northeast: const LatLng(88.0, 22.0), + southwest: const LatLng(11.0, 99.0), + ), + ); + + expect(cluster.clusterManagerId.value, equals('3456787654')); + expect(cluster.markerIds[0].value, equals('23456')); + expect(cluster.position, equals(const LatLng(55.0, 66.0))); + expect( + cluster.bounds, + LatLngBounds( + northeast: const LatLng(88.0, 22.0), + southwest: const LatLng(11.0, 99.0), + )); + }); + + test('constructor markerIds length is > 0', () { + void initWithMarkerIds(List markerIds) { + Cluster( + const ClusterManagerId('3456787654'), + markerIds, + position: const LatLng(55.0, 66.0), + bounds: LatLngBounds( + northeast: const LatLng(88.0, 22.0), + southwest: const LatLng(11.0, 99.0), + ), + ); + } + + expect(() => initWithMarkerIds([const MarkerId('12342323')]), + isNot(throwsAssertionError)); + expect(() => initWithMarkerIds([]), throwsAssertionError); + }); + }); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/marker_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/marker_test.dart index db7afcbb0398..76e5de2b1866 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/marker_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/marker_test.dart @@ -96,7 +96,8 @@ void main() { expect(clone, equals(marker)); }); test('copyWith', () { - const Marker marker = Marker(markerId: MarkerId('ABC123')); + const MarkerId markerId = MarkerId('ABC123'); + const Marker marker = Marker(markerId: markerId); final BitmapDescriptor testDescriptor = BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueCyan); @@ -111,6 +112,8 @@ void main() { const double testRotationParam = 100; final bool testVisibleParam = !marker.visible; const double testZIndexParam = 100; + const ClusterManagerId testClusterManagerIdParam = + ClusterManagerId('DEF123'); final List log = []; final Marker copy = marker.copyWith( @@ -125,6 +128,7 @@ void main() { rotationParam: testRotationParam, visibleParam: testVisibleParam, zIndexParam: testZIndexParam, + clusterManagerIdParam: testClusterManagerIdParam, onTapParam: () { log.add('onTapParam'); }, @@ -139,6 +143,7 @@ void main() { }, ); + expect(copy.markerId, equals(markerId)); expect(copy.alpha, equals(testAlphaParam)); expect(copy.anchor, equals(testAnchorParam)); expect(copy.consumeTapEvents, equals(testConsumeTapEventsParam)); @@ -150,6 +155,7 @@ void main() { expect(copy.rotation, equals(testRotationParam)); expect(copy.visible, equals(testVisibleParam)); expect(copy.zIndex, equals(testZIndexParam)); + expect(copy.clusterManagerId, equals(testClusterManagerIdParam)); copy.onTap!(); expect(log, contains('onTapParam')); diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/utils/cluster_manager_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/utils/cluster_manager_test.dart new file mode 100644 index 000000000000..64eab27b2d4a --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/utils/cluster_manager_test.dart @@ -0,0 +1,30 @@ +// 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_test/flutter_test.dart'; +import 'package:google_maps_flutter_platform_interface/src/types/types.dart'; + +void main() { + group('keyByClusterManagerId', () { + test('returns a Map keyed by clusterManagerId', () { + const ClusterManagerId id1 = ClusterManagerId('id1'); + const ClusterManagerId id2 = ClusterManagerId('id2'); + const ClusterManagerId id3 = ClusterManagerId('id3'); + + final List clusterManagers = [ + const ClusterManager(clusterManagerId: id1), + const ClusterManager(clusterManagerId: id2), + const ClusterManager(clusterManagerId: id3), + ]; + + final Map result = + keyByClusterManagerId(clusterManagers); + + expect(result, isA>()); + expect(result[id1], equals(clusterManagers[0])); + expect(result[id2], equals(clusterManagers[1])); + expect(result[id3], equals(clusterManagers[2])); + }); + }); +} From 268f7d548cbec9f6b8927cb43cfa49791929715e Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 29 Feb 2024 15:24:06 -0500 Subject: [PATCH 043/126] [various] Standardize Obj-C doc comment style (#6232) Standardizes Objective-C code on `///` for documentation comments, instead of a mix of that and `/** ... */`. This does not add enforcement, since my expectation is that if we start from a homogeneous style we will be much less likely to diverge again (and if some do creep in, it's harmless). This only changes comments, so is a no-op for package clients. Fixes https://github.com/flutter/flutter/issues/143868 --- .../ios/Classes/CameraProperties.h | 78 ++---- .../camera_avfoundation/ios/Classes/FLTCam.h | 52 ++-- .../ios/Classes/FLTSavePhotoDelegate.h | 16 +- .../ios/Classes/FLTSavePhotoDelegate_Test.h | 4 +- .../ios/Classes/FLTThreadSafeEventChannel.h | 18 +- .../ios/Classes/FLTThreadSafeFlutterResult.h | 46 +--- .../ios/Classes/FLTThreadSafeFlutterResult.m | 4 +- .../ios/Classes/FLTThreadSafeMethodChannel.h | 16 +- .../Classes/FLTThreadSafeTextureRegistry.h | 38 +-- .../ios/Classes/FFSFileSelectorPlugin_Test.h | 8 +- .../ios/Classes/GoogleMapController_Test.h | 14 +- .../darwin/Classes/FLTGoogleSignInPlugin.m | 22 +- .../ios/Classes/FLTImagePickerPlugin.m | 38 ++- .../ios/Classes/FLTImagePickerPlugin_Test.h | 88 +++--- .../FLTPHPickerSaveImageToPathOperation.h | 20 +- .../FLTPHPickerSaveImageToPathOperation.m | 8 +- .../darwin/Classes/FLALocalAuthPlugin.m | 8 +- .../darwin/Classes/FLALocalAuthPlugin_Test.h | 8 +- .../ios/Classes/AlternateLanguageTestPlugin.m | 4 +- .../Classes/AlternateLanguageTestPlugin.m | 4 +- .../darwin/Classes/AVAssetTrackUtils.h | 12 +- .../darwin/Classes/FVPDisplayLink.h | 18 +- .../darwin/Classes/FVPVideoPlayerPlugin.m | 2 +- .../darwin/Classes/ios/FVPDisplayLink.m | 10 +- .../ios/Classes/FWFDataConverters.h | 253 ++++++++---------- .../ios/Classes/FWFHTTPCookieStoreHostApi.h | 8 +- .../ios/Classes/FWFInstanceManager.h | 123 ++++----- .../ios/Classes/FWFInstanceManager_Test.h | 22 +- .../Classes/FWFNavigationDelegateHostApi.h | 20 +- .../ios/Classes/FWFObjectHostApi.h | 20 +- .../ios/Classes/FWFPreferencesHostApi.h | 8 +- .../Classes/FWFScriptMessageHandlerHostApi.h | 20 +- .../Classes/FWFScrollViewDelegateHostApi.h | 20 +- .../ios/Classes/FWFScrollViewHostApi.h | 8 +- .../ios/Classes/FWFUIDelegateHostApi.h | 20 +- .../ios/Classes/FWFUIViewHostApi.h | 8 +- .../FWFURLAuthenticationChallengeHostApi.h | 18 +- .../ios/Classes/FWFURLCredentialHostApi.h | 10 +- .../ios/Classes/FWFURLHostApi.h | 29 +- .../Classes/FWFURLProtectionSpaceHostApi.h | 18 +- .../Classes/FWFUserContentControllerHostApi.h | 8 +- .../Classes/FWFWebViewConfigurationHostApi.h | 20 +- .../FWFWebViewFlutterWKWebViewExternalAPI.h | 38 ++- .../ios/Classes/FWFWebViewHostApi.h | 20 +- .../ios/Classes/FWFWebsiteDataStoreHostApi.h | 8 +- 45 files changed, 474 insertions(+), 761 deletions(-) diff --git a/packages/camera/camera_avfoundation/ios/Classes/CameraProperties.h b/packages/camera/camera_avfoundation/ios/Classes/CameraProperties.h index aef8fca535a4..406f36af501c 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/CameraProperties.h +++ b/packages/camera/camera_avfoundation/ios/Classes/CameraProperties.h @@ -9,9 +9,7 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - flash mode -/** - * Represents camera's flash mode. Mirrors `FlashMode` enum in flash_mode.dart. - */ +/// Represents camera's flash mode. Mirrors `FlashMode` enum in flash_mode.dart. typedef NS_ENUM(NSInteger, FLTFlashMode) { FLTFlashModeOff, FLTFlashModeAuto, @@ -22,23 +20,17 @@ typedef NS_ENUM(NSInteger, FLTFlashMode) { FLTFlashModeInvalid, }; -/** - * Gets FLTFlashMode from its string representation. - * @param mode a string representation of the FLTFlashMode. - */ +/// Gets FLTFlashMode from its string representation. +/// @param mode a string representation of the FLTFlashMode. extern FLTFlashMode FLTGetFLTFlashModeForString(NSString *mode); -/** - * Gets AVCaptureFlashMode from FLTFlashMode. - * @param mode flash mode. - */ +/// Gets AVCaptureFlashMode from FLTFlashMode. +/// @param mode flash mode. extern AVCaptureFlashMode FLTGetAVCaptureFlashModeForFLTFlashMode(FLTFlashMode mode); #pragma mark - exposure mode -/** - * Represents camera's exposure mode. Mirrors ExposureMode in camera.dart. - */ +/// Represents camera's exposure mode. Mirrors ExposureMode in camera.dart. typedef NS_ENUM(NSInteger, FLTExposureMode) { FLTExposureModeAuto, FLTExposureModeLocked, @@ -47,23 +39,17 @@ typedef NS_ENUM(NSInteger, FLTExposureMode) { FLTExposureModeInvalid, }; -/** - * Gets a string representation of exposure mode. - * @param mode exposure mode - */ +/// Gets a string representation of exposure mode. +/// @param mode exposure mode extern NSString *FLTGetStringForFLTExposureMode(FLTExposureMode mode); -/** - * Gets FLTExposureMode from its string representation. - * @param mode a string representation of the FLTExposureMode. - */ +/// Gets FLTExposureMode from its string representation. +/// @param mode a string representation of the FLTExposureMode. extern FLTExposureMode FLTGetFLTExposureModeForString(NSString *mode); #pragma mark - focus mode -/** - * Represents camera's focus mode. Mirrors FocusMode in camera.dart. - */ +/// Represents camera's focus mode. Mirrors FocusMode in camera.dart. typedef NS_ENUM(NSInteger, FLTFocusMode) { FLTFocusModeAuto, FLTFocusModeLocked, @@ -72,35 +58,25 @@ typedef NS_ENUM(NSInteger, FLTFocusMode) { FLTFocusModeInvalid, }; -/** - * Gets a string representation from FLTFocusMode. - * @param mode focus mode - */ +/// Gets a string representation from FLTFocusMode. +/// @param mode focus mode extern NSString *FLTGetStringForFLTFocusMode(FLTFocusMode mode); -/** - * Gets FLTFocusMode from its string representation. - * @param mode a string representation of focus mode. - */ +/// Gets FLTFocusMode from its string representation. +/// @param mode a string representation of focus mode. extern FLTFocusMode FLTGetFLTFocusModeForString(NSString *mode); #pragma mark - device orientation -/** - * Gets UIDeviceOrientation from its string representation. - */ +/// Gets UIDeviceOrientation from its string representation. extern UIDeviceOrientation FLTGetUIDeviceOrientationForString(NSString *orientation); -/** - * Gets a string representation of UIDeviceOrientation. - */ +/// Gets a string representation of UIDeviceOrientation. extern NSString *FLTGetStringForUIDeviceOrientation(UIDeviceOrientation orientation); #pragma mark - resolution preset -/** - * Represents camera's resolution present. Mirrors ResolutionPreset in camera.dart. - */ +/// Represents camera's resolution present. Mirrors ResolutionPreset in camera.dart. typedef NS_ENUM(NSInteger, FLTResolutionPreset) { FLTResolutionPresetVeryLow, FLTResolutionPresetLow, @@ -114,22 +90,16 @@ typedef NS_ENUM(NSInteger, FLTResolutionPreset) { FLTResolutionPresetInvalid, }; -/** - * Gets FLTResolutionPreset from its string representation. - * @param preset a string representation of FLTResolutionPreset. - */ +/// Gets FLTResolutionPreset from its string representation. +/// @param preset a string representation of FLTResolutionPreset. extern FLTResolutionPreset FLTGetFLTResolutionPresetForString(NSString *preset); #pragma mark - video format -/** - * Gets VideoFormat from its string representation. - */ +/// Gets VideoFormat from its string representation. extern OSType FLTGetVideoFormatFromString(NSString *videoFormatString); -/** - * Represents image format. Mirrors ImageFileFormat in camera.dart. - */ +/// Represents image format. Mirrors ImageFileFormat in camera.dart. typedef NS_ENUM(NSInteger, FCPFileFormat) { FCPFileFormatJPEG, FCPFileFormatHEIF, @@ -138,9 +108,7 @@ typedef NS_ENUM(NSInteger, FCPFileFormat) { #pragma mark - image extension -/** - * Gets a string representation of ImageFileFormat. - */ +/// Gets a string representation of ImageFileFormat. extern FCPFileFormat FCPGetFileFormatFromString(NSString *fileFormatString); NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h index f5979a829ca1..757c56d0a5c4 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h @@ -14,9 +14,7 @@ NS_ASSUME_NONNULL_BEGIN -/** - * A class that manages camera's state and performs camera operations. - */ +/// A class that manages camera's state and performs camera operations. @interface FLTCam : NSObject @property(readonly, nonatomic) AVCaptureDevice *captureDevice; @@ -52,13 +50,11 @@ NS_ASSUME_NONNULL_BEGIN - (void)close; - (void)startVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result; - (void)setImageFileFormat:(FCPFileFormat)fileFormat; -/** - * Starts recording a video with an optional streaming messenger. - * If the messenger is non-null then it will be called for each - * captured frame, allowing streaming concurrently with recording. - * - * @param messenger Nullable messenger for capturing each frame. - */ +/// Starts recording a video with an optional streaming messenger. +/// If the messenger is non-null then it will be called for each +/// captured frame, allowing streaming concurrently with recording. +/// +/// @param messenger Nullable messenger for capturing each frame. - (void)startVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result messengerForStreaming:(nullable NSObject *)messenger; - (void)stopVideoRecordingWithResult:(FLTThreadSafeFlutterResult *)result; @@ -72,28 +68,24 @@ NS_ASSUME_NONNULL_BEGIN - (void)setFocusModeWithResult:(FLTThreadSafeFlutterResult *)result mode:(NSString *)modeStr; - (void)applyFocusMode; -/** - * Acknowledges the receipt of one image stream frame. - * - * This should be called each time a frame is received. Failing to call it may - * cause later frames to be dropped instead of streamed. - */ +/// Acknowledges the receipt of one image stream frame. +/// +/// This should be called each time a frame is received. Failing to call it may +/// cause later frames to be dropped instead of streamed. - (void)receivedImageStreamData; -/** - * Applies FocusMode on the AVCaptureDevice. - * - * If the @c focusMode is set to FocusModeAuto the AVCaptureDevice is configured to use - * AVCaptureFocusModeContinuousModeAutoFocus when supported, otherwise it is set to - * AVCaptureFocusModeAutoFocus. If neither AVCaptureFocusModeContinuousModeAutoFocus nor - * AVCaptureFocusModeAutoFocus are supported focus mode will not be set. - * If @c focusMode is set to FocusModeLocked the AVCaptureDevice is configured to use - * AVCaptureFocusModeAutoFocus. If AVCaptureFocusModeAutoFocus is not supported focus mode will not - * be set. - * - * @param focusMode The focus mode that should be applied to the @captureDevice instance. - * @param captureDevice The AVCaptureDevice to which the @focusMode will be applied. - */ +/// Applies FocusMode on the AVCaptureDevice. +/// +/// If the @c focusMode is set to FocusModeAuto the AVCaptureDevice is configured to use +/// AVCaptureFocusModeContinuousModeAutoFocus when supported, otherwise it is set to +/// AVCaptureFocusModeAutoFocus. If neither AVCaptureFocusModeContinuousModeAutoFocus nor +/// AVCaptureFocusModeAutoFocus are supported focus mode will not be set. +/// If @c focusMode is set to FocusModeLocked the AVCaptureDevice is configured to use +/// AVCaptureFocusModeAutoFocus. If AVCaptureFocusModeAutoFocus is not supported focus mode will not +/// be set. +/// +/// @param focusMode The focus mode that should be applied to the @captureDevice instance. +/// @param captureDevice The AVCaptureDevice to which the @focusMode will be applied. - (void)applyFocusMode:(FLTFocusMode)focusMode onDevice:(AVCaptureDevice *)captureDevice; - (void)pausePreviewWithResult:(FLTThreadSafeFlutterResult *)result; - (void)resumePreviewWithResult:(FLTThreadSafeFlutterResult *)result; diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate.h b/packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate.h index 40e4562e4483..cec35d28f40b 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate.h +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate.h @@ -18,18 +18,14 @@ NS_ASSUME_NONNULL_BEGIN typedef void (^FLTSavePhotoDelegateCompletionHandler)(NSString *_Nullable path, NSError *_Nullable error); -/** - Delegate object that handles photo capture results. - */ +/// Delegate object that handles photo capture results. @interface FLTSavePhotoDelegate : NSObject -/** - * Initialize a photo capture delegate. - * @param path the path for captured photo file. - * @param ioQueue the queue on which captured photos are written to disk. - * @param completionHandler The completion handler block for save photo operations. Can - * be called from either main queue or IO queue. - */ +/// Initialize a photo capture delegate. +/// @param path the path for captured photo file. +/// @param ioQueue the queue on which captured photos are written to disk. +/// @param completionHandler The completion handler block for save photo operations. Can +/// be called from either main queue or IO queue. - (instancetype)initWithPath:(NSString *)path ioQueue:(dispatch_queue_t)ioQueue completionHandler:(FLTSavePhotoDelegateCompletionHandler)completionHandler; diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate_Test.h b/packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate_Test.h index 80e8f77a3b0e..79539e4bd40e 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate_Test.h +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate_Test.h @@ -4,9 +4,7 @@ #import "FLTSavePhotoDelegate.h" -/** - API exposed for unit tests. - */ +/// API exposed for unit tests. @interface FLTSavePhotoDelegate () /// The completion handler block for capture and save photo operations. diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeEventChannel.h b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeEventChannel.h index ddfa75487a28..20a1d4023a31 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeEventChannel.h +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeEventChannel.h @@ -6,22 +6,16 @@ NS_ASSUME_NONNULL_BEGIN -/** - * A thread safe wrapper for FlutterEventChannel that can be called from any thread, by dispatching - * its underlying engine calls to the main thread. - */ +/// A thread safe wrapper for FlutterEventChannel that can be called from any thread, by dispatching +/// its underlying engine calls to the main thread. @interface FLTThreadSafeEventChannel : NSObject -/** - * Creates a FLTThreadSafeEventChannel by wrapping a FlutterEventChannel object. - * @param channel The FlutterEventChannel object to be wrapped. - */ +/// Creates a FLTThreadSafeEventChannel by wrapping a FlutterEventChannel object. +/// @param channel The FlutterEventChannel object to be wrapped. - (instancetype)initWithEventChannel:(FlutterEventChannel *)channel; -/* - * Registers a handler on the main thread for stream setup requests from the Flutter side. - # The completion block runs on the main thread. - */ +/// Registers a handler on the main thread for stream setup requests from the Flutter side. +/// The completion block runs on the main thread. - (void)setStreamHandler:(nullable NSObject *)handler completion:(void (^)(void))completion; diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.h b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.h index 6677505671a3..09c4f43d535f 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.h +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.h @@ -6,56 +6,38 @@ NS_ASSUME_NONNULL_BEGIN -/** - * A thread safe wrapper for FlutterResult that can be called from any thread, by dispatching its - * underlying engine calls to the main thread. - */ +/// A thread safe wrapper for FlutterResult that can be called from any thread, by dispatching its +/// underlying engine calls to the main thread. @interface FLTThreadSafeFlutterResult : NSObject -/** - * Gets the original FlutterResult object wrapped by this FLTThreadSafeFlutterResult instance. - */ +/// Gets the original FlutterResult object wrapped by this FLTThreadSafeFlutterResult instance. @property(readonly, nonatomic) FlutterResult flutterResult; -/** - * Initializes with a FlutterResult object. - * @param result The FlutterResult object that the result will be given to. - */ +/// Initializes with a FlutterResult object. +/// @param result The FlutterResult object that the result will be given to. - (instancetype)initWithResult:(FlutterResult)result; -/** - * Sends a successful result on the main thread without any data. - */ +/// Sends a successful result on the main thread without any data. - (void)sendSuccess; -/** - * Sends a successful result on the main thread with data. - * @param data Result data that is send to the Flutter Dart side. - */ +/// Sends a successful result on the main thread with data. +/// @param data Result data that is send to the Flutter Dart side. - (void)sendSuccessWithData:(id)data; -/** - * Sends an NSError as result on the main thread. - * @param error Error that will be send as FlutterError. - */ +/// Sends an NSError as result on the main thread. +/// @param error Error that will be send as FlutterError. - (void)sendError:(NSError *)error; -/** - * Sends a FlutterError as result on the main thread. - * @param flutterError FlutterError that will be sent to the Flutter Dart side. - */ +/// Sends a FlutterError as result on the main thread. +/// @param flutterError FlutterError that will be sent to the Flutter Dart side. - (void)sendFlutterError:(FlutterError *)flutterError; -/** - * Sends a FlutterError as result on the main thread. - */ +/// Sends a FlutterError as result on the main thread. - (void)sendErrorWithCode:(NSString *)code message:(nullable NSString *)message details:(nullable id)details; -/** - * Sends FlutterMethodNotImplemented as result on the main thread. - */ +/// Sends FlutterMethodNotImplemented as result on the main thread. - (void)sendNotImplemented; @end diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.m b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.m index 283a0d6bc164..ee636cdd867d 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.m @@ -47,9 +47,7 @@ - (void)sendNotImplemented { [self send:FlutterMethodNotImplemented]; } -/** - * Sends result to flutterResult on the main thread. - */ +/// Sends result to flutterResult on the main thread. - (void)send:(id _Nullable)result { FLTEnsureToRunOnMainQueue(^{ // WARNING: Should not use weak self, because `FlutterResult`s are passed as arguments diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeMethodChannel.h b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeMethodChannel.h index 0f6611db03ce..1ca0a7312e45 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeMethodChannel.h +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeMethodChannel.h @@ -6,21 +6,15 @@ NS_ASSUME_NONNULL_BEGIN -/** - * A thread safe wrapper for FlutterMethodChannel that can be called from any thread, by dispatching - * its underlying engine calls to the main thread. - */ +/// A thread safe wrapper for FlutterMethodChannel that can be called from any thread, by +/// dispatching its underlying engine calls to the main thread. @interface FLTThreadSafeMethodChannel : NSObject -/** - * Creates a FLTThreadSafeMethodChannel by wrapping a FlutterMethodChannel object. - * @param channel The FlutterMethodChannel object to be wrapped. - */ +/// Creates a FLTThreadSafeMethodChannel by wrapping a FlutterMethodChannel object. +/// @param channel The FlutterMethodChannel object to be wrapped. - (instancetype)initWithMethodChannel:(FlutterMethodChannel *)channel; -/** - * Invokes the specified flutter method on the main thread with the specified arguments. - */ +/// Invokes the specified flutter method on the main thread with the specified arguments. - (void)invokeMethod:(NSString *)method arguments:(nullable id)arguments; @end diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeTextureRegistry.h b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeTextureRegistry.h index 030e2dbc7818..2f80f684e426 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeTextureRegistry.h +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeTextureRegistry.h @@ -6,39 +6,29 @@ NS_ASSUME_NONNULL_BEGIN -/** - * A thread safe wrapper for FlutterTextureRegistry that can be called from any thread, by - * dispatching its underlying engine calls to the main thread. - */ +/// A thread safe wrapper for FlutterTextureRegistry that can be called from any thread, by +/// dispatching its underlying engine calls to the main thread. @interface FLTThreadSafeTextureRegistry : NSObject -/** - * Creates a FLTThreadSafeTextureRegistry by wrapping an object conforming to - * FlutterTextureRegistry. - * @param registry The FlutterTextureRegistry object to be wrapped. - */ +/// Creates a FLTThreadSafeTextureRegistry by wrapping an object conforming to +/// FlutterTextureRegistry. +/// @param registry The FlutterTextureRegistry object to be wrapped. - (instancetype)initWithTextureRegistry:(NSObject *)registry; -/** - * Registers a `FlutterTexture` on the main thread for usage in Flutter and returns an id that can - * be used to reference that texture when calling into Flutter with channels. - * - * On success the completion block completes with the pointer to the registered texture, else with - * 0. The completion block runs on the main thread. - */ +/// Registers a `FlutterTexture` on the main thread for usage in Flutter and returns an id that can +/// be used to reference that texture when calling into Flutter with channels. +/// +/// On success the completion block completes with the pointer to the registered texture, else with +/// 0. The completion block runs on the main thread. - (void)registerTexture:(NSObject *)texture completion:(void (^)(int64_t))completion; -/** - * Notifies the Flutter engine on the main thread that the given texture has been updated. - */ +/// Notifies the Flutter engine on the main thread that the given texture has been updated. - (void)textureFrameAvailable:(int64_t)textureId; -/** - * Notifies the Flutter engine on the main thread to unregister a `FlutterTexture` that has been - * previously registered with `registerTexture:`. - * @param textureId The result that was previously returned from `registerTexture:`. - */ +/// Notifies the Flutter engine on the main thread to unregister a `FlutterTexture` that has been +/// previously registered with `registerTexture:`. +/// @param textureId The result that was previously returned from `registerTexture:`. - (void)unregisterTexture:(int64_t)textureId; @end diff --git a/packages/file_selector/file_selector_ios/ios/Classes/FFSFileSelectorPlugin_Test.h b/packages/file_selector/file_selector_ios/ios/Classes/FFSFileSelectorPlugin_Test.h index f71a8ae109e6..5935f037f6c4 100644 --- a/packages/file_selector/file_selector_ios/ios/Classes/FFSFileSelectorPlugin_Test.h +++ b/packages/file_selector/file_selector_ios/ios/Classes/FFSFileSelectorPlugin_Test.h @@ -9,14 +9,10 @@ // This header is available in the Test module. Import via "@import file_selector_ios.Test;". @interface FFSFileSelectorPlugin () -/** - * Overrides the view controller used for presenting the document picker. - */ +/// Overrides the view controller used for presenting the document picker. @property(nonatomic) UIViewController *_Nullable presentingViewControllerOverride; -/** - * Overrides the UIDocumentPickerViewController used for file picking. - */ +/// Overrides the UIDocumentPickerViewController used for file picking. @property(nonatomic) UIDocumentPickerViewController *_Nullable documentPickerViewControllerOverride; @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController_Test.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController_Test.h index 84f6f7ca485f..1377b68e9b6e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController_Test.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController_Test.h @@ -9,14 +9,12 @@ NS_ASSUME_NONNULL_BEGIN @interface FLTGoogleMapController (Test) -/** - * Initializes a map controller with a concrete map view. - * - * @param mapView A map view that will be displayed by the controller - * @param viewId A unique identifier for the controller. - * @param args Parameters for initialising the map view. - * @param registrar The plugin registrar passed from Flutter. - */ +/// Initializes a map controller with a concrete map view. +/// +/// @param mapView A map view that will be displayed by the controller +/// @param viewId A unique identifier for the controller. +/// @param args Parameters for initialising the map view. +/// @param registrar The plugin registrar passed from Flutter. - (instancetype)initWithMapView:(GMSMapView *)mapView viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/Classes/FLTGoogleSignInPlugin.m b/packages/google_sign_in/google_sign_in_ios/darwin/Classes/FLTGoogleSignInPlugin.m index d7a5cd85303c..99ee0348f518 100644 --- a/packages/google_sign_in/google_sign_in_ios/darwin/Classes/FLTGoogleSignInPlugin.m +++ b/packages/google_sign_in/google_sign_in_ios/darwin/Classes/FLTGoogleSignInPlugin.m @@ -329,18 +329,16 @@ - (UIViewController *)topViewController { #pragma clang diagnostic pop } -/** - * This method recursively iterate through the view hierarchy - * to return the top most view controller. - * - * It supports the following scenarios: - * - * - The view controller is presenting another view. - * - The view controller is a UINavigationController. - * - The view controller is a UITabBarController. - * - * @return The top most view controller. - */ +/// This method recursively iterate through the view hierarchy +/// to return the top most view controller. +/// +/// It supports the following scenarios: +/// +/// - The view controller is presenting another view. +/// - The view controller is a UINavigationController. +/// - The view controller is a UITabBarController. +/// +/// @return The top most view controller. - (UIViewController *)topViewControllerFromViewController:(UIViewController *)viewController { if ([viewController isKindOfClass:[UINavigationController class]]) { UINavigationController *navigationController = (UINavigationController *)viewController; diff --git a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m index f699ca98c2dd..10081a8c13ec 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m @@ -31,12 +31,10 @@ - (instancetype)initWithResult:(nonnull FlutterResultAdapter)result { @interface FLTImagePickerPlugin () -/** - * The UIImagePickerController instances that will be used when a new - * controller would normally be created. Each call to - * createImagePickerController will remove the current first element from - * the array. - */ +/// The UIImagePickerController instances that will be used when a new +/// controller would normally be created. Each call to +/// createImagePickerController will remove the current first element from +/// the array. @property(strong, nonatomic) NSMutableArray *imagePickerControllerOverrides; @@ -84,11 +82,9 @@ - (UIViewController *)viewControllerWithWindow:(UIWindow *)window { return topController; } -/** - * Returns the UIImagePickerControllerCameraDevice to use given [source]. - * - * @param source The source specification from Dart. - */ +/// Returns the UIImagePickerControllerCameraDevice to use given [source]. +/// +/// @param source The source specification from Dart. - (UIImagePickerControllerCameraDevice)cameraDeviceForSource:(FLTSourceSpecification *)source { switch (source.camera) { case FLTSourceCameraFront: @@ -287,12 +283,10 @@ - (void)pickVideoWithSource:(nonnull FLTSourceSpecification *)source #pragma mark - -/** - * If a call is still in progress, cancels it by returning an error and then clearing state. - * - * TODO(stuartmorgan): Eliminate this, and instead track context per image picker (e.g., using - * associated objects). - */ +/// If a call is still in progress, cancels it by returning an error and then clearing state. +/// +/// TODO(stuartmorgan): Eliminate this, and instead track context per image picker (e.g., using +/// associated objects). - (void)cancelInProgressCall { if (self.callContext) { [self sendCallResultWithError:[FlutterError errorWithCode:@"multiple_request" @@ -687,12 +681,10 @@ - (void)sendCallResultWithSavedPathList:(nullable NSArray *)pathList { self.callContext = nil; } -/** - * Sends the given error via `callContext.result` as the result of the original platform channel - * method call, clearing the in-progress call state. - * - * @param error The error to return. - */ +/// Sends the given error via `callContext.result` as the result of the original platform channel +/// method call, clearing the in-progress call state. +/// +/// @param error The error to return. - (void)sendCallResultWithError:(FlutterError *)error { if (!self.callContext) { return; diff --git a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin_Test.h b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin_Test.h index 99d3ef6e195b..845cdcf878a9 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin_Test.h +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin_Test.h @@ -10,95 +10,77 @@ NS_ASSUME_NONNULL_BEGIN -/** - * The return handler used for all method calls, which internally adapts the provided result list - * to return either a list or a single element depending on the original call. - */ +/// The return handler used for all method calls, which internally adapts the provided result list +/// to return either a list or a single element depending on the original call. typedef void (^FlutterResultAdapter)(NSArray *_Nullable, FlutterError *_Nullable); -/** - * A container class for context to use when handling a method call from the Dart side. - */ +/// A container class for context to use when handling a method call from the Dart side. @interface FLTImagePickerMethodCallContext : NSObject -/** - * Initializes a new context that calls |result| on completion of the operation. - */ +/// Initializes a new context that calls |result| on completion of the operation. - (instancetype)initWithResult:(nonnull FlutterResultAdapter)result; -/** The callback to provide results to the Dart caller. */ +/// The callback to provide results to the Dart caller. @property(nonatomic, copy, nonnull) FlutterResultAdapter result; -/** - * The maximum size to enforce on the results. - * - * If nil, no resizing is done. - */ +/// The maximum size to enforce on the results. +/// +/// If nil, no resizing is done. @property(nonatomic, strong, nullable) FLTMaxSize *maxSize; -/** - * The image quality to resample the results to. - * - * If nil, no resampling is done. - */ +/// The image quality to resample the results to. +/// +/// If nil, no resampling is done. @property(nonatomic, strong, nullable) NSNumber *imageQuality; -/** Maximum number of images to select. 0 indicates no maximum. */ +/// Maximum number of images to select. 0 indicates no maximum. @property(nonatomic, assign) int maxImageCount; -/** Whether the image should be picked with full metadata (requires gallery permissions) */ +/// Whether the image should be picked with full metadata (requires gallery permissions) @property(nonatomic, assign) BOOL requestFullMetadata; -/** Whether the picker should include videos in the list*/ +/// Whether the picker should include videos in the list*/ @property(nonatomic, assign) BOOL includeVideo; @end #pragma mark - -/** Methods exposed for unit testing. */ +/// Methods exposed for unit testing. @interface FLTImagePickerPlugin () -/** - * The context of the Flutter method call that is currently being handled, if any. - */ +/// The context of the Flutter method call that is currently being handled, if any. @property(strong, nonatomic, nullable) FLTImagePickerMethodCallContext *callContext; - (UIViewController *)viewControllerWithWindow:(nullable UIWindow *)window; -/** - * Validates the provided paths list, then sends it via `callContext.result` as the result of the - * original platform channel method call, clearing the in-progress call state. - * - * @param pathList The paths to return. nil indicates a cancelled operation. - */ +/// Validates the provided paths list, then sends it via `callContext.result` as the result of the +/// original platform channel method call, clearing the in-progress call state. +/// +/// @param pathList The paths to return. nil indicates a cancelled operation. - (void)sendCallResultWithSavedPathList:(nullable NSArray *)pathList; -/** - * Tells the delegate that the user cancelled the pick operation. - * - * Your delegate’s implementation of this method should dismiss the picker view - * by calling the dismissModalViewControllerAnimated: method of the parent - * view controller. - * - * Implementation of this method is optional, but expected. - * - * @param picker The controller object managing the image picker interface. - */ +/// Tells the delegate that the user cancelled the pick operation. +/// +/// Your delegate’s implementation of this method should dismiss the picker view +/// by calling the dismissModalViewControllerAnimated: method of the parent +/// view controller. +/// +/// Implementation of this method is optional, but expected. +/// +/// @param picker The controller object managing the image picker interface. - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker; -/** - * Sets UIImagePickerController instances that will be used when a new - * controller would normally be created. Each call to - * createImagePickerController will remove the current first element from - * the array. - * - * Should be used for testing purposes only. - */ +/// Sets UIImagePickerController instances that will be used when a new +/// controller would normally be created. Each call to +/// createImagePickerController will remove the current first element from +/// the array. +/// +/// Should be used for testing purposes only. - (void)setImagePickerControllerOverrides: (NSArray *)imagePickerControllers; diff --git a/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.h b/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.h index 00c1f1dacd6c..dcafc42efe49 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.h +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.h @@ -14,17 +14,15 @@ NS_ASSUME_NONNULL_BEGIN /// Returns either the saved path, or an error. Both cannot be set. typedef void (^FLTGetSavedPath)(NSString *_Nullable savedPath, FlutterError *_Nullable error); -/*! - @class FLTPHPickerSaveImageToPathOperation - - @brief The FLTPHPickerSaveImageToPathOperation class - - @discussion This class was implemented to handle saved image paths and populate the pathList - with the final result by using GetSavedPath type block. - - @superclass SuperClass: NSOperation\n - @helps It helps FLTImagePickerPlugin class. - */ +/// @class FLTPHPickerSaveImageToPathOperation +/// +/// @brief The FLTPHPickerSaveImageToPathOperation class +/// +/// @discussion This class was implemented to handle saved image paths and populate the pathList +/// with the final result by using GetSavedPath type block. +/// +/// @superclass SuperClass: NSOperation\n +/// @helps It helps FLTImagePickerPlugin class. @interface FLTPHPickerSaveImageToPathOperation : NSOperation - (instancetype)initWithResult:(PHPickerResult *)result diff --git a/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m index 3476721ae615..bf3922ae7d3d 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m @@ -124,9 +124,7 @@ - (void)start { } } -/** - * Processes the image. - */ +/// Processes the image. - (void)processImage:(NSData *)pickerImageData API_AVAILABLE(ios(14)) { UIImage *localImage = [[UIImage alloc] initWithData:pickerImageData]; @@ -190,9 +188,7 @@ - (void)processImage:(NSData *)pickerImageData API_AVAILABLE(ios(14)) { } } -/** - * Processes the video. - */ +/// Processes the video. - (void)processVideo API_AVAILABLE(ios(14)) { NSString *typeIdentifier = self.result.itemProvider.registeredTypeIdentifiers.firstObject; [self.result.itemProvider diff --git a/packages/local_auth/local_auth_darwin/darwin/Classes/FLALocalAuthPlugin.m b/packages/local_auth/local_auth_darwin/darwin/Classes/FLALocalAuthPlugin.m index 6b9ef39f6e2d..e57adcdff30d 100644 --- a/packages/local_auth/local_auth_darwin/darwin/Classes/FLALocalAuthPlugin.m +++ b/packages/local_auth/local_auth_darwin/darwin/Classes/FLALocalAuthPlugin.m @@ -8,9 +8,7 @@ typedef void (^FLADAuthCompletion)(FLADAuthResultDetails *_Nullable, FlutterError *_Nullable); -/** - * A default context factory that wraps standard LAContext allocation. - */ +/// A default context factory that wraps standard LAContext allocation. @interface FLADefaultAuthContextFactory : NSObject @end @@ -22,9 +20,7 @@ - (LAContext *)createAuthContext { #pragma mark - -/** - * A data container for sticky auth state. - */ +/// A data container for sticky auth state. @interface FLAStickyAuthState : NSObject @property(nonatomic, strong, nonnull) FLADAuthOptions *options; @property(nonatomic, strong, nonnull) FLADAuthStrings *strings; diff --git a/packages/local_auth/local_auth_darwin/darwin/Classes/FLALocalAuthPlugin_Test.h b/packages/local_auth/local_auth_darwin/darwin/Classes/FLALocalAuthPlugin_Test.h index 79ac48ec8cbc..eb12b29fae3b 100644 --- a/packages/local_auth/local_auth_darwin/darwin/Classes/FLALocalAuthPlugin_Test.h +++ b/packages/local_auth/local_auth_darwin/darwin/Classes/FLALocalAuthPlugin_Test.h @@ -5,17 +5,13 @@ #import #import -/** - * Protocol for a source of LAContext instances. Used to allow context injection in unit tests. - */ +/// Protocol for a source of LAContext instances. Used to allow context injection in unit tests. @protocol FLADAuthContextFactory - (LAContext *)createAuthContext; @end @interface FLALocalAuthPlugin () -/** - * Returns an instance that uses the given factory to create LAContexts. - */ +/// Returns an instance that uses the given factory to create LAContexts. - (instancetype)initWithContextFactory:(NSObject *)factory NS_DESIGNATED_INITIALIZER; @end diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.m b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.m index d1e42a10ccaa..e0feca739952 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.m +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.m @@ -10,9 +10,7 @@ @interface AlternateLanguageTestPlugin () @property(nonatomic) FlutterIntegrationCoreApi *flutterAPI; @end -/** - * This plugin handles the native side of the integration tests in example/integration_test/. - */ +/// This plugin handles the native side of the integration tests in example/integration_test/. @implementation AlternateLanguageTestPlugin + (void)registerWithRegistrar:(NSObject *)registrar { AlternateLanguageTestPlugin *plugin = [[AlternateLanguageTestPlugin alloc] init]; diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/AlternateLanguageTestPlugin.m b/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/AlternateLanguageTestPlugin.m index e9d679f42151..671116aacaec 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/AlternateLanguageTestPlugin.m +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/AlternateLanguageTestPlugin.m @@ -10,9 +10,7 @@ @interface AlternateLanguageTestPlugin () @property(nonatomic) FlutterIntegrationCoreApi *flutterAPI; @end -/** - * This plugin handles the native side of the integration tests in example/integration_test/. - */ +/// This plugin handles the native side of the integration tests in example/integration_test/. @implementation AlternateLanguageTestPlugin + (void)registerWithRegistrar:(NSObject *)registrar { AlternateLanguageTestPlugin *plugin = [[AlternateLanguageTestPlugin alloc] init]; diff --git a/packages/video_player/video_player_avfoundation/darwin/Classes/AVAssetTrackUtils.h b/packages/video_player/video_player_avfoundation/darwin/Classes/AVAssetTrackUtils.h index a09af39a90ef..30dab06f18f2 100644 --- a/packages/video_player/video_player_avfoundation/darwin/Classes/AVAssetTrackUtils.h +++ b/packages/video_player/video_player_avfoundation/darwin/Classes/AVAssetTrackUtils.h @@ -4,11 +4,9 @@ #import -/** - * Returns a standardized transform - * according to the orientation of the track. - * - * Note: https://stackoverflow.com/questions/64161544 - * `AVAssetTrack.preferredTransform` can have wrong `tx` and `ty`. - */ +/// Returns a standardized transform +/// according to the orientation of the track. +/// +/// Note: https://stackoverflow.com/questions/64161544 +/// `AVAssetTrack.preferredTransform` can have wrong `tx` and `ty`. CGAffineTransform FVPGetStandardizedTransformForTrack(AVAssetTrack* track); diff --git a/packages/video_player/video_player_avfoundation/darwin/Classes/FVPDisplayLink.h b/packages/video_player/video_player_avfoundation/darwin/Classes/FVPDisplayLink.h index 67c0bf75f3f5..80d400629b25 100644 --- a/packages/video_player/video_player_avfoundation/darwin/Classes/FVPDisplayLink.h +++ b/packages/video_player/video_player_avfoundation/darwin/Classes/FVPDisplayLink.h @@ -13,19 +13,15 @@ // A cross-platform display link abstraction. @interface FVPDisplayLink : NSObject -/** - * Whether the display link is currently running (i.e., firing events). - * - * Defaults to NO. - */ +/// Whether the display link is currently running (i.e., firing events). +/// +/// Defaults to NO. @property(nonatomic, assign) BOOL running; -/** - * Initializes a display link that calls the given callback when fired. - * - * The display link starts paused, so must be started, by setting 'running' to YES, before the - * callback will fire. - */ +/// Initializes a display link that calls the given callback when fired. +/// +/// The display link starts paused, so must be started, by setting 'running' to YES, before the +/// callback will fire. - (instancetype)initWithRegistrar:(id)registrar callback:(void (^)(void))callback NS_DESIGNATED_INITIALIZER; diff --git a/packages/video_player/video_player_avfoundation/darwin/Classes/FVPVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/darwin/Classes/FVPVideoPlayerPlugin.m index 70ede124f8e6..d90f57d91dec 100644 --- a/packages/video_player/video_player_avfoundation/darwin/Classes/FVPVideoPlayerPlugin.m +++ b/packages/video_player/video_player_avfoundation/darwin/Classes/FVPVideoPlayerPlugin.m @@ -72,7 +72,7 @@ - (AVPlayerItemVideoOutput *)videoOutputWithPixelBufferAttributes: } @end -/** Non-test implementation of the diplay link factory. */ +/// Non-test implementation of the diplay link factory. @interface FVPDefaultDisplayLinkFactory : NSObject @end diff --git a/packages/video_player/video_player_avfoundation/darwin/Classes/ios/FVPDisplayLink.m b/packages/video_player/video_player_avfoundation/darwin/Classes/ios/FVPDisplayLink.m index 51039067f38c..505001bc223f 100644 --- a/packages/video_player/video_player_avfoundation/darwin/Classes/ios/FVPDisplayLink.m +++ b/packages/video_player/video_player_avfoundation/darwin/Classes/ios/FVPDisplayLink.m @@ -7,17 +7,15 @@ #import #import -/** - * A proxy object to act as a CADisplayLink target, to avoid retain loops, since FVPDisplayLink - * owns its CADisplayLink, but CADisplayLink retains its target. - */ +/// A proxy object to act as a CADisplayLink target, to avoid retain loops, since FVPDisplayLink +/// owns its CADisplayLink, but CADisplayLink retains its target. @interface FVPDisplayLinkTarget : NSObject @property(nonatomic) void (^callback)(void); -/** Initializes a target object that runs the given callback when onDisplayLink: is called. */ +/// Initializes a target object that runs the given callback when onDisplayLink: is called. - (instancetype)initWithCallback:(void (^)(void))callback; -/** Method to be called when a CADisplayLink fires. */ +/// Method to be called when a CADisplayLink fires. - (void)onDisplayLink:(CADisplayLink *)link; @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h index e9405f3bb90d..c4d37571d62e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h @@ -8,209 +8,168 @@ NS_ASSUME_NONNULL_BEGIN -/** - * Converts an FWFNSUrlRequestData to an NSURLRequest. - * - * @param data The data object containing information to create an NSURLRequest. - * - * @return An NSURLRequest or nil if data could not be converted. - */ +/// Converts an FWFNSUrlRequestData to an NSURLRequest. +/// +/// @param data The data object containing information to create an NSURLRequest. +/// +/// @return An NSURLRequest or nil if data could not be converted. extern NSURLRequest *_Nullable FWFNativeNSURLRequestFromRequestData(FWFNSUrlRequestData *data); -/** - * Converts an FWFNSHttpCookieData to an NSHTTPCookie. - * - * @param data The data object containing information to create an NSHTTPCookie. - * - * @return An NSHTTPCookie or nil if data could not be converted. - */ +/// Converts an FWFNSHttpCookieData to an NSHTTPCookie. +/// +/// @param data The data object containing information to create an NSHTTPCookie. +/// +/// @return An NSHTTPCookie or nil if data could not be converted. extern NSHTTPCookie *_Nullable FWFNativeNSHTTPCookieFromCookieData(FWFNSHttpCookieData *data); -/** - * Converts an FWFNSKeyValueObservingOptionsEnumData to an NSKeyValueObservingOptions. - * - * @param data The data object containing information to create an NSKeyValueObservingOptions. - * - * @return An NSKeyValueObservingOptions or -1 if data could not be converted. - */ +/// Converts an FWFNSKeyValueObservingOptionsEnumData to an NSKeyValueObservingOptions. +/// +/// @param data The data object containing information to create an NSKeyValueObservingOptions. +/// +/// @return An NSKeyValueObservingOptions or -1 if data could not be converted. extern NSKeyValueObservingOptions FWFNativeNSKeyValueObservingOptionsFromEnumData( FWFNSKeyValueObservingOptionsEnumData *data); -/** - * Converts an FWFNSHTTPCookiePropertyKeyEnumData to an NSHTTPCookiePropertyKey. - * - * @param data The data object containing information to create an NSHTTPCookiePropertyKey. - * - * @return An NSHttpCookiePropertyKey or nil if data could not be converted. - */ +/// Converts an FWFNSHTTPCookiePropertyKeyEnumData to an NSHTTPCookiePropertyKey. +/// +/// @param data The data object containing information to create an NSHTTPCookiePropertyKey. +/// +/// @return An NSHttpCookiePropertyKey or nil if data could not be converted. extern NSHTTPCookiePropertyKey _Nullable FWFNativeNSHTTPCookiePropertyKeyFromEnumData( FWFNSHttpCookiePropertyKeyEnumData *data); -/** - * Converts a WKUserScriptData to a WKUserScript. - * - * @param data The data object containing information to create a WKUserScript. - * - * @return A WKUserScript or nil if data could not be converted. - */ +/// Converts a WKUserScriptData to a WKUserScript. +/// +/// @param data The data object containing information to create a WKUserScript. +/// +/// @return A WKUserScript or nil if data could not be converted. extern WKUserScript *FWFNativeWKUserScriptFromScriptData(FWFWKUserScriptData *data); -/** - * Converts an FWFWKUserScriptInjectionTimeEnumData to a WKUserScriptInjectionTime. - * - * @param data The data object containing information to create a WKUserScriptInjectionTime. - * - * @return A WKUserScriptInjectionTime or -1 if data could not be converted. - */ +/// Converts an FWFWKUserScriptInjectionTimeEnumData to a WKUserScriptInjectionTime. +/// +/// @param data The data object containing information to create a WKUserScriptInjectionTime. +/// +/// @return A WKUserScriptInjectionTime or -1 if data could not be converted. extern WKUserScriptInjectionTime FWFNativeWKUserScriptInjectionTimeFromEnumData( FWFWKUserScriptInjectionTimeEnumData *data); -/** - * Converts an FWFWKAudiovisualMediaTypeEnumData to a WKAudiovisualMediaTypes. - * - * @param data The data object containing information to create a WKAudiovisualMediaTypes. - * - * @return A WKAudiovisualMediaType or -1 if data could not be converted. - */ +/// Converts an FWFWKAudiovisualMediaTypeEnumData to a WKAudiovisualMediaTypes. +/// +/// @param data The data object containing information to create a WKAudiovisualMediaTypes. +/// +/// @return A WKAudiovisualMediaType or -1 if data could not be converted. extern WKAudiovisualMediaTypes FWFNativeWKAudiovisualMediaTypeFromEnumData( FWFWKAudiovisualMediaTypeEnumData *data); -/** - * Converts an FWFWKWebsiteDataTypeEnumData to a WKWebsiteDataType. - * - * @param data The data object containing information to create a WKWebsiteDataType. - * - * @return A WKWebsiteDataType or nil if data could not be converted. - */ +/// Converts an FWFWKWebsiteDataTypeEnumData to a WKWebsiteDataType. +/// +/// @param data The data object containing information to create a WKWebsiteDataType. +/// +/// @return A WKWebsiteDataType or nil if data could not be converted. extern NSString *_Nullable FWFNativeWKWebsiteDataTypeFromEnumData( FWFWKWebsiteDataTypeEnumData *data); -/** - * Converts a WKNavigationAction to an FWFWKNavigationActionData. - * - * @param action The object containing information to create a WKNavigationActionData. - * - * @return A FWFWKNavigationActionData. - */ +/// Converts a WKNavigationAction to an FWFWKNavigationActionData. +/// +/// @param action The object containing information to create a WKNavigationActionData. +/// +/// @return A FWFWKNavigationActionData. extern FWFWKNavigationActionData *FWFWKNavigationActionDataFromNativeWKNavigationAction( WKNavigationAction *action); -/** - * Converts a NSURLRequest to an FWFNSUrlRequestData. - * - * @param request The object containing information to create a WKNavigationActionData. - * - * @return A FWFNSUrlRequestData. - */ +/// Converts a NSURLRequest to an FWFNSUrlRequestData. +/// +/// @param request The object containing information to create a WKNavigationActionData. +/// +/// @return A FWFNSUrlRequestData. extern FWFNSUrlRequestData *FWFNSUrlRequestDataFromNativeNSURLRequest(NSURLRequest *request); -/** - * Converts a WKFrameInfo to an FWFWKFrameInfoData. - * - * @param info The object containing information to create a FWFWKFrameInfoData. - * - * @return A FWFWKFrameInfoData. - */ +/// Converts a WKFrameInfo to an FWFWKFrameInfoData. +/// +/// @param info The object containing information to create a FWFWKFrameInfoData. +/// +/// @return A FWFWKFrameInfoData. extern FWFWKFrameInfoData *FWFWKFrameInfoDataFromNativeWKFrameInfo(WKFrameInfo *info); -/** - * Converts an FWFWKNavigationActionPolicyEnumData to a WKNavigationActionPolicy. - * - * @param data The data object containing information to create a WKNavigationActionPolicy. - * - * @return A WKNavigationActionPolicy or -1 if data could not be converted. - */ +/// Converts an FWFWKNavigationActionPolicyEnumData to a WKNavigationActionPolicy. +/// +/// @param data The data object containing information to create a WKNavigationActionPolicy. +/// +/// @return A WKNavigationActionPolicy or -1 if data could not be converted. extern WKNavigationActionPolicy FWFNativeWKNavigationActionPolicyFromEnumData( FWFWKNavigationActionPolicyEnumData *data); -/** - * Converts a NSError to an FWFNSErrorData. - * - * @param error The object containing information to create a FWFNSErrorData. - * - * @return A FWFNSErrorData. - */ +/// Converts a NSError to an FWFNSErrorData. +/// +/// @param error The object containing information to create a FWFNSErrorData. +/// +/// @return A FWFNSErrorData. extern FWFNSErrorData *FWFNSErrorDataFromNativeNSError(NSError *error); -/** - * Converts an NSKeyValueChangeKey to a FWFNSKeyValueChangeKeyEnumData. - * - * @param key The data object containing information to create a FWFNSKeyValueChangeKeyEnumData. - * - * @return A FWFNSKeyValueChangeKeyEnumData. - */ +/// Converts an NSKeyValueChangeKey to a FWFNSKeyValueChangeKeyEnumData. +/// +/// @param key The data object containing information to create a FWFNSKeyValueChangeKeyEnumData. +/// +/// @return A FWFNSKeyValueChangeKeyEnumData. extern FWFNSKeyValueChangeKeyEnumData *FWFNSKeyValueChangeKeyEnumDataFromNativeNSKeyValueChangeKey( NSKeyValueChangeKey key); -/** - * Converts a WKScriptMessage to an FWFWKScriptMessageData. - * - * @param message The object containing information to create a FWFWKScriptMessageData. - * - * @return A FWFWKScriptMessageData. - */ +/// Converts a WKScriptMessage to an FWFWKScriptMessageData. +/// +/// @param message The object containing information to create a FWFWKScriptMessageData. +/// +/// @return A FWFWKScriptMessageData. extern FWFWKScriptMessageData *FWFWKScriptMessageDataFromNativeWKScriptMessage( WKScriptMessage *message); -/** - * Converts a WKNavigationType to an FWFWKNavigationType. - * - * @param type The object containing information to create a FWFWKNavigationType - * - * @return A FWFWKNavigationType. - */ +/// Converts a WKNavigationType to an FWFWKNavigationType. +/// +/// @param type The object containing information to create a FWFWKNavigationType +/// +/// @return A FWFWKNavigationType. extern FWFWKNavigationType FWFWKNavigationTypeFromNativeWKNavigationType(WKNavigationType type); -/** - * Converts a WKSecurityOrigin to an FWFWKSecurityOriginData. - * - * @param origin The object containing information to create an FWFWKSecurityOriginData. - * - * @return An FWFWKSecurityOriginData. - */ +/// Converts a WKSecurityOrigin to an FWFWKSecurityOriginData. +/// +/// @param origin The object containing information to create an FWFWKSecurityOriginData. +/// +/// @return An FWFWKSecurityOriginData. extern FWFWKSecurityOriginData *FWFWKSecurityOriginDataFromNativeWKSecurityOrigin( WKSecurityOrigin *origin); -/** - * Converts an FWFWKPermissionDecisionData to a WKPermissionDecision. - * - * @param data The data object containing information to create a WKPermissionDecision. - * - * @return A WKPermissionDecision or -1 if data could not be converted. - */ +/// Converts an FWFWKPermissionDecisionData to a WKPermissionDecision. +/// +/// @param data The data object containing information to create a WKPermissionDecision. +/// +/// @return A WKPermissionDecision or -1 if data could not be converted. API_AVAILABLE(ios(15.0)) extern WKPermissionDecision FWFNativeWKPermissionDecisionFromData( FWFWKPermissionDecisionData *data); -/** - * Converts an WKMediaCaptureType to a FWFWKMediaCaptureTypeData. - * - * @param type The data object containing information to create a FWFWKMediaCaptureTypeData. - * - * @return A FWFWKMediaCaptureTypeData or nil if data could not be converted. - */ +/// Converts an WKMediaCaptureType to a FWFWKMediaCaptureTypeData. +/// +/// @param type The data object containing information to create a FWFWKMediaCaptureTypeData. +/// +/// @return A FWFWKMediaCaptureTypeData or nil if data could not be converted. API_AVAILABLE(ios(15.0)) extern FWFWKMediaCaptureTypeData *FWFWKMediaCaptureTypeDataFromNativeWKMediaCaptureType( WKMediaCaptureType type); -/** - * Converts an FWFNSUrlSessionAuthChallengeDisposition to an NSURLSessionAuthChallengeDisposition. - * - * @param value The object containing information to create an NSURLSessionAuthChallengeDisposition. - * - * @return A NSURLSessionAuthChallengeDisposition or -1 if data could not be converted. - */ +/// Converts an FWFNSUrlSessionAuthChallengeDisposition to an NSURLSessionAuthChallengeDisposition. +/// +/// @param value The object containing information to create an +/// NSURLSessionAuthChallengeDisposition. +/// +/// @return A NSURLSessionAuthChallengeDisposition or -1 if data could not be converted. extern NSURLSessionAuthChallengeDisposition FWFNativeNSURLSessionAuthChallengeDispositionFromFWFNSUrlSessionAuthChallengeDisposition( FWFNSUrlSessionAuthChallengeDisposition value); -/** - * Converts an FWFNSUrlCredentialPersistence to an NSURLCredentialPersistence. - * - * @param value The object containing information to create an NSURLCredentialPersistence. - * - * @return A NSURLCredentialPersistence or -1 if data could not be converted. - */ +/// Converts an FWFNSUrlCredentialPersistence to an NSURLCredentialPersistence. +/// +/// @param value The object containing information to create an NSURLCredentialPersistence. +/// +/// @return A NSURLCredentialPersistence or -1 if data could not be converted. extern NSURLCredentialPersistence FWFNativeNSURLCredentialPersistenceFromFWFNSUrlCredentialPersistence( FWFNSUrlCredentialPersistence value); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.h index 887c9f1b3d8b..f1994b7705b5 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.h @@ -10,11 +10,9 @@ NS_ASSUME_NONNULL_BEGIN -/** - * Host api implementation for WKHTTPCookieStore. - * - * Handles creating WKHTTPCookieStore that intercommunicate with a paired Dart object. - */ +/// Host api implementation for WKHTTPCookieStore. +/// +/// Handles creating WKHTTPCookieStore that intercommunicate with a paired Dart object. @interface FWFHTTPCookieStoreHostApiImpl : NSObject - (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.h index 5dec08055ce5..f88370598093 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.h @@ -8,89 +8,76 @@ NS_ASSUME_NONNULL_BEGIN typedef void (^FWFOnDeallocCallback)(long identifier); -/** - * Maintains instances used to communicate with the corresponding objects in Dart. - * - * When an instance is added with an identifier, either can be used to retrieve the other. - * - * Added instances are added as a weak reference and a strong reference. When the strong reference - * is removed with `removeStrongReferenceWithIdentifier:` and the weak reference is deallocated, - * the `deallocCallback` is made with the instance's identifier. However, if the strong reference is - * removed and then the identifier is retrieved with the intention to pass the identifier to Dart - * (e.g. calling `identifierForInstance:identifierWillBePassedToFlutter:` with - * `identifierWillBePassedToFlutter` set to YES), the strong reference to the instance is recreated. - * The strong reference will then need to be removed manually again. - * - * Accessing and inserting to an InstanceManager is thread safe. - */ +/// Maintains instances used to communicate with the corresponding objects in Dart. +/// +/// When an instance is added with an identifier, either can be used to retrieve the other. +/// +/// Added instances are added as a weak reference and a strong reference. When the strong reference +/// is removed with `removeStrongReferenceWithIdentifier:` and the weak reference is deallocated, +/// the `deallocCallback` is made with the instance's identifier. However, if the strong reference +/// is removed and then the identifier is retrieved with the intention to pass the identifier to +/// Dart (e.g. calling `identifierForInstance:identifierWillBePassedToFlutter:` with +/// `identifierWillBePassedToFlutter` set to YES), the strong reference to the instance is +/// recreated. The strong reference will then need to be removed manually again. +/// +/// Accessing and inserting to an InstanceManager is thread safe. @interface FWFInstanceManager : NSObject @property(readonly) FWFOnDeallocCallback deallocCallback; - (instancetype)initWithDeallocCallback:(FWFOnDeallocCallback)callback; + // TODO(bparrishMines): Pairs should not be able to be overwritten and this feature // should be replaced with a call to clear the manager in the event of a hot restart. -/** - * Adds a new instance that was instantiated from Dart. - * - * If an instance or identifier has already been added, it will be replaced by the new values. The - * Dart InstanceManager is considered the source of truth and has the capability to overwrite stored - * pairs in response to hot restarts. - * - * @param instance The instance to be stored. - * @param instanceIdentifier The identifier to be paired with instance. This value must be >= 0. - */ +/// Adds a new instance that was instantiated from Dart. +/// +/// If an instance or identifier has already been added, it will be replaced by the new values. The +/// Dart InstanceManager is considered the source of truth and has the capability to overwrite +/// stored pairs in response to hot restarts. +/// +/// @param instance The instance to be stored. +/// @param instanceIdentifier The identifier to be paired with instance. This value must be >= 0. - (void)addDartCreatedInstance:(NSObject *)instance withIdentifier:(long)instanceIdentifier; -/** - * Adds a new instance that was instantiated from the host platform. - * - * @param instance The instance to be stored. - * @return The unique identifier stored with instance. - */ +/// Adds a new instance that was instantiated from the host platform. +/// +/// @param instance The instance to be stored. +/// @return The unique identifier stored with instance. - (long)addHostCreatedInstance:(nonnull NSObject *)instance; -/** - * Removes `instanceIdentifier` and its associated strongly referenced instance, if present, from - * the manager. - * - * @param instanceIdentifier The identifier paired to an instance. - * - * @return The removed instance if the manager contains the given instanceIdentifier, otherwise - * nil. - */ +/// Removes `instanceIdentifier` and its associated strongly referenced instance, if present, from +/// the manager. +/// +/// @param instanceIdentifier The identifier paired to an instance. +/// +/// @return The removed instance if the manager contains the given instanceIdentifier, otherwise +/// nil. - (nullable NSObject *)removeInstanceWithIdentifier:(long)instanceIdentifier; -/** - * Retrieves the instance associated with identifier. - * - * @param instanceIdentifier The identifier paired to an instance. - * - * @return The instance associated with `instanceIdentifier` if the manager contains the value, - * otherwise nil. - */ +/// Retrieves the instance associated with identifier. +/// +/// @param instanceIdentifier The identifier paired to an instance. +/// +/// @return The instance associated with `instanceIdentifier` if the manager contains the value, +/// otherwise nil. - (nullable NSObject *)instanceForIdentifier:(long)instanceIdentifier; -/** - * Retrieves the identifier paired with an instance. - * - * If the manager contains `instance`, as a strong or weak reference, the strong reference to - * `instance` will be recreated and will need to be removed again with - * `removeInstanceWithIdentifier:`. - * - * This method also expects the Dart `InstanceManager` to have, or recreate, a weak reference to the - * instance the identifier is associated with once it receives it. - * - * @param instance An instance that may be stored in the manager. - * - * @return The identifier associated with `instance` if the manager contains the value, otherwise - * NSNotFound. - */ +/// Retrieves the identifier paired with an instance. +/// +/// If the manager contains `instance`, as a strong or weak reference, the strong reference to +/// `instance` will be recreated and will need to be removed again with +/// `removeInstanceWithIdentifier:`. +/// +/// This method also expects the Dart `InstanceManager` to have, or recreate, a weak reference to +/// the instance the identifier is associated with once it receives it. +/// +/// @param instance An instance that may be stored in the manager. +/// +/// @return The identifier associated with `instance` if the manager contains the value, otherwise +/// NSNotFound. - (long)identifierWithStrongReferenceForInstance:(nonnull NSObject *)instance; -/** - * Returns whether this manager contains the given `instance`. - * - * @return Whether this manager contains the given `instance`. - */ +/// Returns whether this manager contains the given `instance`. +/// +/// @return Whether this manager contains the given `instance`. - (BOOL)containsInstance:(nonnull NSObject *)instance; @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager_Test.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager_Test.h index 63480ce18018..20d1e4847da7 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager_Test.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager_Test.h @@ -7,24 +7,18 @@ NS_ASSUME_NONNULL_BEGIN @interface FWFInstanceManager () -/** - * The next identifier that will be used for a host-created instance. - */ +/// The next identifier that will be used for a host-created instance. @property long nextIdentifier; -/** - * The number of instances stored as a strong reference. - * - * Added for debugging purposes. - */ +/// The number of instances stored as a strong reference. +/// +/// Added for debugging purposes. - (NSUInteger)strongInstanceCount; -/** - * The number of instances stored as a weak reference. - * - * Added for debugging purposes. NSMapTables that store keys or objects as weak reference will be - * reclaimed nondeterministically. - */ +/// The number of instances stored as a weak reference. +/// +/// Added for debugging purposes. NSMapTables that store keys or objects as weak reference will be +/// reclaimed nondeterministically. - (NSUInteger)weakInstanceCount; @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.h index 90e55417cd1b..fb4e076095d9 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.h @@ -12,19 +12,15 @@ NS_ASSUME_NONNULL_BEGIN -/** - * Flutter api implementation for WKNavigationDelegate. - * - * Handles making callbacks to Dart for a WKNavigationDelegate. - */ +/// Flutter api implementation for WKNavigationDelegate. +/// +/// Handles making callbacks to Dart for a WKNavigationDelegate. @interface FWFNavigationDelegateFlutterApiImpl : FWFWKNavigationDelegateFlutterApi - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; @end -/** - * Implementation of WKNavigationDelegate for FWFNavigationDelegateHostApiImpl. - */ +/// Implementation of WKNavigationDelegate for FWFNavigationDelegateHostApiImpl. @interface FWFNavigationDelegate : FWFObject @property(readonly, nonnull, nonatomic) FWFNavigationDelegateFlutterApiImpl *navigationDelegateAPI; @@ -32,11 +28,9 @@ NS_ASSUME_NONNULL_BEGIN instanceManager:(FWFInstanceManager *)instanceManager; @end -/** - * Host api implementation for WKNavigationDelegate. - * - * Handles creating WKNavigationDelegate that intercommunicate with a paired Dart object. - */ +/// Host api implementation for WKNavigationDelegate. +/// +/// Handles creating WKNavigationDelegate that intercommunicate with a paired Dart object. @interface FWFNavigationDelegateHostApiImpl : NSObject - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.h index c1b4355330d4..08080eb5bb8c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.h @@ -9,11 +9,9 @@ NS_ASSUME_NONNULL_BEGIN -/** - * Flutter api implementation for NSObject. - * - * Handles making callbacks to Dart for an NSObject. - */ +/// Flutter api implementation for NSObject. +/// +/// Handles making callbacks to Dart for an NSObject. @interface FWFObjectFlutterApiImpl : FWFNSObjectFlutterApi - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; @@ -25,9 +23,7 @@ NS_ASSUME_NONNULL_BEGIN completion:(void (^)(FlutterError *_Nullable))completion; @end -/** - * Implementation of NSObject for FWFObjectHostApiImpl. - */ +/// Implementation of NSObject for FWFObjectHostApiImpl. @interface FWFObject : NSObject @property(readonly, nonnull, nonatomic) FWFObjectFlutterApiImpl *objectApi; @@ -35,11 +31,9 @@ NS_ASSUME_NONNULL_BEGIN instanceManager:(FWFInstanceManager *)instanceManager; @end -/** - * Host api implementation for NSObject. - * - * Handles creating NSObject that intercommunicate with a paired Dart object. - */ +/// Host api implementation for NSObject. +/// +/// Handles creating NSObject that intercommunicate with a paired Dart object. @interface FWFObjectHostApiImpl : NSObject - (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.h index de2d26491a58..136dccaec8cb 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.h @@ -10,11 +10,9 @@ NS_ASSUME_NONNULL_BEGIN -/** - * Host api implementation for WKPreferences. - * - * Handles creating WKPreferences that intercommunicate with a paired Dart object. - */ +/// Host api implementation for WKPreferences. +/// +/// Handles creating WKPreferences that intercommunicate with a paired Dart object. @interface FWFPreferencesHostApiImpl : NSObject - (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.h index 9c5769e4658b..91e6aa01392f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.h @@ -11,19 +11,15 @@ NS_ASSUME_NONNULL_BEGIN -/** - * Flutter api implementation for WKScriptMessageHandler. - * - * Handles making callbacks to Dart for a WKScriptMessageHandler. - */ +/// Flutter api implementation for WKScriptMessageHandler. +/// +/// Handles making callbacks to Dart for a WKScriptMessageHandler. @interface FWFScriptMessageHandlerFlutterApiImpl : FWFWKScriptMessageHandlerFlutterApi - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; @end -/** - * Implementation of WKScriptMessageHandler for FWFScriptMessageHandlerHostApiImpl. - */ +/// Implementation of WKScriptMessageHandler for FWFScriptMessageHandlerHostApiImpl. @interface FWFScriptMessageHandler : FWFObject @property(readonly, nonnull, nonatomic) FWFScriptMessageHandlerFlutterApiImpl *scriptMessageHandlerAPI; @@ -32,11 +28,9 @@ NS_ASSUME_NONNULL_BEGIN instanceManager:(FWFInstanceManager *)instanceManager; @end -/** - * Host api implementation for WKScriptMessageHandler. - * - * Handles creating WKScriptMessageHandler that intercommunicate with a paired Dart object. - */ +/// Host api implementation for WKScriptMessageHandler. +/// +/// Handles creating WKScriptMessageHandler that intercommunicate with a paired Dart object. @interface FWFScriptMessageHandlerHostApiImpl : NSObject - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewDelegateHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewDelegateHostApi.h index 5324725a8fbe..ec2237c6a0dd 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewDelegateHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewDelegateHostApi.h @@ -11,20 +11,16 @@ NS_ASSUME_NONNULL_BEGIN -/** - * Flutter api implementation for UIScrollViewDelegate. - * - * Handles making callbacks to Dart for a UIScrollViewDelegate. - */ +/// Flutter api implementation for UIScrollViewDelegate. +/// +/// Handles making callbacks to Dart for a UIScrollViewDelegate. @interface FWFScrollViewDelegateFlutterApiImpl : FWFUIScrollViewDelegateFlutterApi - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; @end -/** - * Implementation of WKUIScrollViewDelegate for FWFUIScrollViewDelegateHostApiImpl. - */ +/// Implementation of WKUIScrollViewDelegate for FWFUIScrollViewDelegateHostApiImpl. @interface FWFScrollViewDelegate : FWFObject @property(readonly, nonnull, nonatomic) FWFScrollViewDelegateFlutterApiImpl *scrollViewDelegateAPI; @@ -33,11 +29,9 @@ NS_ASSUME_NONNULL_BEGIN @end -/** - * Host api implementation for UIScrollViewDelegate. - * - * Handles creating UIScrollView that intercommunicate with a paired Dart object. - */ +/// Host api implementation for UIScrollViewDelegate. +/// +/// Handles creating UIScrollView that intercommunicate with a paired Dart object. @interface FWFScrollViewDelegateHostApiImpl : NSObject - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.h index 25f373f374e3..7d87e4d561f9 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.h @@ -10,11 +10,9 @@ NS_ASSUME_NONNULL_BEGIN -/** - * Host api implementation for UIScrollView. - * - * Handles creating UIScrollView that intercommunicate with a paired Dart object. - */ +/// Host api implementation for UIScrollView. +/// +/// Handles creating UIScrollView that intercommunicate with a paired Dart object. @interface FWFScrollViewHostApiImpl : NSObject - (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.h index 7b6b4eec9b8e..fd685c7b4b97 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.h @@ -12,11 +12,9 @@ NS_ASSUME_NONNULL_BEGIN -/** - * Flutter api implementation for WKUIDelegate. - * - * Handles making callbacks to Dart for a WKUIDelegate. - */ +/// Flutter api implementation for WKUIDelegate. +/// +/// Handles making callbacks to Dart for a WKUIDelegate. @interface FWFUIDelegateFlutterApiImpl : FWFWKUIDelegateFlutterApi @property(readonly, nonatomic) FWFWebViewConfigurationFlutterApiImpl *webViewConfigurationFlutterApi; @@ -25,9 +23,7 @@ NS_ASSUME_NONNULL_BEGIN instanceManager:(FWFInstanceManager *)instanceManager; @end -/** - * Implementation of WKUIDelegate for FWFUIDelegateHostApiImpl. - */ +/// Implementation of WKUIDelegate for FWFUIDelegateHostApiImpl. @interface FWFUIDelegate : FWFObject @property(readonly, nonnull, nonatomic) FWFUIDelegateFlutterApiImpl *UIDelegateAPI; @@ -35,11 +31,9 @@ NS_ASSUME_NONNULL_BEGIN instanceManager:(FWFInstanceManager *)instanceManager; @end -/** - * Host api implementation for WKUIDelegate. - * - * Handles creating WKUIDelegate that intercommunicate with a paired Dart object. - */ +/// Host api implementation for WKUIDelegate. +/// +/// Handles creating WKUIDelegate that intercommunicate with a paired Dart object. @interface FWFUIDelegateHostApiImpl : NSObject - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.h index 82edd6b742ca..f63119738a82 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.h @@ -9,11 +9,9 @@ NS_ASSUME_NONNULL_BEGIN -/** - * Host api implementation for UIView. - * - * Handles creating UIView that intercommunicate with a paired Dart object. - */ +/// Host api implementation for UIView. +/// +/// Handles creating UIView that intercommunicate with a paired Dart object. @interface FWFUIViewHostApiImpl : NSObject - (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLAuthenticationChallengeHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLAuthenticationChallengeHostApi.h index 6100b703c29b..2a16ff486b6d 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLAuthenticationChallengeHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLAuthenticationChallengeHostApi.h @@ -9,22 +9,16 @@ NS_ASSUME_NONNULL_BEGIN -/** - * Flutter API implementation for `NSURLAuthenticationChallenge`. - * - * This class may handle instantiating and adding Dart instances that are attached to a native - * instance or sending callback methods from an overridden native class. - */ +/// Flutter API implementation for `NSURLAuthenticationChallenge`. +/// +/// This class may handle instantiating and adding Dart instances that are attached to a native +/// instance or sending callback methods from an overridden native class. @interface FWFURLAuthenticationChallengeFlutterApiImpl : NSObject -/** - * The Flutter API used to send messages back to Dart. - */ +/// The Flutter API used to send messages back to Dart. @property FWFNSUrlAuthenticationChallengeFlutterApi *api; - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; -/** - * Sends a message to Dart to create a new Dart instance and add it to the `InstanceManager`. - */ +/// Sends a message to Dart to create a new Dart instance and add it to the `InstanceManager`. - (void)createWithInstance:(NSURLAuthenticationChallenge *)instance protectionSpace:(NSURLProtectionSpace *)protectionSpace completion:(void (^)(FlutterError *_Nullable))completion; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLCredentialHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLCredentialHostApi.h index fe9b3d0d8d50..3c119e552e09 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLCredentialHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLCredentialHostApi.h @@ -10,12 +10,10 @@ NS_ASSUME_NONNULL_BEGIN -/** - * Host API implementation for `NSURLCredential`. - * - * This class may handle instantiating and adding native object instances that are attached to a - * Dart instance or method calls on the associated native class or an instance of the class. - */ +/// Host API implementation for `NSURLCredential`. +/// +/// This class may handle instantiating and adding native object instances that are attached to a +/// Dart instance or method calls on the associated native class or an instance of the class. @interface FWFURLCredentialHostApiImpl : NSObject - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLHostApi.h index 248f0b7f20b7..919dcc5b3cf2 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLHostApi.h @@ -9,33 +9,26 @@ NS_ASSUME_NONNULL_BEGIN -/** - * Host API implementation for `NSURL`. - * - * This class may handle instantiating and adding native object instances that are attached to a - * Dart instance or method calls on the associated native class or an instance of the class. - */ +/// Host API implementation for `NSURL`. +/// +/// This class may handle instantiating and adding native object instances that are attached to a +/// Dart instance or method calls on the associated native class or an instance of the class. @interface FWFURLHostApiImpl : NSObject - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; @end -/** - * Flutter API implementation for `NSURL`. - * - * This class may handle instantiating and adding Dart instances that are attached to a native - * instance or sending callback methods from an overridden native class. - */ +/// Flutter API implementation for `NSURL`. +/// +/// This class may handle instantiating and adding Dart instances that are attached to a native +/// instance or sending callback methods from an overridden native class. @interface FWFURLFlutterApiImpl : NSObject -/** - * The Flutter API used to send messages back to Dart. - */ +/// The Flutter API used to send messages back to Dart. @property FWFNSUrlFlutterApi *api; - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; -/** - * Sends a message to Dart to create a new Dart instance and add it to the `InstanceManager`. - */ + +/// Sends a message to Dart to create a new Dart instance and add it to the `InstanceManager`. - (void)create:(NSURL *)instance completion:(void (^)(FlutterError *_Nullable))completion; @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLProtectionSpaceHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLProtectionSpaceHostApi.h index 5e57ab5404ab..a4f7c537f1b5 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLProtectionSpaceHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLProtectionSpaceHostApi.h @@ -9,22 +9,16 @@ NS_ASSUME_NONNULL_BEGIN -/** - * Flutter API implementation for `NSURLProtectionSpace`. - * - * This class may handle instantiating and adding Dart instances that are attached to a native - * instance or sending callback methods from an overridden native class. - */ +/// Flutter API implementation for `NSURLProtectionSpace`. +/// +/// This class may handle instantiating and adding Dart instances that are attached to a native +/// instance or sending callback methods from an overridden native class. @interface FWFURLProtectionSpaceFlutterApiImpl : NSObject -/** - * The Flutter API used to send messages back to Dart. - */ +/// The Flutter API used to send messages back to Dart. @property FWFNSUrlProtectionSpaceFlutterApi *api; - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; -/** - * Sends a message to Dart to create a new Dart instance and add it to the `InstanceManager`. - */ +/// Sends a message to Dart to create a new Dart instance and add it to the `InstanceManager`. - (void)createWithInstance:(NSURLProtectionSpace *)instance host:(nullable NSString *)host realm:(nullable NSString *)realm diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.h index f0e5a1383ac3..8ca3b69b6a39 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.h @@ -10,11 +10,9 @@ NS_ASSUME_NONNULL_BEGIN -/** - * Host api implementation for WKUserContentController. - * - * Handles creating WKUserContentController that intercommunicate with a paired Dart object. - */ +/// Host api implementation for WKUserContentController. +/// +/// Handles creating WKUserContentController that intercommunicate with a paired Dart object. @interface FWFUserContentControllerHostApiImpl : NSObject - (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.h index 363fe6ae6664..c11ce1f29567 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.h @@ -11,11 +11,9 @@ NS_ASSUME_NONNULL_BEGIN -/** - * Flutter api implementation for WKWebViewConfiguration. - * - * Handles making callbacks to Dart for a WKWebViewConfiguration. - */ +/// Flutter api implementation for WKWebViewConfiguration. +/// +/// Handles making callbacks to Dart for a WKWebViewConfiguration. @interface FWFWebViewConfigurationFlutterApiImpl : FWFWKWebViewConfigurationFlutterApi - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; @@ -24,9 +22,7 @@ NS_ASSUME_NONNULL_BEGIN completion:(void (^)(FlutterError *_Nullable))completion; @end -/** - * Implementation of WKWebViewConfiguration for FWFWebViewConfigurationHostApiImpl. - */ +/// Implementation of WKWebViewConfiguration for FWFWebViewConfigurationHostApiImpl. @interface FWFWebViewConfiguration : WKWebViewConfiguration @property(readonly, nonnull, nonatomic) FWFObjectFlutterApiImpl *objectApi; @@ -34,11 +30,9 @@ NS_ASSUME_NONNULL_BEGIN instanceManager:(FWFInstanceManager *)instanceManager; @end -/** - * Host api implementation for WKWebViewConfiguration. - * - * Handles creating WKWebViewConfiguration that intercommunicate with a paired Dart object. - */ +/// Host api implementation for WKWebViewConfiguration. +/// +/// Handles creating WKWebViewConfiguration that intercommunicate with a paired Dart object. @interface FWFWebViewConfigurationHostApiImpl : NSObject - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewFlutterWKWebViewExternalAPI.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewFlutterWKWebViewExternalAPI.h index 297f8c37ec3e..b316f333cdde 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewFlutterWKWebViewExternalAPI.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewFlutterWKWebViewExternalAPI.h @@ -8,28 +8,24 @@ NS_ASSUME_NONNULL_BEGIN -/** - * App and package facing native API provided by the `webview_flutter_wkwebview` plugin. - * - * This class follows the convention of breaking changes of the Dart API, which means that any - * changes to the class that are not backwards compatible will only be made with a major version - * change of the plugin. Native code other than this external API does not follow breaking change - * conventions, so app or plugin clients should not use any other native APIs. - */ +/// App and package facing native API provided by the `webview_flutter_wkwebview` plugin. +/// +/// This class follows the convention of breaking changes of the Dart API, which means that any +/// changes to the class that are not backwards compatible will only be made with a major version +/// change of the plugin. Native code other than this external API does not follow breaking change +/// conventions, so app or plugin clients should not use any other native APIs. @interface FWFWebViewFlutterWKWebViewExternalAPI : NSObject -/** - * Retrieves the `WKWebView` that is associated with `identifier`. - * - * See the Dart method `WebKitWebViewController.webViewIdentifier` to get the identifier of an - * underlying `WKWebView`. - * - * @param identifier The associated identifier of the `WebView`. - * @param registry The plugin registry the `FLTWebViewFlutterPlugin` should belong to. If - * the registry doesn't contain an attached instance of `FLTWebViewFlutterPlugin`, - * this method returns nil. - * @return The `WKWebView` associated with `identifier` or nil if a `WKWebView` instance associated - * with `identifier` could not be found. - */ +/// Retrieves the `WKWebView` that is associated with `identifier`. +/// +/// See the Dart method `WebKitWebViewController.webViewIdentifier` to get the identifier of an +/// underlying `WKWebView`. +/// +/// @param identifier The associated identifier of the `WebView`. +/// @param registry The plugin registry the `FLTWebViewFlutterPlugin` should belong to. If +/// the registry doesn't contain an attached instance of `FLTWebViewFlutterPlugin`, +/// this method returns nil. +/// @return The `WKWebView` associated with `identifier` or nil if a `WKWebView` instance associated +/// with `identifier` could not be found. + (nullable WKWebView *)webViewForIdentifier:(long)identifier withPluginRegistry:(id)registry; @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.h index f1bb59bcb9ae..037ea11f8494 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.h @@ -11,18 +11,14 @@ NS_ASSUME_NONNULL_BEGIN -/** - * A set of Flutter and Dart assets used by a `FlutterEngine` to initialize execution. - * - * Default implementation delegates methods to FlutterDartProject. - */ +/// A set of Flutter and Dart assets used by a `FlutterEngine` to initialize execution. +/// +/// Default implementation delegates methods to FlutterDartProject. @interface FWFAssetManager : NSObject - (NSString *)lookupKeyForAsset:(NSString *)asset; @end -/** - * Implementation of WKWebView that can be used as a FlutterPlatformView. - */ +/// Implementation of WKWebView that can be used as a FlutterPlatformView. @interface FWFWebView : WKWebView @property(readonly, nonnull, nonatomic) FWFObjectFlutterApiImpl *objectApi; @@ -32,11 +28,9 @@ NS_ASSUME_NONNULL_BEGIN instanceManager:(FWFInstanceManager *)instanceManager; @end -/** - * Host api implementation for WKWebView. - * - * Handles creating WKWebViews that intercommunicate with a paired Dart object. - */ +/// Host api implementation for WKWebView. +/// +/// Handles creating WKWebViews that intercommunicate with a paired Dart object. @interface FWFWebViewHostApiImpl : NSObject - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.h index 72f00e032ee4..256c3f9d41f6 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.h @@ -10,11 +10,9 @@ NS_ASSUME_NONNULL_BEGIN -/** - * Host api implementation for WKWebsiteDataStore. - * - * Handles creating WKWebsiteDataStore that intercommunicate with a paired Dart object. - */ +/// Host api implementation for WKWebsiteDataStore. +/// +/// Handles creating WKWebsiteDataStore that intercommunicate with a paired Dart object. @interface FWFWebsiteDataStoreHostApiImpl : NSObject - (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; @end From a9c68b83356be977304c7775a84178a25b08b1e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Mar 2024 09:37:18 +0000 Subject: [PATCH 044/126] Bump github/codeql-action from 3.24.5 to 3.24.6 (#6234) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.24.5 to 3.24.6.
Changelog

Sourced from github/codeql-action's changelog.

CodeQL Action Changelog

See the releases page for the relevant changes to the CodeQL CLI and language packs.

Note that the only difference between v2 and v3 of the CodeQL Action is the node version they support, with v3 running on node 20 while we continue to release v2 to support running on node 16. For example 3.22.11 was the first v3 release and is functionally identical to 2.22.11. This approach ensures an easy way to track exactly which features are included in different versions, indicated by the minor and patch version numbers.

[UNRELEASED]

No user facing changes.

3.24.6 - 29 Feb 2024

No user facing changes.

3.24.5 - 23 Feb 2024

  • Update default CodeQL bundle version to 2.16.3. #2156

3.24.4 - 21 Feb 2024

  • Fix an issue where an existing, but empty, /sys/fs/cgroup/cpuset.cpus file always resulted in a single-threaded run. #2151

3.24.3 - 15 Feb 2024

  • Fix an issue where the CodeQL Action would fail to load a configuration specified by the config input to the init Action. #2147

3.24.2 - 15 Feb 2024

  • Enable improved multi-threaded performance on larger runners for GitHub Enterprise Server users. This feature is already available to GitHub.com users. #2141

3.24.1 - 13 Feb 2024

  • Update default CodeQL bundle version to 2.16.2. #2124
  • The CodeQL action no longer fails if it can't write to the telemetry api endpoint. #2121

3.24.0 - 02 Feb 2024

  • CodeQL Python analysis will no longer install dependencies on GitHub Enterprise Server, as is already the case for GitHub.com. See release notes for 3.23.0 for more details. #2106

3.23.2 - 26 Jan 2024

  • On Linux, the maximum possible value for the --threads option now respects the CPU count as specified in cgroup files to more accurately reflect the number of available cores when running in containers. #2083
  • Update default CodeQL bundle version to 2.16.1. #2096

3.23.1 - 17 Jan 2024

  • Update default CodeQL bundle version to 2.16.0. #2073
  • Change the retention period for uploaded debug artifacts to 7 days. Previously, this was whatever the repository default was. #2079

3.23.0 - 08 Jan 2024

... (truncated)

Commits
  • 8a470fd Merge pull request #2176 from github/update-v3.24.6-158d0780d
  • d59eafb Update changelog for v3.24.6
  • 158d078 Merge pull request #2175 from github/henrymercer/fix-languages-init-success
  • 2307ff5 Add languages and build_mode to init success status report
  • ccd3f02 Merge pull request #2173 from github/henrymercer/mark-third-party-limits-erro...
  • c091725 Refactoring: Convert ActionName to enum
  • e12a8cc Use isFirstPartyAnalysis for a stronger check
  • 888ab31 Mark third-party SARIF limits errors as configuration errors
  • bd56a05 Rename InvalidRequestError to InvalidSarifUploadError
  • 7bde906 Merge pull request #2172 from github/henrymercer/fix-readme-typos
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github/codeql-action&package-manager=github_actions&previous-version=3.24.5&new-version=3.24.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- .github/workflows/scorecards-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index f5a305cde026..cd08fedd157d 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -49,6 +49,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@47b3d888fe66b639e431abf22ebca059152f1eea # v1.0.26 + uses: github/codeql-action/upload-sarif@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v1.0.26 with: sarif_file: results.sarif From 06c1fa4311d183a065b2f1e30757d9edfb84f895 Mon Sep 17 00:00:00 2001 From: Sergei Date: Fri, 1 Mar 2024 18:11:14 +0300 Subject: [PATCH 045/126] [camera_avfoundation] Set highest available resolution for ResolutionPreset.Max (#5245) The current implementation of the camera plugin for iOS iterates though all other resolution presets when set to FLTResolutionPresetMax. This results in a resolution of 3840x2160 at most, while native camera is able to produce 4032x3024 This change should partially address these issues at least. - https://github.com/flutter/flutter/issues/58163 - https://github.com/flutter/flutter/issues/78247 - https://github.com/flutter/flutter/issues/45906 P.S. I'm not really sure about tests - it seems that resolution presets are not covered by any. Any feedback is appreciated! --- .../camera/camera_avfoundation/CHANGELOG.md | 4 + .../ios/Runner.xcodeproj/project.pbxproj | 4 + .../RunnerTests/CameraSessionPresetsTests.m | 78 +++++++++++++++++++ .../example/ios/RunnerTests/CameraTestUtils.h | 18 +++++ .../example/ios/RunnerTests/CameraTestUtils.m | 49 +++++++++++- .../camera_avfoundation/ios/Classes/FLTCam.h | 1 + .../camera_avfoundation/ios/Classes/FLTCam.m | 72 ++++++++++++++++- .../ios/Classes/FLTCam_Test.h | 21 +++++ .../camera/camera_avfoundation/pubspec.yaml | 3 +- 9 files changed, 244 insertions(+), 6 deletions(-) create mode 100644 packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSessionPresetsTests.m diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index 98db153b11f0..d70fd9e73c6a 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.14+1 + +* Fixes bug where max resolution preset does not produce highest available resolution on iOS. + ## 0.9.14 * Adds support to HEIF format. diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj index df0879fad89d..ac2e7ffb6ec4 100644 --- a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj @@ -22,6 +22,7 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + CEF6611A2B5E36A500D33FD4 /* CameraSessionPresetsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CEF661192B5E36A500D33FD4 /* CameraSessionPresetsTests.m */; }; E01EE4A82799F3A5008C1950 /* QueueUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */; }; E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */; }; E04F108627A87CA600573D0C /* FLTSavePhotoDelegateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */; }; @@ -89,6 +90,7 @@ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9C5CC6CAD53AD388B2694F3A /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; A24F9E418BA48BCC7409B117 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + CEF661192B5E36A500D33FD4 /* CameraSessionPresetsTests.m */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = CameraSessionPresetsTests.m; sourceTree = ""; }; E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QueueUtilsTests.m; sourceTree = ""; }; E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraCaptureSessionQueueRaceConditionTests.m; sourceTree = ""; }; E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTSavePhotoDelegateTests.m; sourceTree = ""; }; @@ -151,6 +153,7 @@ E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */, 788A065927B0E02900533D74 /* StreamingTest.m */, 43ED1536282570DE00EB00DE /* AvailableCamerasTest.m */, + CEF661192B5E36A500D33FD4 /* CameraSessionPresetsTests.m */, ); path = RunnerTests; sourceTree = ""; @@ -451,6 +454,7 @@ F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */, E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */, 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */, + CEF6611A2B5E36A500D33FD4 /* CameraSessionPresetsTests.m in Sources */, E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */, 788A065A27B0E02900533D74 /* StreamingTest.m in Sources */, E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */, diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSessionPresetsTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSessionPresetsTests.m new file mode 100644 index 000000000000..a5130ad8288e --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraSessionPresetsTests.m @@ -0,0 +1,78 @@ +// 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 camera_avfoundation; +@import camera_avfoundation.Test; + +@import AVFoundation; +@import XCTest; +#import +#import "CameraTestUtils.h" + +/// Includes test cases related to resolution presets setting operations for FLTCam class. +@interface FLTCamSessionPresetsTest : XCTestCase +@end + +@implementation FLTCamSessionPresetsTest + +- (void)testResolutionPresetWithBestFormat_mustUpdateCaptureSessionPreset { + NSString *expectedPreset = AVCaptureSessionPresetInputPriority; + + id videoSessionMock = OCMClassMock([AVCaptureSession class]); + OCMStub([videoSessionMock addInputWithNoConnections:[OCMArg any]]); + + id captureFormatMock = OCMClassMock([AVCaptureDeviceFormat class]); + id captureDeviceMock = OCMClassMock([AVCaptureDevice class]); + OCMStub([captureDeviceMock formats]).andReturn(@[ captureFormatMock ]); + + OCMExpect([captureDeviceMock activeFormat]).andReturn(captureFormatMock); + OCMExpect([captureDeviceMock lockForConfiguration:NULL]).andReturn(YES); + OCMExpect([videoSessionMock setSessionPreset:expectedPreset]); + + FLTCreateCamWithVideoDimensionsForFormat(videoSessionMock, @"max", captureDeviceMock, + ^CMVideoDimensions(AVCaptureDeviceFormat *format) { + CMVideoDimensions videoDimensions; + videoDimensions.width = 1; + videoDimensions.height = 1; + return videoDimensions; + }); + + OCMVerifyAll(captureDeviceMock); + OCMVerifyAll(videoSessionMock); +} + +- (void)testResolutionPresetWithCanSetSessionPresetMax_mustUpdateCaptureSessionPreset { + NSString *expectedPreset = AVCaptureSessionPreset3840x2160; + + id videoSessionMock = OCMClassMock([AVCaptureSession class]); + OCMStub([videoSessionMock addInputWithNoConnections:[OCMArg any]]); + + // Make sure that setting resolution preset for session always succeeds. + OCMStub([videoSessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES); + + OCMExpect([videoSessionMock setSessionPreset:expectedPreset]); + + FLTCreateCamWithVideoCaptureSession(videoSessionMock, @"max"); + + OCMVerifyAll(videoSessionMock); +} + +- (void)testResolutionPresetWithCanSetSessionPresetUltraHigh_mustUpdateCaptureSessionPreset { + NSString *expectedPreset = AVCaptureSessionPreset3840x2160; + + id videoSessionMock = OCMClassMock([AVCaptureSession class]); + OCMStub([videoSessionMock addInputWithNoConnections:[OCMArg any]]); + + // Make sure that setting resolution preset for session always succeeds. + OCMStub([videoSessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES); + + // Expect that setting "ultraHigh" resolutionPreset correctly updates videoCaptureSession. + OCMExpect([videoSessionMock setSessionPreset:expectedPreset]); + + FLTCreateCamWithVideoCaptureSession(videoSessionMock, @"ultraHigh"); + + OCMVerifyAll(videoSessionMock); +} + +@end diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h index 0c7e62f9fbb5..cdc11bff6c82 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h @@ -11,6 +11,24 @@ NS_ASSUME_NONNULL_BEGIN /// @return an FLTCam object. extern FLTCam *FLTCreateCamWithCaptureSessionQueue(dispatch_queue_t captureSessionQueue); +/// Creates an `FLTCam` with a given captureSession and resolutionPreset +/// @param captureSession AVCaptureSession for video +/// @param resolutionPreset preset for camera's captureSession resolution +/// @return an FLTCam object. +extern FLTCam *FLTCreateCamWithVideoCaptureSession(AVCaptureSession *captureSession, + NSString *resolutionPreset); + +/// Creates an `FLTCam` with a given captureSession and resolutionPreset. +/// Allows to inject a capture device and a block to compute the video dimensions. +/// @param captureSession AVCaptureSession for video +/// @param resolutionPreset preset for camera's captureSession resolution +/// @param captureDevice AVCaptureDevice to be used +/// @param videoDimensionsForFormat custom code to determine video dimensions +/// @return an FLTCam object. +extern FLTCam *FLTCreateCamWithVideoDimensionsForFormat( + AVCaptureSession *captureSession, NSString *resolutionPreset, AVCaptureDevice *captureDevice, + VideoDimensionsForFormat videoDimensionsForFormat); + /// Creates a test sample buffer. /// @return a test sample buffer. extern CMSampleBufferRef FLTCreateTestSampleBuffer(void); diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m index bb98f7cf71e9..d0456f7aa544 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m @@ -12,11 +12,11 @@ .andReturn(inputMock); id videoSessionMock = OCMClassMock([AVCaptureSession class]); - OCMStub([videoSessionMock addInputWithNoConnections:[OCMArg any]]); // no-op + OCMStub([videoSessionMock addInputWithNoConnections:[OCMArg any]]); OCMStub([videoSessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES); id audioSessionMock = OCMClassMock([AVCaptureSession class]); - OCMStub([audioSessionMock addInputWithNoConnections:[OCMArg any]]); // no-op + OCMStub([audioSessionMock addInputWithNoConnections:[OCMArg any]]); OCMStub([audioSessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES); return [[FLTCam alloc] initWithCameraName:@"camera" @@ -29,6 +29,51 @@ error:nil]; } +FLTCam *FLTCreateCamWithVideoCaptureSession(AVCaptureSession *captureSession, + NSString *resolutionPreset) { + id inputMock = OCMClassMock([AVCaptureDeviceInput class]); + OCMStub([inputMock deviceInputWithDevice:[OCMArg any] error:[OCMArg setTo:nil]]) + .andReturn(inputMock); + + id audioSessionMock = OCMClassMock([AVCaptureSession class]); + OCMStub([audioSessionMock addInputWithNoConnections:[OCMArg any]]); + OCMStub([audioSessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES); + + return [[FLTCam alloc] initWithCameraName:@"camera" + resolutionPreset:resolutionPreset + enableAudio:true + orientation:UIDeviceOrientationPortrait + videoCaptureSession:captureSession + audioCaptureSession:audioSessionMock + captureSessionQueue:dispatch_queue_create("capture_session_queue", NULL) + error:nil]; +} + +FLTCam *FLTCreateCamWithVideoDimensionsForFormat( + AVCaptureSession *captureSession, NSString *resolutionPreset, AVCaptureDevice *captureDevice, + VideoDimensionsForFormat videoDimensionsForFormat) { + id inputMock = OCMClassMock([AVCaptureDeviceInput class]); + OCMStub([inputMock deviceInputWithDevice:[OCMArg any] error:[OCMArg setTo:nil]]) + .andReturn(inputMock); + + id audioSessionMock = OCMClassMock([AVCaptureSession class]); + OCMStub([audioSessionMock addInputWithNoConnections:[OCMArg any]]); + OCMStub([audioSessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES); + + return + [[FLTCam alloc] initWithResolutionPreset:resolutionPreset + enableAudio:true + orientation:UIDeviceOrientationPortrait + videoCaptureSession:captureSession + audioCaptureSession:audioSessionMock + captureSessionQueue:dispatch_queue_create("capture_session_queue", NULL) + captureDeviceFactory:^AVCaptureDevice *(void) { + return captureDevice; + } + videoDimensionsForFormat:videoDimensionsForFormat + error:nil]; +} + CMSampleBufferRef FLTCreateTestSampleBuffer(void) { CVPixelBufferRef pixelBuffer; CVPixelBufferCreate(kCFAllocatorDefault, 100, 100, kCVPixelFormatType_32BGRA, NULL, &pixelBuffer); diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h index 757c56d0a5c4..5234233f4c2e 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h @@ -43,6 +43,7 @@ NS_ASSUME_NONNULL_BEGIN orientation:(UIDeviceOrientation)orientation captureSessionQueue:(dispatch_queue_t)captureSessionQueue error:(NSError **)error; + - (void)start; - (void)stop; - (void)setDeviceOrientation:(UIDeviceOrientation)orientation; diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index 6f5040f2a1e1..b16d65fe40e3 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -86,6 +86,11 @@ @interface FLTCam () maxPixelCount) { + maxPixelCount = pixelCount; + bestFormat = format; + } + } + return bestFormat; +} + - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection { @@ -935,7 +1001,7 @@ - (void)setDescriptionWhileRecording:(NSString *)cameraName return; } - _captureDevice = [AVCaptureDevice deviceWithUniqueID:cameraName]; + _captureDevice = self.captureDeviceFactory(); AVCaptureConnection *oldConnection = [_captureVideoOutput connectionWithMediaType:AVMediaTypeVideo]; diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam_Test.h b/packages/camera/camera_avfoundation/ios/Classes/FLTCam_Test.h index acc64846cb2c..94993feaa74e 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam_Test.h +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam_Test.h @@ -5,6 +5,14 @@ #import "FLTCam.h" #import "FLTSavePhotoDelegate.h" +/// Determines the video dimensions (width and height) for a given capture device format. +/// Used in tests to mock CMVideoFormatDescriptionGetDimensions. +typedef CMVideoDimensions (^VideoDimensionsForFormat)(AVCaptureDeviceFormat *); + +/// Factory block returning an AVCaptureDevice. +/// Used in tests to inject a device into FLTCam. +typedef AVCaptureDevice * (^CaptureDeviceFactory)(void); + @interface FLTImageStreamHandler : NSObject /// The queue on which `eventSink` property should be accessed. @@ -55,6 +63,19 @@ captureSessionQueue:(dispatch_queue_t)captureSessionQueue error:(NSError **)error; +/// Initializes a camera instance. +/// Allows for testing with specified resolution, audio preference, orientation, +/// and direct access to capture sessions and blocks. +- (instancetype)initWithResolutionPreset:(NSString *)resolutionPreset + enableAudio:(BOOL)enableAudio + orientation:(UIDeviceOrientation)orientation + videoCaptureSession:(AVCaptureSession *)videoCaptureSession + audioCaptureSession:(AVCaptureSession *)audioCaptureSession + captureSessionQueue:(dispatch_queue_t)captureSessionQueue + captureDeviceFactory:(CaptureDeviceFactory)captureDeviceFactory + videoDimensionsForFormat:(VideoDimensionsForFormat)videoDimensionsForFormat + error:(NSError **)error; + /// Start streaming images. - (void)startImageStreamWithMessenger:(NSObject *)messenger imageStreamHandler:(FLTImageStreamHandler *)imageStreamHandler; diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index fd9e4eeffa59..1d83c8567c61 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -2,7 +2,8 @@ name: camera_avfoundation description: iOS implementation of the camera plugin. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.14 + +version: 0.9.14+1 environment: sdk: ^3.2.3 From 8ccf7f6f52965b6c6f512fff6579944d6832f0fb Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 1 Mar 2024 10:47:07 -0500 Subject: [PATCH 046/126] Roll Flutter from e92bca3ff5d2 to ba719bc588ed (37 revisions) (#6235) Roll Flutter from e92bca3ff5d2 to ba719bc588ed (37 revisions) https://github.com/flutter/flutter/compare/e92bca3ff5d2...ba719bc588ed 2024-03-01 tessertaha@gmail.com Fix `CalendarDatePicker` day selection shape and overlay (flutter/flutter#144317) 2024-03-01 tessertaha@gmail.com Fix chips use square delete button `InkWell` shape instead of circular (flutter/flutter#144319) 2024-03-01 leroux_bruno@yahoo.fr InputDecorator M3 tests migration - Step4 - Hint tests (flutter/flutter#144169) 2024-03-01 engine-flutter-autoroll@skia.org Roll Flutter Engine from f0037d4fbd67 to 2a5a9a6dead0 (1 revision) (flutter/flutter#144468) 2024-03-01 engine-flutter-autoroll@skia.org Roll Flutter Engine from 16e04d264145 to f0037d4fbd67 (1 revision) (flutter/flutter#144464) 2024-03-01 engine-flutter-autoroll@skia.org Roll Flutter Engine from 1dff89788b18 to 16e04d264145 (1 revision) (flutter/flutter#144459) 2024-03-01 leroux_bruno@yahoo.fr Horizontally expand text selection toolbar buttons in overflow menu (flutter/flutter#144391) 2024-03-01 engine-flutter-autoroll@skia.org Roll Flutter Engine from f68cc57cbe12 to 1dff89788b18 (2 revisions) (flutter/flutter#144457) 2024-03-01 engine-flutter-autoroll@skia.org Roll Flutter Engine from 76140bc9cec9 to f68cc57cbe12 (2 revisions) (flutter/flutter#144455) 2024-03-01 engine-flutter-autoroll@skia.org Roll Flutter Engine from f300ced56a4e to 76140bc9cec9 (1 revision) (flutter/flutter#144453) 2024-03-01 engine-flutter-autoroll@skia.org Roll Flutter Engine from 34cf95c7bd82 to f300ced56a4e (1 revision) (flutter/flutter#144452) 2024-03-01 engine-flutter-autoroll@skia.org Roll Flutter Engine from 0db468f7bae5 to 34cf95c7bd82 (1 revision) (flutter/flutter#144450) 2024-03-01 engine-flutter-autoroll@skia.org Roll Flutter Engine from 6f8044436eb3 to 0db468f7bae5 (3 revisions) (flutter/flutter#144448) 2024-03-01 goderbauer@google.com Remove master from API docs (flutter/flutter#144425) 2024-03-01 goderbauer@google.com Enable missing-code-block-language (flutter/flutter#144443) 2024-03-01 engine-flutter-autoroll@skia.org Roll Flutter Engine from 7e8fefe4a084 to 6f8044436eb3 (5 revisions) (flutter/flutter#144436) 2024-02-29 49699333+dependabot[bot]@users.noreply.github.com Bump github/codeql-action from 3.24.5 to 3.24.6 (flutter/flutter#144424) 2024-02-29 jhy03261997@gmail.com Reland [a11y] Fix date picker cannot focus on the edit field (flutter/flutter#144198) 2024-02-29 goderbauer@google.com Reland "Use dartpad's main channel for master/main docs (#144329)" (flutter/flutter#144431) 2024-02-29 chingjun@google.com Make daemon server work on ipv6-only machines. (flutter/flutter#144359) 2024-02-29 36861262+QuncCccccc@users.noreply.github.com Remove deprecated `backgroundColor` from `ThemeData` (flutter/flutter#144079) 2024-02-29 amirpanahandeh@yahoo.com Add stateful reordering test for TwoDimensionalViewport (flutter/flutter#142375) 2024-02-29 jacksongardner@google.com Always use local CanvasKit/Skwasm in benchmarks for better hermeticity. (flutter/flutter#144423) 2024-02-29 engine-flutter-autoroll@skia.org Roll Flutter Engine from bb6c6a01000f to 7e8fefe4a084 (2 revisions) (flutter/flutter#144419) 2024-02-29 chingjun@google.com Fix a crash in remote device daemon. (flutter/flutter#144358) 2024-02-29 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Use dartpad's main channel for master/main docs (#144329)" (flutter/flutter#144429) 2024-02-29 36861262+QuncCccccc@users.noreply.github.com Remove `toggleableActiveColor` from `ThemeData` (flutter/flutter#144178) 2024-02-29 31859944+LongCatIsLooong@users.noreply.github.com Add `FocusNode.focusabilityListenable` (flutter/flutter#144280) 2024-02-29 engine-flutter-autoroll@skia.org Roll Flutter Engine from 1f24eaa4979d to bb6c6a01000f (2 revisions) (flutter/flutter#144416) 2024-02-29 jonahwilliams@google.com [devicelab] fix motog4 tests and update comment. (flutter/flutter#144410) 2024-02-29 goderbauer@google.com Bump dartdocs to 8.0.6 (flutter/flutter#144413) 2024-02-29 goderbauer@google.com Use dartpad's main channel for master/main docs (flutter/flutter#144329) 2024-02-29 engine-flutter-autoroll@skia.org Roll Flutter Engine from 7b3ef43fa7e6 to 1f24eaa4979d (2 revisions) (flutter/flutter#144405) 2024-02-29 engine-flutter-autoroll@skia.org Roll Flutter Engine from 7dd404724c50 to 7b3ef43fa7e6 (1 revision) (flutter/flutter#144399) 2024-02-29 engine-flutter-autoroll@skia.org Roll Flutter Engine from 8179b0ed778e to 7dd404724c50 (3 revisions) (flutter/flutter#144395) 2024-02-29 engine-flutter-autoroll@skia.org Roll Packages from 353086c9a61e to 6d02f0359368 (14 revisions) (flutter/flutter#144393) 2024-02-29 engine-flutter-autoroll@skia.org Roll Flutter Engine from d068d980f952 to 8179b0ed778e (1 revision) (flutter/flutter#144378) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC bmparr@google.com,rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose ... --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 2630132fc273..fa25da89cc74 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e92bca3ff5d27b120ed39411c70d0110c21b4afa +ba719bc588ed22b388ad9692877ffa56e081d54f From 06258277070fc8b919aaae0750475e14fe1fb7a1 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 2 Mar 2024 10:42:09 -0500 Subject: [PATCH 047/126] Roll Flutter from ba719bc588ed to 65cd84b58885 (5 revisions) (#6239) https://github.com/flutter/flutter/compare/ba719bc588ed...65cd84b58885 2024-03-02 engine-flutter-autoroll@skia.org Roll Flutter Engine from 8ef8543d6001 to 2706c732b22a (1 revision) (flutter/flutter#144493) 2024-03-02 xilaizhang@google.com [github actions] rewrite cherry pick template (flutter/flutter#144065) 2024-03-01 engine-flutter-autoroll@skia.org Roll Flutter Engine from 2a5a9a6dead0 to 8ef8543d6001 (1 revision) (flutter/flutter#144480) 2024-03-01 engine-flutter-autoroll@skia.org Roll Packages from 6d02f0359368 to a9c68b83356b (5 revisions) (flutter/flutter#144477) 2024-03-01 tessertaha@gmail.com Fix `showDateRangePicker` is missing `dartpad` tag and cleanup (flutter/flutter#144475) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC bmparr@google.com,rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index fa25da89cc74..b8a1f23a6859 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ba719bc588ed22b388ad9692877ffa56e081d54f +65cd84b58885fb78bd2d27f60fa7209bedf5be25 From 05f97df8d934aae20cdb5e7420805b9db4952cc0 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 4 Mar 2024 17:20:17 -0500 Subject: [PATCH 048/126] [webview_flutter] Make WebKitWebViewWidget unit tests stateless (#6228) This eliminates the state in the Dart unit tests for this class, bringing it into alignment with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#make-each-test-entirely-self-contained. - `testController` is now returned by `buildWidget`, which clarifies and enforces that the variable can only be used after `buildWidget` is called - All the mocks are now in a data object that is created via a `configureMocks` call in each test. The latter will allow easily adjusting specific mocks in individual tests (vs having to create a new group that has an almost-identical duplicate of `setUp`, but with slight changes) which will be useful when adding macOS support, where the mock `WKWebView` will need to be a different class when testing macOS/iOS codepath divergence. --- .../legacy/web_kit_webview_widget_test.dart | 549 +++++++++++------- 1 file changed, 341 insertions(+), 208 deletions(-) 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 8c421193d187..b5488a0e3931 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 @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:async'; import 'dart:math'; import 'package:flutter/material.dart'; @@ -35,44 +36,32 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); group('WebKitWebViewWidget', () { - late MockWKWebView mockWebView; - late MockWebViewWidgetProxy mockWebViewWidgetProxy; - late MockWKUserContentController mockUserContentController; - late MockWKPreferences mockPreferences; - late MockWKWebViewConfiguration mockWebViewConfiguration; - late MockWKUIDelegate mockUIDelegate; - late MockUIScrollView mockScrollView; - late MockWKWebsiteDataStore mockWebsiteDataStore; - late MockWKNavigationDelegate mockNavigationDelegate; - - late MockWebViewPlatformCallbacksHandler mockCallbacksHandler; - late MockJavascriptChannelRegistry mockJavascriptChannelRegistry; - - late WebKitWebViewPlatformController testController; - - setUp(() { - mockWebView = MockWKWebView(); - mockWebViewConfiguration = MockWKWebViewConfiguration(); - mockUserContentController = MockWKUserContentController(); - mockPreferences = MockWKPreferences(); - mockUIDelegate = MockWKUIDelegate(); - mockScrollView = MockUIScrollView(); - mockWebsiteDataStore = MockWKWebsiteDataStore(); - mockNavigationDelegate = MockWKNavigationDelegate(); - mockWebViewWidgetProxy = MockWebViewWidgetProxy(); + _WebViewMocks configureMocks() { + final _WebViewMocks mocks = _WebViewMocks( + webView: MockWKWebView(), + webViewWidgetProxy: MockWebViewWidgetProxy(), + userContentController: MockWKUserContentController(), + preferences: MockWKPreferences(), + webViewConfiguration: MockWKWebViewConfiguration(), + uiDelegate: MockWKUIDelegate(), + scrollView: MockUIScrollView(), + websiteDataStore: MockWKWebsiteDataStore(), + navigationDelegate: MockWKNavigationDelegate(), + callbacksHandler: MockWebViewPlatformCallbacksHandler(), + javascriptChannelRegistry: MockJavascriptChannelRegistry()); when( - mockWebViewWidgetProxy.createWebView( + mocks.webViewWidgetProxy.createWebView( any, observeValue: anyNamed('observeValue'), ), - ).thenReturn(mockWebView); + ).thenReturn(mocks.webView); when( - mockWebViewWidgetProxy.createUIDelgate( + mocks.webViewWidgetProxy.createUIDelgate( onCreateWebView: captureAnyNamed('onCreateWebView'), ), - ).thenReturn(mockUIDelegate); - when(mockWebViewWidgetProxy.createNavigationDelegate( + ).thenReturn(mocks.uiDelegate); + when(mocks.webViewWidgetProxy.createNavigationDelegate( didFinishNavigation: anyNamed('didFinishNavigation'), didStartProvisionalNavigation: anyNamed('didStartProvisionalNavigation'), @@ -82,30 +71,33 @@ void main() { didFailProvisionalNavigation: anyNamed('didFailProvisionalNavigation'), webViewWebContentProcessDidTerminate: anyNamed('webViewWebContentProcessDidTerminate'), - )).thenReturn(mockNavigationDelegate); - when(mockWebView.configuration).thenReturn(mockWebViewConfiguration); - when(mockWebViewConfiguration.userContentController).thenReturn( - mockUserContentController, + )).thenReturn(mocks.navigationDelegate); + when(mocks.webView.configuration).thenReturn(mocks.webViewConfiguration); + when(mocks.webViewConfiguration.userContentController).thenReturn( + mocks.userContentController, ); - when(mockWebViewConfiguration.preferences).thenReturn(mockPreferences); + when(mocks.webViewConfiguration.preferences) + .thenReturn(mocks.preferences); - when(mockWebView.scrollView).thenReturn(mockScrollView); + when(mocks.webView.scrollView).thenReturn(mocks.scrollView); - when(mockWebViewConfiguration.websiteDataStore).thenReturn( - mockWebsiteDataStore, + when(mocks.webViewConfiguration.websiteDataStore).thenReturn( + mocks.websiteDataStore, ); + return mocks; + } - mockCallbacksHandler = MockWebViewPlatformCallbacksHandler(); - mockJavascriptChannelRegistry = MockJavascriptChannelRegistry(); - }); - - // Builds a WebViewCupertinoWidget with default parameters. - Future buildWidget( - WidgetTester tester, { + // Builds a WebViewCupertinoWidget with default parameters and returns its + // controller. + Future buildWidget( + WidgetTester tester, + _WebViewMocks mocks, { CreationParams? creationParams, bool hasNavigationDelegate = false, bool hasProgressTracking = false, }) async { + final Completer testController = + Completer(); await tester.pumpWidget(WebKitWebViewWidget( creationParams: creationParams ?? CreationParams( @@ -114,28 +106,31 @@ void main() { hasNavigationDelegate: hasNavigationDelegate, hasProgressTracking: hasProgressTracking, )), - callbacksHandler: mockCallbacksHandler, - javascriptChannelRegistry: mockJavascriptChannelRegistry, - webViewProxy: mockWebViewWidgetProxy, - configuration: mockWebViewConfiguration, + callbacksHandler: mocks.callbacksHandler, + javascriptChannelRegistry: mocks.javascriptChannelRegistry, + webViewProxy: mocks.webViewWidgetProxy, + configuration: mocks.webViewConfiguration, onBuildWidget: (WebKitWebViewPlatformController controller) { - testController = controller; + testController.complete(controller); return Container(); }, )); await tester.pumpAndSettle(); + return testController.future; } - testWidgets('build $WebKitWebViewWidget', (WidgetTester tester) async { - await buildWidget(tester); + testWidgets('build WebKitWebViewWidget', (WidgetTester tester) async { + final _WebViewMocks mocks = configureMocks(); + await buildWidget(tester, mocks); }); testWidgets('Requests to open a new window loads request in same window', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + await buildWidget(tester, mocks); final void Function(WKWebView, WKWebViewConfiguration, WKNavigationAction) - onCreateWebView = verify(mockWebViewWidgetProxy.createUIDelgate( + onCreateWebView = verify(mocks.webViewWidgetProxy.createUIDelgate( onCreateWebView: captureAnyNamed('onCreateWebView'))) .captured .single @@ -144,8 +139,8 @@ void main() { const NSUrlRequest request = NSUrlRequest(url: 'https://google.com'); onCreateWebView( - mockWebView, - mockWebViewConfiguration, + mocks.webView, + mocks.webViewConfiguration, const WKNavigationAction( request: request, targetFrame: WKFrameInfo(isMainFrame: false, request: request), @@ -153,13 +148,15 @@ void main() { ), ); - verify(mockWebView.loadRequest(request)); + verify(mocks.webView.loadRequest(request)); }); group('CreationParams', () { testWidgets('initialUrl', (WidgetTester tester) async { + final _WebViewMocks mocks = configureMocks(); await buildWidget( tester, + mocks, creationParams: CreationParams( initialUrl: 'https://www.google.com', webSettings: WebSettings( @@ -168,15 +165,17 @@ void main() { ), ), ); - final NSUrlRequest request = verify(mockWebView.loadRequest(captureAny)) - .captured - .single as NSUrlRequest; + final NSUrlRequest request = + verify(mocks.webView.loadRequest(captureAny)).captured.single + as NSUrlRequest; expect(request.url, 'https://www.google.com'); }); testWidgets('backgroundColor', (WidgetTester tester) async { + final _WebViewMocks mocks = configureMocks(); await buildWidget( tester, + mocks, creationParams: CreationParams( backgroundColor: Colors.red, webSettings: WebSettings( @@ -186,14 +185,16 @@ void main() { ), ); - verify(mockWebView.setOpaque(false)); - verify(mockWebView.setBackgroundColor(Colors.transparent)); - verify(mockScrollView.setBackgroundColor(Colors.red)); + verify(mocks.webView.setOpaque(false)); + verify(mocks.webView.setBackgroundColor(Colors.transparent)); + verify(mocks.scrollView.setBackgroundColor(Colors.red)); }); testWidgets('userAgent', (WidgetTester tester) async { + final _WebViewMocks mocks = configureMocks(); await buildWidget( tester, + mocks, creationParams: CreationParams( userAgent: 'MyUserAgent', webSettings: WebSettings( @@ -203,12 +204,14 @@ void main() { ), ); - verify(mockWebView.setCustomUserAgent('MyUserAgent')); + verify(mocks.webView.setCustomUserAgent('MyUserAgent')); }); testWidgets('autoMediaPlaybackPolicy true', (WidgetTester tester) async { + final _WebViewMocks mocks = configureMocks(); await buildWidget( tester, + mocks, creationParams: CreationParams( webSettings: WebSettings( userAgent: const WebSetting.absent(), @@ -217,15 +220,17 @@ void main() { ), ); - verify(mockWebViewConfiguration + verify(mocks.webViewConfiguration .setMediaTypesRequiringUserActionForPlayback({ WKAudiovisualMediaType.all, })); }); testWidgets('autoMediaPlaybackPolicy false', (WidgetTester tester) async { + final _WebViewMocks mocks = configureMocks(); await buildWidget( tester, + mocks, creationParams: CreationParams( autoMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, webSettings: WebSettings( @@ -235,15 +240,16 @@ void main() { ), ); - verify(mockWebViewConfiguration + verify(mocks.webViewConfiguration .setMediaTypesRequiringUserActionForPlayback({ WKAudiovisualMediaType.none, })); }); testWidgets('javascriptChannelNames', (WidgetTester tester) async { + final _WebViewMocks mocks = configureMocks(); when( - mockWebViewWidgetProxy.createScriptMessageHandler( + mocks.webViewWidgetProxy.createScriptMessageHandler( didReceiveScriptMessage: anyNamed('didReceiveScriptMessage'), ), ).thenReturn( @@ -252,6 +258,7 @@ void main() { await buildWidget( tester, + mocks, creationParams: CreationParams( javascriptChannelNames: {'a', 'b'}, webSettings: WebSettings( @@ -262,7 +269,7 @@ void main() { ); final List javaScriptChannels = verify( - mockUserContentController.addScriptMessageHandler( + mocks.userContentController.addScriptMessageHandler( captureAny, captureAny, ), @@ -281,8 +288,10 @@ void main() { group('WebSettings', () { testWidgets('javascriptMode', (WidgetTester tester) async { + final _WebViewMocks mocks = configureMocks(); await buildWidget( tester, + mocks, creationParams: CreationParams( webSettings: WebSettings( userAgent: const WebSetting.absent(), @@ -292,12 +301,14 @@ void main() { ), ); - verify(mockPreferences.setJavaScriptEnabled(true)); + verify(mocks.preferences.setJavaScriptEnabled(true)); }); testWidgets('userAgent', (WidgetTester tester) async { + final _WebViewMocks mocks = configureMocks(); await buildWidget( tester, + mocks, creationParams: CreationParams( webSettings: WebSettings( userAgent: const WebSetting.of('myUserAgent'), @@ -306,22 +317,25 @@ void main() { ), ); - verify(mockWebView.setCustomUserAgent('myUserAgent')); + verify(mocks.webView.setCustomUserAgent('myUserAgent')); }); testWidgets( 'enabling zoom re-adds JavaScript channels', (WidgetTester tester) async { + final _WebViewMocks mocks = configureMocks(); when( - mockWebViewWidgetProxy.createScriptMessageHandler( + mocks.webViewWidgetProxy.createScriptMessageHandler( didReceiveScriptMessage: anyNamed('didReceiveScriptMessage'), ), ).thenReturn( MockWKScriptMessageHandler(), ); - await buildWidget( + final WebKitWebViewPlatformController testController = + await buildWidget( tester, + mocks, creationParams: CreationParams( webSettings: WebSettings( userAgent: const WebSetting.absent(), @@ -332,7 +346,7 @@ void main() { ), ); - clearInteractions(mockUserContentController); + clearInteractions(mocks.userContentController); await testController.updateSettings(WebSettings( userAgent: const WebSetting.absent(), @@ -340,9 +354,10 @@ void main() { )); final List javaScriptChannels = verifyInOrder([ - mockUserContentController.removeAllUserScripts(), - mockUserContentController.removeScriptMessageHandler('myChannel'), - mockUserContentController.addScriptMessageHandler( + mocks.userContentController.removeAllUserScripts(), + mocks.userContentController + .removeScriptMessageHandler('myChannel'), + mocks.userContentController.addScriptMessageHandler( captureAny, captureAny, ), @@ -359,8 +374,11 @@ void main() { testWidgets( 'enabling zoom removes script', (WidgetTester tester) async { - await buildWidget( + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget( tester, + mocks, creationParams: CreationParams( webSettings: WebSettings( userAgent: const WebSetting.absent(), @@ -370,15 +388,15 @@ void main() { ), ); - clearInteractions(mockUserContentController); + clearInteractions(mocks.userContentController); await testController.updateSettings(WebSettings( userAgent: const WebSetting.absent(), zoomEnabled: true, )); - verify(mockUserContentController.removeAllUserScripts()); - verifyNever(mockUserContentController.addScriptMessageHandler( + verify(mocks.userContentController.removeAllUserScripts()); + verifyNever(mocks.userContentController.addScriptMessageHandler( any, any, )); @@ -386,8 +404,10 @@ void main() { ); testWidgets('zoomEnabled is false', (WidgetTester tester) async { + final _WebViewMocks mocks = configureMocks(); await buildWidget( tester, + mocks, creationParams: CreationParams( webSettings: WebSettings( userAgent: const WebSetting.absent(), @@ -398,7 +418,7 @@ void main() { ); final WKUserScript zoomScript = - verify(mockUserContentController.addUserScript(captureAny)) + verify(mocks.userContentController.addUserScript(captureAny)) .captured .first as WKUserScript; expect(zoomScript.isMainFrameOnly, isTrue); @@ -415,8 +435,10 @@ void main() { }); testWidgets('allowsInlineMediaPlayback', (WidgetTester tester) async { + final _WebViewMocks mocks = configureMocks(); await buildWidget( tester, + mocks, creationParams: CreationParams( webSettings: WebSettings( userAgent: const WebSetting.absent(), @@ -425,52 +447,60 @@ void main() { ), ); - verify(mockWebViewConfiguration.setAllowsInlineMediaPlayback(true)); + verify(mocks.webViewConfiguration.setAllowsInlineMediaPlayback(true)); }); }); }); group('WebKitWebViewPlatformController', () { testWidgets('loadFile', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); await testController.loadFile('/path/to/file.html'); - verify(mockWebView.loadFileUrl( + verify(mocks.webView.loadFileUrl( '/path/to/file.html', readAccessUrl: '/path/to', )); }); testWidgets('loadFlutterAsset', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); await testController.loadFlutterAsset('test_assets/index.html'); - verify(mockWebView.loadFlutterAsset('test_assets/index.html')); + verify(mocks.webView.loadFlutterAsset('test_assets/index.html')); }); testWidgets('loadHtmlString', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); const String htmlString = 'Test data.'; await testController.loadHtmlString(htmlString, baseUrl: 'baseUrl'); - verify(mockWebView.loadHtmlString( + verify(mocks.webView.loadHtmlString( 'Test data.', baseUrl: 'baseUrl', )); }); testWidgets('loadUrl', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); await testController.loadUrl( 'https://www.google.com', {'a': 'header'}, ); - final NSUrlRequest request = verify(mockWebView.loadRequest(captureAny)) - .captured - .single as NSUrlRequest; + final NSUrlRequest request = + verify(mocks.webView.loadRequest(captureAny)).captured.single + as NSUrlRequest; expect(request.url, 'https://www.google.com'); expect(request.allHttpHeaderFields, {'a': 'header'}); }); @@ -478,7 +508,9 @@ void main() { group('loadRequest', () { testWidgets('Throws ArgumentError for empty scheme', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); expect( () async => testController.loadRequest( @@ -491,7 +523,9 @@ void main() { }); testWidgets('GET without headers', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); await testController.loadRequest(WebViewRequest( uri: Uri.parse('https://www.google.com'), @@ -499,7 +533,7 @@ void main() { )); final NSUrlRequest request = - verify(mockWebView.loadRequest(captureAny)).captured.single + verify(mocks.webView.loadRequest(captureAny)).captured.single as NSUrlRequest; expect(request.url, 'https://www.google.com'); expect(request.allHttpHeaderFields, {}); @@ -507,7 +541,9 @@ void main() { }); testWidgets('GET with headers', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); await testController.loadRequest(WebViewRequest( uri: Uri.parse('https://www.google.com'), @@ -516,7 +552,7 @@ void main() { )); final NSUrlRequest request = - verify(mockWebView.loadRequest(captureAny)).captured.single + verify(mocks.webView.loadRequest(captureAny)).captured.single as NSUrlRequest; expect(request.url, 'https://www.google.com'); expect(request.allHttpHeaderFields, {'a': 'header'}); @@ -524,7 +560,9 @@ void main() { }); testWidgets('POST without body', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); await testController.loadRequest(WebViewRequest( uri: Uri.parse('https://www.google.com'), @@ -532,14 +570,16 @@ void main() { )); final NSUrlRequest request = - verify(mockWebView.loadRequest(captureAny)).captured.single + verify(mocks.webView.loadRequest(captureAny)).captured.single as NSUrlRequest; expect(request.url, 'https://www.google.com'); expect(request.httpMethod, 'post'); }); testWidgets('POST with body', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); await testController.loadRequest(WebViewRequest( uri: Uri.parse('https://www.google.com'), @@ -547,7 +587,7 @@ void main() { body: Uint8List.fromList('Test Body'.codeUnits))); final NSUrlRequest request = - verify(mockWebView.loadRequest(captureAny)).captured.single + verify(mocks.webView.loadRequest(captureAny)).captured.single as NSUrlRequest; expect(request.url, 'https://www.google.com'); expect(request.httpMethod, 'post'); @@ -559,48 +599,60 @@ void main() { }); testWidgets('canGoBack', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); - when(mockWebView.canGoBack()).thenAnswer( + when(mocks.webView.canGoBack()).thenAnswer( (_) => Future.value(false), ); expect(testController.canGoBack(), completion(false)); }); testWidgets('canGoForward', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); - when(mockWebView.canGoForward()).thenAnswer( + when(mocks.webView.canGoForward()).thenAnswer( (_) => Future.value(true), ); expect(testController.canGoForward(), completion(true)); }); testWidgets('goBack', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); await testController.goBack(); - verify(mockWebView.goBack()); + verify(mocks.webView.goBack()); }); testWidgets('goForward', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); await testController.goForward(); - verify(mockWebView.goForward()); + verify(mocks.webView.goForward()); }); testWidgets('reload', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); await testController.reload(); - verify(mockWebView.reload()); + verify(mocks.webView.reload()); }); testWidgets('evaluateJavascript', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); - when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + when(mocks.webView.evaluateJavaScript('runJavaScript')).thenAnswer( (_) => Future.value('returnString'), ); expect( @@ -611,9 +663,11 @@ void main() { testWidgets('evaluateJavascript with null return value', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); - when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + when(mocks.webView.evaluateJavaScript('runJavaScript')).thenAnswer( (_) => Future.value(), ); // The legacy implementation of webview_flutter_wkwebview would convert @@ -627,9 +681,11 @@ void main() { testWidgets('evaluateJavascript with bool return value', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); - when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + when(mocks.webView.evaluateJavaScript('runJavaScript')).thenAnswer( (_) => Future.value(true), ); // The legacy implementation of webview_flutter_wkwebview would convert @@ -644,9 +700,11 @@ void main() { testWidgets('evaluateJavascript with double return value', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); - when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + when(mocks.webView.evaluateJavaScript('runJavaScript')).thenAnswer( (_) => Future.value(1.0), ); // The legacy implementation of webview_flutter_wkwebview would convert @@ -663,9 +721,11 @@ void main() { testWidgets('evaluateJavascript with list return value', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); - when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + when(mocks.webView.evaluateJavaScript('runJavaScript')).thenAnswer( (_) => Future.value([1, 'string', null]), ); // The legacy implementation of webview_flutter_wkwebview would convert @@ -679,9 +739,11 @@ void main() { testWidgets('evaluateJavascript with map return value', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); - when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + when(mocks.webView.evaluateJavaScript('runJavaScript')).thenAnswer( (_) => Future.value({ 1: 'string', null: null, @@ -698,9 +760,11 @@ void main() { testWidgets('evaluateJavascript throws exception', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); - when(mockWebView.evaluateJavaScript('runJavaScript')) + when(mocks.webView.evaluateJavaScript('runJavaScript')) .thenThrow(Error()); expect( testController.evaluateJavascript('runJavaScript'), @@ -709,9 +773,11 @@ void main() { }); testWidgets('runJavascriptReturningResult', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); - when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + when(mocks.webView.evaluateJavaScript('runJavaScript')).thenAnswer( (_) => Future.value('returnString'), ); expect( @@ -723,9 +789,11 @@ void main() { testWidgets( 'runJavascriptReturningResult throws error on null return value', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); - when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + when(mocks.webView.evaluateJavaScript('runJavaScript')).thenAnswer( (_) => Future.value(), ); expect( @@ -736,9 +804,11 @@ void main() { testWidgets('runJavascriptReturningResult with bool return value', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); - when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + when(mocks.webView.evaluateJavaScript('runJavaScript')).thenAnswer( (_) => Future.value(false), ); // The legacy implementation of webview_flutter_wkwebview would convert @@ -752,9 +822,11 @@ void main() { }); testWidgets('runJavascript', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); - when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + when(mocks.webView.evaluateJavaScript('runJavaScript')).thenAnswer( (_) => Future.value('returnString'), ); expect( @@ -766,9 +838,11 @@ void main() { testWidgets( 'runJavascript ignores exception with unsupported javascript type', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); - when(mockWebView.evaluateJavaScript('runJavaScript')) + when(mocks.webView.evaluateJavaScript('runJavaScript')) .thenThrow(PlatformException( code: '', details: const NSError( @@ -783,57 +857,70 @@ void main() { }); testWidgets('getTitle', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); - when(mockWebView.getTitle()) + when(mocks.webView.getTitle()) .thenAnswer((_) => Future.value('Web Title')); expect(testController.getTitle(), completion('Web Title')); }); testWidgets('currentUrl', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); - when(mockWebView.getUrl()) + when(mocks.webView.getUrl()) .thenAnswer((_) => Future.value('myUrl.com')); expect(testController.currentUrl(), completion('myUrl.com')); }); testWidgets('scrollTo', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); await testController.scrollTo(2, 4); - verify(mockScrollView.setContentOffset(const Point(2.0, 4.0))); + verify( + mocks.scrollView.setContentOffset(const Point(2.0, 4.0))); }); testWidgets('scrollBy', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); await testController.scrollBy(2, 4); - verify(mockScrollView.scrollBy(const Point(2.0, 4.0))); + verify(mocks.scrollView.scrollBy(const Point(2.0, 4.0))); }); testWidgets('getScrollX', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); - when(mockScrollView.getContentOffset()).thenAnswer( + when(mocks.scrollView.getContentOffset()).thenAnswer( (_) => Future>.value(const Point(8.0, 16.0))); expect(testController.getScrollX(), completion(8.0)); }); testWidgets('getScrollY', (WidgetTester tester) async { - await buildWidget(tester); - - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); - when(mockScrollView.getContentOffset()).thenAnswer( + when(mocks.scrollView.getContentOffset()).thenAnswer( (_) => Future>.value(const Point(8.0, 16.0))); expect(testController.getScrollY(), completion(16.0)); }); testWidgets('clearCache', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); when( - mockWebsiteDataStore.removeDataOfTypes( + mocks.websiteDataStore.removeDataOfTypes( { WKWebsiteDataType.memoryCache, WKWebsiteDataType.diskCache, @@ -848,20 +935,22 @@ void main() { }); testWidgets('addJavascriptChannels', (WidgetTester tester) async { + final _WebViewMocks mocks = configureMocks(); when( - mockWebViewWidgetProxy.createScriptMessageHandler( + mocks.webViewWidgetProxy.createScriptMessageHandler( didReceiveScriptMessage: anyNamed('didReceiveScriptMessage'), ), ).thenReturn( MockWKScriptMessageHandler(), ); - await buildWidget(tester); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); await testController.addJavascriptChannels({'c', 'd'}); final List javaScriptChannels = verify( - mockUserContentController.addScriptMessageHandler( - captureAny, captureAny), + mocks.userContentController + .addScriptMessageHandler(captureAny, captureAny), ).captured; expect( javaScriptChannels[0], @@ -875,7 +964,7 @@ void main() { expect(javaScriptChannels[3], 'd'); final List userScripts = - verify(mockUserContentController.addUserScript(captureAny)) + verify(mocks.userContentController.addUserScript(captureAny)) .captured .cast(); expect(userScripts[0].source, 'window.c = webkit.messageHandlers.c;'); @@ -893,27 +982,29 @@ void main() { }); testWidgets('removeJavascriptChannels', (WidgetTester tester) async { + final _WebViewMocks mocks = configureMocks(); when( - mockWebViewWidgetProxy.createScriptMessageHandler( + mocks.webViewWidgetProxy.createScriptMessageHandler( didReceiveScriptMessage: anyNamed('didReceiveScriptMessage'), ), ).thenReturn( MockWKScriptMessageHandler(), ); - await buildWidget(tester); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); await testController.addJavascriptChannels({'c', 'd'}); - reset(mockUserContentController); + reset(mocks.userContentController); await testController.removeJavascriptChannels({'c'}); - verify(mockUserContentController.removeAllUserScripts()); - verify(mockUserContentController.removeScriptMessageHandler('c')); - verify(mockUserContentController.removeScriptMessageHandler('d')); + verify(mocks.userContentController.removeAllUserScripts()); + verify(mocks.userContentController.removeScriptMessageHandler('c')); + verify(mocks.userContentController.removeScriptMessageHandler('d')); final List javaScriptChannels = verify( - mockUserContentController.addScriptMessageHandler( + mocks.userContentController.addScriptMessageHandler( captureAny, captureAny, ), @@ -925,7 +1016,7 @@ void main() { expect(javaScriptChannels[1], 'd'); final List userScripts = - verify(mockUserContentController.addUserScript(captureAny)) + verify(mocks.userContentController.addUserScript(captureAny)) .captured .cast(); expect(userScripts[0].source, 'window.d = webkit.messageHandlers.d;'); @@ -938,16 +1029,19 @@ void main() { testWidgets('removeJavascriptChannels with zoom disabled', (WidgetTester tester) async { + final _WebViewMocks mocks = configureMocks(); when( - mockWebViewWidgetProxy.createScriptMessageHandler( + mocks.webViewWidgetProxy.createScriptMessageHandler( didReceiveScriptMessage: anyNamed('didReceiveScriptMessage'), ), ).thenReturn( MockWKScriptMessageHandler(), ); - await buildWidget( + final WebKitWebViewPlatformController testController = + await buildWidget( tester, + mocks, creationParams: CreationParams( webSettings: WebSettings( userAgent: const WebSetting.absent(), @@ -958,11 +1052,11 @@ void main() { ); await testController.addJavascriptChannels({'c'}); - clearInteractions(mockUserContentController); + clearInteractions(mocks.userContentController); await testController.removeJavascriptChannels({'c'}); final WKUserScript zoomScript = - verify(mockUserContentController.addUserScript(captureAny)) + verify(mocks.userContentController.addUserScript(captureAny)) .captured .first as WKUserScript; expect(zoomScript.isMainFrameOnly, isTrue); @@ -981,10 +1075,11 @@ void main() { group('WebViewPlatformCallbacksHandler', () { testWidgets('onPageStarted', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + await buildWidget(tester, mocks); final void Function(WKWebView, String) didStartProvisionalNavigation = - verify(mockWebViewWidgetProxy.createNavigationDelegate( + verify(mocks.webViewWidgetProxy.createNavigationDelegate( didFinishNavigation: anyNamed('didFinishNavigation'), didStartProvisionalNavigation: captureAnyNamed('didStartProvisionalNavigation'), @@ -996,16 +1091,17 @@ void main() { webViewWebContentProcessDidTerminate: anyNamed('webViewWebContentProcessDidTerminate'), )).captured.single as void Function(WKWebView, String); - didStartProvisionalNavigation(mockWebView, 'https://google.com'); + didStartProvisionalNavigation(mocks.webView, 'https://google.com'); - verify(mockCallbacksHandler.onPageStarted('https://google.com')); + verify(mocks.callbacksHandler.onPageStarted('https://google.com')); }); testWidgets('onPageFinished', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + await buildWidget(tester, mocks); final void Function(WKWebView, String) didFinishNavigation = - verify(mockWebViewWidgetProxy.createNavigationDelegate( + verify(mocks.webViewWidgetProxy.createNavigationDelegate( didFinishNavigation: captureAnyNamed('didFinishNavigation'), didStartProvisionalNavigation: anyNamed('didStartProvisionalNavigation'), @@ -1017,17 +1113,18 @@ void main() { webViewWebContentProcessDidTerminate: anyNamed('webViewWebContentProcessDidTerminate'), )).captured.single as void Function(WKWebView, String); - didFinishNavigation(mockWebView, 'https://google.com'); + didFinishNavigation(mocks.webView, 'https://google.com'); - verify(mockCallbacksHandler.onPageFinished('https://google.com')); + verify(mocks.callbacksHandler.onPageFinished('https://google.com')); }); testWidgets('onWebResourceError from didFailNavigation', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + await buildWidget(tester, mocks); final void Function(WKWebView, NSError) didFailNavigation = - verify(mockWebViewWidgetProxy.createNavigationDelegate( + verify(mocks.webViewWidgetProxy.createNavigationDelegate( didFinishNavigation: anyNamed('didFinishNavigation'), didStartProvisionalNavigation: anyNamed('didStartProvisionalNavigation'), @@ -1041,7 +1138,7 @@ void main() { )).captured.single as void Function(WKWebView, NSError); didFailNavigation( - mockWebView, + mocks.webView, const NSError( code: WKErrorCode.webViewInvalidated, domain: 'domain', @@ -1052,7 +1149,7 @@ void main() { ); final WebResourceError error = - verify(mockCallbacksHandler.onWebResourceError(captureAny)) + verify(mocks.callbacksHandler.onWebResourceError(captureAny)) .captured .single as WebResourceError; expect(error.description, 'my desc'); @@ -1063,10 +1160,11 @@ void main() { testWidgets('onWebResourceError from didFailProvisionalNavigation', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + await buildWidget(tester, mocks); final void Function(WKWebView, NSError) didFailProvisionalNavigation = - verify(mockWebViewWidgetProxy.createNavigationDelegate( + verify(mocks.webViewWidgetProxy.createNavigationDelegate( didFinishNavigation: anyNamed('didFinishNavigation'), didStartProvisionalNavigation: anyNamed('didStartProvisionalNavigation'), @@ -1080,7 +1178,7 @@ void main() { )).captured.single as void Function(WKWebView, NSError); didFailProvisionalNavigation( - mockWebView, + mocks.webView, const NSError( code: WKErrorCode.webContentProcessTerminated, domain: 'domain', @@ -1091,7 +1189,7 @@ void main() { ); final WebResourceError error = - verify(mockCallbacksHandler.onWebResourceError(captureAny)) + verify(mocks.callbacksHandler.onWebResourceError(captureAny)) .captured .single as WebResourceError; expect(error.description, 'my desc'); @@ -1106,10 +1204,11 @@ void main() { testWidgets( 'onWebResourceError from webViewWebContentProcessDidTerminate', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + await buildWidget(tester, mocks); final void Function(WKWebView) webViewWebContentProcessDidTerminate = - verify(mockWebViewWidgetProxy.createNavigationDelegate( + verify(mocks.webViewWidgetProxy.createNavigationDelegate( didFinishNavigation: anyNamed('didFinishNavigation'), didStartProvisionalNavigation: anyNamed('didStartProvisionalNavigation'), @@ -1121,10 +1220,10 @@ void main() { webViewWebContentProcessDidTerminate: captureAnyNamed('webViewWebContentProcessDidTerminate'), )).captured.single as void Function(WKWebView); - webViewWebContentProcessDidTerminate(mockWebView); + webViewWebContentProcessDidTerminate(mocks.webView); final WebResourceError error = - verify(mockCallbacksHandler.onWebResourceError(captureAny)) + verify(mocks.callbacksHandler.onWebResourceError(captureAny)) .captured .single as WebResourceError; expect(error.description, ''); @@ -1138,11 +1237,12 @@ void main() { testWidgets('onNavigationRequest from decidePolicyForNavigationAction', (WidgetTester tester) async { - await buildWidget(tester, hasNavigationDelegate: true); + final _WebViewMocks mocks = configureMocks(); + await buildWidget(tester, mocks, hasNavigationDelegate: true); final Future Function( WKWebView, WKNavigationAction) decidePolicyForNavigationAction = - verify(mockWebViewWidgetProxy.createNavigationDelegate( + verify(mocks.webViewWidgetProxy.createNavigationDelegate( didFinishNavigation: anyNamed('didFinishNavigation'), didStartProvisionalNavigation: anyNamed('didStartProvisionalNavigation'), @@ -1156,14 +1256,14 @@ void main() { )).captured.single as Future Function( WKWebView, WKNavigationAction); - when(mockCallbacksHandler.onNavigationRequest( + when(mocks.callbacksHandler.onNavigationRequest( isForMainFrame: argThat(isFalse, named: 'isForMainFrame'), url: 'https://google.com', )).thenReturn(true); expect( decidePolicyForNavigationAction( - mockWebView, + mocks.webView, const WKNavigationAction( request: NSUrlRequest(url: 'https://google.com'), targetFrame: WKFrameInfo( @@ -1175,17 +1275,18 @@ void main() { completion(WKNavigationActionPolicy.allow), ); - verify(mockCallbacksHandler.onNavigationRequest( + verify(mocks.callbacksHandler.onNavigationRequest( url: 'https://google.com', isForMainFrame: false, )); }); testWidgets('onProgress', (WidgetTester tester) async { - await buildWidget(tester, hasProgressTracking: true); + final _WebViewMocks mocks = configureMocks(); + await buildWidget(tester, mocks, hasProgressTracking: true); - verify(mockWebView.addObserver( - mockWebView, + verify(mocks.webView.addObserver( + mocks.webView, keyPath: 'estimatedProgress', options: { NSKeyValueObservingOptions.newValue, @@ -1193,7 +1294,7 @@ void main() { )); final void Function(String, NSObject, Map) - observeValue = verify(mockWebViewWidgetProxy.createWebView(any, + observeValue = verify(mocks.webViewWidgetProxy.createWebView(any, observeValue: captureAnyNamed('observeValue'))) .captured .single @@ -1202,19 +1303,20 @@ void main() { observeValue( 'estimatedProgress', - mockWebView, + mocks.webView, {NSKeyValueChangeKey.newValue: 0.32}, ); - verify(mockCallbacksHandler.onProgress(32)); + verify(mocks.callbacksHandler.onProgress(32)); }); testWidgets('progress observer is not removed without being set first', (WidgetTester tester) async { - await buildWidget(tester); + final _WebViewMocks mocks = configureMocks(); + await buildWidget(tester, mocks); - verifyNever(mockWebView.removeObserver( - mockWebView, + verifyNever(mocks.webView.removeObserver( + mocks.webView, keyPath: 'estimatedProgress', )); }); @@ -1222,20 +1324,22 @@ void main() { group('JavascriptChannelRegistry', () { testWidgets('onJavascriptChannelMessage', (WidgetTester tester) async { + final _WebViewMocks mocks = configureMocks(); when( - mockWebViewWidgetProxy.createScriptMessageHandler( + mocks.webViewWidgetProxy.createScriptMessageHandler( didReceiveScriptMessage: anyNamed('didReceiveScriptMessage'), ), ).thenReturn( MockWKScriptMessageHandler(), ); - await buildWidget(tester); + final WebKitWebViewPlatformController testController = + await buildWidget(tester, mocks); await testController.addJavascriptChannels({'hello'}); final void Function(WKUserContentController, WKScriptMessage) - didReceiveScriptMessage = verify( - mockWebViewWidgetProxy.createScriptMessageHandler( + didReceiveScriptMessage = verify(mocks.webViewWidgetProxy + .createScriptMessageHandler( didReceiveScriptMessage: captureAnyNamed('didReceiveScriptMessage'))) .captured @@ -1243,10 +1347,10 @@ void main() { as void Function(WKUserContentController, WKScriptMessage); didReceiveScriptMessage( - mockUserContentController, + mocks.userContentController, const WKScriptMessage(name: 'hello', body: 'A message.'), ); - verify(mockJavascriptChannelRegistry.onJavascriptChannelMessage( + verify(mocks.javascriptChannelRegistry.onJavascriptChannelMessage( 'hello', 'A message.', )); @@ -1254,3 +1358,32 @@ void main() { }); }); } + +/// A collection of mocks used in constructing a WebViewWidget. +class _WebViewMocks { + _WebViewMocks({ + required this.webView, + required this.webViewWidgetProxy, + required this.userContentController, + required this.preferences, + required this.webViewConfiguration, + required this.uiDelegate, + required this.scrollView, + required this.websiteDataStore, + required this.navigationDelegate, + required this.callbacksHandler, + required this.javascriptChannelRegistry, + }); + + final MockWKWebView webView; + final MockWebViewWidgetProxy webViewWidgetProxy; + final MockWKUserContentController userContentController; + final MockWKPreferences preferences; + final MockWKWebViewConfiguration webViewConfiguration; + final MockWKUIDelegate uiDelegate; + final MockUIScrollView scrollView; + final MockWKWebsiteDataStore websiteDataStore; + final MockWKNavigationDelegate navigationDelegate; + final MockWebViewPlatformCallbacksHandler callbacksHandler; + final MockJavascriptChannelRegistry javascriptChannelRegistry; +} From 5fed0479030228a3ee49dcf8fe7df9af2a28b4f0 Mon Sep 17 00:00:00 2001 From: Patrick Zierahn <5384625+pzierahn@users.noreply.github.com> Date: Tue, 5 Mar 2024 11:19:05 +0100 Subject: [PATCH 049/126] [flutter_markdown] Fix WidgetSpan Support in MarkdownElementBuilder (#6225) This pull request addresses a critical issue identified in the `flutter_markdown` package, specifically relating to the handling of `WidgetSpan` elements within `Text.rich` elements when utilized inside `MarkdownElementBuilder`s. Prior to this fix, the inclusion of `WidgetSpan` instances within markdown content led to casting issues, disrupting the normal rendering flow of the markdown content. Key Changes: - Resolved casting problems associated with using `WidgetSpan` inside `Text.rich` elements by refining the type handling and span merging logic within the affected functions. - Thoroughly commented and cleaned up the code within the error-prone functions to enhance readability and maintainability, ensuring that future modifications can be made more easily. - Adjusted existing tests to align with the newly introduced span merging logic, which is now more efficient and produces more predictable outcomes. - Introduced new tests specifically designed to cover scenarios where `WidgetSpan` elements are included within markdown content, ensuring that this issue does not resurface in future updates. The adjustments made in this pull request ensure that developers utilizing the `flutter_markdown` package can now seamlessly incorporate `WidgetSpan` elements within their markdown content without encountering the previously observed casting issues. This fix not only improves the package's robustness but also expands its flexibility, allowing for richer text compositions within markdown. This pull request closes issue [#144383](https://github.com/flutter/flutter/issues/144383), effectively addressing the reported bug and enhancing the overall functionality of the `flutter_markdown` package. I'm welcoming feedback and suggestions for improvement. Fixed issues: * [#144383](https://github.com/flutter/flutter/issues/144383) --- packages/flutter_markdown/CHANGELOG.md | 4 + .../flutter_markdown/lib/src/builder.dart | 170 +++++++++++++----- packages/flutter_markdown/pubspec.yaml | 2 +- .../test/custom_syntax_test.dart | 10 +- .../test/inline_widget_test.dart | 78 ++++++++ 5 files changed, 210 insertions(+), 54 deletions(-) create mode 100644 packages/flutter_markdown/test/inline_widget_test.dart diff --git a/packages/flutter_markdown/CHANGELOG.md b/packages/flutter_markdown/CHANGELOG.md index 7b19e96b64af..e0fe8c99c3f5 100644 --- a/packages/flutter_markdown/CHANGELOG.md +++ b/packages/flutter_markdown/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.21 + +* Fixes support for `WidgetSpan` in `Text.rich` elements inside `MarkdownElementBuilder`. + ## 0.6.20+1 * Updates minimum supported SDK version to Flutter 3.19. diff --git a/packages/flutter_markdown/lib/src/builder.dart b/packages/flutter_markdown/lib/src/builder.dart index 91371dd1c6e2..6188099429f0 100644 --- a/packages/flutter_markdown/lib/src/builder.dart +++ b/packages/flutter_markdown/lib/src/builder.dart @@ -707,52 +707,119 @@ class MarkdownBuilder implements md.NodeVisitor { } } + /// Extracts all spans from an inline element and merges them into a single list + Iterable _getInlineSpans(InlineSpan span) { + // If the span is not a TextSpan or it has no children, return the span + if (span is! TextSpan || span.children == null) { + return [span]; + } + + // Merge the style of the parent with the style of the children + final Iterable spans = + span.children!.map((InlineSpan childSpan) { + if (childSpan is TextSpan) { + return TextSpan( + text: childSpan.text, + recognizer: childSpan.recognizer, + semanticsLabel: childSpan.semanticsLabel, + style: childSpan.style?.merge(span.style), + ); + } else { + return childSpan; + } + }); + + return spans; + } + /// Merges adjacent [TextSpan] children List _mergeInlineChildren( List children, TextAlign? textAlign, ) { + // List of merged text spans and widgets final List mergedTexts = []; + for (final Widget child in children) { - if (mergedTexts.isNotEmpty && mergedTexts.last is Text && child is Text) { - final Text previous = mergedTexts.removeLast() as Text; - final TextSpan previousTextSpan = previous.textSpan! as TextSpan; - final List children = previousTextSpan.children != null - ? previousTextSpan.children! - .map((InlineSpan span) => span is! TextSpan - ? TextSpan(children: [span]) - : span) - .toList() - : [previousTextSpan]; - children.add(child.textSpan! as TextSpan); - final TextSpan? mergedSpan = _mergeSimilarTextSpans(children); - mergedTexts.add(_buildRichText( - mergedSpan, - textAlign: textAlign, - )); - } else if (mergedTexts.isNotEmpty && - mergedTexts.last is SelectableText && - child is SelectableText) { - final SelectableText previous = - mergedTexts.removeLast() as SelectableText; - final TextSpan previousTextSpan = previous.textSpan!; - final List children = previousTextSpan.children != null - ? List.from(previousTextSpan.children!) - : [previousTextSpan]; - if (child.textSpan != null) { - children.add(child.textSpan!); + // If the list is empty, add the current widget to the list + if (mergedTexts.isEmpty) { + mergedTexts.add(child); + continue; + } + + // Remove last widget from the list to merge it with the current widget + final Widget last = mergedTexts.removeLast(); + + // Extracted spans from the last and the current widget + List spans = []; + + // Extract the text spans from the last widget + if (last is SelectableText) { + final TextSpan span = last.textSpan!; + spans.addAll(_getInlineSpans(span)); + } else if (last is Text) { + final InlineSpan span = last.textSpan!; + spans.addAll(_getInlineSpans(span)); + } else if (last is RichText) { + final InlineSpan span = last.text; + spans.addAll(_getInlineSpans(span)); + } else { + // If the last widget is not a text widget, + // add both the last and the current widget to the list + mergedTexts.addAll([last, child]); + continue; + } + + // Extract the text spans from the current widget + if (child is Text) { + final InlineSpan span = child.textSpan!; + spans.addAll(_getInlineSpans(span)); + } else if (child is SelectableText) { + final TextSpan span = child.textSpan!; + spans.addAll(_getInlineSpans(span)); + } else if (child is RichText) { + final InlineSpan span = child.text; + spans.addAll(_getInlineSpans(span)); + } else { + // If the current widget is not a text widget, + // add both the last and the current widget to the list + mergedTexts.addAll([last, child]); + continue; + } + + if (spans.isNotEmpty) { + // Merge similar text spans + spans = _mergeSimilarTextSpans(spans); + + // Create a new text widget with the merged text spans + InlineSpan child; + if (spans.length == 1) { + child = spans.first; + } else { + child = TextSpan(children: spans); + } + + // Add the new text widget to the list + if (selectable) { + mergedTexts.add(SelectableText.rich( + TextSpan(children: spans), + textScaler: styleSheet.textScaler, + textAlign: textAlign ?? TextAlign.start, + onTap: onTapText, + )); + } else { + mergedTexts.add(Text.rich( + child, + textScaler: styleSheet.textScaler, + textAlign: textAlign ?? TextAlign.start, + )); } - final TextSpan? mergedSpan = _mergeSimilarTextSpans(children); - mergedTexts.add( - _buildRichText( - mergedSpan, - textAlign: textAlign, - ), - ); } else { + // If no text spans were found, add the current widget to the list mergedTexts.add(child); } } + return mergedTexts; } @@ -827,19 +894,30 @@ class MarkdownBuilder implements md.NodeVisitor { } /// Combine text spans with equivalent properties into a single span. - TextSpan? _mergeSimilarTextSpans(List? textSpans) { - if (textSpans == null || textSpans.length < 2) { - return TextSpan(children: textSpans); + List _mergeSimilarTextSpans(List textSpans) { + if (textSpans.length < 2) { + return textSpans; } - final List mergedSpans = [textSpans.first]; + final List mergedSpans = []; for (int index = 1; index < textSpans.length; index++) { - final TextSpan nextChild = textSpans[index]; - if (nextChild.recognizer == mergedSpans.last.recognizer && - nextChild.semanticsLabel == mergedSpans.last.semanticsLabel && - nextChild.style == mergedSpans.last.style) { - final TextSpan previous = mergedSpans.removeLast(); + final InlineSpan previous = + mergedSpans.isEmpty ? textSpans.first : mergedSpans.removeLast(); + final InlineSpan nextChild = textSpans[index]; + + final bool previousIsTextSpan = previous is TextSpan; + final bool nextIsTextSpan = nextChild is TextSpan; + if (!previousIsTextSpan || !nextIsTextSpan) { + mergedSpans.addAll([previous, nextChild]); + continue; + } + + final bool matchStyle = nextChild.recognizer == previous.recognizer && + nextChild.semanticsLabel == previous.semanticsLabel && + nextChild.style == previous.style; + + if (matchStyle) { mergedSpans.add(TextSpan( text: previous.toPlainText() + nextChild.toPlainText(), recognizer: previous.recognizer, @@ -847,15 +925,13 @@ class MarkdownBuilder implements md.NodeVisitor { style: previous.style, )); } else { - mergedSpans.add(nextChild); + mergedSpans.addAll([previous, nextChild]); } } // When the mergered spans compress into a single TextSpan return just that // TextSpan, otherwise bundle the set of TextSpans under a single parent. - return mergedSpans.length == 1 - ? mergedSpans.first - : TextSpan(children: mergedSpans); + return mergedSpans; } Widget _buildRichText(TextSpan? text, {TextAlign? textAlign, String? key}) { diff --git a/packages/flutter_markdown/pubspec.yaml b/packages/flutter_markdown/pubspec.yaml index c5b8d4706f11..9811a80d213f 100644 --- a/packages/flutter_markdown/pubspec.yaml +++ b/packages/flutter_markdown/pubspec.yaml @@ -4,7 +4,7 @@ description: A Markdown renderer for Flutter. Create rich text output, formatted with simple Markdown tags. repository: https://github.com/flutter/packages/tree/main/packages/flutter_markdown issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_markdown%22 -version: 0.6.20+1 +version: 0.6.21 environment: sdk: ^3.3.0 diff --git a/packages/flutter_markdown/test/custom_syntax_test.dart b/packages/flutter_markdown/test/custom_syntax_test.dart index 1db01e3c0f9e..2bee4ebbf0bd 100644 --- a/packages/flutter_markdown/test/custom_syntax_test.dart +++ b/packages/flutter_markdown/test/custom_syntax_test.dart @@ -77,9 +77,8 @@ void defineTests() { ); final Text textWidget = tester.widget(find.byType(Text)); - final TextSpan span = - (textWidget.textSpan! as TextSpan).children![0] as TextSpan; - final WidgetSpan widgetSpan = span.children![0] as WidgetSpan; + final TextSpan textSpan = textWidget.textSpan! as TextSpan; + final WidgetSpan widgetSpan = textSpan.children![0] as WidgetSpan; expect(widgetSpan.child, isInstanceOf()); }, ); @@ -133,10 +132,9 @@ void defineTests() { final TextSpan textSpan = textWidget.textSpan! as TextSpan; final TextSpan start = textSpan.children![0] as TextSpan; expect(start.text, 'this test replaces a string with a '); - final TextSpan end = textSpan.children![1] as TextSpan; - final TextSpan foo = end.children![0] as TextSpan; + final TextSpan foo = textSpan.children![1] as TextSpan; expect(foo.text, 'foo'); - final WidgetSpan widgetSpan = end.children![1] as WidgetSpan; + final WidgetSpan widgetSpan = textSpan.children![2] as WidgetSpan; expect(widgetSpan.child, isInstanceOf()); }, ); diff --git a/packages/flutter_markdown/test/inline_widget_test.dart b/packages/flutter_markdown/test/inline_widget_test.dart new file mode 100644 index 000000000000..d00102cc218a --- /dev/null +++ b/packages/flutter_markdown/test/inline_widget_test.dart @@ -0,0 +1,78 @@ +// 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/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_markdown/flutter_markdown.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:markdown/markdown.dart' as md; + +import 'utils.dart'; + +void main() => defineTests(); + +void defineTests() { + group('InlineWidget', () { + testWidgets( + 'Test inline widget', + (WidgetTester tester) async { + await tester.pumpWidget( + boilerplate( + MarkdownBody( + data: 'Hello, foo bar', + builders: { + 'sub': SubscriptBuilder(), + }, + extensionSet: md.ExtensionSet( + [], + [SubscriptSyntax()], + ), + ), + ), + ); + + final Text textWidget = tester.firstWidget(find.byType(Text)); + final TextSpan span = textWidget.textSpan! as TextSpan; + + final TextSpan part1 = span.children![0] as TextSpan; + expect(part1.toPlainText(), 'Hello, '); + + final WidgetSpan part2 = span.children![1] as WidgetSpan; + expect(part2.alignment, PlaceholderAlignment.middle); + expect(part2.child, isA()); + expect((part2.child as Text).data, 'foo'); + + final TextSpan part3 = span.children![2] as TextSpan; + expect(part3.toPlainText(), ' bar'); + }, + ); + }); +} + +class SubscriptBuilder extends MarkdownElementBuilder { + @override + Widget visitElementAfterWithContext( + BuildContext context, + md.Element element, + TextStyle? preferredStyle, + TextStyle? parentStyle, + ) { + return Text.rich(WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: Text(element.textContent), + )); + } +} + +class SubscriptSyntax extends md.InlineSyntax { + SubscriptSyntax() : super(_pattern); + + static const String _pattern = r'(foo)'; + + @override + bool onMatch(md.InlineParser parser, Match match) { + parser.addNode(md.Element.text('sub', match[1]!)); + return true; + } +} From 4a9c4be19e0b10ce97f2d09771b6fd569a759194 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 5 Mar 2024 05:23:18 -0500 Subject: [PATCH 050/126] [pigeon] Separates message call code generation into separate methods in the SwiftGenerator (#5959) Separates message call code generation into separate methods in the SwiftGenerator for https://github.com/flutter/flutter/issues/134777. The ProxyApi generator uses similar code to the HostApi and FlutterApi, so this makes the code reusable. From suggestion: https://github.com/flutter/packages/pull/5544#discussion_r1445298086 --- packages/pigeon/CHANGELOG.md | 4 + packages/pigeon/lib/generator_tools.dart | 2 +- packages/pigeon/lib/swift_generator.dart | 536 +++++++++++++---------- packages/pigeon/pubspec.yaml | 2 +- 4 files changed, 300 insertions(+), 244 deletions(-) diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md index 6e7b64d9829f..1fef8dbb39d1 100644 --- a/packages/pigeon/CHANGELOG.md +++ b/packages/pigeon/CHANGELOG.md @@ -1,3 +1,7 @@ +## 17.1.2 + +* [swift] Separates message call code generation into separate methods. + ## 17.1.1 * Removes heap allocation in generated C++ code. diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart index 00ce3fede820..8540fdaec151 100644 --- a/packages/pigeon/lib/generator_tools.dart +++ b/packages/pigeon/lib/generator_tools.dart @@ -13,7 +13,7 @@ import 'ast.dart'; /// The current version of pigeon. /// /// This must match the version in pubspec.yaml. -const String pigeonVersion = '17.1.1'; +const String pigeonVersion = '17.1.2'; /// Read all the content from [stdin] to a String. String readStdin() { diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart index 61e2ea20faf3..1004a4f38d5b 100644 --- a/packages/pigeon/lib/swift_generator.dart +++ b/packages/pigeon/lib/swift_generator.dart @@ -263,17 +263,6 @@ class SwiftGenerator extends StructuredGenerator { AstFlutterApi api, { required String dartPackageName, }) { - /// Returns an argument name that can be used in a context where it is possible to collide. - String getEnumSafeArgumentExpression( - Root root, int count, NamedType argument) { - String enumTag = ''; - if (argument.type.isEnum) { - enumTag = argument.type.isNullable ? '?.rawValue' : '.rawValue'; - } - - return '${_getArgumentName(count, argument)}Arg$enumTag'; - } - final bool isCustomCodec = getCodecClasses(api, root).isNotEmpty; if (isCustomCodec) { _writeCodec(indent, api, root); @@ -289,7 +278,15 @@ class SwiftGenerator extends StructuredGenerator { for (final Method func in api.methods) { addDocumentationComments( indent, func.documentationComments, _docCommentSpec); - indent.writeln(_getMethodSignature(func)); + indent.writeln(_getMethodSignature( + name: func.name, + parameters: func.parameters, + returnType: func.returnType, + errorTypeName: 'FlutterError', + isAsynchronous: true, + swiftFunction: func.swiftFunction, + getParameterName: _getSafeArgumentName, + )); } }); @@ -309,65 +306,19 @@ class SwiftGenerator extends StructuredGenerator { indent.writeln('return $codecName.shared'); }); } + for (final Method func in api.methods) { addDocumentationComments( indent, func.documentationComments, _docCommentSpec); - indent.writeScoped('${_getMethodSignature(func)} {', '}', () { - final Iterable enumSafeArgNames = func.parameters - .asMap() - .entries - .map((MapEntry e) => - getEnumSafeArgumentExpression(root, e.key, e.value)); - final String sendArgument = func.parameters.isEmpty - ? 'nil' - : '[${enumSafeArgNames.join(', ')}] as [Any?]'; - const String channel = 'channel'; - indent.writeln( - 'let channelName: String = "${makeChannelName(api, func, dartPackageName)}"'); - indent.writeln( - 'let $channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger$codecArgumentString)'); - indent.write('$channel.sendMessage($sendArgument) '); - - indent.addScoped('{ response in', '}', () { - indent.writeScoped( - 'guard let listResponse = response as? [Any?] else {', '}', () { - indent.writeln( - 'completion(.failure(createConnectionError(withChannelName: channelName)))'); - indent.writeln('return'); - }); - indent.writeScoped('if listResponse.count > 1 {', '} ', () { - indent.writeln('let code: String = listResponse[0] as! String'); - indent.writeln( - 'let message: String? = nilOrValue(listResponse[1])'); - indent.writeln( - 'let details: String? = nilOrValue(listResponse[2])'); - indent.writeln( - 'completion(.failure(FlutterError(code: code, message: message, details: details)))'); - }, addTrailingNewline: false); - if (!func.returnType.isNullable && !func.returnType.isVoid) { - indent.addScoped('else if listResponse[0] == nil {', '} ', () { - indent.writeln( - 'completion(.failure(FlutterError(code: "null-error", message: "Flutter api returned null value for non-null return value.", details: "")))'); - }, addTrailingNewline: false); - } - indent.addScoped('else {', '}', () { - if (func.returnType.isVoid) { - indent.writeln('completion(.success(Void()))'); - } else { - final String fieldType = _swiftTypeForDartType(func.returnType); - - _writeGenericCasting( - indent: indent, - value: 'listResponse[0]', - variableName: 'result', - fieldType: fieldType, - type: func.returnType); - - indent.writeln('completion(.success(result))'); - } - }); - }); - }); + _writeFlutterMethod( + indent, + name: func.name, + channelName: makeChannelName(api, func, dartPackageName), + parameters: func.parameters, + returnType: func.returnType, + codecArgumentString: codecArgumentString, + swiftFunction: func.swiftFunction, + ); } }); } @@ -400,37 +351,16 @@ class SwiftGenerator extends StructuredGenerator { indent.write('protocol $apiName '); indent.addScoped('{', '}', () { for (final Method method in api.methods) { - final _SwiftFunctionComponents components = - _SwiftFunctionComponents.fromMethod(method); - final List argSignature = - components.arguments.map((_SwiftFunctionArgument argument) { - final String? label = argument.label; - final String name = argument.name; - final String type = _nullsafeSwiftTypeForDartType(argument.type); - return '${label == null ? '' : '$label '}$name: $type'; - }).toList(); - - final String returnType = method.returnType.isVoid - ? 'Void' - : _nullsafeSwiftTypeForDartType(method.returnType); - - final String escapeType = - method.returnType.isVoid ? 'Void' : returnType; - addDocumentationComments( indent, method.documentationComments, _docCommentSpec); - - if (method.isAsynchronous) { - argSignature.add( - 'completion: @escaping (Result<$escapeType, Error>) -> Void'); - indent.writeln('func ${components.name}(${argSignature.join(', ')})'); - } else if (method.returnType.isVoid) { - indent.writeln( - 'func ${components.name}(${argSignature.join(', ')}) throws'); - } else { - indent.writeln( - 'func ${components.name}(${argSignature.join(', ')}) throws -> $returnType'); - } + indent.writeln(_getMethodSignature( + name: method.name, + parameters: method.parameters, + returnType: method.returnType, + errorTypeName: 'Error', + isAsynchronous: method.isAsynchronous, + swiftFunction: method.swiftFunction, + )); } }); @@ -453,108 +383,17 @@ class SwiftGenerator extends StructuredGenerator { 'static func setUp(binaryMessenger: FlutterBinaryMessenger, api: $apiName?) '); indent.addScoped('{', '}', () { for (final Method method in api.methods) { - final _SwiftFunctionComponents components = - _SwiftFunctionComponents.fromMethod(method); - - final String channelName = - makeChannelName(api, method, dartPackageName); - final String varChannelName = '${method.name}Channel'; - addDocumentationComments( - indent, method.documentationComments, _docCommentSpec); - - indent.writeln( - 'let $varChannelName = FlutterBasicMessageChannel(name: "$channelName", binaryMessenger: binaryMessenger$codecArgumentString)'); - indent.write('if let api = api '); - indent.addScoped('{', '}', () { - indent.write('$varChannelName.setMessageHandler '); - final String messageVarName = - method.parameters.isNotEmpty ? 'message' : '_'; - indent.addScoped('{ $messageVarName, reply in', '}', () { - final List methodArgument = []; - if (components.arguments.isNotEmpty) { - indent.writeln('let args = message as! [Any?]'); - enumerate(components.arguments, - (int index, _SwiftFunctionArgument arg) { - final String argName = - _getSafeArgumentName(index, arg.namedType); - final String argIndex = 'args[$index]'; - final String fieldType = _swiftTypeForDartType(arg.type); - - _writeGenericCasting( - indent: indent, - value: argIndex, - variableName: argName, - fieldType: fieldType, - type: arg.type); - - if (arg.label == '_') { - methodArgument.add(argName); - } else { - methodArgument.add('${arg.label ?? arg.name}: $argName'); - } - }); - } - final String tryStatement = method.isAsynchronous ? '' : 'try '; - // Empty parens are not required when calling a method whose only - // argument is a trailing closure. - final String argumentString = - methodArgument.isEmpty && method.isAsynchronous - ? '' - : '(${methodArgument.join(', ')})'; - final String call = - '${tryStatement}api.${components.name}$argumentString'; - if (method.isAsynchronous) { - final String resultName = - method.returnType.isVoid ? 'nil' : 'res'; - final String successVariableInit = - method.returnType.isVoid ? '' : '(let res)'; - indent.write('$call '); - - indent.addScoped('{ result in', '}', () { - indent.write('switch result '); - indent.addScoped('{', '}', nestCount: 0, () { - final String nullsafe = - method.returnType.isNullable ? '?' : ''; - final String enumTag = - method.returnType.isEnum ? '$nullsafe.rawValue' : ''; - indent.writeln('case .success$successVariableInit:'); - indent.nest(1, () { - indent.writeln('reply(wrapResult($resultName$enumTag))'); - }); - indent.writeln('case .failure(let error):'); - indent.nest(1, () { - indent.writeln('reply(wrapError(error))'); - }); - }); - }); - } else { - indent.write('do '); - indent.addScoped('{', '}', () { - if (method.returnType.isVoid) { - indent.writeln(call); - indent.writeln('reply(wrapResult(nil))'); - } else { - String enumTag = ''; - if (method.returnType.isEnum) { - enumTag = '.rawValue'; - } - enumTag = - method.returnType.isNullable && method.returnType.isEnum - ? '?$enumTag' - : enumTag; - indent.writeln('let result = $call'); - indent.writeln('reply(wrapResult(result$enumTag))'); - } - }, addTrailingNewline: false); - indent.addScoped(' catch {', '}', () { - indent.writeln('reply(wrapError(error))'); - }); - } - }); - }, addTrailingNewline: false); - indent.addScoped(' else {', '}', () { - indent.writeln('$varChannelName.setMessageHandler(nil)'); - }); + _writeHostMethodMessageHandler( + indent, + name: method.name, + channelName: makeChannelName(api, method, dartPackageName), + parameters: method.parameters, + returnType: method.returnType, + isAsynchronous: method.isAsynchronous, + codecArgumentString: codecArgumentString, + swiftFunction: method.swiftFunction, + documentationComments: method.documentationComments, + ); } }); }); @@ -825,6 +664,199 @@ private func nilOrValue(_ value: Any?) -> T? { _writeIsNullish(indent); _writeNilOrValue(indent); } + + void _writeFlutterMethod( + Indent indent, { + required String name, + required String channelName, + required List parameters, + required TypeDeclaration returnType, + required String codecArgumentString, + required String? swiftFunction, + }) { + final String methodSignature = _getMethodSignature( + name: name, + parameters: parameters, + returnType: returnType, + errorTypeName: 'FlutterError', + isAsynchronous: true, + swiftFunction: swiftFunction, + getParameterName: _getSafeArgumentName, + ); + + /// Returns an argument name that can be used in a context where it is possible to collide. + String getEnumSafeArgumentExpression(int count, NamedType argument) { + String enumTag = ''; + if (argument.type.isEnum) { + enumTag = argument.type.isNullable ? '?.rawValue' : '.rawValue'; + } + + return '${_getArgumentName(count, argument)}Arg$enumTag'; + } + + indent.writeScoped('$methodSignature {', '}', () { + final Iterable enumSafeArgNames = parameters.asMap().entries.map( + (MapEntry e) => + getEnumSafeArgumentExpression(e.key, e.value)); + final String sendArgument = parameters.isEmpty + ? 'nil' + : '[${enumSafeArgNames.join(', ')}] as [Any?]'; + const String channel = 'channel'; + indent.writeln('let channelName: String = "$channelName"'); + indent.writeln( + 'let $channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger$codecArgumentString)'); + indent.write('$channel.sendMessage($sendArgument) '); + + indent.addScoped('{ response in', '}', () { + indent.writeScoped( + 'guard let listResponse = response as? [Any?] else {', '}', () { + indent.writeln( + 'completion(.failure(createConnectionError(withChannelName: channelName)))'); + indent.writeln('return'); + }); + indent.writeScoped('if listResponse.count > 1 {', '} ', () { + indent.writeln('let code: String = listResponse[0] as! String'); + indent.writeln('let message: String? = nilOrValue(listResponse[1])'); + indent.writeln('let details: String? = nilOrValue(listResponse[2])'); + indent.writeln( + 'completion(.failure(FlutterError(code: code, message: message, details: details)))'); + }, addTrailingNewline: false); + if (!returnType.isNullable && !returnType.isVoid) { + indent.addScoped('else if listResponse[0] == nil {', '} ', () { + indent.writeln( + 'completion(.failure(FlutterError(code: "null-error", message: "Flutter api returned null value for non-null return value.", details: "")))'); + }, addTrailingNewline: false); + } + indent.addScoped('else {', '}', () { + if (returnType.isVoid) { + indent.writeln('completion(.success(Void()))'); + } else { + final String fieldType = _swiftTypeForDartType(returnType); + + _writeGenericCasting( + indent: indent, + value: 'listResponse[0]', + variableName: 'result', + fieldType: fieldType, + type: returnType, + ); + + indent.writeln('completion(.success(result))'); + } + }); + }); + }); + } + + void _writeHostMethodMessageHandler( + Indent indent, { + required String name, + required String channelName, + required Iterable parameters, + required TypeDeclaration returnType, + required bool isAsynchronous, + required String codecArgumentString, + required String? swiftFunction, + List documentationComments = const [], + }) { + final _SwiftFunctionComponents components = _SwiftFunctionComponents( + name: name, + parameters: parameters, + returnType: returnType, + swiftFunction: swiftFunction, + ); + + final String varChannelName = '${name}Channel'; + addDocumentationComments(indent, documentationComments, _docCommentSpec); + + indent.writeln( + 'let $varChannelName = FlutterBasicMessageChannel(name: "$channelName", binaryMessenger: binaryMessenger$codecArgumentString)'); + indent.write('if let api = api '); + indent.addScoped('{', '}', () { + indent.write('$varChannelName.setMessageHandler '); + final String messageVarName = parameters.isNotEmpty ? 'message' : '_'; + indent.addScoped('{ $messageVarName, reply in', '}', () { + final List methodArgument = []; + if (components.arguments.isNotEmpty) { + indent.writeln('let args = message as! [Any?]'); + enumerate(components.arguments, + (int index, _SwiftFunctionArgument arg) { + final String argName = _getSafeArgumentName(index, arg.namedType); + final String argIndex = 'args[$index]'; + final String fieldType = _swiftTypeForDartType(arg.type); + + _writeGenericCasting( + indent: indent, + value: argIndex, + variableName: argName, + fieldType: fieldType, + type: arg.type); + + if (arg.label == '_') { + methodArgument.add(argName); + } else { + methodArgument.add('${arg.label ?? arg.name}: $argName'); + } + }); + } + final String tryStatement = isAsynchronous ? '' : 'try '; + // Empty parens are not required when calling a method whose only + // argument is a trailing closure. + final String argumentString = methodArgument.isEmpty && isAsynchronous + ? '' + : '(${methodArgument.join(', ')})'; + final String call = + '${tryStatement}api.${components.name}$argumentString'; + if (isAsynchronous) { + final String resultName = returnType.isVoid ? 'nil' : 'res'; + final String successVariableInit = + returnType.isVoid ? '' : '(let res)'; + indent.write('$call '); + + indent.addScoped('{ result in', '}', () { + indent.write('switch result '); + indent.addScoped('{', '}', nestCount: 0, () { + final String nullsafe = returnType.isNullable ? '?' : ''; + final String enumTag = + returnType.isEnum ? '$nullsafe.rawValue' : ''; + indent.writeln('case .success$successVariableInit:'); + indent.nest(1, () { + indent.writeln('reply(wrapResult($resultName$enumTag))'); + }); + indent.writeln('case .failure(let error):'); + indent.nest(1, () { + indent.writeln('reply(wrapError(error))'); + }); + }); + }); + } else { + indent.write('do '); + indent.addScoped('{', '}', () { + if (returnType.isVoid) { + indent.writeln(call); + indent.writeln('reply(wrapResult(nil))'); + } else { + String enumTag = ''; + if (returnType.isEnum) { + enumTag = '.rawValue'; + } + enumTag = returnType.isNullable && returnType.isEnum + ? '?$enumTag' + : enumTag; + indent.writeln('let result = $call'); + indent.writeln('reply(wrapResult(result$enumTag))'); + } + }, addTrailingNewline: false); + indent.addScoped(' catch {', '}', () { + indent.writeln('reply(wrapError(error))'); + }); + } + }); + }, addTrailingNewline: false); + indent.addScoped(' else {', '}', () { + indent.writeln('$varChannelName.setMessageHandler(nil)'); + }); + } } /// Calculates the name of the codec that will be generated for [api]. @@ -902,28 +934,49 @@ String _nullsafeSwiftTypeForDartType(TypeDeclaration type) { return '${_swiftTypeForDartType(type)}$nullSafe'; } -String _getMethodSignature(Method func) { - final _SwiftFunctionComponents components = - _SwiftFunctionComponents.fromMethod(func); - final String returnType = func.returnType.isVoid - ? 'Void' - : _nullsafeSwiftTypeForDartType(func.returnType); - - if (func.parameters.isEmpty) { - return 'func ${func.name}(completion: @escaping (Result<$returnType, FlutterError>) -> Void)'; +String _getMethodSignature({ + required String name, + required Iterable parameters, + required TypeDeclaration returnType, + required String errorTypeName, + bool isAsynchronous = false, + String? swiftFunction, + String Function(int index, NamedType argument) getParameterName = + _getArgumentName, +}) { + final _SwiftFunctionComponents components = _SwiftFunctionComponents( + name: name, + parameters: parameters, + returnType: returnType, + swiftFunction: swiftFunction, + ); + final String returnTypeString = + returnType.isVoid ? 'Void' : _nullsafeSwiftTypeForDartType(returnType); + + final Iterable types = + parameters.map((NamedType e) => _nullsafeSwiftTypeForDartType(e.type)); + final Iterable labels = indexMap(components.arguments, + (int index, _SwiftFunctionArgument argument) { + return argument.label ?? _getArgumentName(index, argument.namedType); + }); + final Iterable names = indexMap(parameters, getParameterName); + final String parameterSignature = + map3(types, labels, names, (String type, String label, String name) { + return '${label != name ? '$label ' : ''}$name: $type'; + }).join(', '); + + if (isAsynchronous) { + if (parameters.isEmpty) { + return 'func ${components.name}(completion: @escaping (Result<$returnTypeString, $errorTypeName>) -> Void)'; + } else { + return 'func ${components.name}($parameterSignature, completion: @escaping (Result<$returnTypeString, $errorTypeName>) -> Void)'; + } } else { - final Iterable argTypes = func.parameters - .map((NamedType e) => _nullsafeSwiftTypeForDartType(e.type)); - final Iterable argLabels = indexMap(components.arguments, - (int index, _SwiftFunctionArgument argument) { - return argument.label ?? _getArgumentName(index, argument.namedType); - }); - final Iterable argNames = - indexMap(func.parameters, _getSafeArgumentName); - final String argsSignature = map3(argTypes, argLabels, argNames, - (String type, String label, String name) => '$label $name: $type') - .join(', '); - return 'func ${components.name}($argsSignature, completion: @escaping (Result<$returnType, FlutterError>) -> Void)'; + if (returnType.isVoid) { + return 'func ${components.name}($parameterSignature) throws'; + } else { + return 'func ${components.name}($parameterSignature) throws -> $returnTypeString'; + } } } @@ -954,45 +1007,40 @@ class _SwiftFunctionArgument { /// The [returnType] is the return type of the function. /// The [method] is the method that this function signature is generated from. class _SwiftFunctionComponents { - _SwiftFunctionComponents._({ - required this.name, - required this.arguments, - required this.returnType, - required this.method, - }); - /// Constructor that generates a [_SwiftFunctionComponents] from a [Method]. - factory _SwiftFunctionComponents.fromMethod(Method method) { - if (method.swiftFunction.isEmpty) { + factory _SwiftFunctionComponents({ + required String name, + required Iterable parameters, + required TypeDeclaration returnType, + String? swiftFunction, + }) { + if (swiftFunction == null || swiftFunction.isEmpty) { return _SwiftFunctionComponents._( - name: method.name, - returnType: method.returnType, - arguments: method.parameters + name: name, + returnType: returnType, + arguments: parameters .map((NamedType field) => _SwiftFunctionArgument( name: field.name, type: field.type, namedType: field, )) .toList(), - method: method, ); } - final String argsExtractor = - repeat(r'(\w+):', method.parameters.length).join(); + final String argsExtractor = repeat(r'(\w+):', parameters.length).join(); final RegExp signatureRegex = RegExp(r'(\w+) *\(' + argsExtractor + r'\)'); - final RegExpMatch match = signatureRegex.firstMatch(method.swiftFunction)!; + final RegExpMatch match = signatureRegex.firstMatch(swiftFunction)!; final Iterable labels = match - .groups(List.generate( - method.parameters.length, (int index) => index + 2)) + .groups(List.generate(parameters.length, (int index) => index + 2)) .whereType(); return _SwiftFunctionComponents._( name: match.group(1)!, - returnType: method.returnType, + returnType: returnType, arguments: map2( - method.parameters, + parameters, labels, (NamedType field, String label) => _SwiftFunctionArgument( name: field.name, @@ -1001,12 +1049,16 @@ class _SwiftFunctionComponents { namedType: field, ), ).toList(), - method: method, ); } + _SwiftFunctionComponents._({ + required this.name, + required this.arguments, + required this.returnType, + }); + final String name; final List<_SwiftFunctionArgument> arguments; final TypeDeclaration returnType; - final Method method; } diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml index c6b29262ff35..a0cbdbd1f82a 100644 --- a/packages/pigeon/pubspec.yaml +++ b/packages/pigeon/pubspec.yaml @@ -2,7 +2,7 @@ name: pigeon description: Code generator tool to make communication between Flutter and the host platform type-safe and easier. repository: https://github.com/flutter/packages/tree/main/packages/pigeon issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+pigeon%22 -version: 17.1.1 # This must match the version in lib/generator_tools.dart +version: 17.1.2 # This must match the version in lib/generator_tools.dart environment: sdk: ^3.1.0 From 4ece1ddc93518c846bfb2ac1e50d6926afc7d62d Mon Sep 17 00:00:00 2001 From: Slowhand Date: Tue, 5 Mar 2024 21:31:17 +0900 Subject: [PATCH 051/126] [video_player] Fix typo in readme (#6264) This pull request fixes the typo `suppport` to the correct value `support` in the README.md. --- packages/video_player/video_player/CHANGELOG.md | 3 ++- packages/video_player/video_player/README.md | 2 +- packages/video_player/video_player/pubspec.yaml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index c585875c22eb..cb872ce7c9a9 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.8.3 +* Fixes typo in `README.md`. * Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. * Updates support matrix in README to indicate that iOS 11 is no longer supported. * Clients on versions of Flutter that still support iOS 11 can continue to use this diff --git a/packages/video_player/video_player/README.md b/packages/video_player/video_player/README.md index 99a6c0a9795b..4d39cec336e7 100644 --- a/packages/video_player/video_player/README.md +++ b/packages/video_player/video_player/README.md @@ -41,7 +41,7 @@ entitlement](https://docs.flutter.dev/platform-integration/macos/building#entitl ### Web -> The Web platform does **not** suppport `dart:io`, so avoid using the `VideoPlayerController.file` constructor for the plugin. Using the constructor attempts to create a `VideoPlayerController.file` that will throw an `UnimplementedError`. +> The Web platform does **not** support `dart:io`, so avoid using the `VideoPlayerController.file` constructor for the plugin. Using the constructor attempts to create a `VideoPlayerController.file` that will throw an `UnimplementedError`. \* Different web browsers may have different video-playback capabilities (supported formats, autoplay...). Check [package:video_player_web](https://pub.dev/packages/video_player_web) for more web-specific information. diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 1b169ff64a42..7b128216f415 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for displaying inline video with other Flutter widgets on Android, iOS, and web. repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.8.2 +version: 2.8.3 environment: sdk: ">=3.1.0 <4.0.0" From 2aa6e3f6bafefc23b8a336bfc23bfdc530caccdc Mon Sep 17 00:00:00 2001 From: Gabriel Terwesten Date: Tue, 5 Mar 2024 19:29:51 +0100 Subject: [PATCH 052/126] [in_app_purchase_storekit] Fix type of error code returned from native code in `SKReceiptManager.retrieveReceiptData` (#6265) The native code did not convert the integer `code` returned from `FIAObjectTranslator getMapFromNSError` to a string before passing it to `FlutterError errorWithCode`. This allowed an `int` to flow into a position where the corresponding Dart code expects a `String`. Fixes https://github.com/flutter/flutter/issues/144595 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [relevant style guides] and ran the auto-formatter. (Unlike the flutter/flutter repo, the flutter/packages repo does use `dart format`.) - [x] I signed the [CLA]. - [x] The title of the PR starts with the name of the package surrounded by square brackets, e.g. `[shared_preferences]` - [x] I [linked to at least one issue that this PR fixes] in the description above. - [x] I updated `pubspec.yaml` with an appropriate new version according to the [pub versioning philosophy], or this PR is [exempt from version changes]. - [x] I updated `CHANGELOG.md` to add a description of the change, [following repository CHANGELOG style]. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [Contributor Guide]: https://github.com/flutter/packages/blob/main/CONTRIBUTING.md [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [relevant style guides]: https://github.com/flutter/packages/blob/main/CONTRIBUTING.md#style [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat [linked to at least one issue that this PR fixes]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [pub versioning philosophy]: https://dart.dev/tools/pub/versioning [exempt from version changes]: https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#version-and-changelog-updates [following repository CHANGELOG style]: https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changelog-style [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests Co-authored-by: LouiseHsu --- .../in_app_purchase/in_app_purchase_storekit/CHANGELOG.md | 4 ++++ .../darwin/Classes/FIAPReceiptManager.m | 7 ++++--- .../example/shared/RunnerTests/InAppPurchasePluginTests.m | 1 + .../in_app_purchase/in_app_purchase_storekit/pubspec.yaml | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md index 309510b3af50..ce53f98fc462 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.12+1 + +* Fixes type of error code returned from native code in SKReceiptManager.retrieveReceiptData. + ## 0.3.12 * Converts `refreshReceipt()`, `startObservingPaymentQueue()`, `stopObservingPaymentQueue()`, diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAPReceiptManager.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAPReceiptManager.m index 320e6072d046..22a3973b7fca 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAPReceiptManager.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAPReceiptManager.m @@ -29,9 +29,10 @@ - (NSString *)retrieveReceiptWithError:(FlutterError **)flutterError { if (!receipt || receiptError) { if (flutterError) { NSDictionary *errorMap = [FIAObjectTranslator getMapFromNSError:receiptError]; - *flutterError = [FlutterError errorWithCode:errorMap[@"code"] - message:errorMap[@"domain"] - details:errorMap[@"userInfo"]]; + *flutterError = + [FlutterError errorWithCode:[NSString stringWithFormat:@"%@", errorMap[@"code"]] + message:errorMap[@"domain"] + details:errorMap[@"userInfo"]]; } return nil; } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m index 1c0f734bc014..0695d9deb1c3 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m @@ -307,6 +307,7 @@ - (void)testRetrieveReceiptDataError { XCTAssertNil(result); XCTAssertNotNil(error); + XCTAssert([error.code isKindOfClass:[NSString class]]); NSDictionary *details = error.details; XCTAssertNotNil(details[@"error"]); NSNumber *errorCode = (NSNumber *)details[@"error"][@"code"]; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml index ee7422b10009..fcfcde9fc53c 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_storekit description: An implementation for the iOS and macOS platforms of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/packages/tree/main/packages/in_app_purchase/in_app_purchase_storekit issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.3.12 +version: 0.3.12+1 environment: sdk: ^3.2.3 From 420017724fd5d47c361ea4c533f9e781c15ceded Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 5 Mar 2024 14:03:21 -0500 Subject: [PATCH 053/126] Roll Flutter from 65cd84b58885 to 3b5a2ecf992d (26 revisions) (#6269) https://github.com/flutter/flutter/compare/65cd84b58885...3b5a2ecf992d 2024-03-05 engine-flutter-autoroll@skia.org Roll Flutter Engine from 17a4b66e0729 to 8916bb32b7b8 (1 revision) (flutter/flutter#144615) 2024-03-05 engine-flutter-autoroll@skia.org Roll Flutter Engine from d514a302cdff to 17a4b66e0729 (8 revisions) (flutter/flutter#144607) 2024-03-05 matej.knopp@gmail.com Do not shorten native assets framework names (flutter/flutter#144568) 2024-03-05 leroux_bruno@yahoo.fr Restorable CupertinoTextFormFieldRow (flutter/flutter#144541) 2024-03-05 hans.muller@gmail.com Updated the TextButton image button example artwork (flutter/flutter#144583) 2024-03-05 engine-flutter-autoroll@skia.org Roll Flutter Engine from 728aa4ef05d4 to d514a302cdff (2 revisions) (flutter/flutter#144581) 2024-03-05 engine-flutter-autoroll@skia.org Roll Flutter Engine from 62cf93be60b2 to 728aa4ef05d4 (3 revisions) (flutter/flutter#144578) 2024-03-05 jonahwilliams@google.com Disable super flakey impeller test. (flutter/flutter#144573) 2024-03-04 engine-flutter-autoroll@skia.org Roll Flutter Engine from 40018811ec0e to 62cf93be60b2 (1 revision) (flutter/flutter#144572) 2024-03-04 engine-flutter-autoroll@skia.org Roll Flutter Engine from 0d8588b1de7a to 40018811ec0e (6 revisions) (flutter/flutter#144570) 2024-03-04 magder@google.com Print warning and exit when iOS device is unpaired (flutter/flutter#144551) 2024-03-04 engine-flutter-autoroll@skia.org Roll Flutter Engine from a31209171949 to 0d8588b1de7a (1 revision) (flutter/flutter#144558) 2024-03-04 31859944+LongCatIsLooong@users.noreply.github.com Remove unnecessary (and the only) `RenderObject.markParentNeedsLayout` override (flutter/flutter#144466) 2024-03-04 leroux_bruno@yahoo.fr Fix text color for default CupertinoContextMenuAction (flutter/flutter#144542) 2024-03-04 nate.w5687@gmail.com Add missing parameter to `TableBorder.symmetric`, and improve class constructors (flutter/flutter#144279) 2024-03-04 matej.knopp@gmail.com Fix build mode not propagated in android native asset build (flutter/flutter#144550) 2024-03-04 engine-flutter-autoroll@skia.org Roll Packages from a9c68b83356b to 06258277070f (3 revisions) (flutter/flutter#144556) 2024-03-04 engine-flutter-autoroll@skia.org Roll Flutter Engine from afd2d9f9421d to a31209171949 (1 revision) (flutter/flutter#144554) 2024-03-04 36861262+QuncCccccc@users.noreply.github.com Doc fix for `DropdownButtonFormField.value` (flutter/flutter#144427) 2024-03-04 pateltirth454@gmail.com Fix Small Typo in Skia_Client Doc Comment (flutter/flutter#144490) 2024-03-04 engine-flutter-autoroll@skia.org Roll Flutter Engine from 2338a557e018 to afd2d9f9421d (1 revision) (flutter/flutter#144549) 2024-03-04 737941+loic-sharma@users.noreply.github.com [Windows] Update keyboard modifiers link (flutter/flutter#144426) 2024-03-04 engine-flutter-autoroll@skia.org Roll Flutter Engine from 9cbca80e2196 to 2338a557e018 (2 revisions) (flutter/flutter#144545) 2024-03-04 engine-flutter-autoroll@skia.org Roll Flutter Engine from c52b3033eca6 to 9cbca80e2196 (1 revision) (flutter/flutter#144523) 2024-03-04 engine-flutter-autoroll@skia.org Roll Flutter Engine from 024ea680ceed to c52b3033eca6 (1 revision) (flutter/flutter#144522) 2024-03-03 engine-flutter-autoroll@skia.org Roll Flutter Engine from 2706c732b22a to 024ea680ceed (1 revision) (flutter/flutter#144519) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index b8a1f23a6859..1e02d9cc1b1c 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -65cd84b58885fb78bd2d27f60fa7209bedf5be25 +3b5a2ecf992d2b6b2643bebb2621420eacfd4ef2 From 83b72baf6ebd04ddd315d989f77c058482058497 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Tue, 5 Mar 2024 14:25:59 -0800 Subject: [PATCH 054/126] [web] Use TrustedTypes from pkg web. (#6273) During the `package:web` migration, some packages that needed the TrustedTypes API defined those as custom JS-interop. In `google_identity_services_web`, the names of the JS-interop types clashed with those from the incoming package:web, breaking the build. In `google_maps_flutter_web`, the whole definition code is redundant now that the standard API is exposed through package:web. Part of: https://github.com/flutter/flutter/issues/117022 --- .../google_identity_services_web/CHANGELOG.md | 4 + .../example/pubspec.yaml | 6 +- .../src/js_interop/package_web_tweaks.dart | 54 ++----------- .../lib/src/js_loader.dart | 6 +- .../google_identity_services_web/pubspec.yaml | 4 +- .../google_maps_flutter_web/CHANGELOG.md | 4 + .../lib/src/convert.dart | 15 ++-- .../lib/src/dom_window_extension.dart | 76 +++---------------- .../google_maps_flutter_web/pubspec.yaml | 4 +- 9 files changed, 45 insertions(+), 128 deletions(-) diff --git a/packages/google_identity_services_web/CHANGELOG.md b/packages/google_identity_services_web/CHANGELOG.md index fe739cab00e5..d7ed98a3c8cd 100644 --- a/packages/google_identity_services_web/CHANGELOG.md +++ b/packages/google_identity_services_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.1+1 + +* Uses `TrustedTypes` from `web: ^0.5.1`. + ## 0.3.1 * Updates web code to package `web: ^0.5.0`. diff --git a/packages/google_identity_services_web/example/pubspec.yaml b/packages/google_identity_services_web/example/pubspec.yaml index 46bdaf47c9e0..fa625a5edd69 100644 --- a/packages/google_identity_services_web/example/pubspec.yaml +++ b/packages/google_identity_services_web/example/pubspec.yaml @@ -3,8 +3,8 @@ description: An example for the google_identity_services_web package, OneTap. publish_to: 'none' environment: - flutter: ">=3.16.0" - sdk: ">=3.2.0 <4.0.0" + sdk: ^3.3.0 + flutter: ">=3.19.0" dependencies: flutter: @@ -12,7 +12,7 @@ dependencies: google_identity_services_web: path: ../ http: ">=0.13.0 <2.0.0" - web: ^0.5.0 + web: ^0.5.1 dev_dependencies: build_runner: ^2.1.10 # To extract README excerpts only. diff --git a/packages/google_identity_services_web/lib/src/js_interop/package_web_tweaks.dart b/packages/google_identity_services_web/lib/src/js_interop/package_web_tweaks.dart index b05fbc5a49a2..ed55d5d81298 100644 --- a/packages/google_identity_services_web/lib/src/js_interop/package_web_tweaks.dart +++ b/packages/google_identity_services_web/lib/src/js_interop/package_web_tweaks.dart @@ -17,60 +17,20 @@ extension NullableTrustedTypesGetter on web.Window { /// /// See: https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API @JS('trustedTypes') - external TrustedTypePolicyFactory? get nullableTrustedTypes; - - /// Bindings to window.trustedTypes. - /// - /// This will crash if accessed in a browser that doesn't support the - /// Trusted Types API. - /// - /// See: https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API - @JS('trustedTypes') - external TrustedTypePolicyFactory get trustedTypes; + external web.TrustedTypePolicyFactory? get nullableTrustedTypes; } -/// This extension allows setting a TrustedScriptURL as the src of a script element, -/// which currently only accepts a string. +/// Allows setting a TrustedScriptURL as the src of a script element. extension TrustedTypeSrcAttribute on web.HTMLScriptElement { @JS('src') - external set trustedSrc(TrustedScriptURL value); -} - -// TODO(kevmoo): drop all of this once `pkg:web` publishes `0.5.1`. - -/// Bindings to a JS TrustedScriptURL. -/// -/// See: https://developer.mozilla.org/en-US/docs/Web/API/TrustedScriptURL -extension type TrustedScriptURL._(JSObject _) implements JSObject {} - -/// Bindings to a JS TrustedTypePolicyFactory. -/// -/// See: https://developer.mozilla.org/en-US/docs/Web/API/TrustedTypePolicyFactory -extension type TrustedTypePolicyFactory._(JSObject _) implements JSObject { - /// - external TrustedTypePolicy createPolicy( - String policyName, [ - TrustedTypePolicyOptions policyOptions, - ]); + external set trustedSrc(web.TrustedScriptURL value); } -/// Bindings to a JS TrustedTypePolicy. -/// -/// See: https://developer.mozilla.org/en-US/docs/Web/API/TrustedTypePolicy -extension type TrustedTypePolicy._(JSObject _) implements JSObject { - /// +/// Allows creating a script URL only from a string, with no arguments. +extension CreateScriptUrlNoArgs on web.TrustedTypePolicy { + /// Allows calling `createScriptURL` with only the `input` argument. @JS('createScriptURL') - external TrustedScriptURL createScriptURLNoArgs( + external web.TrustedScriptURL createScriptURLNoArgs( String input, ); } - -/// Bindings to a JS TrustedTypePolicyOptions (anonymous). -/// -/// See: https://developer.mozilla.org/en-US/docs/Web/API/TrustedTypePolicyFactory/createPolicy#policyoptions -extension type TrustedTypePolicyOptions._(JSObject _) implements JSObject { - /// - external factory TrustedTypePolicyOptions({ - JSFunction createScriptURL, - }); -} diff --git a/packages/google_identity_services_web/lib/src/js_loader.dart b/packages/google_identity_services_web/lib/src/js_loader.dart index 620ad2129269..e32f59c802b1 100644 --- a/packages/google_identity_services_web/lib/src/js_loader.dart +++ b/packages/google_identity_services_web/lib/src/js_loader.dart @@ -25,15 +25,15 @@ Future loadWebSdk({ onGoogleLibraryLoad = () => completer.complete(); // If TrustedTypes are available, prepare a trusted URL. - TrustedScriptURL? trustedUrl; + web.TrustedScriptURL? trustedUrl; if (web.window.nullableTrustedTypes != null) { web.console.debug( 'TrustedTypes available. Creating policy: $trustedTypePolicyName'.toJS, ); try { - final TrustedTypePolicy policy = web.window.trustedTypes.createPolicy( + final web.TrustedTypePolicy policy = web.window.trustedTypes.createPolicy( trustedTypePolicyName, - TrustedTypePolicyOptions( + web.TrustedTypePolicyOptions( createScriptURL: ((JSString url) => _url).toJS, )); trustedUrl = policy.createScriptURLNoArgs(_url); diff --git a/packages/google_identity_services_web/pubspec.yaml b/packages/google_identity_services_web/pubspec.yaml index 84502a1f6a49..0c61580d79e2 100644 --- a/packages/google_identity_services_web/pubspec.yaml +++ b/packages/google_identity_services_web/pubspec.yaml @@ -2,14 +2,14 @@ name: google_identity_services_web description: A Dart JS-interop layer for Google Identity Services. Google's new sign-in SDK for Web that supports multiple types of credentials. repository: https://github.com/flutter/packages/tree/main/packages/google_identity_services_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_identiy_services_web%22 -version: 0.3.1 +version: 0.3.1+1 environment: sdk: ^3.3.0 dependencies: meta: ^1.3.0 - web: ^0.5.0 + web: ^0.5.1 dev_dependencies: path: ^1.8.1 diff --git a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md index 1df35f0c3c04..08d4608228c9 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.5.6+2 + +* Uses `TrustedTypes` from `web: ^0.5.1`. + ## 0.5.6+1 * Fixes an issue where `dart:js_interop` object literal factories did not diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart index 6702d5f520b8..0f73a7df0445 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/convert.dart @@ -9,6 +9,9 @@ final gmaps.LatLng _nullGmapsLatLng = gmaps.LatLng(0, 0); final gmaps.LatLngBounds _nullGmapsLatLngBounds = gmaps.LatLngBounds(_nullGmapsLatLng, _nullGmapsLatLng); +// The TrustedType Policy used by this plugin. Used to sanitize InfoWindow contents. +TrustedTypePolicy? _gmapsTrustedTypePolicy; + // Converts a [Color] into a valid CSS value #RRGGBB. String _getCssColor(Color color) { return '#${color.value.toRadixString(16).padLeft(8, '0').substring(2)}'; @@ -222,17 +225,17 @@ gmaps.InfoWindowOptions? _infoWindowOptionsFromMarker(Marker marker) { // Firefox and Safari don't support Trusted Types yet. // See https://developer.mozilla.org/en-US/docs/Web/API/TrustedTypePolicyFactory#browser_compatibility if (window.nullableTrustedTypes != null) { - final GoogleMapsTrustedTypePolicy trustedTypePolicy = - window.nullableTrustedTypes!.getGoogleMapsTrustedTypesPolicy( - GoogleMapsTrustedTypePolicyOptions( - createHTML: (String html, JSAny? arguments) { - return sanitizeHtml(html); + _gmapsTrustedTypePolicy ??= window.trustedTypes.createPolicy( + 'google_maps_flutter_sanitize', + TrustedTypePolicyOptions( + createHTML: (String html) { + return sanitizeHtml(html).toJS; }.toJS, ), ); snippet.trustedInnerHTML = - trustedTypePolicy.createHTML(markerSnippet, null); + _gmapsTrustedTypePolicy!.createHTMLNoArgs(markerSnippet); } else { // `sanitizeHtml` is used to clean the (potential) user input from (potential) // XSS attacks through the contents of the marker InfoWindow. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/dom_window_extension.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/dom_window_extension.dart index 9eecc8641da6..8c75229eb0cb 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/dom_window_extension.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/dom_window_extension.dart @@ -9,7 +9,6 @@ library; import 'dart:js_interop'; - import 'package:web/web.dart' as web; /// This extension gives [web.Window] a nullable getter to the `trustedTypes` @@ -21,75 +20,22 @@ extension NullableTrustedTypesGetter on web.Window { /// /// See: https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API @JS('trustedTypes') - external GoogleMapsTrustedTypePolicyFactory? get nullableTrustedTypes; -} - -// TODO(ditman): remove this extension type when we depend on package:web 0.5.1 -/// This extension exists as a stop gap until `package:web 0.5.1` is released. -/// That version provides the `TrustedTypes` API. -@JS('TrustedTypePolicyFactory') -extension type GoogleMapsTrustedTypePolicyFactory._(JSObject _) - implements JSObject { - /// The `TrustedTypePolicy` for Google Maps Flutter. - static GoogleMapsTrustedTypePolicy? _policy; - - @JS('createPolicy') - external GoogleMapsTrustedTypePolicy _createPolicy( - String policyName, [ - GoogleMapsTrustedTypePolicyOptions policyOptions, - ]); - - /// Get a new [GoogleMapsTrustedTypePolicy]. - /// - /// If a policy already exists, it will be returned. - /// Otherwise, a new policy is created. - /// - /// Because of we only cache one _policy, this method - /// specifically hardcoded to the GoogleMaps use case. - GoogleMapsTrustedTypePolicy getGoogleMapsTrustedTypesPolicy( - GoogleMapsTrustedTypePolicyOptions policyOptions, - ) { - const String policyName = 'google_maps_flutter_sanitize'; - _policy ??= _createPolicy(policyName, policyOptions); - - return _policy!; - } + external web.TrustedTypePolicyFactory? get nullableTrustedTypes; } -// TODO(ditman): remove this extension type when we depend on package:web 0.5.1 -/// This extension exists as a stop gap until `package:web 0.5.1` is released. -/// That version provides the `TrustedTypes` API. -@JS('TrustedTypePolicy') -extension type GoogleMapsTrustedTypePolicy._(JSObject _) implements JSObject { - /// Create a new `TrustedHTML` instance with the given [input] and [arguments]. - external GoogleMapsTrustedHTML createHTML( - String input, - JSAny? arguments, - ); -} - -// TODO(ditman): remove this extension type when we depend on package:web 0.5.1 -/// This extension exists as a stop gap until `package:web 0.5.1` is released. -/// That version provides the `TrustedTypes` API. -@JS('TrustedTypePolicyOptions') -extension type GoogleMapsTrustedTypePolicyOptions._(JSObject _) - implements JSObject { - /// Create a new `TrustedTypePolicyOptions` instance. - external factory GoogleMapsTrustedTypePolicyOptions({ - JSFunction createHTML, - }); -} - -// TODO(ditman): remove this extension type when we depend on package:web 0.5.1 -/// This extension exists as a stop gap until `package:web 0.5.1` is released. -/// That version provides the `TrustedTypes` API. -@JS('TrustedHTML') -extension type GoogleMapsTrustedHTML._(JSObject _) implements JSObject {} - /// This extension provides a setter for the [web.HTMLElement] `innerHTML` property, /// that accepts trusted HTML only. extension TrustedInnerHTML on web.HTMLElement { /// Set the inner HTML of this element to the given [trustedHTML]. @JS('innerHTML') - external set trustedInnerHTML(GoogleMapsTrustedHTML trustedHTML); + external set trustedInnerHTML(web.TrustedHTML trustedHTML); +} + +/// Allows creating a TrustedHTML object from a string, with no arguments. +extension CreateHTMLNoArgs on web.TrustedTypePolicy { + /// Allows calling `createHTML` with only the `input` argument. + @JS('createHTML') + external web.TrustedHTML createHTMLNoArgs( + String input, + ); } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml index 6f59a0433c75..f5c17935406e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_web description: Web platform implementation of google_maps_flutter repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 0.5.6+1 +version: 0.5.6+2 environment: sdk: ^3.3.0 @@ -26,7 +26,7 @@ dependencies: google_maps_flutter_platform_interface: ^2.5.0 sanitize_html: ^2.0.0 stream_transform: ^2.0.0 - web: ^0.5.0 + web: ^0.5.1 dev_dependencies: flutter_test: From 6a4e2ffbc4d6241abd4b7bf1ba86fd1ab34be318 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 5 Mar 2024 17:34:58 -0500 Subject: [PATCH 055/126] [tool] Add features to support GCB auto-publish flow (#6218) Adds the flowing to the tool: - A new `--exact-match-only` flag to be used with `--packages` to prevent group matching (i.e., a selection like `--packages=path_provider --exact-match-only` would only run on `packages/path_provider/path_provider`, not `packages/path_provider/*`). - Two new `publish` command flags: - `--tag-for-auto-publish`, to do all the steps that `publish` currently does except for the real `pub publish`, so it would dry-run the publish and then create and push the tag if successful. - `--already-tagged`, to skip the step of adding and pushing a tag, and replace it with a check that `HEAD` already has the expected tag. This set of additions supports a workflow where the current `release` step is changed to use `--tag-for-auto-publish`, and then the separate auto-publish system would publish each package with `... publish --already-tagged --packages= --exact-match-only`. See https://github.com/flutter/packages/pull/5005#discussion_r1344542422 for previous discussion/context. Part of https://github.com/flutter/flutter/issues/126827 --- .../tool/lib/src/common/package_command.dart | 89 ++++++++++---- script/tool/lib/src/publish_command.dart | 53 +++++++-- .../test/common/package_command_test.dart | 23 ++-- script/tool/test/publish_command_test.dart | 110 ++++++++++++++++++ 4 files changed, 239 insertions(+), 36 deletions(-) diff --git a/script/tool/lib/src/common/package_command.dart b/script/tool/lib/src/common/package_command.dart index f51c011ec036..94a808afc17b 100644 --- a/script/tool/lib/src/common/package_command.dart +++ b/script/tool/lib/src/common/package_command.dart @@ -50,7 +50,11 @@ abstract class PackageCommand extends Command { argParser.addMultiOption( _packagesArg, help: - 'Specifies which packages the command should run on (before sharding).\n', + 'Specifies which packages the command should run on (before sharding).\n' + 'If a package name is the name of a plugin group, it will include ' + 'the entire group; to avoid this, use group/package as the name ' + '(e.g., shared_preferences/shared_preferences), or pass ' + '--$_exactMatchOnlyArg', valueHelp: 'package1,package2,...', aliases: [_pluginsLegacyAliasArg], ); @@ -67,6 +71,9 @@ abstract class PackageCommand extends Command { valueHelp: 'n', defaultsTo: '1', ); + argParser.addFlag(_exactMatchOnlyArg, + help: 'Disables package group matching in package selection.', + negatable: false); argParser.addMultiOption( _excludeArg, abbr: 'e', @@ -136,6 +143,7 @@ abstract class PackageCommand extends Command { static const String _pluginsLegacyAliasArg = 'plugins'; static const String _runOnChangedPackagesArg = 'run-on-changed-packages'; static const String _runOnDirtyPackagesArg = 'run-on-dirty-packages'; + static const String _exactMatchOnlyArg = 'exact-match-only'; static const String _excludeArg = 'exclude'; static const String _filterPackagesArg = 'filter-packages-to'; // Diff base selection. @@ -361,6 +369,15 @@ abstract class PackageCommand extends Command { throw ToolExit(exitInvalidArguments); } + // Whether to require that a package name exactly match to be included, + // rather than allowing package groups for federated plugins. Any cases + // where the set of packages is determined programatically based on repo + // state should use exact matching. + final bool allowGroupMatching = !(getBoolArg(_exactMatchOnlyArg) || + argResults!.wasParsed(_runOnChangedPackagesArg) || + argResults!.wasParsed(_runOnDirtyPackagesArg) || + argResults!.wasParsed(_packagesForBranchArg)); + Set packages = Set.from(getStringListArg(_packagesArg)); final GitVersionFinder? changedFileFinder; @@ -458,6 +475,30 @@ abstract class PackageCommand extends Command { excludeAllButPackageNames.intersection(possibleNames).isEmpty; } + await for (final RepositoryPackage package in _everyTopLevelPackage()) { + if (packages.isEmpty || + packages + .intersection(_possiblePackageIdentifiers(package, + allowGroup: allowGroupMatching)) + .isNotEmpty) { + // Exclusion is always human input, so groups should always be allowed + // unless they have been specifically forbidden. + final bool excluded = isExcluded(_possiblePackageIdentifiers(package, + allowGroup: !getBoolArg(_exactMatchOnlyArg))); + yield PackageEnumerationEntry(package, excluded: excluded); + } + } + } + + /// Returns every top-level package in the repository, according to repository + /// conventions. + /// + /// In particular, it returns: + /// - Every package that is a direct child of one of the know "packages" + /// directories. + /// - Every package that is a direct child of a non-package subdirectory of + /// one of those directories (to cover federated plugin groups). + Stream _everyTopLevelPackage() async* { for (final Directory dir in [ packagesDir, if (thirdPartyPackagesDir.existsSync()) thirdPartyPackagesDir, @@ -466,33 +507,14 @@ abstract class PackageCommand extends Command { in dir.list(followLinks: false)) { // A top-level Dart package is a standard package. if (isPackage(entity)) { - if (packages.isEmpty || packages.contains(p.basename(entity.path))) { - yield PackageEnumerationEntry( - RepositoryPackage(entity as Directory), - excluded: isExcluded({entity.basename})); - } + yield RepositoryPackage(entity as Directory); } else if (entity is Directory) { // Look for Dart packages under this top-level directory; this is the // standard structure for federated plugins. await for (final FileSystemEntity subdir in entity.list(followLinks: false)) { if (isPackage(subdir)) { - // There are three ways for a federated plugin to match: - // - package name (path_provider_android) - // - fully specified name (path_provider/path_provider_android) - // - group name (path_provider), which matches all packages in - // the group - final Set possibleMatches = { - path.basename(subdir.path), // package name - path.basename(entity.path), // group name - path.relative(subdir.path, from: dir.path), // fully specified - }; - if (packages.isEmpty || - packages.intersection(possibleMatches).isNotEmpty) { - yield PackageEnumerationEntry( - RepositoryPackage(subdir as Directory), - excluded: isExcluded(possibleMatches)); - } + yield RepositoryPackage(subdir as Directory); } } } @@ -500,6 +522,29 @@ abstract class PackageCommand extends Command { } } + Set _possiblePackageIdentifiers( + RepositoryPackage package, { + required bool allowGroup, + }) { + final String packageName = path.basename(package.path); + if (package.isFederated) { + // There are three ways for a federated plugin to be identified: + // - package name (path_provider_android). + // - fully specified name (path_provider/path_provider_android). + // - group name (path_provider), which includes all packages in + // the group. + final io.Directory parentDir = package.directory.parent; + return { + packageName, + path.relative(package.path, + from: parentDir.parent.path), // fully specified + if (allowGroup) path.basename(parentDir.path), // group name + }; + } else { + return {packageName}; + } + } + /// Returns all Dart package folders (typically, base package + example) of /// the packages involved in this command execution. /// diff --git a/script/tool/lib/src/publish_command.dart b/script/tool/lib/src/publish_command.dart index eedea90e737c..b678e36b7aba 100644 --- a/script/tool/lib/src/publish_command.dart +++ b/script/tool/lib/src/publish_command.dart @@ -58,6 +58,11 @@ class PublishCommand extends PackageLoopingCommand { }) : _pubVersionFinder = PubVersionFinder(httpClient: httpClient ?? http.Client()), _stdin = stdinput ?? io.stdin { + argParser.addFlag(_alreadyTaggedFlag, + help: + 'Instead of tagging, validates that the current checkout is already tagged with the expected version.\n' + 'This is primarily intended for use in CI publish steps triggered by tagging.', + negatable: false); argParser.addMultiOption(_pubFlagsOption, help: 'A list of options that will be forwarded on to pub. Separate multiple flags with commas.'); @@ -83,13 +88,20 @@ class PublishCommand extends PackageLoopingCommand { argParser.addFlag(_skipConfirmationFlag, help: 'Run the command without asking for Y/N inputs.\n' 'This command will add a `--force` flag to the `pub publish` command if it is not added with $_pubFlagsOption\n'); + argParser.addFlag(_tagForAutoPublishFlag, + help: + 'Runs the dry-run publish, and tags if it succeeds, but does not actually publish.\n' + 'This is intended for use with a separate publish step that is based on tag push events.', + negatable: false); } + static const String _alreadyTaggedFlag = 'already-tagged'; static const String _pubFlagsOption = 'pub-publish-flags'; static const String _remoteOption = 'remote'; static const String _allChangedFlag = 'all-changed'; static const String _dryRunFlag = 'dry-run'; static const String _skipConfirmationFlag = 'skip-confirmation'; + static const String _tagForAutoPublishFlag = 'tag-for-auto-publish'; static const String _pubCredentialName = 'PUB_CREDENTIALS'; @@ -193,15 +205,27 @@ class PublishCommand extends PackageLoopingCommand { return PackageResult.fail(['uncommitted changes']); } - if (!await _publish(package)) { - return PackageResult.fail(['publish failed']); + final bool tagOnly = getBoolArg(_tagForAutoPublishFlag); + if (!tagOnly) { + if (!await _publish(package)) { + return PackageResult.fail(['publish failed']); + } } - if (!await _tagRelease(package)) { - return PackageResult.fail(['tagging failed']); + final String tag = _getTag(package); + if (getBoolArg(_alreadyTaggedFlag)) { + if (!(await _getCurrentTags()).contains(tag)) { + printError('The current checkout is not already tagged "$tag"'); + return PackageResult.fail(['missing tag']); + } + } else { + if (!await _tagRelease(package, tag)) { + return PackageResult.fail(['tagging failed']); + } } - print('\nPublished ${package.directory.basename} successfully!'); + final String action = tagOnly ? 'Tagged' : 'Published'; + print('\n$action ${package.directory.basename} successfully!'); return PackageResult.success(); } @@ -277,8 +301,7 @@ Safe to ignore if the package is deleted in this commit. // Tag the release with -v, and push it to the remote. // // Return `true` if successful, `false` otherwise. - Future _tagRelease(RepositoryPackage package) async { - final String tag = _getTag(package); + Future _tagRelease(RepositoryPackage package, String tag) async { print('Tagging release $tag...'); if (!getBoolArg(_dryRunFlag)) { final io.ProcessResult result = await (await gitDir).runCommand( @@ -301,6 +324,22 @@ Safe to ignore if the package is deleted in this commit. return success; } + Future> _getCurrentTags() async { + // git tag --points-at HEAD + final io.ProcessResult tagsResult = await (await gitDir).runCommand( + ['tag', '--points-at', 'HEAD'], + throwOnError: false, + ); + if (tagsResult.exitCode != 0) { + return []; + } + + return (tagsResult.stdout as String) + .split('\n') + .map((String line) => line.trim()) + .where((String line) => line.isNotEmpty); + } + Future _checkGitStatus(RepositoryPackage package) async { final io.ProcessResult statusResult = await (await gitDir).runCommand( [ diff --git a/script/tool/test/common/package_command_test.dart b/script/tool/test/common/package_command_test.dart index a71fc2a8eace..f80db4181d34 100644 --- a/script/tool/test/common/package_command_test.dart +++ b/script/tool/test/common/package_command_test.dart @@ -222,11 +222,6 @@ void main() { test( 'explicitly specifying the plugin (group) name of a federated plugin ' 'should include all plugins in the group', () async { - processRunner.mockProcessesForExecutable['git-diff'] = [ - FakeProcessInfo(MockProcess(stdout: ''' -packages/plugin1/plugin1/plugin1.dart -''')), - ]; final Directory pluginGroup = packagesDir.childDirectory('plugin1'); final RepositoryPackage appFacingPackage = createFakePlugin('plugin1', pluginGroup); @@ -235,8 +230,7 @@ packages/plugin1/plugin1/plugin1.dart final RepositoryPackage implementationPackage = createFakePlugin('plugin1_web', pluginGroup); - await runCapturingPrint( - runner, ['sample', '--base-sha=main', '--packages=plugin1']); + await runCapturingPrint(runner, ['sample', '--packages=plugin1']); expect( command.plugins, @@ -247,6 +241,21 @@ packages/plugin1/plugin1/plugin1.dart ])); }); + test( + 'specifying the app-facing package of a federated plugin with ' + '--exact-match-only should only include only that package', () async { + final Directory pluginGroup = packagesDir.childDirectory('plugin1'); + final RepositoryPackage appFacingPackage = + createFakePlugin('plugin1', pluginGroup); + createFakePlugin('plugin1_platform_interface', pluginGroup); + createFakePlugin('plugin1_web', pluginGroup); + + await runCapturingPrint(runner, + ['sample', '--packages=plugin1', '--exact-match-only']); + + expect(command.plugins, unorderedEquals([appFacingPackage.path])); + }); + test( 'specifying the app-facing package of a federated plugin using its ' 'fully qualified name should include only that package', () async { diff --git a/script/tool/test/publish_command_test.dart b/script/tool/test/publish_command_test.dart index 3af74a1607bc..1f3c0958b92d 100644 --- a/script/tool/test/publish_command_test.dart +++ b/script/tool/test/publish_command_test.dart @@ -349,6 +349,33 @@ void main() { ), ); }); + + test('skips publish with --tag-for-auto-publish', () async { + const String packageName = 'a_package'; + createFakePackage(packageName, packagesDir); + + final List output = + await runCapturingPrint(commandRunner, [ + 'publish', + '--packages=$packageName', + '--tag-for-auto-publish', + ]); + + // There should be no variant of any command containing "publish". + expect( + processRunner.recordedCalls + .map((ProcessCall call) => call.toString()), + isNot(contains(contains('publish')))); + // The output should indicate that it was tagged, not published. + expect( + output, + containsAllInOrder( + [ + contains('Tagged a_package successfully!'), + ], + ), + ); + }); }); group('Tags release', () { @@ -390,6 +417,18 @@ void main() { isNot(contains( const ProcessCall('git-tag', ['foo-v0.0.1'], null)))); }); + + test('when passed --tag-for-auto-publish', () async { + createFakePlugin('foo', packagesDir, examples: []); + await runCapturingPrint(commandRunner, [ + 'publish', + '--packages=foo', + '--tag-for-auto-publish', + ]); + + expect(processRunner.recordedCalls, + contains(const ProcessCall('git-tag', ['foo-v0.0.1'], null))); + }); }); group('Pushes tags', () { @@ -439,6 +478,21 @@ void main() { ])); }); + test('when passed --tag-for-auto-publish', () async { + createFakePlugin('foo', packagesDir, examples: []); + await runCapturingPrint(commandRunner, [ + 'publish', + '--packages=foo', + '--skip-confirmation', + '--tag-for-auto-publish', + ]); + + expect( + processRunner.recordedCalls, + contains(const ProcessCall( + 'git-push', ['upstream', 'foo-v0.0.1'], null))); + }); + test('to upstream by default, dry run', () async { final RepositoryPackage plugin = createFakePlugin('foo', packagesDir, examples: []); @@ -488,6 +542,62 @@ void main() { }); }); + group('--already-tagged', () { + test('passes when HEAD has the expected tag', () async { + createFakePlugin('foo', packagesDir, examples: []); + + processRunner.mockProcessesForExecutable['git-tag'] = [ + FakeProcessInfo(MockProcess()), // Skip the initializeRun call. + FakeProcessInfo(MockProcess(stdout: 'foo-v0.0.1\n'), + ['--points-at', 'HEAD']) + ]; + + await runCapturingPrint(commandRunner, + ['publish', '--packages=foo', '--already-tagged']); + }); + + test('fails if HEAD does not have the expected tag', () async { + createFakePlugin('foo', packagesDir, examples: []); + + Error? commandError; + final List output = await runCapturingPrint(commandRunner, + ['publish', '--packages=foo', '--already-tagged'], + errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The current checkout is not already tagged "foo-v0.0.1"'), + contains('missing tag'), + ])); + }); + + test('does not create or push tags', () async { + createFakePlugin('foo', packagesDir, examples: []); + + processRunner.mockProcessesForExecutable['git-tag'] = [ + FakeProcessInfo(MockProcess()), // Skip the initializeRun call. + FakeProcessInfo(MockProcess(stdout: 'foo-v0.0.1\n'), + ['--points-at', 'HEAD']) + ]; + + await runCapturingPrint(commandRunner, + ['publish', '--packages=foo', '--already-tagged']); + + expect( + processRunner.recordedCalls, + isNot(contains( + const ProcessCall('git-tag', ['foo-v0.0.1'], null)))); + expect( + processRunner.recordedCalls + .map((ProcessCall call) => call.executable), + isNot(contains('git-push'))); + }); + }); + group('Auto release (all-changed flag)', () { test('can release newly created plugins', () async { mockHttpResponses['plugin1'] = { From b5fe05d8f35edf03582eeca1df023a5e4defa43a Mon Sep 17 00:00:00 2001 From: LouiseHsu Date: Tue, 5 Mar 2024 14:35:33 -0800 Subject: [PATCH 056/126] [in_app_purchase_storekit] backfill native tests for more complete test coverage (#6209) Added more native tests for more complete test coverage. Fixes https://github.com/flutter/flutter/issues/143889 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [relevant style guides] and ran the auto-formatter. (Unlike the flutter/flutter repo, the flutter/packages repo does use `dart format`.) - [x] I signed the [CLA]. - [x] The title of the PR starts with the name of the package surrounded by square brackets, e.g. `[shared_preferences]` - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated `pubspec.yaml` with an appropriate new version according to the [pub versioning philosophy], or this PR is [exempt from version changes]. - [x] I updated `CHANGELOG.md` to add a description of the change, [following repository CHANGELOG style]. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] All existing and new tests are passing. --- .../in_app_purchase_storekit/CHANGELOG.md | 4 + .../Classes/InAppPurchasePlugin+TestOnly.h | 32 ++ .../darwin/Classes/InAppPurchasePlugin.m | 23 +- .../RunnerTests/InAppPurchasePluginTests.m | 318 ++++++++++++++++++ .../example/shared/RunnerTests/Stubs.h | 4 + .../example/shared/RunnerTests/Stubs.m | 13 +- .../in_app_purchase_storekit/pubspec.yaml | 2 +- 7 files changed, 383 insertions(+), 13 deletions(-) create mode 100644 packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin+TestOnly.h diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md index ce53f98fc462..8f0f7839f709 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.13 + +* Added new native tests for more complete test coverage. + ## 0.3.12+1 * Fixes type of error code returned from native code in SKReceiptManager.retrieveReceiptData. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin+TestOnly.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin+TestOnly.h new file mode 100644 index 000000000000..ca090b6b9928 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin+TestOnly.h @@ -0,0 +1,32 @@ +// 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 +#import "FIAPRequestHandler.h" +#import "InAppPurchasePlugin.h" + +@interface InAppPurchasePlugin () + +// Holding strong references to FIAPRequestHandlers. Remove the handlers from the set after +// the request is finished. +@property(strong, nonatomic, readonly) NSMutableSet *requestHandlers; + +// Callback channel to dart used for when a function from the transaction observer is triggered. +@property(strong, nonatomic) FlutterMethodChannel *transactionObserverCallbackChannel; + +// Callback channel to dart used for when a function from the transaction observer is triggered. +@property(copy, nonatomic) FIAPRequestHandler * (^handlerFactory)(SKRequest *); + +// Convenience initializer with dependancy injection +- (instancetype)initWithReceiptManager:(FIAPReceiptManager *)receiptManager + handlerFactory:(FIAPRequestHandler * (^)(SKRequest *))handlerFactory; + +// Transaction observer methods +- (void)handleTransactionsUpdated:(NSArray *)transactions; +- (void)handleTransactionsRemoved:(NSArray *)transactions; +- (void)handleTransactionRestoreFailed:(NSError *)error; +- (void)restoreCompletedTransactionsFinished; +- (BOOL)shouldAddStorePayment:(SKPayment *)payment product:(SKProduct *)product; + +@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m index 0a8b162bf1a5..2e2abaaed35b 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m @@ -9,20 +9,14 @@ #import "FIAPReceiptManager.h" #import "FIAPRequestHandler.h" #import "FIAPaymentQueueHandler.h" +#import "InAppPurchasePlugin+TestOnly.h" @interface InAppPurchasePlugin () -// Holding strong references to FIAPRequestHandlers. Remove the handlers from the set after -// the request is finished. -@property(strong, nonatomic, readonly) NSMutableSet *requestHandlers; - // After querying the product, the available products will be saved in the map to be used // for purchase. @property(strong, nonatomic, readonly) NSMutableDictionary *productsCache; -// Callback channel to dart used for when a function from the transaction observer is triggered. -@property(strong, nonatomic, readonly) FlutterMethodChannel *transactionObserverCallbackChannel; - // Callback channel to dart used for when a function from the payment queue delegate is triggered. @property(strong, nonatomic, readonly) FlutterMethodChannel *paymentQueueDelegateCallbackChannel; @property(strong, nonatomic, readonly) NSObject *registrar; @@ -52,6 +46,16 @@ - (instancetype)initWithReceiptManager:(FIAPReceiptManager *)receiptManager { _receiptManager = receiptManager; _requestHandlers = [NSMutableSet new]; _productsCache = [NSMutableDictionary new]; + _handlerFactory = ^FIAPRequestHandler *(SKRequest *request) { + return [[FIAPRequestHandler alloc] initWithRequest:request]; + }; + return self; +} + +- (instancetype)initWithReceiptManager:(FIAPReceiptManager *)receiptManager + handlerFactory:(FIAPRequestHandler * (^)(SKRequest *))handlerFactory { + self = [self initWithReceiptManager:receiptManager]; + _handlerFactory = [handlerFactory copy]; return self; } @@ -117,7 +121,7 @@ - (void)startProductRequestProductIdentifiers:(NSArray *)productIden FlutterError *_Nullable))completion { SKProductsRequest *request = [self getProductRequestWithIdentifiers:[NSSet setWithArray:productIdentifiers]]; - FIAPRequestHandler *handler = [[FIAPRequestHandler alloc] initWithRequest:request]; + FIAPRequestHandler *handler = self.handlerFactory(request); [self.requestHandlers addObject:handler]; __weak typeof(self) weakSelf = self; @@ -271,7 +275,7 @@ - (void)refreshReceiptReceiptProperties:(nullable NSDictionary *)receiptProperti request = [self getRefreshReceiptRequest:nil]; } - FIAPRequestHandler *handler = [[FIAPRequestHandler alloc] initWithRequest:request]; + FIAPRequestHandler *handler = self.handlerFactory(request); [self.requestHandlers addObject:handler]; __weak typeof(self) weakSelf = self; [handler startProductRequestWithCompletionHandler:^(SKProductsResponse *_Nullable response, @@ -282,6 +286,7 @@ - (void)refreshReceiptReceiptProperties:(nullable NSDictionary *)receiptProperti message:error.localizedDescription details:error.description]; completion(requestError); + return; } completion(nil); [weakSelf.requestHandlers removeObject:handler]; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m index 0695d9deb1c3..905903df0569 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m @@ -5,6 +5,7 @@ #import #import #import "FIAPaymentQueueHandler.h" +#import "InAppPurchasePlugin+TestOnly.h" #import "Stubs.h" @import in_app_purchase_storekit; @@ -108,6 +109,142 @@ - (void)testGetProductResponse { [self waitForExpectations:@[ expectation ] timeout:5]; } +- (void)testFinishTransactionSucceeds { + NSDictionary *args = @{ + @"transactionIdentifier" : @"567", + @"productIdentifier" : @"unique_identifier", + }; + + NSDictionary *transactionMap = @{ + @"transactionIdentifier" : @"567", + @"transactionState" : @(SKPaymentTransactionStatePurchasing), + @"payment" : [NSNull null], + @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" + code:123 + userInfo:@{}]], + @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), + }; + + SKPaymentTransactionStub *paymentTransaction = + [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; + NSArray *array = @[ paymentTransaction ]; + + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + OCMStub([mockHandler getUnfinishedTransactions]).andReturn(array); + + self.plugin.paymentQueueHandler = mockHandler; + + FlutterError *error; + [self.plugin finishTransactionFinishMap:args error:&error]; + + XCTAssertNil(error); +} + +- (void)testFinishTransactionSucceedsWithNilTransaction { + NSDictionary *args = @{ + @"transactionIdentifier" : [NSNull null], + @"productIdentifier" : @"unique_identifier", + }; + + NSDictionary *paymentMap = @{ + @"productIdentifier" : @"123", + @"requestData" : @"abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh", + @"quantity" : @(2), + @"applicationUsername" : @"app user name", + @"simulatesAskToBuyInSandbox" : @(NO) + }; + + NSDictionary *transactionMap = @{ + @"transactionState" : @(SKPaymentTransactionStatePurchasing), + @"payment" : paymentMap, + @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" + code:123 + userInfo:@{}]], + @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), + }; + + SKPaymentTransactionStub *paymentTransaction = + [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; + + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + OCMStub([mockHandler getUnfinishedTransactions]).andReturn(@[ paymentTransaction ]); + + self.plugin.paymentQueueHandler = mockHandler; + + FlutterError *error; + [self.plugin finishTransactionFinishMap:args error:&error]; + + XCTAssertNil(error); +} + +- (void)testGetProductResponseWithRequestError { + NSArray *argument = @[ @"123" ]; + XCTestExpectation *expectation = + [self expectationWithDescription:@"completion handler successfully called"]; + + id mockHandler = OCMClassMock([FIAPRequestHandler class]); + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] + initWithReceiptManager:nil + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return mockHandler; + }]; + + NSError *error = [NSError errorWithDomain:@"errorDomain" + code:0 + userInfo:@{NSLocalizedDescriptionKey : @"description"}]; + + OCMStub([mockHandler + startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], error, + nil])]); + + [plugin + startProductRequestProductIdentifiers:argument + completion:^(SKProductsResponseMessage *_Nullable response, + FlutterError *_Nullable startProductRequestError) { + [expectation fulfill]; + XCTAssertNotNil(error); + XCTAssertNotNil(startProductRequestError); + XCTAssertEqualObjects( + startProductRequestError.code, + @"storekit_getproductrequest_platform_error"); + }]; + [self waitForExpectations:@[ expectation ] timeout:5]; +} + +- (void)testGetProductResponseWithNoResponse { + NSArray *argument = @[ @"123" ]; + XCTestExpectation *expectation = + [self expectationWithDescription:@"completion handler successfully called"]; + + id mockHandler = OCMClassMock([FIAPRequestHandler class]); + + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] + initWithReceiptManager:nil + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return mockHandler; + }]; + + NSError *error = [NSError errorWithDomain:@"errorDomain" + code:0 + userInfo:@{NSLocalizedDescriptionKey : @"description"}]; + + OCMStub([mockHandler + startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], + [NSNull null], nil])]); + + [plugin + startProductRequestProductIdentifiers:argument + completion:^(SKProductsResponseMessage *_Nullable response, + FlutterError *_Nullable startProductRequestError) { + [expectation fulfill]; + XCTAssertNotNil(error); + XCTAssertNotNil(startProductRequestError); + XCTAssertEqualObjects(startProductRequestError.code, + @"storekit_platform_no_response"); + }]; + [self waitForExpectations:@[ expectation ] timeout:5]; +} + - (void)testAddPaymentShouldReturnFlutterErrorWhenPaymentFails { NSDictionary *argument = @{ @"productIdentifier" : @"123", @@ -132,6 +269,27 @@ - (void)testAddPaymentShouldReturnFlutterErrorWhenPaymentFails { XCTAssertEqualObjects(argument, error.details); } +- (void)testAddPaymentShouldReturnFlutterErrorWhenInvalidProduct { + NSDictionary *argument = @{ + // stubbed function will return nil for an empty productIdentifier + @"productIdentifier" : @"", + @"quantity" : @(1), + @"simulatesAskToBuyInSandbox" : @YES, + }; + + FlutterError *error; + + [self.plugin addPaymentPaymentMap:argument error:&error]; + + XCTAssertEqualObjects(@"storekit_invalid_payment_object", error.code); + XCTAssertEqualObjects( + @"You have requested a payment for an invalid product. Either the " + @"`productIdentifier` of the payment is not valid or the product has not been " + @"fetched before adding the payment to the payment queue.", + error.message); + XCTAssertEqualObjects(argument, error.details); +} + - (void)testAddPaymentSuccessWithoutPaymentDiscount { NSDictionary *argument = @{ @"productIdentifier" : @"123", @@ -324,6 +482,56 @@ - (void)testRefreshReceiptRequest { [self waitForExpectations:@[ expectation ] timeout:5]; } +- (void)testRefreshReceiptRequestWithParams { + NSDictionary *properties = @{ + @"isExpired" : @NO, + @"isRevoked" : @NO, + @"isVolumePurchase" : @NO, + }; + + XCTestExpectation *expectation = + [self expectationWithDescription:@"completion handler successfully called"]; + [self.plugin refreshReceiptReceiptProperties:properties + completion:^(FlutterError *_Nullable error) { + [expectation fulfill]; + }]; + [self waitForExpectations:@[ expectation ] timeout:5]; +} + +- (void)testRefreshReceiptRequestWithError { + NSDictionary *properties = @{ + @"isExpired" : @NO, + @"isRevoked" : @NO, + @"isVolumePurchase" : @NO, + }; + XCTestExpectation *expectation = + [self expectationWithDescription:@"completion handler successfully called"]; + + id mockHandler = OCMClassMock([FIAPRequestHandler class]); + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] + initWithReceiptManager:nil + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return mockHandler; + }]; + + NSError *recieptError = [NSError errorWithDomain:@"errorDomain" + code:0 + userInfo:@{NSLocalizedDescriptionKey : @"description"}]; + + OCMStub([mockHandler + startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], + recieptError, nil])]); + + [plugin refreshReceiptReceiptProperties:properties + completion:^(FlutterError *_Nullable error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects( + error.code, @"storekit_refreshreceiptrequest_platform_error"); + [expectation fulfill]; + }]; + [self waitForExpectations:@[ expectation ] timeout:5]; +} + /// presentCodeRedemptionSheetWithError:error is only available on iOS #if TARGET_OS_IOS - (void)testPresentCodeRedemptionSheet { @@ -442,6 +650,116 @@ - (void)testRemovePaymentQueueDelegate { } } +- (void)testHandleTransactionsUpdated { + NSDictionary *transactionMap = @{ + @"transactionIdentifier" : @"567", + @"transactionState" : @(SKPaymentTransactionStatePurchasing), + @"payment" : [NSNull null], + @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" + code:123 + userInfo:@{}]], + @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), + }; + + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); + plugin.transactionObserverCallbackChannel = mockChannel; + OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); + + SKPaymentTransactionStub *paymentTransaction = + [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; + NSArray *array = [NSArray arrayWithObjects:paymentTransaction, nil]; + NSMutableArray *maps = [NSMutableArray new]; + [maps addObject:[FIAObjectTranslator getMapFromSKPaymentTransaction:paymentTransaction]]; + + [plugin handleTransactionsUpdated:array]; + OCMVerify(times(1), [mockChannel invokeMethod:@"updatedTransactions" arguments:[OCMArg any]]); +} + +- (void)testHandleTransactionsRemoved { + NSDictionary *transactionMap = @{ + @"transactionIdentifier" : @"567", + @"transactionState" : @(SKPaymentTransactionStatePurchasing), + @"payment" : [NSNull null], + @"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub" + code:123 + userInfo:@{}]], + @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), + }; + + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); + plugin.transactionObserverCallbackChannel = mockChannel; + OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); + + SKPaymentTransactionStub *paymentTransaction = + [[SKPaymentTransactionStub alloc] initWithMap:transactionMap]; + NSArray *array = [NSArray arrayWithObjects:paymentTransaction, nil]; + NSMutableArray *maps = [NSMutableArray new]; + [maps addObject:[FIAObjectTranslator getMapFromSKPaymentTransaction:paymentTransaction]]; + + [plugin handleTransactionsRemoved:array]; + OCMVerify(times(1), [mockChannel invokeMethod:@"removedTransactions" arguments:maps]); +} + +- (void)testHandleTransactionRestoreFailed { + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); + plugin.transactionObserverCallbackChannel = mockChannel; + OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); + + NSError *error; + [plugin handleTransactionRestoreFailed:error]; + OCMVerify(times(1), [mockChannel invokeMethod:@"restoreCompletedTransactionsFailed" + arguments:[FIAObjectTranslator getMapFromNSError:error]]); +} + +- (void)testRestoreCompletedTransactionsFinished { + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); + plugin.transactionObserverCallbackChannel = mockChannel; + OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); + + [plugin restoreCompletedTransactionsFinished]; + OCMVerify(times(1), [mockChannel invokeMethod:@"paymentQueueRestoreCompletedTransactionsFinished" + arguments:nil]); +} + +- (void)testShouldAddStorePayment { + NSDictionary *paymentMap = @{ + @"productIdentifier" : @"123", + @"requestData" : @"abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh", + @"quantity" : @(2), + @"applicationUsername" : @"app user name", + @"simulatesAskToBuyInSandbox" : @(NO) + }; + + NSDictionary *productMap = @{ + @"price" : @"1", + @"priceLocale" : [FIAObjectTranslator getMapFromNSLocale:NSLocale.systemLocale], + @"productIdentifier" : @"123", + @"localizedTitle" : @"title", + @"localizedDescription" : @"des", + }; + + SKMutablePayment *payment = [FIAObjectTranslator getSKMutablePaymentFromMap:paymentMap]; + SKProductStub *product = [[SKProductStub alloc] initWithMap:productMap]; + + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); + plugin.transactionObserverCallbackChannel = mockChannel; + OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); + + NSDictionary *args = @{ + @"payment" : [FIAObjectTranslator getMapFromSKPayment:payment], + @"product" : [FIAObjectTranslator getMapFromSKProduct:product] + }; + + BOOL result = [plugin shouldAddStorePayment:payment product:product]; + XCTAssertEqual(result, NO); + OCMVerify(times(1), [mockChannel invokeMethod:@"shouldAddStorePayment" arguments:args]); +} + #if TARGET_OS_IOS - (void)testShowPriceConsentIfNeeded { FIAPaymentQueueHandler *mockQueueHandler = OCMClassMock(FIAPaymentQueueHandler.class); diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.h b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.h index d4e8df3eba72..2ef8e23181a7 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.h @@ -23,6 +23,7 @@ API_AVAILABLE(ios(11.2), macos(10.13.2)) @end @interface SKProductRequestStub : SKProductsRequest +@property(assign, nonatomic) BOOL returnError; - (instancetype)initWithProductIdentifiers:(NSSet *)productIdentifiers; - (instancetype)initWithFailureError:(NSError *)error; @end @@ -34,6 +35,9 @@ API_AVAILABLE(ios(11.2), macos(10.13.2)) @interface InAppPurchasePluginStub : InAppPurchasePlugin @end +@interface SKRequestStub : SKRequest +@end + @interface SKPaymentQueueStub : SKPaymentQueue @property(assign, nonatomic) SKPaymentTransactionState testState; @property(strong, nonatomic, nullable) id observer; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.m b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.m index 38081bb18f9c..b4dba710f026 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.m @@ -111,8 +111,13 @@ - (void)start { for (NSString *identifier in self.identifers) { [productArray addObject:@{@"productIdentifier" : identifier}]; } - SKProductsResponseStub *response = - [[SKProductsResponseStub alloc] initWithMap:@{@"products" : productArray}]; + SKProductsResponseStub *response; + if (self.returnError) { + response = nil; + } else { + response = [[SKProductsResponseStub alloc] initWithMap:@{@"products" : productArray}]; + } + if (self.error) { [self.delegate request:self didFailWithError:self.error]; } else { @@ -140,7 +145,6 @@ - (instancetype)initWithMap:(NSDictionary *)map { @end @interface InAppPurchasePluginStub () - @end @implementation InAppPurchasePluginStub @@ -150,6 +154,9 @@ - (SKProductRequestStub *)getProductRequestWithIdentifiers:(NSSet *)identifiers } - (SKProduct *)getProduct:(NSString *)productID { + if ([productID isEqualToString:@""]) { + return nil; + } return [[SKProductStub alloc] initWithProductID:productID]; } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml index fcfcde9fc53c..852bd127fe17 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_storekit description: An implementation for the iOS and macOS platforms of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/packages/tree/main/packages/in_app_purchase/in_app_purchase_storekit issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.3.12+1 +version: 0.3.13 environment: sdk: ^3.2.3 From 10cd61c606e1da53f02789cfe2fa38a1ccfa45e2 Mon Sep 17 00:00:00 2001 From: Ian Hickson Date: Tue, 5 Mar 2024 15:01:05 -0800 Subject: [PATCH 057/126] [rfw] Change test coverage logic to enforce 100% coverage (#6272) --- packages/rfw/CHANGELOG.md | 1 + .../rfw/lib/src/flutter/core_widgets.dart | 2 +- packages/rfw/lib/src/flutter/runtime.dart | 6 +- packages/rfw/test/core_widgets_test.dart | 2 +- .../rfw/test_coverage/bin/test_coverage.dart | 73 ++++++++----------- 5 files changed, 37 insertions(+), 47 deletions(-) diff --git a/packages/rfw/CHANGELOG.md b/packages/rfw/CHANGELOG.md index b3082c795e66..5b2b6cbbfd61 100644 --- a/packages/rfw/CHANGELOG.md +++ b/packages/rfw/CHANGELOG.md @@ -1,4 +1,5 @@ ## 1.0.24 + * Adds `InkResponse` material widget. * Adds `Material` material widget. * Adds the `child` to `Opacity` core widget. diff --git a/packages/rfw/lib/src/flutter/core_widgets.dart b/packages/rfw/lib/src/flutter/core_widgets.dart index e8d1d8f74b3b..a60e17f04073 100644 --- a/packages/rfw/lib/src/flutter/core_widgets.dart +++ b/packages/rfw/lib/src/flutter/core_widgets.dart @@ -277,7 +277,7 @@ Map get _coreWidgetsDefinitions => (Clip.values, source, ['clipBehavior']) ?? Clip.antiAlias, child: source.optionalChild(['child']), ); - }, + }, 'ColoredBox': (BuildContext context, DataSource source) { return ColoredBox( diff --git a/packages/rfw/lib/src/flutter/runtime.dart b/packages/rfw/lib/src/flutter/runtime.dart index a610a762ce9d..7d874e78e8b2 100644 --- a/packages/rfw/lib/src/flutter/runtime.dart +++ b/packages/rfw/lib/src/flutter/runtime.dart @@ -249,12 +249,12 @@ class Runtime extends ChangeNotifier { /// /// The returned map is an immutable view of the map updated by calls to /// [update] and [clearLibraries]. - /// + /// /// The keys are instances [LibraryName] which encode fully qualified library /// names, and the values are the corresponding [WidgetLibrary]s. - /// + /// /// The returned map is an immutable copy of the registered libraries - /// at the time of this call. + /// at the time of this call. /// /// See also: /// diff --git a/packages/rfw/test/core_widgets_test.dart b/packages/rfw/test/core_widgets_test.dart index aaeeacefe50c..75f5141c1c1e 100644 --- a/packages/rfw/test/core_widgets_test.dart +++ b/packages/rfw/test/core_widgets_test.dart @@ -293,7 +293,7 @@ void main() { ''')); await tester.pump(); expect(find.byType(ClipRRect), findsOneWidget); - final RenderClipRRect renderClip = tester.allRenderObjects.whereType().first; + final RenderClipRRect renderClip = tester.allRenderObjects.whereType().first; expect(renderClip.clipBehavior, equals(Clip.antiAlias)); expect(renderClip.borderRadius, equals(BorderRadius.zero)); }); diff --git a/packages/rfw/test_coverage/bin/test_coverage.dart b/packages/rfw/test_coverage/bin/test_coverage.dart index 4781693cb75b..6097086dae44 100644 --- a/packages/rfw/test_coverage/bin/test_coverage.dart +++ b/packages/rfw/test_coverage/bin/test_coverage.dart @@ -18,15 +18,15 @@ import 'package:meta/meta.dart'; // once it is loaded you can call `M-x coverlay-display-stats` to get a summary // of the files to look at.) -// Please update these targets when you update this package. -// Please ensure that test coverage continues to be 100%. -// Don't forget to update the lastUpdate date too! -const int targetLines = 3333; -const String targetPercent = '100'; -const String lastUpdate = '2024-02-26'; +// If Dart coverage increases the number of lines that could be covered, it is +// possible that this package will no longer report 100% coverage even though +// nothing has changed about the package itself. In the event that that happens, +// set this constant to the number of lines currently being covered and file a +// bug, cc'ing the current package owner (Hixie) so that they can add more tests. +const int? targetLines = null; @immutable -/* final */ class LcovLine { +final class LcovLine { const LcovLine(this.filename, this.line); final String filename; final int line; @@ -161,53 +161,42 @@ Future main(List arguments) async { final String coveredPercent = (100.0 * coveredLines / totalLines).toStringAsFixed(1); - // We only check the TARGET_LINES matches, not the TARGET_PERCENT, - // because we expect the percentage to drop over time as Dart fixes - // various bugs in how it determines what lines are coverable. - if (coveredLines < targetLines && targetLines <= totalLines) { + if (targetLines != null) { + if (targetLines! < totalLines) { + print( + 'Warning: test_coverage has an override set to reduce the expected number of covered lines from $totalLines to $targetLines.\n' + 'New tests should be written to cover all lines in the package.', + ); + totalLines = targetLines!; + } else if (targetLines == totalLines) { + print( + 'Warning: test_coverage has a redundant targetLines; it is equal to the actual number of coverable lines ($totalLines).\n' + 'Update test_coverage.dart to set the targetLines constant to null.', + ); + } else { + print( + 'Warning: test_coverage has an outdated targetLines ($targetLines) that is above the total number of lines in the package ($totalLines).\n' + 'Update test_coverage.dart to set the targetLines constant to null.', + ); + } + } + + if (coveredLines < totalLines) { print(''); print(' ╭──────────────────────────────╮'); print(' │ COVERAGE REGRESSION DETECTED │'); print(' ╰──────────────────────────────╯'); print(''); print( - 'Coverage has reduced to only $coveredLines lines ($coveredPercent%). This is lower than', - ); - print( - 'it was as of $lastUpdate, when coverage was $targetPercent%, covering $targetLines lines.', - ); - print( - 'Please add sufficient tests to get coverage back to 100%, and update', - ); - print( - 'test_coverage/bin/test_coverage.dart to have the appropriate targets.', + 'Coverage has reduced to only $coveredLines lines ($coveredPercent%), out\n' + 'of $totalLines total lines; ${totalLines - coveredLines} lines are not covered by tests.\n' + 'Please add sufficient tests to get coverage back to 100%.', ); print(''); print( 'When in doubt, ask @Hixie for advice. Thanks!', ); exit(1); - } else { - if (coveredLines < totalLines) { - print( - 'Warning: Coverage of package:rfw is no longer 100%. (Coverage is now $coveredPercent%, $coveredLines/$totalLines lines.)', - ); - } - if (coveredLines > targetLines) { - print( - 'Total lines of covered code has increased, and coverage script is now out of date.\n' - 'Coverage is now $coveredPercent%, $coveredLines/$totalLines lines, whereas previously there were only $targetLines lines.\n' - 'Update the "targetLines" constant at the top of rfw/test_coverage/bin/test_coverage.dart (to $coveredLines).', - ); - } - if (targetLines > totalLines) { - print( - 'Total lines of code has reduced, and coverage script is now out of date.\n' - 'Coverage is now $coveredPercent%, $coveredLines/$totalLines lines, but previously there were $targetLines lines.\n' - 'Update the "targetLines" constant at the top of rfw/test_coverage/bin/test_coverage.dart (to $totalLines).', - ); - exit(1); - } } coverageDirectory.deleteSync(recursive: true); From 79faa244741072fecfc01204a8564615ca937013 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Mar 2024 01:18:07 +0000 Subject: [PATCH 058/126] [in_app_pur]: Bump org.json:json from 20240205 to 20240303 in /packages/in_app_purchase/in_app_purchase/example/android/app (#6253) Bumps [org.json:json](https://github.com/douglascrockford/JSON-java) from 20240205 to 20240303.
Release notes

Sourced from org.json:json's releases.

20240303

Pull Request Description
#875 20240303-pre-release-updates
#874 Deployment and Pipeline action updates
#869 Revert recent obj long get long changes
#860 Added missing Javadocs for Java 21
#858 [cleanup-after-commit for #854 and #856](stleary/JSON-java#858)
#856 add ability for custom delimiters
Changelog

Sourced from org.json:json's changelog.

20240303 Revert optLong/getLong changes, and recent commits.

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.json:json&package-manager=gradle&previous-version=20240205&new-version=20240303)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- .../in_app_purchase/example/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/in_app_purchase/in_app_purchase/example/android/app/build.gradle b/packages/in_app_purchase/in_app_purchase/example/android/app/build.gradle index 9e1388cb73bb..af2658da94d3 100644 --- a/packages/in_app_purchase/in_app_purchase/example/android/app/build.gradle +++ b/packages/in_app_purchase/in_app_purchase/example/android/app/build.gradle @@ -110,7 +110,7 @@ dependencies { implementation 'com.android.billingclient:billing:3.0.2' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:5.0.0' - testImplementation 'org.json:json:20240205' + testImplementation 'org.json:json:20240303' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } From b097d99f6ff672b8a8db72125e8f41e43616012b Mon Sep 17 00:00:00 2001 From: Balvinder Singh Gambhir Date: Wed, 6 Mar 2024 07:58:46 +0530 Subject: [PATCH 059/126] [video_player_web] migrates to package:web (#5800) Updates the web implementation of `video_player_web` to `package:web`. Also: prevents an infinite event loop when seeking to the end of a video after it's done. ### Issues * Fixes: https://github.com/flutter/flutter/issues/139752 Co-authored-by: ToddZeil <120418414+ToddZeil@users.noreply.github.com> --- .../video_player_web/CHANGELOG.md | 5 ++ .../integration_test/pkg_web_tweaks.dart | 75 ++++++++++++++++ .../example/integration_test/utils.dart | 56 +++++------- .../integration_test/video_player_test.dart | 88 ++++++++++++------- .../video_player_web/example/pubspec.yaml | 2 +- .../lib/src/pkg_web_tweaks.dart | 17 ++++ .../lib/src/video_player.dart | 76 +++++++++------- .../lib/video_player_web.dart | 4 +- .../video_player_web/pubspec.yaml | 3 +- 9 files changed, 224 insertions(+), 102 deletions(-) create mode 100644 packages/video_player/video_player_web/example/integration_test/pkg_web_tweaks.dart create mode 100644 packages/video_player/video_player_web/lib/src/pkg_web_tweaks.dart diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index 8e9e9f21f736..cac6a397996f 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.3.0 + +* Migrates package and tests to `package:web``. +* Fixes infinite event loop caused by `seekTo` when the video ends. + ## 2.2.0 * Updates SDK version to Dart `^3.3.0`. Flutter `^3.19.0`. diff --git a/packages/video_player/video_player_web/example/integration_test/pkg_web_tweaks.dart b/packages/video_player/video_player_web/example/integration_test/pkg_web_tweaks.dart new file mode 100644 index 000000000000..ccf5b32e5c65 --- /dev/null +++ b/packages/video_player/video_player_web/example/integration_test/pkg_web_tweaks.dart @@ -0,0 +1,75 @@ +// 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. + +@JS() +library video_player_web_integration_test_pkg_web_tweaks; + +import 'dart:js_interop'; +import 'package:web/web.dart' as web; + +/// Adds a `controlsList` and `disablePictureInPicture` getters. +extension NonStandardGettersOnVideoElement on web.HTMLVideoElement { + external web.DOMTokenList? get controlsList; + external JSBoolean get disablePictureInPicture; +} + +/// Adds a `disableRemotePlayback` getter. +extension NonStandardGettersOnMediaElement on web.HTMLMediaElement { + external JSBoolean get disableRemotePlayback; +} + +/// Defines JS interop to access static methods from `Object`. +@JS('Object') +extension type DomObject._(JSAny _) { + @JS('defineProperty') + external static void _defineProperty( + JSAny? object, JSString property, Descriptor value); + + /// `Object.defineProperty`. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty + static void defineProperty( + JSObject object, String property, Descriptor descriptor) { + return _defineProperty(object, property.toJS, descriptor); + } +} + +/// The descriptor for the property being defined or modified with `defineProperty`. +/// +/// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#description +extension type Descriptor._(JSObject _) implements JSObject { + /// Builds a "data descriptor". + factory Descriptor.data({ + bool? writable, + JSAny? value, + }) => + Descriptor._data( + writable: writable?.toJS, + value: value.jsify(), + ); + + /// Builds an "accessor descriptor". + factory Descriptor.accessor({ + void Function(JSAny? value)? set, + JSAny? Function()? get, + }) => + Descriptor._accessor( + set: set?.toJS, + get: get?.toJS, + ); + + external factory Descriptor._accessor({ + // JSBoolean configurable, + // JSBoolean enumerable, + JSFunction? set, + JSFunction? get, + }); + + external factory Descriptor._data({ + // JSBoolean configurable, + // JSBoolean enumerable, + JSBoolean? writable, + JSAny? value, + }); +} diff --git a/packages/video_player/video_player_web/example/integration_test/utils.dart b/packages/video_player/video_player_web/example/integration_test/utils.dart index 75af525d4103..ba15c39c57d6 100644 --- a/packages/video_player/video_player_web/example/integration_test/utils.dart +++ b/packages/video_player/video_player_web/example/integration_test/utils.dart @@ -3,8 +3,9 @@ // found in the LICENSE file. import 'dart:js_interop'; -import 'dart:js_interop_unsafe'; + import 'package:web/web.dart' as web; +import 'pkg_web_tweaks.dart'; // Returns the URL to load an asset from this example app as a network source. // @@ -19,40 +20,29 @@ String getUrlForAssetAsNetworkSource(String assetKey) { '?raw=true'; } -extension type Descriptor._(JSObject _) implements JSObject { - // May also contain "configurable" and "enumerable" bools. - // See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#description - external factory Descriptor({ - // bool configurable, - // bool enumerable, - JSBoolean writable, - JSAny value, - }); -} - -void _defineProperty( - Object object, - String property, - Descriptor description, -) { - (globalContext['Object'] as JSObject?)?.callMethod( - 'defineProperty'.toJS, - object as JSObject, - property.toJS, - description, - ); -} - /// Forces a VideoElement to report "Infinity" duration. /// /// Uses JS Object.defineProperty to set the value of a readonly property. -void setInfinityDuration(Object videoElement) { - assert(videoElement is web.HTMLVideoElement); - _defineProperty( - videoElement, - 'duration', - Descriptor( - writable: true.toJS, - value: double.infinity.toJS, +void setInfinityDuration(web.HTMLVideoElement element) { + DomObject.defineProperty( + element, + 'duration', + Descriptor.data( + writable: true, + value: double.infinity.toJS, + ), + ); +} + +/// Makes the `currentTime` setter throw an exception if used. +void makeSetCurrentTimeThrow(web.HTMLVideoElement element) { + DomObject.defineProperty( + element, + 'currentTime', + Descriptor.accessor( + set: (JSAny? value) { + throw Exception('Unexpected call to currentTime with value: $value'); + }, + get: () => 100.toJS, )); } diff --git a/packages/video_player/video_player_web/example/integration_test/video_player_test.dart b/packages/video_player/video_player_web/example/integration_test/video_player_test.dart index 01f8e2f34357..51199ba79d2b 100644 --- a/packages/video_player/video_player_web/example/integration_test/video_player_test.dart +++ b/packages/video_player/video_player_web/example/integration_test/video_player_test.dart @@ -3,27 +3,28 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:html' as html; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; import 'package:video_player_web/src/duration_utils.dart'; import 'package:video_player_web/src/video_player.dart'; +import 'package:web/web.dart' as web; +import 'pkg_web_tweaks.dart'; import 'utils.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); group('VideoPlayer', () { - late html.VideoElement video; + late web.HTMLVideoElement video; setUp(() { // Never set "src" on the video, so this test doesn't hit the network! - video = html.VideoElement() + video = web.HTMLVideoElement() ..controls = true - ..setAttribute('playsinline', 'false'); + ..playsInline = false; }); testWidgets('fixes critical video element config', (WidgetTester _) async { @@ -36,8 +37,7 @@ void main() { // see: https://developer.mozilla.org/en-US/docs/Glossary/Boolean/HTML expect(video.getAttribute('autoplay'), isNull, reason: 'autoplay attribute on video tag must NOT be set'); - expect(video.getAttribute('playsinline'), 'true', - reason: 'Needed by safari iOS'); + expect(video.playsInline, true, reason: 'Needed by safari iOS'); }); testWidgets('setVolume', (WidgetTester tester) async { @@ -69,12 +69,32 @@ void main() { }, throwsAssertionError, reason: 'Playback speed cannot be == 0'); }); - testWidgets('seekTo', (WidgetTester tester) async { - final VideoPlayer player = VideoPlayer(videoElement: video)..initialize(); + group('seekTo', () { + testWidgets('negative time - throws assert', (WidgetTester tester) async { + final VideoPlayer player = VideoPlayer(videoElement: video) + ..initialize(); - expect(() { - player.seekTo(const Duration(seconds: -1)); - }, throwsAssertionError, reason: 'Cannot seek into negative numbers'); + expect(() { + player.seekTo(const Duration(seconds: -1)); + }, throwsAssertionError, reason: 'Cannot seek into negative numbers'); + }); + + testWidgets('setting currentTime to its current value - noop', + (WidgetTester tester) async { + makeSetCurrentTimeThrow(video); + final VideoPlayer player = VideoPlayer(videoElement: video) + ..initialize(); + + expect(() { + // Self-test... + video.currentTime = 123; + }, throwsException, reason: 'Setting currentTime must throw!'); + + expect(() { + // Should not set currentTime (and throw) when seekTo current time. + player.seekTo(Duration(seconds: video.currentTime.toInt())); + }, returnsNormally); + }); }); // The events tested in this group do *not* represent the actual sequence @@ -145,7 +165,7 @@ void main() { player.setBuffering(true); // Simulate "canplay" event... - video.dispatchEvent(html.Event('canplay')); + video.dispatchEvent(web.Event('canplay')); final List events = await stream; @@ -166,7 +186,7 @@ void main() { player.setBuffering(true); // Simulate "canplaythrough" event... - video.dispatchEvent(html.Event('canplaythrough')); + video.dispatchEvent(web.Event('canplaythrough')); final List events = await stream; @@ -177,9 +197,9 @@ void main() { testWidgets('initialized dispatches only once', (WidgetTester tester) async { // Dispatch some bogus "canplay" events from the video object - video.dispatchEvent(html.Event('canplay')); - video.dispatchEvent(html.Event('canplay')); - video.dispatchEvent(html.Event('canplay')); + video.dispatchEvent(web.Event('canplay')); + video.dispatchEvent(web.Event('canplay')); + video.dispatchEvent(web.Event('canplay')); // Take all the "initialized" events that we see during the next few seconds final Future> stream = timedStream @@ -187,9 +207,9 @@ void main() { event.eventType == VideoEventType.initialized) .toList(); - video.dispatchEvent(html.Event('canplay')); - video.dispatchEvent(html.Event('canplay')); - video.dispatchEvent(html.Event('canplay')); + video.dispatchEvent(web.Event('canplay')); + video.dispatchEvent(web.Event('canplay')); + video.dispatchEvent(web.Event('canplay')); final List events = await stream; @@ -200,8 +220,8 @@ void main() { // Issue: https://github.com/flutter/flutter/issues/137023 testWidgets('loadedmetadata dispatches initialized', (WidgetTester tester) async { - video.dispatchEvent(html.Event('loadedmetadata')); - video.dispatchEvent(html.Event('loadedmetadata')); + video.dispatchEvent(web.Event('loadedmetadata')); + video.dispatchEvent(web.Event('loadedmetadata')); final Future> stream = timedStream .where((VideoEvent event) => @@ -224,7 +244,7 @@ void main() { event.eventType == VideoEventType.initialized) .toList(); - video.dispatchEvent(html.Event('canplay')); + video.dispatchEvent(web.Event('canplay')); final List events = await stream; @@ -238,7 +258,7 @@ void main() { late VideoPlayer player; setUp(() { - video = html.VideoElement(); + video = web.HTMLVideoElement(); player = VideoPlayer(videoElement: video)..initialize(); }); @@ -271,7 +291,7 @@ void main() { expect(video.controlsList?.contains('nodownload'), isFalse); expect(video.controlsList?.contains('nofullscreen'), isFalse); expect(video.controlsList?.contains('noplaybackrate'), isFalse); - expect(video.getAttribute('disablePictureInPicture'), isNull); + expect(video.disablePictureInPicture, isFalse); }); testWidgets('and no download expect correct controls', @@ -290,7 +310,7 @@ void main() { expect(video.controlsList?.contains('nodownload'), isTrue); expect(video.controlsList?.contains('nofullscreen'), isFalse); expect(video.controlsList?.contains('noplaybackrate'), isFalse); - expect(video.getAttribute('disablePictureInPicture'), isNull); + expect(video.disablePictureInPicture, isFalse); }); testWidgets('and no fullscreen expect correct controls', @@ -309,7 +329,7 @@ void main() { expect(video.controlsList?.contains('nodownload'), isFalse); expect(video.controlsList?.contains('nofullscreen'), isTrue); expect(video.controlsList?.contains('noplaybackrate'), isFalse); - expect(video.getAttribute('disablePictureInPicture'), isNull); + expect(video.disablePictureInPicture, isFalse); }); testWidgets('and no playback rate expect correct controls', @@ -328,7 +348,7 @@ void main() { expect(video.controlsList?.contains('nodownload'), isFalse); expect(video.controlsList?.contains('nofullscreen'), isFalse); expect(video.controlsList?.contains('noplaybackrate'), isTrue); - expect(video.getAttribute('disablePictureInPicture'), isNull); + expect(video.disablePictureInPicture, isFalse); }); testWidgets('and no picture in picture expect correct controls', @@ -347,7 +367,7 @@ void main() { expect(video.controlsList?.contains('nodownload'), isFalse); expect(video.controlsList?.contains('nofullscreen'), isFalse); expect(video.controlsList?.contains('noplaybackrate'), isFalse); - expect(video.getAttribute('disablePictureInPicture'), 'true'); + expect(video.disablePictureInPicture, isTrue); }); }); }); @@ -362,7 +382,7 @@ void main() { ), ); - expect(video.getAttribute('disableRemotePlayback'), isNull); + expect(video.disableRemotePlayback, isFalse); }); testWidgets('when disabled expect attribute', @@ -373,7 +393,7 @@ void main() { ), ); - expect(video.getAttribute('disableRemotePlayback'), 'true'); + expect(video.disableRemotePlayback, isTrue); }); }); @@ -398,8 +418,8 @@ void main() { expect(video.controlsList?.contains('nodownload'), isTrue); expect(video.controlsList?.contains('nofullscreen'), isTrue); expect(video.controlsList?.contains('noplaybackrate'), isTrue); - expect(video.getAttribute('disablePictureInPicture'), 'true'); - expect(video.getAttribute('disableRemotePlayback'), 'true'); + expect(video.disablePictureInPicture, isTrue); + expect(video.disableRemotePlayback, isTrue); }); group('when called once more', () { @@ -421,8 +441,8 @@ void main() { expect(video.controlsList?.contains('nodownload'), isFalse); expect(video.controlsList?.contains('nofullscreen'), isFalse); expect(video.controlsList?.contains('noplaybackrate'), isFalse); - expect(video.getAttribute('disablePictureInPicture'), isNull); - expect(video.getAttribute('disableRemotePlayback'), isNull); + expect(video.disablePictureInPicture, isFalse); + expect(video.disableRemotePlayback, isFalse); }); }); }); diff --git a/packages/video_player/video_player_web/example/pubspec.yaml b/packages/video_player/video_player_web/example/pubspec.yaml index bd2f3ba48788..27aba7660bb8 100644 --- a/packages/video_player/video_player_web/example/pubspec.yaml +++ b/packages/video_player/video_player_web/example/pubspec.yaml @@ -11,7 +11,7 @@ dependencies: video_player_platform_interface: ^6.1.0 video_player_web: path: ../ - web: ^0.5.0 + web: ^0.5.1 dev_dependencies: flutter_test: diff --git a/packages/video_player/video_player_web/lib/src/pkg_web_tweaks.dart b/packages/video_player/video_player_web/lib/src/pkg_web_tweaks.dart new file mode 100644 index 000000000000..c0ae661c96b2 --- /dev/null +++ b/packages/video_player/video_player_web/lib/src/pkg_web_tweaks.dart @@ -0,0 +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. + +import 'dart:js_interop'; +import 'package:web/web.dart' as web; + +/// Adds a "disablePictureInPicture" setter to [web.HTMLVideoElement]s. +extension NonStandardSettersOnVideoElement on web.HTMLVideoElement { + external set disablePictureInPicture(JSBoolean disabled); +} + +/// Adds a "disableRemotePlayback" and "controlsList" setters to [web.HTMLMediaElement]s. +extension NonStandardSettersOnMediaElement on web.HTMLMediaElement { + external set disableRemotePlayback(JSBoolean disabled); + external set controlsList(JSString? controlsList); +} diff --git a/packages/video_player/video_player_web/lib/src/video_player.dart b/packages/video_player/video_player_web/lib/src/video_player.dart index 4adb2e1e8662..012463fc780d 100644 --- a/packages/video_player/video_player_web/lib/src/video_player.dart +++ b/packages/video_player/video_player_web/lib/src/video_player.dart @@ -3,13 +3,16 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:html' as html; +import 'dart:js_interop'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; +import 'package:web/helpers.dart'; +import 'package:web/web.dart' as web; import 'duration_utils.dart'; +import 'pkg_web_tweaks.dart'; // An error code value to error name Map. // See: https://developer.mozilla.org/en-US/docs/Web/API/MediaError/code @@ -34,29 +37,29 @@ const Map _kErrorValueToErrorDescription = { const String _kDefaultErrorMessage = 'No further diagnostic information can be determined or provided.'; -/// Wraps a [html.VideoElement] so its API complies with what is expected by the plugin. +/// Wraps a [web.HTMLVideoElement] so its API complies with what is expected by the plugin. class VideoPlayer { - /// Create a [VideoPlayer] from a [html.VideoElement] instance. + /// Create a [VideoPlayer] from a [web.HTMLVideoElement] instance. VideoPlayer({ - required html.VideoElement videoElement, + required web.HTMLVideoElement videoElement, @visibleForTesting StreamController? eventController, }) : _videoElement = videoElement, _eventController = eventController ?? StreamController(); final StreamController _eventController; - final html.VideoElement _videoElement; - void Function(html.Event)? _onContextMenu; + final web.HTMLVideoElement _videoElement; + web.EventHandler? _onContextMenu; bool _isInitialized = false; bool _isBuffering = false; - /// Returns the [Stream] of [VideoEvent]s from the inner [html.VideoElement]. + /// Returns the [Stream] of [VideoEvent]s from the inner [web.HTMLVideoElement]. Stream get events => _eventController.stream; - /// Initializes the wrapped [html.VideoElement]. + /// Initializes the wrapped [web.HTMLVideoElement]. /// /// This method sets the required DOM attributes so videos can [play] programmatically, - /// and attaches listeners to the internal events from the [html.VideoElement] + /// and attaches listeners to the internal events from the [web.HTMLVideoElement] /// to react to them / expose them through the [VideoPlayer.events] stream. /// /// The [src] parameter is the URL of the video. It is passed in from the plugin @@ -71,14 +74,8 @@ class VideoPlayer { }) { _videoElement ..autoplay = false - ..controls = false; - - // Allows Safari iOS to play the video inline. - // - // This property is not exposed through dart:html so we use the - // HTML Boolean attribute form (when present with any value => true) - // See: https://developer.mozilla.org/en-US/docs/Glossary/Boolean/HTML - _videoElement.setAttribute('playsinline', true); + ..controls = false + ..playsInline = true; _videoElement.onCanPlay.listen(_onVideoElementInitialization); // Needed for Safari iOS 17, which may not send `canplay`. @@ -98,12 +95,12 @@ class VideoPlayer { }); // The error event fires when some form of error occurs while attempting to load or perform the media. - _videoElement.onError.listen((html.Event _) { + _videoElement.onError.listen((web.Event _) { setBuffering(false); // The Event itself (_) doesn't contain info about the actual error. // We need to look at the HTMLMediaElement.error. // See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/error - final html.MediaError error = _videoElement.error!; + final web.MediaError error = _videoElement.error!; _eventController.addError(PlatformException( code: _kErrorValueToErrorName[error.code]!, message: error.message != '' ? error.message : _kDefaultErrorMessage, @@ -145,18 +142,19 @@ class VideoPlayer { /// When called from some user interaction (a tap on a button), the above /// limitation should disappear. Future play() { - return _videoElement.play().catchError((Object e) { + return _videoElement.play().toDart.catchError((Object e) { // play() attempts to begin playback of the media. It returns // a Promise which can get rejected in case of failure to begin // playback for any reason, such as permission issues. - // The rejection handler is called with a DomException. + // The rejection handler is called with a DOMException. // See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play - final html.DomException exception = e as html.DomException; + final web.DOMException exception = e as web.DOMException; _eventController.addError(PlatformException( code: exception.name, message: exception.message, )); - }, test: (Object e) => e is html.DomException); + return null; + }, test: (Object e) => e is web.DOMException); } /// Pauses the video in the current position. @@ -175,7 +173,7 @@ class VideoPlayer { /// Values must fall between 0 and 1, where 0 is muted and 1 is the loudest. /// /// When volume is set to 0, the `muted` property is also applied to the - /// [html.VideoElement]. This is required for auto-play on the web. + /// [web.HTMLVideoElement]. This is required for auto-play on the web. void setVolume(double volume) { assert(volume >= 0 && volume <= 1); @@ -208,12 +206,28 @@ class VideoPlayer { void seekTo(Duration position) { assert(!position.isNegative); + // Don't seek if video is already at target position. + // + // This is needed because the core plugin will pause and seek to the end of + // the video when it finishes, and that causes an infinite loop of `ended` + // events on the web. + // + // See: https://github.com/flutter/flutter/issues/77674 + if (position == _videoElementCurrentTime) { + return; + } + _videoElement.currentTime = position.inMilliseconds.toDouble() / 1000; } /// Returns the current playback head position as a [Duration]. Duration getPosition() { _sendBufferingRangesUpdate(); + return _videoElementCurrentTime; + } + + /// Returns the currentTime of the underlying video element. + Duration get _videoElementCurrentTime { return Duration(milliseconds: (_videoElement.currentTime * 1000).round()); } @@ -226,21 +240,21 @@ class VideoPlayer { _videoElement.controls = true; final String controlsList = options.controls.controlsList; if (controlsList.isNotEmpty) { - _videoElement.setAttribute('controlsList', controlsList); + _videoElement.controlsList = controlsList.toJS; } if (!options.controls.allowPictureInPicture) { - _videoElement.setAttribute('disablePictureInPicture', true); + _videoElement.disablePictureInPicture = true.toJS; } } if (!options.allowContextMenu) { - _onContextMenu = (html.Event event) => event.preventDefault(); + _onContextMenu = ((web.Event event) => event.preventDefault()).toJS; _videoElement.addEventListener('contextmenu', _onContextMenu); } if (!options.allowRemotePlayback) { - _videoElement.setAttribute('disableRemotePlayback', true); + _videoElement.disableRemotePlayback = true.toJS; } } @@ -255,7 +269,7 @@ class VideoPlayer { _videoElement.removeAttribute('disableRemotePlayback'); } - /// Disposes of the current [html.VideoElement]. + /// Disposes of the current [web.HTMLVideoElement]. void dispose() { _videoElement.removeAttribute('src'); if (_onContextMenu != null) { @@ -316,7 +330,7 @@ class VideoPlayer { } } - // Broadcasts the [html.VideoElement.buffered] status through the [events] stream. + // Broadcasts the [web.HTMLVideoElement.buffered] status through the [events] stream. void _sendBufferingRangesUpdate() { _eventController.add(VideoEvent( buffered: _toDurationRange(_videoElement.buffered), @@ -325,7 +339,7 @@ class VideoPlayer { } // Converts from [html.TimeRanges] to our own List. - List _toDurationRange(html.TimeRanges buffered) { + List _toDurationRange(web.TimeRanges buffered) { final List durationRange = []; for (int i = 0; i < buffered.length; i++) { durationRange.add(DurationRange( diff --git a/packages/video_player/video_player_web/lib/video_player_web.dart b/packages/video_player/video_player_web/lib/video_player_web.dart index 67cf2746977d..8f5c0265e965 100644 --- a/packages/video_player/video_player_web/lib/video_player_web.dart +++ b/packages/video_player/video_player_web/lib/video_player_web.dart @@ -3,12 +3,12 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:html'; import 'dart:ui_web' as ui_web; import 'package:flutter/material.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; +import 'package:web/web.dart' as web; import 'src/video_player.dart'; @@ -71,7 +71,7 @@ class VideoPlayerPlugin extends VideoPlayerPlatform { 'web implementation of video_player cannot play content uri')); } - final VideoElement videoElement = VideoElement() + final web.HTMLVideoElement videoElement = web.HTMLVideoElement() ..id = 'videoElement-$textureId' ..style.border = 'none' ..style.height = '100%' diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index 0cb69f88b743..dd876328b014 100644 --- a/packages/video_player/video_player_web/pubspec.yaml +++ b/packages/video_player/video_player_web/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_web description: Web platform implementation of video_player. repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.2.0 +version: 2.3.0 environment: sdk: ^3.3.0 @@ -22,6 +22,7 @@ dependencies: flutter_web_plugins: sdk: flutter video_player_platform_interface: ^6.2.0 + web: ^0.5.1 dev_dependencies: flutter_test: From 9b88dbc539927fa0f4c8ae9aa8792043ecd0beed Mon Sep 17 00:00:00 2001 From: Balvinder Singh Gambhir Date: Wed, 6 Mar 2024 09:51:18 +0530 Subject: [PATCH 060/126] [image_picker_for_web] migrates to package:web (#5799) Updates the web implementation of `image_picker_for_web` to `package:web`. ### Issues * Fixes https://github.com/flutter/flutter/issues/139751 --- .../image_picker_for_web/CHANGELOG.md | 5 +- .../image_picker_for_web_test.dart | 108 ++++++++++-------- .../integration_test/image_resizer_test.dart | 47 ++++---- .../image_picker_for_web/example/pubspec.yaml | 5 +- .../lib/image_picker_for_web.dart | 81 +++++++------ .../lib/src/image_resizer.dart | 99 +++++++++------- .../lib/src/pkg_web_tweaks.dart | 14 +++ .../image_picker_for_web/pubspec.yaml | 7 +- 8 files changed, 211 insertions(+), 155 deletions(-) create mode 100644 packages/image_picker/image_picker_for_web/lib/src/pkg_web_tweaks.dart diff --git a/packages/image_picker/image_picker_for_web/CHANGELOG.md b/packages/image_picker/image_picker_for_web/CHANGELOG.md index bc80d9584d93..2cfa022e0b97 100644 --- a/packages/image_picker/image_picker_for_web/CHANGELOG.md +++ b/packages/image_picker/image_picker_for_web/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 3.0.3 -* Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. +* Migrates package and tests to `package:web`. +* Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. ## 3.0.2 diff --git a/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart b/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart index 94adce2411e4..a467a1895378 100644 --- a/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart +++ b/packages/image_picker/image_picker_for_web/example/integration_test/image_picker_for_web_test.dart @@ -3,25 +3,29 @@ // found in the LICENSE file. import 'dart:convert'; -import 'dart:html' as html; +import 'dart:js_interop'; import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; import 'package:image_picker_for_web/image_picker_for_web.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; import 'package:integration_test/integration_test.dart'; +import 'package:web/web.dart' as web; const String expectedStringContents = 'Hello, world!'; const String otherStringContents = 'Hello again, world!'; final Uint8List bytes = const Utf8Encoder().convert(expectedStringContents); final Uint8List otherBytes = const Utf8Encoder().convert(otherStringContents); -final Map options = { - 'type': 'text/plain', - 'lastModified': DateTime.utc(2017, 12, 13).millisecondsSinceEpoch, -}; -final html.File textFile = html.File([bytes], 'hello.txt', options); -final html.File secondTextFile = - html.File([otherBytes], 'secondFile.txt'); +// TODO(dit): When web:0.6.0 lands, move `type` to the [web.FilePropertyBag] constructor. +// See: https://github.com/dart-lang/web/pull/197 +final web.FilePropertyBag options = web.FilePropertyBag( + lastModified: DateTime.utc(2017, 12, 13).millisecondsSinceEpoch, +)..type = 'text/plain'; + +final web.File textFile = + web.File([bytes.toJS].toJS, 'hello.txt', options); +final web.File secondTextFile = + web.File([otherBytes.toJS].toJS, 'secondFile.txt'); void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -36,12 +40,12 @@ void main() { testWidgets('getImageFromSource can select a file', ( WidgetTester _, ) async { - final html.FileUploadInputElement mockInput = html.FileUploadInputElement(); - + final web.HTMLInputElement mockInput = web.HTMLInputElement() + ..type = 'file'; final ImagePickerPluginTestOverrides overrides = ImagePickerPluginTestOverrides() ..createInputElement = ((_, __) => mockInput) - ..getMultipleFilesFromInput = ((_) => [textFile]); + ..getMultipleFilesFromInput = ((_) => [textFile]); final ImagePickerPlugin plugin = ImagePickerPlugin(overrides: overrides); @@ -50,11 +54,12 @@ void main() { source: ImageSource.camera, ); - expect(html.querySelector('flt-image-picker-inputs')?.children.isEmpty, - isFalse); + expect( + web.document.querySelector('flt-image-picker-inputs')?.children.length, + isNonZero); // Mock the browser behavior of selecting a file... - mockInput.dispatchEvent(html.Event('change')); + mockInput.dispatchEvent(web.Event('change')); // Now the file should be available expect(image, completes); @@ -69,22 +74,24 @@ void main() { expect( file.lastModified(), completion( - DateTime.fromMillisecondsSinceEpoch(textFile.lastModified!), + DateTime.fromMillisecondsSinceEpoch(textFile.lastModified), )); - expect(html.querySelector('flt-image-picker-inputs')?.children.isEmpty, - isTrue); + expect( + web.document.querySelector('flt-image-picker-inputs')?.children.length, + isZero); }); testWidgets('getMultiImageWithOptions can select multiple files', ( WidgetTester _, ) async { - final html.FileUploadInputElement mockInput = html.FileUploadInputElement(); + final web.HTMLInputElement mockInput = web.HTMLInputElement() + ..type = 'file'; final ImagePickerPluginTestOverrides overrides = ImagePickerPluginTestOverrides() ..createInputElement = ((_, __) => mockInput) ..getMultipleFilesFromInput = - ((_) => [textFile, secondTextFile]); + ((_) => [textFile, secondTextFile]); final ImagePickerPlugin plugin = ImagePickerPlugin(overrides: overrides); @@ -92,7 +99,7 @@ void main() { final Future> files = plugin.getMultiImageWithOptions(); // Mock the browser behavior of selecting a file... - mockInput.dispatchEvent(html.Event('change')); + mockInput.dispatchEvent(web.Event('change')); // Now the file should be available expect(files, completes); @@ -108,13 +115,14 @@ void main() { }); testWidgets('getMedia can select multiple files', (WidgetTester _) async { - final html.FileUploadInputElement mockInput = html.FileUploadInputElement(); + final web.HTMLInputElement mockInput = web.HTMLInputElement() + ..type = 'file'; final ImagePickerPluginTestOverrides overrides = ImagePickerPluginTestOverrides() ..createInputElement = ((_, __) => mockInput) ..getMultipleFilesFromInput = - ((_) => [textFile, secondTextFile]); + ((_) => [textFile, secondTextFile]); final ImagePickerPlugin plugin = ImagePickerPlugin(overrides: overrides); @@ -123,7 +131,7 @@ void main() { plugin.getMedia(options: const MediaOptions(allowMultiple: true)); // Mock the browser behavior of selecting a file... - mockInput.dispatchEvent(html.Event('change')); + mockInput.dispatchEvent(web.Event('change')); // Now the file should be available expect(files, completes); @@ -139,20 +147,20 @@ void main() { }); group('cancel event', () { - late html.FileUploadInputElement mockInput; + late web.HTMLInputElement mockInput; late ImagePickerPluginTestOverrides overrides; late ImagePickerPlugin plugin; setUp(() { - mockInput = html.FileUploadInputElement(); + mockInput = web.HTMLInputElement()..type = 'file'; overrides = ImagePickerPluginTestOverrides() ..createInputElement = ((_, __) => mockInput) - ..getMultipleFilesFromInput = ((_) => [textFile]); + ..getMultipleFilesFromInput = ((_) => [textFile]); plugin = ImagePickerPlugin(overrides: overrides); }); void mockCancel() { - mockInput.dispatchEvent(html.Event('cancel')); + mockInput.dispatchEvent(web.Event('cancel')); } testWidgets('getFiles - returns empty list', (WidgetTester _) async { @@ -226,61 +234,61 @@ void main() { group('createInputElement', () { testWidgets('accept: any, capture: null', (WidgetTester tester) async { - final html.Element input = plugin.createInputElement('any', null); + final web.Element input = plugin.createInputElement('any', null); - expect(input.attributes, containsPair('accept', 'any')); - expect(input.attributes, isNot(contains('capture'))); - expect(input.attributes, isNot(contains('multiple'))); + expect(input.getAttribute('accept'), 'any'); + expect(input.hasAttribute('capture'), false); + expect(input.hasAttribute('multiple'), false); }); testWidgets('accept: any, capture: something', (WidgetTester tester) async { - final html.Element input = plugin.createInputElement('any', 'something'); + final web.Element input = plugin.createInputElement('any', 'something'); - expect(input.attributes, containsPair('accept', 'any')); - expect(input.attributes, containsPair('capture', 'something')); - expect(input.attributes, isNot(contains('multiple'))); + expect(input.getAttribute('accept'), 'any'); + expect(input.getAttribute('capture'), 'something'); + expect(input.hasAttribute('multiple'), false); }); testWidgets('accept: any, capture: null, multi: true', (WidgetTester tester) async { - final html.Element input = + final web.Element input = plugin.createInputElement('any', null, multiple: true); - expect(input.attributes, containsPair('accept', 'any')); - expect(input.attributes, isNot(contains('capture'))); - expect(input.attributes, contains('multiple')); + expect(input.getAttribute('accept'), 'any'); + expect(input.hasAttribute('capture'), false); + expect(input.hasAttribute('multiple'), true); }); testWidgets('accept: any, capture: something, multi: true', (WidgetTester tester) async { - final html.Element input = + final web.Element input = plugin.createInputElement('any', 'something', multiple: true); - expect(input.attributes, containsPair('accept', 'any')); - expect(input.attributes, containsPair('capture', 'something')); - expect(input.attributes, contains('multiple')); + expect(input.getAttribute('accept'), 'any'); + expect(input.getAttribute('capture'), 'something'); + expect(input.hasAttribute('multiple'), true); }); }); group('Deprecated methods', () { - late html.FileUploadInputElement mockInput; + late web.HTMLInputElement mockInput; late ImagePickerPluginTestOverrides overrides; late ImagePickerPlugin plugin; setUp(() { - mockInput = html.FileUploadInputElement(); + mockInput = web.HTMLInputElement()..type = 'file'; overrides = ImagePickerPluginTestOverrides() ..createInputElement = ((_, __) => mockInput) - ..getMultipleFilesFromInput = ((_) => [textFile]); + ..getMultipleFilesFromInput = ((_) => [textFile]); plugin = ImagePickerPlugin(overrides: overrides); }); void mockCancel() { - mockInput.dispatchEvent(html.Event('cancel')); + mockInput.dispatchEvent(web.Event('cancel')); } void mockChange() { - mockInput.dispatchEvent(html.Event('change')); + mockInput.dispatchEvent(web.Event('change')); } group('getImage', () { @@ -306,7 +314,7 @@ void main() { expect( file.lastModified(), completion( - DateTime.fromMillisecondsSinceEpoch(textFile.lastModified!), + DateTime.fromMillisecondsSinceEpoch(textFile.lastModified), )); }); @@ -326,7 +334,7 @@ void main() { testWidgets('can select multiple files', (WidgetTester _) async { // Override the returned files... overrides.getMultipleFilesFromInput = - (_) => [textFile, secondTextFile]; + (_) => [textFile, secondTextFile]; // ignore: deprecated_member_use final Future> files = plugin.getMultiImage(); diff --git a/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart b/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart index 0ff6d2380004..dc01ef6d1972 100644 --- a/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart +++ b/packages/image_picker/image_picker_for_web/example/integration_test/image_resizer_test.dart @@ -3,7 +3,7 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:html' as html; +import 'dart:js_interop'; import 'dart:typed_data'; import 'dart:ui'; @@ -11,6 +11,8 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:image_picker_for_web/src/image_resizer.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; import 'package:integration_test/integration_test.dart'; +import 'package:web/helpers.dart'; +import 'package:web/web.dart' as web; //This is a sample 10x10 png image const String pngFileBase64Contents = @@ -24,14 +26,13 @@ void main() { late XFile pngFile; setUp(() { imageResizer = ImageResizer(); - final html.File pngHtmlFile = - _base64ToFile(pngFileBase64Contents, 'pngImage.png'); - pngFile = XFile(html.Url.createObjectUrl(pngHtmlFile), - name: pngHtmlFile.name, mimeType: pngHtmlFile.type); + final web.Blob pngHtmlFile = _base64ToBlob(pngFileBase64Contents); + pngFile = XFile(web.URL.createObjectURL(pngHtmlFile), + name: 'pngImage.png', mimeType: 'image/png'); }); testWidgets('image is loaded correctly ', (WidgetTester tester) async { - final html.ImageElement imageElement = + final web.HTMLImageElement imageElement = await imageResizer.loadImage(pngFile.path); expect(imageElement.width, 10); expect(imageElement.height, 10); @@ -40,9 +41,9 @@ void main() { testWidgets( "canvas is loaded with image's width and height when max width and max height are null", (WidgetTester widgetTester) async { - final html.ImageElement imageElement = + final web.HTMLImageElement imageElement = await imageResizer.loadImage(pngFile.path); - final html.CanvasElement canvas = + final web.HTMLCanvasElement canvas = imageResizer.resizeImageElement(imageElement, null, null); expect(canvas.width, imageElement.width); expect(canvas.height, imageElement.height); @@ -51,9 +52,9 @@ void main() { testWidgets( 'canvas size is scaled when max width and max height are not null', (WidgetTester widgetTester) async { - final html.ImageElement imageElement = + final web.HTMLImageElement imageElement = await imageResizer.loadImage(pngFile.path); - final html.CanvasElement canvas = + final web.HTMLCanvasElement canvas = imageResizer.resizeImageElement(imageElement, 8, 8); expect(canvas.width, 8); expect(canvas.height, 8); @@ -61,9 +62,9 @@ void main() { testWidgets('resized image is returned after converting canvas to file', (WidgetTester widgetTester) async { - final html.ImageElement imageElement = + final web.HTMLImageElement imageElement = await imageResizer.loadImage(pngFile.path); - final html.CanvasElement canvas = + final web.HTMLCanvasElement canvas = imageResizer.resizeImageElement(imageElement, null, null); final XFile resizedImage = await imageResizer.writeCanvasToFile(pngFile, canvas, null); @@ -112,19 +113,21 @@ void main() { Future _getImageSize(XFile file) async { final Completer completer = Completer(); - final html.ImageElement image = html.ImageElement(src: file.path); - image.onLoad.listen((html.Event event) { - completer.complete(Size(image.width!.toDouble(), image.height!.toDouble())); - }); - image.onError.listen((html.Event event) { - completer.complete(Size.zero); - }); + final web.HTMLImageElement image = web.HTMLImageElement(); + image + ..onLoad.listen((web.Event event) { + completer.complete(Size(image.width.toDouble(), image.height.toDouble())); + }) + ..onError.listen((web.Event event) { + completer.complete(Size.zero); + }) + ..src = file.path; return completer.future; } -html.File _base64ToFile(String data, String fileName) { +web.Blob _base64ToBlob(String data) { final List arr = data.split(','); - final String bstr = html.window.atob(arr[1]); + final String bstr = web.window.atob(arr[1]); int n = bstr.length; final Uint8List u8arr = Uint8List(n); @@ -133,5 +136,5 @@ html.File _base64ToFile(String data, String fileName) { n--; } - return html.File([u8arr], fileName); + return Blob([u8arr.toJS].toJS); } diff --git a/packages/image_picker/image_picker_for_web/example/pubspec.yaml b/packages/image_picker/image_picker_for_web/example/pubspec.yaml index 2efe50fa6375..f4f85645c58d 100644 --- a/packages/image_picker/image_picker_for_web/example/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/example/pubspec.yaml @@ -2,8 +2,8 @@ name: image_picker_for_web_integration_tests publish_to: none environment: - sdk: ^3.1.0 - flutter: ">=3.13.0" + sdk: ^3.3.0 + flutter: ">=3.19.0" dependencies: flutter: @@ -11,6 +11,7 @@ dependencies: image_picker_for_web: path: ../ image_picker_platform_interface: ^2.8.0 + web: ^0.5.1 dev_dependencies: flutter_test: diff --git a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart index 315b7ddd4469..0241e0750a75 100644 --- a/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart +++ b/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart @@ -3,14 +3,16 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:html' as html; +import 'dart:js_interop'; import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; import 'package:mime/mime.dart' as mime; +import 'package:web/web.dart' as web; import 'src/image_resizer.dart'; +import 'src/pkg_web_tweaks.dart'; const String _kImagePickerInputsDomId = '__image_picker_web-file-input'; const String _kAcceptImageMimeType = 'image/*'; @@ -33,7 +35,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { bool get _hasOverrides => _overrides != null; - late html.Element _target; + late web.Element _target; late ImageResizer _imageResizer; @@ -151,14 +153,16 @@ class ImagePickerPlugin extends ImagePickerPlatform { String? capture, bool multiple = false, }) { - final html.FileUploadInputElement input = createInputElement( + final web.HTMLInputElement input = createInputElement( accept, capture, multiple: multiple, - ) as html.FileUploadInputElement; + ); _injectAndActivate(input); - return _getSelectedXFiles(input).whenComplete(input.remove); + return _getSelectedXFiles(input).whenComplete(() { + input.remove(); + }); } // Deprecated methods follow... @@ -226,51 +230,52 @@ class ImagePickerPlugin extends ImagePickerPlatform { return null; } - List? _getFilesFromInput(html.FileUploadInputElement input) { + List? _getFilesFromInput(web.HTMLInputElement input) { if (_hasOverrides) { return _overrides!.getMultipleFilesFromInput(input); } - return input.files; + return input.files?.toList; } /// Handles the OnChange event from a FileUploadInputElement object /// Returns a list of selected files. - List? _handleOnChangeEvent(html.Event event) { - final html.FileUploadInputElement? input = - event.target as html.FileUploadInputElement?; + List? _handleOnChangeEvent(web.Event event) { + final web.HTMLInputElement? input = event.target as web.HTMLInputElement?; return input == null ? null : _getFilesFromInput(input); } /// Monitors an and returns the selected file(s). - Future> _getSelectedXFiles(html.FileUploadInputElement input) { + Future> _getSelectedXFiles(web.HTMLInputElement input) { final Completer> completer = Completer>(); + // TODO(dit): Migrate all this to Streams (onChange, onError, onCancel) when onCancel is available. + // See: https://github.com/dart-lang/web/issues/199 // Observe the input until we can return something - input.onChange.first.then((html.Event event) { - final List? files = _handleOnChangeEvent(event); + input.onchange = (web.Event event) { + final List? files = _handleOnChangeEvent(event); if (!completer.isCompleted && files != null) { - completer.complete(files.map((html.File file) { + completer.complete(files.map((web.File file) { return XFile( - html.Url.createObjectUrl(file), + web.URL.createObjectURL(file), name: file.name, length: file.size, lastModified: DateTime.fromMillisecondsSinceEpoch( - file.lastModified ?? DateTime.now().millisecondsSinceEpoch, + file.lastModified, ), mimeType: file.type, ); }).toList()); } - }); + }.toJS; - input.addEventListener('cancel', (html.Event _) { + input.oncancel = (web.Event _) { completer.complete([]); - }); + }.toJS; - input.onError.first.then((html.Event event) { + input.onerror = (web.Event event) { if (!completer.isCompleted) { completer.completeError(event); } - }); + }.toJS; // Note that we don't bother detaching from these streams, since the // "input" gets re-created in the DOM every time the user needs to // pick a file. @@ -278,13 +283,13 @@ class ImagePickerPlugin extends ImagePickerPlatform { } /// Initializes a DOM container where we can host input elements. - html.Element _ensureInitialized(String id) { - html.Element? target = html.querySelector('#$id'); + web.Element _ensureInitialized(String id) { + web.Element? target = web.document.querySelector('#$id'); if (target == null) { - final html.Element targetElement = - html.Element.tag('flt-image-picker-inputs')..id = id; - - html.querySelector('body')!.children.add(targetElement); + final web.Element targetElement = + web.document.createElement('flt-image-picker-inputs')..id = id; + // TODO(ditman): Append inside the `view` of the running app. + web.document.body!.append(targetElement); target = targetElement; } return target; @@ -293,7 +298,7 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// Creates an input element that accepts certain file types, and /// allows to `capture` from the device's cameras (where supported) @visibleForTesting - html.Element createInputElement( + web.HTMLInputElement createInputElement( String? accept, String? capture, { bool multiple = false, @@ -302,10 +307,14 @@ class ImagePickerPlugin extends ImagePickerPlatform { return _overrides!.createInputElement(accept, capture); } - final html.Element element = html.FileUploadInputElement() - ..accept = accept + final web.HTMLInputElement element = web.HTMLInputElement() + ..type = 'file' ..multiple = multiple; + if (accept != null) { + element.accept = accept; + } + if (capture != null) { element.setAttribute('capture', capture); } @@ -314,9 +323,9 @@ class ImagePickerPlugin extends ImagePickerPlatform { } /// Injects the file input element, and clicks on it - void _injectAndActivate(html.Element element) { - _target.children.clear(); - _target.children.add(element); + void _injectAndActivate(web.HTMLElement element) { + _target.replaceChildren([].toJS); + _target.append(element); // TODO(dit): Reimplement this with the showPicker() API, https://github.com/flutter/flutter/issues/130365 element.click(); } @@ -325,15 +334,15 @@ class ImagePickerPlugin extends ImagePickerPlatform { // Some tools to override behavior for unit-testing /// A function that creates a file input with the passed in `accept` and `capture` attributes. @visibleForTesting -typedef OverrideCreateInputFunction = html.Element Function( +typedef OverrideCreateInputFunction = web.HTMLInputElement Function( String? accept, String? capture, ); /// A function that extracts list of files from the file `input` passed in. @visibleForTesting -typedef OverrideExtractMultipleFilesFromInputFunction = List - Function(html.Element? input); +typedef OverrideExtractMultipleFilesFromInputFunction = List Function( + web.HTMLInputElement? input); /// Overrides for some of the functionality above. @visibleForTesting diff --git a/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart b/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart index 7cca935c6c91..7b32a451b67d 100644 --- a/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart +++ b/packages/image_picker/image_picker_for_web/lib/src/image_resizer.dart @@ -3,88 +3,107 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:html' as html; +import 'dart:js_interop'; import 'dart:math'; import 'dart:ui'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; +import 'package:web/helpers.dart'; +import 'package:web/web.dart' as web; import 'image_resizer_utils.dart'; /// Helper class that resizes images. class ImageResizer { /// Resizes the image if needed. + /// /// (Does not support gif images) - Future resizeImageIfNeeded(XFile file, double? maxWidth, - double? maxHeight, int? imageQuality) async { + Future resizeImageIfNeeded( + XFile file, + double? maxWidth, + double? maxHeight, + int? imageQuality, + ) async { if (!imageResizeNeeded(maxWidth, maxHeight, imageQuality) || file.mimeType == 'image/gif') { // Implement maxWidth and maxHeight for image/gif return file; } try { - final html.ImageElement imageElement = await loadImage(file.path); - final html.CanvasElement canvas = + final web.HTMLImageElement imageElement = await loadImage(file.path); + final web.HTMLCanvasElement canvas = resizeImageElement(imageElement, maxWidth, maxHeight); final XFile resizedImage = await writeCanvasToFile(file, canvas, imageQuality); - html.Url.revokeObjectUrl(file.path); + web.URL.revokeObjectURL(file.path); return resizedImage; } catch (e) { return file; } } - /// function that loads the blobUrl into an imageElement - Future loadImage(String blobUrl) { - final Completer imageLoadCompleter = - Completer(); - final html.ImageElement imageElement = html.ImageElement(); - // ignore: unsafe_html - imageElement.src = blobUrl; - - imageElement.onLoad.listen((html.Event event) { - imageLoadCompleter.complete(imageElement); - }); - imageElement.onError.listen((html.Event event) { - const String exception = 'Error while loading image.'; - imageElement.remove(); - imageLoadCompleter.completeError(exception); - }); + /// Loads the `blobUrl` into a [web.HTMLImageElement]. + Future loadImage(String blobUrl) { + final Completer imageLoadCompleter = + Completer(); + final web.HTMLImageElement imageElement = web.HTMLImageElement(); + imageElement + // ignore: unsafe_html + ..src = blobUrl + ..onLoad.listen((web.Event event) { + imageLoadCompleter.complete(imageElement); + }) + ..onError.listen((web.Event event) { + const String exception = 'Error while loading image.'; + imageElement.remove(); + imageLoadCompleter.completeError(exception); + }); return imageLoadCompleter.future; } - /// Draws image to a canvas while resizing the image to fit the [maxWidth],[maxHeight] constraints - html.CanvasElement resizeImageElement( - html.ImageElement source, double? maxWidth, double? maxHeight) { + /// Resizing the image in a canvas to fit the [maxWidth], [maxHeight] constraints. + web.HTMLCanvasElement resizeImageElement( + web.HTMLImageElement source, + double? maxWidth, + double? maxHeight, + ) { final Size newImageSize = calculateSizeOfDownScaledImage( - Size(source.width!.toDouble(), source.height!.toDouble()), + Size(source.width.toDouble(), source.height.toDouble()), maxWidth, maxHeight); - final html.CanvasElement canvas = html.CanvasElement(); - canvas.width = newImageSize.width.toInt(); - canvas.height = newImageSize.height.toInt(); - final html.CanvasRenderingContext2D context = canvas.context2D; + final web.HTMLCanvasElement canvas = web.HTMLCanvasElement() + ..width = newImageSize.width.toInt() + ..height = newImageSize.height.toInt(); + final web.CanvasRenderingContext2D context = canvas.context2D; if (maxHeight == null && maxWidth == null) { context.drawImage(source, 0, 0); } else { - context.drawImageScaled(source, 0, 0, canvas.width!, canvas.height!); + context.drawImageScaled( + source, 0, 0, canvas.width.toDouble(), canvas.height.toDouble()); } return canvas; } - /// function that converts a canvas element to Xfile + /// Converts a canvas element to [XFile]. + /// /// [imageQuality] is only supported for jpeg and webp images. Future writeCanvasToFile( - XFile originalFile, html.CanvasElement canvas, int? imageQuality) async { + XFile originalFile, + web.HTMLCanvasElement canvas, + int? imageQuality, + ) async { final double calculatedImageQuality = (min(imageQuality ?? 100, 100)) / 100.0; - final html.Blob blob = - await canvas.toBlob(originalFile.mimeType, calculatedImageQuality); - return XFile(html.Url.createObjectUrlFromBlob(blob), - mimeType: originalFile.mimeType, - name: 'scaled_${originalFile.name}', - lastModified: DateTime.now(), - length: blob.size); + final Completer completer = Completer(); + final web.BlobCallback blobCallback = (web.Blob blob) { + completer.complete(XFile(web.URL.createObjectURL(blob), + mimeType: originalFile.mimeType, + name: 'scaled_${originalFile.name}', + lastModified: DateTime.now(), + length: blob.size)); + }.toJS; + canvas.toBlob( + blobCallback, originalFile.mimeType ?? '', calculatedImageQuality.toJS); + return completer.future; } } diff --git a/packages/image_picker/image_picker_for_web/lib/src/pkg_web_tweaks.dart b/packages/image_picker/image_picker_for_web/lib/src/pkg_web_tweaks.dart new file mode 100644 index 000000000000..1152b01b2d4c --- /dev/null +++ b/packages/image_picker/image_picker_for_web/lib/src/pkg_web_tweaks.dart @@ -0,0 +1,14 @@ +// 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:web/web.dart' as web; + +/// Adds a `toList` method to [web.FileList] objects. +extension WebFileListToDartList on web.FileList { + /// Converts a [web.FileList] into a [List] of [web.File]. + /// + /// This method makes a copy. + List get toList => + [for (int i = 0; i < length; i++) item(i)!]; +} diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index b4394843b87e..85567a582094 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -2,11 +2,11 @@ name: image_picker_for_web description: Web platform implementation of image_picker repository: https://github.com/flutter/packages/tree/main/packages/image_picker/image_picker_for_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 3.0.2 +version: 3.0.3 environment: - sdk: ^3.1.0 - flutter: ">=3.13.0" + sdk: ^3.3.0 + flutter: ">=3.19.0" flutter: plugin: @@ -23,6 +23,7 @@ dependencies: sdk: flutter image_picker_platform_interface: ^2.9.0 mime: ^1.0.4 + web: ^0.5.1 dev_dependencies: flutter_test: From cc3f2a3a6028db55a123e970c35ab0a09685037d Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 6 Mar 2024 11:30:33 -0500 Subject: [PATCH 061/126] Roll Flutter from 3b5a2ecf992d to 8f84f3f32ddc (18 revisions) (#6276) https://github.com/flutter/flutter/compare/3b5a2ecf992d...8f84f3f32ddc 2024-03-06 engine-flutter-autoroll@skia.org Roll Packages from 2aa6e3f6bafe to 9b88dbc53992 (8 revisions) (flutter/flutter#144693) 2024-03-06 engine-flutter-autoroll@skia.org Roll Flutter Engine from 370e7d5866d9 to b6efe0dd88fe (1 revision) (flutter/flutter#144668) 2024-03-06 engine-flutter-autoroll@skia.org Roll Flutter Engine from d374c78bcf52 to 370e7d5866d9 (1 revision) (flutter/flutter#144661) 2024-03-06 engine-flutter-autoroll@skia.org Roll Flutter Engine from effcf97a1f7c to d374c78bcf52 (5 revisions) (flutter/flutter#144659) 2024-03-06 greg@zulip.com Fill in SliverConstraints fields missing from ==, hashCode, toString (flutter/flutter#143661) 2024-03-06 engine-flutter-autoroll@skia.org Roll Flutter Engine from 49bc1577f317 to effcf97a1f7c (10 revisions) (flutter/flutter#144653) 2024-03-05 49699333+dependabot[bot]@users.noreply.github.com Bump codecov/codecov-action from 4.0.2 to 4.1.0 (flutter/flutter#144174) 2024-03-05 15619084+vashworth@users.noreply.github.com Fix embedding FlutterMacOS.framework for macOS add2app via cocoapods (flutter/flutter#144248) 2024-03-05 andrewrkolos@gmail.com Enable asset transformation feature in hot reload workflow (excluding Web) (flutter/flutter#144161) 2024-03-05 engine-flutter-autoroll@skia.org Roll Flutter Engine from 3e8b0deffe4e to 49bc1577f317 (5 revisions) (flutter/flutter#144639) 2024-03-05 hans.muller@gmail.com Updated the smiley TextButton example again (flutter/flutter#144630) 2024-03-05 pateltirth454@gmail.com Adds missing `style` to `PopupMenuButton` (flutter/flutter#143392) 2024-03-05 engine-flutter-autoroll@skia.org Roll Flutter Engine from a7c785884903 to 3e8b0deffe4e (1 revision) (flutter/flutter#144629) 2024-03-05 goderbauer@google.com Add regression test for TabBar crash (flutter/flutter#144627) 2024-03-05 engine-flutter-autoroll@skia.org Roll Packages from 06258277070f to 2aa6e3f6bafe (5 revisions) (flutter/flutter#144628) 2024-03-05 andrewrkolos@gmail.com remove unused `firstBuildTime` parameter in `DevFS::update` (flutter/flutter#144576) 2024-03-05 engine-flutter-autoroll@skia.org Roll Flutter Engine from 8916bb32b7b8 to a7c785884903 (1 revision) (flutter/flutter#144624) 2024-03-05 goderbauer@google.com Revert "_DefaultTabControllerState should dispose all created TabContoller instances. (#136608)" (flutter/flutter#144579) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 1e02d9cc1b1c..db88c8b3da7e 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -3b5a2ecf992d2b6b2643bebb2621420eacfd4ef2 +8f84f3f32ddcd271b603f429fd0a4946ac0d7351 From ceb3dfd15fec44fde96dac772e202104a36bf144 Mon Sep 17 00:00:00 2001 From: Gabriel Terwesten Date: Wed, 6 Mar 2024 21:46:16 +0100 Subject: [PATCH 062/126] [in_app_purchase_storekit] Handle translation of errors nested in dictionaries (#6277) The native userInfo object can contain nested dictionaries. Previously this information was lost because `FIAObjectTranslator encodeNSErrorUserInfo` did not handle `NSDictionary`s. Fixes https://github.com/flutter/flutter/issues/138407 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [relevant style guides] and ran the auto-formatter. (Unlike the flutter/flutter repo, the flutter/packages repo does use `dart format`.) - [x] I signed the [CLA]. - [x] The title of the PR starts with the name of the package surrounded by square brackets, e.g. `[shared_preferences]` - [x] I [linked to at least one issue that this PR fixes] in the description above. - [x] I updated `pubspec.yaml` with an appropriate new version according to the [pub versioning philosophy], or this PR is [exempt from version changes]. - [x] I updated `CHANGELOG.md` to add a description of the change, [following repository CHANGELOG style]. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [Contributor Guide]: https://github.com/flutter/packages/blob/main/CONTRIBUTING.md [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [relevant style guides]: https://github.com/flutter/packages/blob/main/CONTRIBUTING.md#style [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat [linked to at least one issue that this PR fixes]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [pub versioning philosophy]: https://dart.dev/tools/pub/versioning [exempt from version changes]: https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#version-and-changelog-updates [following repository CHANGELOG style]: https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changelog-style [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests --- .../in_app_purchase_storekit/CHANGELOG.md | 4 ++++ .../darwin/Classes/FIAObjectTranslator.m | 17 ++++++++++------ .../shared/RunnerTests/TranslatorTests.m | 20 +++++++++++++++++++ .../in_app_purchase_storekit/pubspec.yaml | 2 +- 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md index 8f0f7839f709..fce36eb13a6b 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.13+1 + +* Handle translation of errors nested in dictionaries. + ## 0.3.13 * Added new native tests for more complete test coverage. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m index b8c6a269b8ce..b9509eb4bee9 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m @@ -166,12 +166,11 @@ + (NSDictionary *)getMapFromNSError:(NSError *)error { return nil; } - NSMutableDictionary *userInfo = [NSMutableDictionary new]; - for (NSErrorUserInfoKey key in error.userInfo) { - id value = error.userInfo[key]; - userInfo[key] = [FIAObjectTranslator encodeNSErrorUserInfo:value]; - } - return @{@"code" : @(error.code), @"domain" : error.domain ?: @"", @"userInfo" : userInfo}; + return @{ + @"code" : @(error.code), + @"domain" : error.domain ?: @"", + @"userInfo" : [FIAObjectTranslator encodeNSErrorUserInfo:error.userInfo] + }; } + (id)encodeNSErrorUserInfo:(id)value { @@ -189,6 +188,12 @@ + (id)encodeNSErrorUserInfo:(id)value { [errors addObject:[FIAObjectTranslator encodeNSErrorUserInfo:error]]; } return errors; + } else if ([value isKindOfClass:[NSDictionary class]]) { + NSMutableDictionary *errors = [[NSMutableDictionary alloc] init]; + for (id key in value) { + errors[key] = [FIAObjectTranslator encodeNSErrorUserInfo:value[key]]; + } + return errors; } else { return [NSString stringWithFormat: diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/TranslatorTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/TranslatorTests.m index 0060051dad6a..7ffe4c6ac7ff 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/TranslatorTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/TranslatorTests.m @@ -191,6 +191,26 @@ - (void)testErrorWithMultipleUnderlyingErrors { XCTAssertEqualObjects(expectedMap, map); } +- (void)testErrorWithNestedUnderlyingError { + NSError *underlyingError = [NSError errorWithDomain:SKErrorDomain code:2 userInfo:nil]; + NSError *mainError = + [NSError errorWithDomain:SKErrorDomain + code:3 + userInfo:@{@"nesting" : @{@"underlyingError" : underlyingError}}]; + NSDictionary *expectedMap = @{ + @"domain" : SKErrorDomain, + @"code" : @3, + @"userInfo" : @{ + @"nesting" : @{ + @"underlyingError" : @{@"domain" : SKErrorDomain, @"code" : @2, @"userInfo" : @{}}, + + } + } + }; + NSDictionary *map = [FIAObjectTranslator getMapFromNSError:mainError]; + XCTAssertEqualObjects(expectedMap, map); +} + - (void)testErrorWithUnsupportedUserInfo { NSError *error = [NSError errorWithDomain:SKErrorDomain code:3 diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml index 852bd127fe17..08ba026a014a 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_storekit description: An implementation for the iOS and macOS platforms of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/packages/tree/main/packages/in_app_purchase/in_app_purchase_storekit issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.3.13 +version: 0.3.13+1 environment: sdk: ^3.2.3 From 6701c9e618041574cf4cc53cb028a38de8322b53 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Wed, 6 Mar 2024 13:02:06 -0800 Subject: [PATCH 063/126] [rfw] Add support for widget builders (#5907) This PR adds support for widget builders. https://github.com/flutter/flutter/issues/141658 --- packages/rfw/CHANGELOG.md | 3 + packages/rfw/lib/src/dart/binary.dart | 25 ++ packages/rfw/lib/src/dart/model.dart | 47 +++ packages/rfw/lib/src/dart/text.dart | 160 +++++-- packages/rfw/lib/src/flutter/runtime.dart | 491 +++++++++++++++++++--- packages/rfw/pubspec.yaml | 2 +- packages/rfw/test/binary_test.dart | 37 ++ packages/rfw/test/runtime_test.dart | 466 ++++++++++++++++++++ packages/rfw/test/text_test.dart | 141 +++++++ 9 files changed, 1285 insertions(+), 87 deletions(-) diff --git a/packages/rfw/CHANGELOG.md b/packages/rfw/CHANGELOG.md index 5b2b6cbbfd61..b34abdb06489 100644 --- a/packages/rfw/CHANGELOG.md +++ b/packages/rfw/CHANGELOG.md @@ -1,3 +1,6 @@ +## 1.0.25 +* Adds support for wildget builders. + ## 1.0.24 * Adds `InkResponse` material widget. diff --git a/packages/rfw/lib/src/dart/binary.dart b/packages/rfw/lib/src/dart/binary.dart index f36690f2c7d0..cd999377f207 100644 --- a/packages/rfw/lib/src/dart/binary.dart +++ b/packages/rfw/lib/src/dart/binary.dart @@ -298,6 +298,8 @@ const int _msEvent = 0x0E; const int _msSwitch = 0x0F; const int _msDefault = 0x10; const int _msSetState = 0x11; +const int _msWidgetBuilder = 0x12; +const int _msWidgetBuilderArgReference = 0x13; /// API for decoding Remote Flutter Widgets binary blobs. /// @@ -453,6 +455,10 @@ class _BlobDecoder { return _readSwitch(); case _msSetState: return SetStateHandler(StateReference(_readPartList()), _readArgument()); + case _msWidgetBuilder: + return _readWidgetBuilder(); + case _msWidgetBuilderArgReference: + return WidgetBuilderArgReference(_readString(), _readPartList()); default: return _parseValue(type, _readArgument); } @@ -468,6 +474,16 @@ class _BlobDecoder { return ConstructorCall(name, _readMap(_readArgument)!); } + WidgetBuilderDeclaration _readWidgetBuilder() { + final String argumentName = _readString(); + final int type = _readByte(); + if (type != _msWidget && type != _msSwitch) { + throw FormatException('Unrecognized data type 0x${type.toRadixString(16).toUpperCase().padLeft(2, "0")} while decoding widget builder blob.'); + } + final BlobNode widget = type == _msWidget ? _readWidget() : _readSwitch(); + return WidgetBuilderDeclaration(argumentName, widget); + } + WidgetDeclaration _readDeclaration() { final String name = _readString(); final DynamicMap? initialState = _readMap(readValue, nullIfEmpty: true); @@ -613,6 +629,10 @@ class _BlobEncoder { bytes.addByte(_msWidget); _writeString(value.name); _writeMap(value.arguments, _writeArgument); + } else if (value is WidgetBuilderDeclaration) { + bytes.addByte(_msWidgetBuilder); + _writeString(value.argumentName); + _writeArgument(value.widget); } else if (value is ArgsReference) { bytes.addByte(_msArgsReference); _writeInt64(value.parts.length); @@ -621,6 +641,11 @@ class _BlobEncoder { bytes.addByte(_msDataReference); _writeInt64(value.parts.length); value.parts.forEach(_writePart); + } else if (value is WidgetBuilderArgReference) { + bytes.addByte(_msWidgetBuilderArgReference); + _writeString(value.argumentName); + _writeInt64(value.parts.length); + value.parts.forEach(_writePart); } else if (value is LoopReference) { bytes.addByte(_msLoopReference); _writeInt64(value.loop); diff --git a/packages/rfw/lib/src/dart/model.dart b/packages/rfw/lib/src/dart/model.dart index b6a6b9921263..22be39c3acb7 100644 --- a/packages/rfw/lib/src/dart/model.dart +++ b/packages/rfw/lib/src/dart/model.dart @@ -439,6 +439,28 @@ class ConstructorCall extends BlobNode { String toString() => '$name($arguments)'; } +/// Representation of functions that return widgets in Remote Flutter Widgets library blobs. +class WidgetBuilderDeclaration extends BlobNode { + /// Represents a callback that takes a single argument [argumentName] and returns the [widget]. + const WidgetBuilderDeclaration(this.argumentName, this.widget); + + /// The callback single argument name. + /// + /// In `Builder(builder: (scope) => Container());`, [argumentName] is "scope". + final String argumentName; + + /// The widget that will be returned when the builder callback is called. + /// + /// This is usually a [ConstructorCall], but may be a [Switch] (so long as + /// that [Switch] resolves to a [ConstructorCall]. Other values (or a [Switch] + /// that does not resolve to a constructor call) will result in an + /// [ErrorWidget] being used. + final BlobNode widget; + + @override + String toString() => '($argumentName) => $widget'; +} + /// Base class for various kinds of references in the RFW data structures. abstract class Reference extends BlobNode { /// Abstract const constructor. This constructor enables subclasses to provide @@ -534,6 +556,31 @@ class DataReference extends Reference { String toString() => 'data.${parts.join(".")}'; } +/// Reference to the single argument of type [DynamicMap] passed into the widget builder. +/// +/// This class is used to represent references to a function argument. +/// In `(scope) => Container(width: scope.width)`, this represents "scope.width". +/// +/// See also: +/// +/// * [WidgetBuilderDeclaration], which represents a widget builder definition. +class WidgetBuilderArgReference extends Reference { + /// Wraps the given [argumentName] and [parts] as a [WidgetBuilderArgReference]. + /// + /// The parts must not be mutated after the object is created. + const WidgetBuilderArgReference(this.argumentName, super.parts); + + /// A reference to a [WidgetBuilderDeclaration.argumentName]. + /// + /// In `Builder(builder: (scope) => Text(text: scope.result.text));`, + /// "scope.result.text" is the [WidgetBuilderArgReference]. + /// The [argumentName] is "scope" and its [parts] are `["result", "text"]`. + final String argumentName; + + @override + String toString() => '$argumentName.${parts.join('.')}'; +} + /// Unbound reference to a [Loop]. class LoopReference extends Reference { /// Wraps the given [loop] and [parts] as a [LoopReference]. diff --git a/packages/rfw/lib/src/dart/text.dart b/packages/rfw/lib/src/dart/text.dart index 88e0843ef3dd..4db42dcfbe0d 100644 --- a/packages/rfw/lib/src/dart/text.dart +++ b/packages/rfw/lib/src/dart/text.dart @@ -272,8 +272,8 @@ DynamicMap parseDataFile(String file) { /// declaration, along with its arguments. Arguments are a map of key-value /// pairs, where the values can be any of the types in the data model defined /// above plus any of the types defined below in this section, such as -/// references to arguments, the data model, loops, state, switches, or -/// event handlers. +/// references to arguments, the data model, widget builders, loops, state, +/// switches or event handlers. /// /// In this example, several constructor calls are nested together: /// @@ -283,6 +283,9 @@ DynamicMap parseDataFile(String file) { /// Container( /// child: Text(text: "Hello"), /// ), +/// Builder( +/// builder: (scope) => Text(text: scope.world), +/// ), /// ], /// ); /// ``` @@ -293,6 +296,35 @@ DynamicMap parseDataFile(String file) { /// constructor call also has only one argument, `child`, whose value, again, is /// a constructor call, in this case creating a `Text` widget. /// +/// ### Widget Builders +/// +/// Widget builders take a single argument and return a widget. +/// The [DynamicMap] argument consists of key-value pairs where values +/// can be of any types in the data model. Widget builders arguments are lexically +/// scoped so a given constructor call has access to any arguments where it is +/// defined plus arguments defined by its parents (if any). +/// +/// In this example several widget builders are nested together: +/// +/// ``` +/// widget Foo {text: 'this is cool'} = Builder( +/// builder: (foo) => Builder( +/// builder: (bar) => Builder( +/// builder: (baz) => Text( +/// text: [ +/// args.text, +/// state.text, +/// data.text, +/// foo.text, +/// bar.text, +/// baz.text, +/// ], +/// ), +/// ), +/// ), +/// ); +/// ``` +/// /// ### References /// /// Remote widget libraries typically contain _references_, e.g. to the @@ -610,6 +642,12 @@ const Set _reservedWords = { 'true', }; +void _checkIsNotReservedWord(String identifier, _Token identifierToken) { + if (_reservedWords.contains(identifier)) { + throw ParserException._fromToken('$identifier is a reserved word', identifierToken); + } +} + sealed class _Token { _Token(this.line, this.column, this.start, this.end); final int line; @@ -630,6 +668,7 @@ class _SymbolToken extends _Token { static const int colon = 0x3A; // U+003A COLON character (:) static const int semicolon = 0x3B; // U+003B SEMICOLON character (;) static const int equals = 0x3D; // U+003D EQUALS SIGN character (=) + static const int greatherThan = 0x3E; // U+003D GREATHER THAN character (>) static const int openBracket = 0x5B; // U+005B LEFT SQUARE BRACKET character ([) static const int closeBracket = 0x5D; // U+005D RIGHT SQUARE BRACKET character (]) static const int openBrace = 0x7B; // U+007B LEFT CURLY BRACKET character ({) @@ -812,6 +851,7 @@ Iterable<_Token> _tokenize(String file) sync* { case 0x3A: // U+003A COLON character (:) case 0x3B: // U+003B SEMICOLON character (;) case 0x3D: // U+003D EQUALS SIGN character (=) + case 0x3E: // U+003E GREATHER THAN SIGN character (>) case 0x5B: // U+005B LEFT SQUARE BRACKET character ([) case 0x5D: // U+005D RIGHT SQUARE BRACKET character (]) case 0x7B: // U+007B LEFT CURLY BRACKET character ({) @@ -2132,14 +2172,23 @@ class _Parser { return _readString(); } - DynamicMap _readMap({ required bool extended }) { + DynamicMap _readMap({ + required bool extended, + List widgetBuilderScope = const [], + }) { _expectSymbol(_SymbolToken.openBrace); - final DynamicMap results = _readMapBody(extended: extended); + final DynamicMap results = _readMapBody( + widgetBuilderScope: widgetBuilderScope, + extended: extended, + ); _expectSymbol(_SymbolToken.closeBrace); return results; } - DynamicMap _readMapBody({ required bool extended }) { + DynamicMap _readMapBody({ + required bool extended, + List widgetBuilderScope = const [], + }) { final DynamicMap results = DynamicMap(); // ignore: prefer_collection_literals while (_source.current is! _SymbolToken) { final String key = _readKey(); @@ -2147,7 +2196,11 @@ class _Parser { throw ParserException._fromToken('Duplicate key "$key" in map', _source.current); } _expectSymbol(_SymbolToken.colon); - final Object value = _readValue(extended: extended, nullOk: true); + final Object value = _readValue( + extended: extended, + nullOk: true, + widgetBuilderScope: widgetBuilderScope, + ); if (value != missing) { results[key] = value; } @@ -2162,7 +2215,10 @@ class _Parser { final List _loopIdentifiers = []; - DynamicList _readList({ required bool extended }) { + DynamicList _readList({ + required bool extended, + List widgetBuilderScope = const [], + }) { final DynamicList results = DynamicList.empty(growable: true); _expectSymbol(_SymbolToken.openBracket); while (!_foundSymbol(_SymbolToken.closeBracket)) { @@ -2172,19 +2228,26 @@ class _Parser { _expectIdentifier('for'); final _Token loopIdentifierToken = _source.current; final String loopIdentifier = _readIdentifier(); - if (_reservedWords.contains(loopIdentifier)) { - throw ParserException._fromToken('$loopIdentifier is a reserved word', loopIdentifierToken); - } + _checkIsNotReservedWord(loopIdentifier, loopIdentifierToken); _expectIdentifier('in'); - final Object collection = _readValue(extended: true); + final Object collection = _readValue( + widgetBuilderScope: widgetBuilderScope, + extended: true, + ); _expectSymbol(_SymbolToken.colon); _loopIdentifiers.add(loopIdentifier); - final Object template = _readValue(extended: extended); + final Object template = _readValue( + widgetBuilderScope: widgetBuilderScope, + extended: extended, + ); assert(_loopIdentifiers.last == loopIdentifier); _loopIdentifiers.removeLast(); results.add(_withSourceRange(Loop(collection, template), start)); } else { - final Object value = _readValue(extended: extended); + final Object value = _readValue( + widgetBuilderScope: widgetBuilderScope, + extended: extended, + ); results.add(value); } if (_foundSymbol(_SymbolToken.comma)) { @@ -2197,8 +2260,10 @@ class _Parser { return results; } - Switch _readSwitch(SourceLocation? start) { - final Object value = _readValue(extended: true); + Switch _readSwitch(SourceLocation? start, { + List widgetBuilderScope = const [], + }) { + final Object value = _readValue(extended: true, widgetBuilderScope: widgetBuilderScope); final Map cases = {}; _expectSymbol(_SymbolToken.openBrace); while (_source.current is! _SymbolToken) { @@ -2210,13 +2275,13 @@ class _Parser { key = null; _advance(); } else { - key = _readValue(extended: true); + key = _readValue(extended: true, widgetBuilderScope: widgetBuilderScope); if (cases.containsKey(key)) { throw ParserException._fromToken('Switch has duplicate cases for key $key', _source.current); } } _expectSymbol(_SymbolToken.colon); - final Object value = _readValue(extended: true); + final Object value = _readValue(extended: true, widgetBuilderScope: widgetBuilderScope); cases[key] = value; if (_foundSymbol(_SymbolToken.comma)) { _advance(); @@ -2249,13 +2314,19 @@ class _Parser { return results; } - Object _readValue({ required bool extended, bool nullOk = false }) { + Object _readValue({ + required bool extended, + bool nullOk = false, + List widgetBuilderScope = const [], + }) { if (_source.current is _SymbolToken) { switch ((_source.current as _SymbolToken).symbol) { case _SymbolToken.openBracket: - return _readList(extended: extended); + return _readList(widgetBuilderScope: widgetBuilderScope, extended: extended); case _SymbolToken.openBrace: - return _readMap(extended: extended); + return _readMap(widgetBuilderScope: widgetBuilderScope, extended: extended); + case _SymbolToken.openParen: + return _readWidgetBuilderDeclaration(widgetBuilderScope: widgetBuilderScope); } } else if (_source.current is _IntegerToken) { final Object result = (_source.current as _IntegerToken).value; @@ -2289,7 +2360,13 @@ class _Parser { if (identifier == 'event') { final SourceLocation? start = _getSourceLocation(); _advance(); - return _withSourceRange(EventHandler(_readString(), _readMap(extended: true)), start); + return _withSourceRange( + EventHandler( + _readString(), + _readMap(widgetBuilderScope: widgetBuilderScope, extended: true), + ), + start, + ); } if (identifier == 'args') { final SourceLocation? start = _getSourceLocation(); @@ -2309,7 +2386,7 @@ class _Parser { if (identifier == 'switch') { final SourceLocation? start = _getSourceLocation(); _advance(); - return _readSwitch(start); + return _readSwitch(start, widgetBuilderScope: widgetBuilderScope); } if (identifier == 'set') { final SourceLocation? start = _getSourceLocation(); @@ -2318,25 +2395,56 @@ class _Parser { _expectIdentifier('state'); final StateReference stateReference = _withSourceRange(StateReference(_readParts()), innerStart); _expectSymbol(_SymbolToken.equals); - final Object value = _readValue(extended: true); + final Object value = _readValue(widgetBuilderScope: widgetBuilderScope, extended: true); return _withSourceRange(SetStateHandler(stateReference, value), start); } + if (widgetBuilderScope.contains(identifier)) { + final SourceLocation? start = _getSourceLocation(); + _advance(); + return _withSourceRange(WidgetBuilderArgReference(identifier, _readParts()), start); + } final int index = _loopIdentifiers.lastIndexOf(identifier) + 1; if (index > 0) { final SourceLocation? start = _getSourceLocation(); _advance(); return _withSourceRange(LoopReference(_loopIdentifiers.length - index, _readParts(optional: true)), start); } - return _readConstructorCall(); + return _readConstructorCall(widgetBuilderScope: widgetBuilderScope); } throw ParserException._unexpected(_source.current); } - ConstructorCall _readConstructorCall() { + WidgetBuilderDeclaration _readWidgetBuilderDeclaration({ + List widgetBuilderScope = const [], + }) { + _expectSymbol(_SymbolToken.openParen); + final _Token argumentNameToken = _source.current; + final String argumentName = _readIdentifier(); + _checkIsNotReservedWord(argumentName, argumentNameToken); + _expectSymbol(_SymbolToken.closeParen); + _expectSymbol(_SymbolToken.equals); + _expectSymbol(_SymbolToken.greatherThan); + final _Token valueToken = _source.current; + final Object widget = _readValue( + extended: true, + widgetBuilderScope: [...widgetBuilderScope, argumentName], + ); + if (widget is! ConstructorCall && widget is! Switch) { + throw ParserException._fromToken('Expecting a switch or constructor call got $widget', valueToken); + } + return WidgetBuilderDeclaration(argumentName, widget as BlobNode); + } + + ConstructorCall _readConstructorCall({ + List widgetBuilderScope = const [], + }) { final SourceLocation? start = _getSourceLocation(); final String name = _readIdentifier(); _expectSymbol(_SymbolToken.openParen); - final DynamicMap arguments = _readMapBody(extended: true); + final DynamicMap arguments = _readMapBody( + extended: true, + widgetBuilderScope: widgetBuilderScope, + ); _expectSymbol(_SymbolToken.closeParen); return _withSourceRange(ConstructorCall(name, arguments), start); } diff --git a/packages/rfw/lib/src/flutter/runtime.dart b/packages/rfw/lib/src/flutter/runtime.dart index 7d874e78e8b2..f66b7cd22547 100644 --- a/packages/rfw/lib/src/flutter/runtime.dart +++ b/packages/rfw/lib/src/flutter/runtime.dart @@ -19,6 +19,9 @@ import 'content.dart'; /// [LocalWidgetBuilder] callbacks. typedef LocalWidgetBuilder = Widget Function(BuildContext context, DataSource source); +/// Signature of builders for remote widgets. +typedef _RemoteWidgetBuilder = _CurriedWidget Function(DynamicMap builderArg); + /// Signature of the callback passed to a [RemoteWidget]. /// /// This is used by [RemoteWidget] and [Runtime.build] as the callback for @@ -126,6 +129,25 @@ abstract class DataSource { /// non-widget nodes replaced by [ErrorWidget]. List childList(List argsKey); + /// Builds the widget builder at the given key. + /// + /// If the node is not a widget builder, returns an [ErrorWidget]. + /// + /// See also: + /// + /// * [optionalBuilder], which returns null if the widget builder is missing. + Widget builder(List argsKey, DynamicMap builderArg); + + /// Builds the widget builder at the given key. + /// + /// If the node is not a widget builder, returns null. + /// + /// See also: + /// + /// * [builder], which returns an [ErrorWidget] instead of null if the widget + /// builder is missing. + Widget? optionalBuilder(List argsKey, DynamicMap builderArg); + /// Gets a [VoidCallback] event handler at the given key. /// /// If the node specified is an [AnyEventHandler] or a [DynamicList] of @@ -284,11 +306,23 @@ class Runtime extends ChangeNotifier { /// /// The `remoteEventTarget` argument is the callback that the RFW runtime will /// invoke whenever a remote widget event handler is triggered. - Widget build(BuildContext context, FullyQualifiedWidgetName widget, DynamicContent data, RemoteEventHandler remoteEventTarget) { + Widget build( + BuildContext context, + FullyQualifiedWidgetName widget, + DynamicContent data, + RemoteEventHandler remoteEventTarget, + ) { _CurriedWidget? boundWidget = _widgets[widget]; if (boundWidget == null) { _checkForImportLoops(widget.library); - boundWidget = _applyConstructorAndBindArguments(widget, const {}, -1, {}, null); + boundWidget = _applyConstructorAndBindArguments( + widget, + const {}, + const {}, + -1, + {}, + null, + ); _widgets[widget] = boundWidget; } return boundWidget.build(context, data, remoteEventTarget, const <_WidgetState>[]); @@ -410,13 +444,22 @@ class Runtime extends ChangeNotifier { /// [LocalWidgetBuilder] rather than a [WidgetDeclaration], and is used to /// provide source information for local widgets (which otherwise could not be /// associated with a part of the source). See also [Runtime.blobNodeFor]. - _CurriedWidget _applyConstructorAndBindArguments(FullyQualifiedWidgetName fullName, DynamicMap arguments, int stateDepth, Set usedWidgets, BlobNode? source) { + _CurriedWidget _applyConstructorAndBindArguments( + FullyQualifiedWidgetName fullName, + DynamicMap arguments, + DynamicMap widgetBuilderScope, + int stateDepth, + Set usedWidgets, + BlobNode? source, + ) { final _ResolvedConstructor? widget = _findConstructor(fullName); if (widget != null) { if (widget.constructor is WidgetDeclaration) { if (usedWidgets.contains(widget.fullName)) { - return _CurriedLocalWidget.error(fullName, 'Widget loop: Tried to call ${widget.fullName} constructor reentrantly.') - ..propagateSource(source); + return _CurriedLocalWidget.error( + fullName, + 'Widget loop: Tried to call ${widget.fullName} constructor reentrantly.', + )..propagateSource(source); } usedWidgets = usedWidgets.toSet()..add(widget.fullName); final WidgetDeclaration constructor = widget.constructor as WidgetDeclaration; @@ -426,22 +469,43 @@ class Runtime extends ChangeNotifier { } else { newDepth = stateDepth; } - Object result = _bindArguments(widget.fullName, constructor.root, arguments, newDepth, usedWidgets); + Object result = _bindArguments( + widget.fullName, + constructor.root, + arguments, + widgetBuilderScope, + newDepth, + usedWidgets, + ); if (result is Switch) { - result = _CurriedSwitch(widget.fullName, result, arguments, constructor.initialState) - ..propagateSource(result); + result = _CurriedSwitch( + widget.fullName, + result, + arguments, + widgetBuilderScope, + constructor.initialState, + )..propagateSource(result); } else { result as _CurriedWidget; if (constructor.initialState != null) { - result = _CurriedRemoteWidget(widget.fullName, result, arguments, constructor.initialState) - ..propagateSource(result); + result = _CurriedRemoteWidget( + widget.fullName, + result, + arguments, + widgetBuilderScope, + constructor.initialState, + )..propagateSource(result); } } return result as _CurriedWidget; } assert(widget.constructor is LocalWidgetBuilder); - return _CurriedLocalWidget(widget.fullName, widget.constructor as LocalWidgetBuilder, arguments) - ..propagateSource(source); + return _CurriedLocalWidget( + widget.fullName, + widget.constructor as LocalWidgetBuilder, + arguments, + widgetBuilderScope, + )..propagateSource(source); } final Set missingLibraries = _findMissingLibraries(fullName.library).toSet(); if (missingLibraries.isNotEmpty) { @@ -455,37 +519,93 @@ class Runtime extends ChangeNotifier { ..propagateSource(source); } - Object _bindArguments(FullyQualifiedWidgetName context, Object node, Object arguments, int stateDepth, Set usedWidgets) { + Object _bindArguments( + FullyQualifiedWidgetName context, + Object node, Object arguments, + DynamicMap widgetBuilderScope, + int stateDepth, + Set usedWidgets, + ) { if (node is ConstructorCall) { - final DynamicMap subArguments = _bindArguments(context, node.arguments, arguments, stateDepth, usedWidgets) as DynamicMap; - return _applyConstructorAndBindArguments(FullyQualifiedWidgetName(context.library, node.name), subArguments, stateDepth, usedWidgets, node); + final DynamicMap subArguments = _bindArguments( + context, + node.arguments, + arguments, + widgetBuilderScope, + stateDepth, + usedWidgets, + ) as DynamicMap; + return _applyConstructorAndBindArguments( + FullyQualifiedWidgetName(context.library, node.name), + subArguments, + widgetBuilderScope, + stateDepth, + usedWidgets, + node, + ); } + if (node is WidgetBuilderDeclaration) { + return (DynamicMap widgetBuilderArg) { + final DynamicMap newWidgetBuilderScope = { + ...widgetBuilderScope, + node.argumentName: widgetBuilderArg, + }; + final Object result = _bindArguments( + context, + node.widget, + arguments, + newWidgetBuilderScope, + stateDepth, + usedWidgets, + ); + if (result is Switch) { + return _CurriedSwitch( + FullyQualifiedWidgetName(context.library, ''), + result, + arguments as DynamicMap, + newWidgetBuilderScope, + const {}, + )..propagateSource(result); + } + return result as _CurriedWidget; + }; + } if (node is DynamicMap) { return node.map( - (String name, Object? value) => MapEntry(name, _bindArguments(context, value!, arguments, stateDepth, usedWidgets)), + (String name, Object? value) => MapEntry( + name, + _bindArguments(context, value!, arguments, widgetBuilderScope, stateDepth, usedWidgets), + ), ); } if (node is DynamicList) { return List.generate( node.length, - (int index) => _bindArguments(context, node[index]!, arguments, stateDepth, usedWidgets), + (int index) => _bindArguments( + context, + node[index]!, + arguments, + widgetBuilderScope, + stateDepth, + usedWidgets, + ), growable: false, ); } if (node is Loop) { - final Object input = _bindArguments(context, node.input, arguments, stateDepth, usedWidgets); - final Object output = _bindArguments(context, node.output, arguments, stateDepth, usedWidgets); + final Object input = _bindArguments(context, node.input, arguments, widgetBuilderScope, stateDepth, usedWidgets); + final Object output = _bindArguments(context, node.output, arguments, widgetBuilderScope, stateDepth, usedWidgets); return Loop(input, output) ..propagateSource(node); } if (node is Switch) { return Switch( - _bindArguments(context, node.input, arguments, stateDepth, usedWidgets), + _bindArguments(context, node.input, arguments, widgetBuilderScope, stateDepth, usedWidgets), node.outputs.map( (Object? key, Object value) { return MapEntry( - key == null ? key : _bindArguments(context, key, arguments, stateDepth, usedWidgets), - _bindArguments(context, value, arguments, stateDepth, usedWidgets), + key == null ? key : _bindArguments(context, key, arguments, widgetBuilderScope, stateDepth, usedWidgets), + _bindArguments(context, value, arguments, widgetBuilderScope, stateDepth, usedWidgets), ); }, ), @@ -498,14 +618,25 @@ class Runtime extends ChangeNotifier { return node.bind(stateDepth)..propagateSource(node); } if (node is EventHandler) { - return EventHandler(node.eventName, _bindArguments(context, node.eventArguments, arguments, stateDepth, usedWidgets) as DynamicMap) - ..propagateSource(node); + return EventHandler( + node.eventName, + _bindArguments( + context, + node.eventArguments, + arguments, + widgetBuilderScope, + stateDepth, + usedWidgets, + ) as DynamicMap, + )..propagateSource(node); } if (node is SetStateHandler) { assert(node.stateReference is StateReference); final BoundStateReference stateReference = (node.stateReference as StateReference).bind(stateDepth); - return SetStateHandler(stateReference, _bindArguments(context, node.value, arguments, stateDepth, usedWidgets)) - ..propagateSource(node); + return SetStateHandler( + stateReference, + _bindArguments(context, node.value, arguments, widgetBuilderScope, stateDepth, usedWidgets), + )..propagateSource(node); } assert(node is! WidgetDeclaration); return node; @@ -528,12 +659,19 @@ class _ResolvedDynamicList { typedef _DataResolverCallback = Object Function(List dataKey); typedef _StateResolverCallback = Object Function(List stateKey, int depth); +typedef _WidgetBuilderArgResolverCallback = Object Function(List argKey); abstract class _CurriedWidget extends BlobNode { - const _CurriedWidget(this.fullName, this.arguments, this.initialState); + const _CurriedWidget( + this.fullName, + this.arguments, + this.widgetBuilderScope, + this.initialState, + ); final FullyQualifiedWidgetName fullName; final DynamicMap arguments; + final DynamicMap widgetBuilderScope; final DynamicMap? initialState; static Object _bindLoopVariable(Object node, Object argument, int depth) { @@ -569,6 +707,7 @@ abstract class _CurriedWidget extends BlobNode { node.fullName, node.child, _bindLoopVariable(node.arguments, argument, depth) as DynamicMap, + _bindLoopVariable(node.widgetBuilderScope, argument, depth) as DynamicMap, )..propagateSource(node); } if (node is _CurriedRemoteWidget) { @@ -576,6 +715,7 @@ abstract class _CurriedWidget extends BlobNode { node.fullName, _bindLoopVariable(node.child, argument, depth) as _CurriedWidget, _bindLoopVariable(node.arguments, argument, depth) as DynamicMap, + _bindLoopVariable(node.widgetBuilderScope, argument, depth) as DynamicMap, node.initialState, )..propagateSource(node); } @@ -584,6 +724,7 @@ abstract class _CurriedWidget extends BlobNode { node.fullName, _bindLoopVariable(node.root, argument, depth) as Switch, _bindLoopVariable(node.arguments, argument, depth) as DynamicMap, + _bindLoopVariable(node.widgetBuilderScope, argument, depth) as DynamicMap, node.initialState, )..propagateSource(node); } @@ -615,7 +756,13 @@ abstract class _CurriedWidget extends BlobNode { // // TODO(ianh): This really should have some sort of caching. Right now, evaluating a whole list // ends up being around O(N^2) since we have to walk the list from the start for every entry. - static _ResolvedDynamicList _listLookup(DynamicList list, int targetEffectiveIndex, _StateResolverCallback stateResolver, _DataResolverCallback dataResolver) { + static _ResolvedDynamicList _listLookup( + DynamicList list, + int targetEffectiveIndex, + _StateResolverCallback stateResolver, + _DataResolverCallback dataResolver, + _WidgetBuilderArgResolverCallback widgetBuilderArgResolver, + ) { int currentIndex = 0; // where we are in `list` (some entries of which might represent multiple values, because they are themselves loops) int effectiveIndex = 0; // where we are in the fully expanded list (the coordinate space in which we're aiming for `targetEffectiveIndex`) while ((effectiveIndex <= targetEffectiveIndex || targetEffectiveIndex < 0) && currentIndex < list.length) { @@ -624,22 +771,46 @@ abstract class _CurriedWidget extends BlobNode { Object inputList = node.input; while (inputList is! DynamicList) { if (inputList is BoundArgsReference) { - inputList = _resolveFrom(inputList.arguments, inputList.parts, stateResolver, dataResolver); + inputList = _resolveFrom( + inputList.arguments, + inputList.parts, + stateResolver, + dataResolver, + widgetBuilderArgResolver, + ); } else if (inputList is DataReference) { inputList = dataResolver(inputList.parts); } else if (inputList is BoundStateReference) { inputList = stateResolver(inputList.parts, inputList.depth); } else if (inputList is BoundLoopReference) { - inputList = _resolveFrom(inputList.value, inputList.parts, stateResolver, dataResolver); + inputList = _resolveFrom( + inputList.value, + inputList.parts, + stateResolver, + dataResolver, + widgetBuilderArgResolver, + ); } else if (inputList is Switch) { - inputList = _resolveFrom(inputList, const [], stateResolver, dataResolver); + inputList = _resolveFrom( + inputList, + const [], + stateResolver, + dataResolver, + widgetBuilderArgResolver, + ); } else { // e.g. it's a map or something else that isn't indexable inputList = DynamicList.empty(); } assert(inputList is! _ResolvedDynamicList); } - final _ResolvedDynamicList entry = _listLookup(inputList, targetEffectiveIndex >= 0 ? targetEffectiveIndex - effectiveIndex : -1, stateResolver, dataResolver); + final _ResolvedDynamicList entry = _listLookup( + inputList, + targetEffectiveIndex >= 0 ? targetEffectiveIndex - effectiveIndex : -1, + stateResolver, + dataResolver, + widgetBuilderArgResolver, + ); if (entry.result != null) { final Object boundResult = _bindLoopVariable(node.output, entry.result!, 0); return _ResolvedDynamicList(null, boundResult, null); @@ -656,7 +827,13 @@ abstract class _CurriedWidget extends BlobNode { return _ResolvedDynamicList(list, null, effectiveIndex); } - static Object _resolveFrom(Object root, List parts, _StateResolverCallback stateResolver, _DataResolverCallback dataResolver) { + static Object _resolveFrom( + Object root, + List parts, + _StateResolverCallback stateResolver, + _DataResolverCallback dataResolver, + _WidgetBuilderArgResolverCallback widgetBuilderArgResolver, + ) { int index = 0; Object current = root; while (true) { @@ -667,6 +844,9 @@ abstract class _CurriedWidget extends BlobNode { } current = dataResolver(current.parts); continue; + } else if (current is WidgetBuilderArgReference) { + current = widgetBuilderArgResolver([current.argumentName, ...current.parts]); + continue; } else if (current is BoundArgsReference) { List nextParts = current.parts; if (index < parts.length) { @@ -693,7 +873,13 @@ abstract class _CurriedWidget extends BlobNode { index = 0; continue; } else if (current is Switch) { - final Object key = _resolveFrom(current.input, const [], stateResolver, dataResolver); + final Object key = _resolveFrom( + current.input, + const [], + stateResolver, + dataResolver, + widgetBuilderArgResolver, + ); Object? value = current.outputs[key]; if (value == null) { value = current.outputs[null]; @@ -707,9 +893,15 @@ abstract class _CurriedWidget extends BlobNode { // We've reached the end of the line. // We handle some special leaf cases that still need processing before we return. if (current is EventHandler) { - current = EventHandler(current.eventName, _fix(current.eventArguments, stateResolver, dataResolver) as DynamicMap); + current = EventHandler( + current.eventName, + _fix(current.eventArguments, stateResolver, dataResolver, widgetBuilderArgResolver) as DynamicMap, + ); } else if (current is SetStateHandler) { - current = SetStateHandler(current.stateReference, _fix(current.value, stateResolver, dataResolver)); + current = SetStateHandler( + current.stateReference, + _fix(current.value, stateResolver, dataResolver, widgetBuilderArgResolver), + ); } // else `current` is nothing special, and we'll just return it below. break; // This is where the loop ends. @@ -725,7 +917,13 @@ abstract class _CurriedWidget extends BlobNode { if (parts[index] is! int) { return missing; } - current = _listLookup(current, parts[index] as int, stateResolver, dataResolver).result ?? missing; + current = _listLookup( + current, + parts[index] as int, + stateResolver, + dataResolver, + widgetBuilderArgResolver, + ).result ?? missing; } else { assert(current is! ArgsReference); assert(current is! StateReference); @@ -740,27 +938,60 @@ abstract class _CurriedWidget extends BlobNode { return current; } - static Object _fix(Object root, _StateResolverCallback stateResolver, _DataResolverCallback dataResolver) { + static Object _fix( + Object root, + _StateResolverCallback stateResolver, + _DataResolverCallback dataResolver, + _WidgetBuilderArgResolverCallback widgetBuilderArgResolver, + ) { if (root is DynamicMap) { - return root.map((String key, Object? value) => MapEntry(key, _fix(root[key]!, stateResolver, dataResolver))); + return root.map((String key, Object? value) => + MapEntry( + key, + _fix(root[key]!, stateResolver, dataResolver, widgetBuilderArgResolver), + ), + ); } else if (root is DynamicList) { if (root.any((Object? entry) => entry is Loop)) { - final int length = _listLookup(root, -1, stateResolver, dataResolver).length!; - return DynamicList.generate(length, (int index) => _fix(_listLookup(root, index, stateResolver, dataResolver).result!, stateResolver, dataResolver)); + final int length = _listLookup( + root, + -1, + stateResolver, + dataResolver, + widgetBuilderArgResolver, + ).length!; + return DynamicList.generate( + length, + (int index) => _fix( + _listLookup(root, index, stateResolver, dataResolver, widgetBuilderArgResolver).result!, + stateResolver, + dataResolver, + widgetBuilderArgResolver, + ), + ); } else { - return DynamicList.generate(root.length, (int index) => _fix(root[index]!, stateResolver, dataResolver)); + return DynamicList.generate( + root.length, + (int index) => _fix(root[index]!, stateResolver, dataResolver, widgetBuilderArgResolver), + ); } } else if (root is BlobNode) { - return _resolveFrom(root, const [], stateResolver, dataResolver); + return _resolveFrom(root, const [], stateResolver, dataResolver, widgetBuilderArgResolver); } else { return root; } } - Object resolve(List parts, _StateResolverCallback stateResolver, _DataResolverCallback dataResolver, { required bool expandLists }) { - Object result = _resolveFrom(arguments, parts, stateResolver, dataResolver); + Object resolve( + List parts, + _StateResolverCallback stateResolver, + _DataResolverCallback dataResolver, + _WidgetBuilderArgResolverCallback widgetBuilderArgResolver, { + required bool expandLists, + }) { + Object result = _resolveFrom(arguments, parts, stateResolver, dataResolver, widgetBuilderArgResolver); if (result is DynamicList && expandLists) { - result = _listLookup(result, -1, stateResolver, dataResolver); + result = _listLookup(result, -1, stateResolver, dataResolver, widgetBuilderArgResolver); } assert(result is! Reference); assert(result is! Switch); @@ -768,38 +999,92 @@ abstract class _CurriedWidget extends BlobNode { return result; } - Widget build(BuildContext context, DynamicContent data, RemoteEventHandler remoteEventTarget, List<_WidgetState> states) { - return _Widget(curriedWidget: this, data: data, remoteEventTarget: remoteEventTarget, states: states); + Widget build( + BuildContext context, + DynamicContent data, + RemoteEventHandler remoteEventTarget, + List<_WidgetState> states, + ) { + return _Widget( + curriedWidget: this, + data: data, + widgetBuilderScope: DynamicContent(widgetBuilderScope), + remoteEventTarget: remoteEventTarget, + states: states, + ); } - Widget buildChild(BuildContext context, DataSource source, DynamicContent data, RemoteEventHandler remoteEventTarget, List<_WidgetState> states, _StateResolverCallback stateResolver, _DataResolverCallback dataResolver); + Widget buildChild( + BuildContext context, + DataSource source, + DynamicContent data, + RemoteEventHandler remoteEventTarget, + List<_WidgetState> states, + _StateResolverCallback stateResolver, + _DataResolverCallback dataResolver, + _WidgetBuilderArgResolverCallback widgetBuilderArgResolver, + ); @override String toString() => '$fullName ${initialState ?? "{}"} $arguments'; } class _CurriedLocalWidget extends _CurriedWidget { - const _CurriedLocalWidget(FullyQualifiedWidgetName fullName, this.child, DynamicMap arguments) : super(fullName, arguments, null); + const _CurriedLocalWidget( + FullyQualifiedWidgetName fullName, + this.child, + DynamicMap arguments, + DynamicMap widgetBuilderScope, + ) : super(fullName, arguments, widgetBuilderScope, null); factory _CurriedLocalWidget.error(FullyQualifiedWidgetName fullName, String message) { - return _CurriedLocalWidget(fullName, (BuildContext context, DataSource data) => _buildErrorWidget(message), const {}); + return _CurriedLocalWidget( + fullName, + (BuildContext context, DataSource data) => _buildErrorWidget(message), + const {}, + const {}, + ); } final LocalWidgetBuilder child; @override - Widget buildChild(BuildContext context, DataSource source, DynamicContent data, RemoteEventHandler remoteEventTarget, List<_WidgetState> states, _StateResolverCallback stateResolver, _DataResolverCallback dataResolver) { + Widget buildChild( + BuildContext context, + DataSource source, + DynamicContent data, + RemoteEventHandler remoteEventTarget, + List<_WidgetState> states, + _StateResolverCallback stateResolver, + _DataResolverCallback dataResolver, + _WidgetBuilderArgResolverCallback widgetBuilderArgResolver, + ) { return child(context, source); } } class _CurriedRemoteWidget extends _CurriedWidget { - const _CurriedRemoteWidget(FullyQualifiedWidgetName fullName, this.child, DynamicMap arguments, DynamicMap? initialState) : super(fullName, arguments, initialState); + const _CurriedRemoteWidget( + FullyQualifiedWidgetName fullName, + this.child, + DynamicMap arguments, + DynamicMap widgetBuilderScope, + DynamicMap? initialState, + ) : super(fullName, arguments, widgetBuilderScope, initialState); final _CurriedWidget child; @override - Widget buildChild(BuildContext context, DataSource source, DynamicContent data, RemoteEventHandler remoteEventTarget, List<_WidgetState> states, _StateResolverCallback stateResolver, _DataResolverCallback dataResolver) { + Widget buildChild( + BuildContext context, + DataSource source, + DynamicContent data, + RemoteEventHandler remoteEventTarget, + List<_WidgetState> states, + _StateResolverCallback stateResolver, + _DataResolverCallback dataResolver, + _WidgetBuilderArgResolverCallback widgetBuilderArgResolver, + ) { return child.build(context, data, remoteEventTarget, states); } @@ -808,13 +1093,34 @@ class _CurriedRemoteWidget extends _CurriedWidget { } class _CurriedSwitch extends _CurriedWidget { - const _CurriedSwitch(FullyQualifiedWidgetName fullName, this.root, DynamicMap arguments, DynamicMap? initialState) : super(fullName, arguments, initialState); + const _CurriedSwitch( + FullyQualifiedWidgetName fullName, + this.root, + DynamicMap arguments, + DynamicMap widgetBuilderScope, + DynamicMap? initialState, + ) : super(fullName, arguments, widgetBuilderScope, initialState); final Switch root; @override - Widget buildChild(BuildContext context, DataSource source, DynamicContent data, RemoteEventHandler remoteEventTarget, List<_WidgetState> states, _StateResolverCallback stateResolver, _DataResolverCallback dataResolver) { - final Object resolvedWidget = _CurriedWidget._resolveFrom(root, const [], stateResolver, dataResolver); + Widget buildChild( + BuildContext context, + DataSource source, + DynamicContent data, + RemoteEventHandler remoteEventTarget, + List<_WidgetState> states, + _StateResolverCallback stateResolver, + _DataResolverCallback dataResolver, + _WidgetBuilderArgResolverCallback widgetBuilderArgResolver, + ) { + final Object resolvedWidget = _CurriedWidget._resolveFrom( + root, + const [], + stateResolver, + dataResolver, + widgetBuilderArgResolver, + ); if (resolvedWidget is _CurriedWidget) { return resolvedWidget.build(context, data, remoteEventTarget, states); } @@ -826,12 +1132,20 @@ class _CurriedSwitch extends _CurriedWidget { } class _Widget extends StatefulWidget { - const _Widget({ required this.curriedWidget, required this.data, required this.remoteEventTarget, required this.states }); + const _Widget({ + required this.curriedWidget, + required this.data, + required this.widgetBuilderScope, + required this.remoteEventTarget, + required this.states, + }); final _CurriedWidget curriedWidget; final DynamicContent data; + final DynamicContent widgetBuilderScope; + final RemoteEventHandler remoteEventTarget; final List<_WidgetState> states; @@ -1014,6 +1328,36 @@ class _WidgetState extends State<_Widget> implements DataSource { ]; } + @override + Widget builder(List argsKey, DynamicMap builderArg) { + return _fetchBuilder(argsKey, builderArg, optional: false)!; + } + + @override + Widget? optionalBuilder(List argsKey, DynamicMap builderArg) { + return _fetchBuilder(argsKey, builderArg); + } + + Widget? _fetchBuilder( + List argsKey, + DynamicMap builderArg, { + bool optional = true, + }) { + final Object value = _fetch(argsKey, expandLists: false); + if (value is _RemoteWidgetBuilder) { + final _CurriedWidget curriedWidget = value(builderArg); + return curriedWidget.build( + context, + widget.data, + widget.remoteEventTarget, + widget.states, + ); + } + return optional + ? null + : _buildErrorWidget('Not a builder at $argsKey (got $value) for ${widget.curriedWidget.fullName}.'); + } + @override VoidCallback? voidHandler(List argsKey, [ DynamicMap? extraArguments ]) { return handler(argsKey, (HandlerTrigger callback) => () => callback(extraArguments)); @@ -1064,7 +1408,13 @@ class _WidgetState extends State<_Widget> implements DataSource { assert(!_debugFetching); try { _debugFetching = true; - final Object result = widget.curriedWidget.resolve(argsKey, _stateResolver, _dataResolver, expandLists: expandLists); + final Object result = widget.curriedWidget.resolve( + argsKey, + _stateResolver, + _dataResolver, + _widgetBuilderArgResolver, + expandLists: expandLists, + ); for (final _Subscription subscription in _dependencies) { subscription.addClient(key); } @@ -1095,6 +1445,17 @@ class _WidgetState extends State<_Widget> implements DataSource { return subscription.value; } + Object _widgetBuilderArgResolver(List rawDataKey) { + final _Key widgetBuilderArgKey = _Key(_kWidgetBuilderArgSection, rawDataKey); + final _Subscription subscription = _subscriptions[widgetBuilderArgKey] ??= _Subscription( + widget.widgetBuilderScope, + this, + rawDataKey, + ); + _dependencies.add(subscription); + return subscription.value; + } + Object _stateResolver(List rawStateKey, int depth) { final _Key stateKey = _Key(depth, rawStateKey); final _Subscription subscription; @@ -1126,7 +1487,16 @@ class _WidgetState extends State<_Widget> implements DataSource { @override Widget build(BuildContext context) { // TODO(ianh): what if this creates some _dependencies? - return widget.curriedWidget.buildChild(context, this, widget.data, widget.remoteEventTarget, _states, _stateResolver, _dataResolver); + return widget.curriedWidget.buildChild( + context, + this, + widget.data, + widget.remoteEventTarget, + _states, + _stateResolver, + _dataResolver, + _widgetBuilderArgResolver, + ); } @override @@ -1138,6 +1508,7 @@ class _WidgetState extends State<_Widget> implements DataSource { const int _kDataSection = -1; const int _kArgsSection = -2; +const int _kWidgetBuilderArgSection = -3; @immutable class _Key { diff --git a/packages/rfw/pubspec.yaml b/packages/rfw/pubspec.yaml index 6056af37679b..310d7bd6535c 100644 --- a/packages/rfw/pubspec.yaml +++ b/packages/rfw/pubspec.yaml @@ -2,7 +2,7 @@ name: rfw description: "Remote Flutter widgets: a library for rendering declarative widget description files at runtime." repository: https://github.com/flutter/packages/tree/main/packages/rfw issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+rfw%22 -version: 1.0.24 +version: 1.0.25 environment: sdk: ^3.2.0 diff --git a/packages/rfw/test/binary_test.dart b/packages/rfw/test/binary_test.dart index 0eca7888bbe9..916f92bf15e5 100644 --- a/packages/rfw/test/binary_test.dart +++ b/packages/rfw/test/binary_test.dart @@ -503,4 +503,41 @@ void main() { expect((value.widgets.first.root as ConstructorCall).name, 'c'); expect((value.widgets.first.root as ConstructorCall).arguments, isEmpty); }); + + testWidgets('Library encoder: widget builders work', (WidgetTester tester) async { + const String source = ''' + widget Foo = Builder( + builder: (scope) => Text(text: scope.text), + ); + '''; + final RemoteWidgetLibrary library = parseLibraryFile(source); + final Uint8List encoded = encodeLibraryBlob(library); + final RemoteWidgetLibrary decoded = decodeLibraryBlob(encoded); + + expect(library.toString(), decoded.toString()); + }); + + testWidgets('Library encoder: widget builders throws', (WidgetTester tester) async { + const RemoteWidgetLibrary remoteWidgetLibrary = RemoteWidgetLibrary( + [], + [ + WidgetDeclaration( + 'a', + {}, + ConstructorCall( + 'c', + { + 'builder': WidgetBuilderDeclaration('scope', ArgsReference([])), + }, + ), + ), + ], + ); + try { + decodeLibraryBlob(encodeLibraryBlob(remoteWidgetLibrary)); + fail('did not throw exception'); + } on FormatException catch (e) { + expect('$e', contains('Unrecognized data type 0x0A while decoding widget builder blob.')); + } + }); } diff --git a/packages/rfw/test/runtime_test.dart b/packages/rfw/test/runtime_test.dart index 89cb96d62d51..f081df3e8126 100644 --- a/packages/rfw/test/runtime_test.dart +++ b/packages/rfw/test/runtime_test.dart @@ -1088,4 +1088,470 @@ void main() { data.update('c', 'test'); expect(log, ['leaf: 2', 'root: {a: [2, 3], b: [q, r]}', 'root: {a: [2, 3], b: [q, r], c: test}']); }); + + testWidgets('Data source - optional builder works', (WidgetTester tester) async { + const LibraryName coreLibraryName = LibraryName(['core']); + const LibraryName localLibraryName = LibraryName(['local']); + const LibraryName remoteLibraryName = LibraryName(['remote']); + final Runtime runtime = Runtime(); + final DynamicContent data = DynamicContent(); + runtime.update(coreLibraryName, createCoreWidgets()); + runtime.update(localLibraryName, LocalWidgetLibrary( { + 'Builder': (BuildContext context, DataSource source) { + final Widget? builder = source.optionalBuilder(['builder'], {}); + return builder ?? const Text('Hello World!', textDirection: TextDirection.ltr); + }, + })); + runtime.update(remoteLibraryName, parseLibraryFile(''' + import core; + import local; + + widget test = Builder( + builder: Text(text: 'Not a builder :/'), + ); + ''')); + await tester.pumpWidget(RemoteWidget( + runtime: runtime, + data: data, + widget: const FullyQualifiedWidgetName(remoteLibraryName, 'test'), + )); + + + final Finder textFinder = find.byType(Text); + expect(textFinder, findsOneWidget); + expect(tester.widget(textFinder).data, 'Hello World!'); + }); + + testWidgets('Data source - builder returns an error widget', (WidgetTester tester) async { + const LibraryName coreLibraryName = LibraryName(['core']); + const LibraryName localLibraryName = LibraryName(['local']); + const LibraryName remoteLibraryName = LibraryName(['remote']); + final Runtime runtime = Runtime(); + final DynamicContent data = DynamicContent(); + const String expectedErrorMessage = 'Not a builder at [builder] (got core:Text {} {text: Not a builder :/}) for local:Builder.'; + + runtime.update(coreLibraryName, createCoreWidgets()); + runtime.update(localLibraryName, LocalWidgetLibrary( { + 'Builder': (BuildContext context, DataSource source) { + return source.builder(['builder'], {}); + }, + })); + runtime.update(remoteLibraryName, parseLibraryFile(''' + import core; + import local; + + widget test = Builder( + builder: Text(text: 'Not a builder :/'), + ); + ''')); + await tester.pumpWidget(RemoteWidget( + runtime: runtime, + data: data, + widget: const FullyQualifiedWidgetName(remoteLibraryName, 'test'), + )); + + expect(tester.takeException().toString(), contains(expectedErrorMessage)); + expect(find.byType(ErrorWidget), findsOneWidget); + expect(tester.widget(find.byType(ErrorWidget)).message, expectedErrorMessage); + }); + + testWidgets('Widget builders - work when scope is not used', (WidgetTester tester) async { + const LibraryName coreLibraryName = LibraryName(['core']); + const LibraryName localLibraryName = LibraryName(['local']); + const LibraryName remoteLibraryName = LibraryName(['remote']); + final Runtime runtime = Runtime(); + final DynamicContent data = DynamicContent(); + final Finder textFinder = find.byType(Text); + + runtime.update(coreLibraryName, createCoreWidgets()); + runtime.update(localLibraryName, LocalWidgetLibrary( { + 'Builder': (BuildContext context, DataSource source) { + return source.builder(['builder'], {}); + }, + })); + runtime.update(remoteLibraryName, parseLibraryFile(''' + import core; + import local; + + widget test = Builder( + builder: (scope) => Text(text: 'Hello World!', textDirection: 'ltr'), + ); + ''')); + await tester.pumpWidget(RemoteWidget( + runtime: runtime, + data: data, + widget: const FullyQualifiedWidgetName(remoteLibraryName, 'test'), + )); + + expect(textFinder, findsOneWidget); + expect(tester.widget(textFinder).data, 'Hello World!'); + }); + + testWidgets('Widget builders - work when scope is used', (WidgetTester tester) async { + const LibraryName coreLibraryName = LibraryName(['core']); + const LibraryName localLibraryName = LibraryName(['local']); + const LibraryName remoteLibraryName = LibraryName(['remote']); + final Runtime runtime = Runtime(); + final DynamicContent data = DynamicContent(); + final Finder textFinder = find.byType(Text); + + runtime.update(coreLibraryName, createCoreWidgets()); + runtime.update(localLibraryName, LocalWidgetLibrary( { + 'HelloWorld': (BuildContext context, DataSource source) { + const String result = 'Hello World!'; + return source.builder(['builder'], {'result': result}); + }, + })); + runtime.update(remoteLibraryName, parseLibraryFile(''' + import core; + import local; + + widget test = HelloWorld( + builder: (result) => Text(text: result.result, textDirection: 'ltr'), + ); + ''')); + await tester.pumpWidget(RemoteWidget( + runtime: runtime, + data: data, + widget: const FullyQualifiedWidgetName(remoteLibraryName, 'test'), + )); + + expect(textFinder, findsOneWidget); + expect(tester.widget(textFinder).data, 'Hello World!'); + }); + + testWidgets('Widget builders - work with state', (WidgetTester tester) async { + const LibraryName coreLibraryName = LibraryName(['core']); + const LibraryName localLibraryName = LibraryName(['local']); + const LibraryName remoteLibraryName = LibraryName(['remote']); + final Runtime runtime = Runtime(); + final DynamicContent data = DynamicContent(); + final Finder textFinder = find.byType(Text); + + runtime.update(coreLibraryName, createCoreWidgets()); + runtime.update(localLibraryName, LocalWidgetLibrary( { + 'IntToString': (BuildContext context, DataSource source) { + final int value = source.v(['value'])!; + final String result = value.toString(); + return source.builder(['builder'], {'result': result}); + }, + })); + runtime.update(remoteLibraryName, parseLibraryFile(''' + import core; + import local; + + widget test {value: 0} = IntToString( + value: state.value, + builder: (result) => Text(text: result.result, textDirection: 'ltr'), + ); + ''')); + await tester.pumpWidget(RemoteWidget( + runtime: runtime, + data: data, + widget: const FullyQualifiedWidgetName(remoteLibraryName, 'test'), + )); + + expect(textFinder, findsOneWidget); + expect(tester.widget(textFinder).data, '0'); + }); + + + testWidgets('Widget builders - work with data', (WidgetTester tester) async { + const LibraryName coreLibraryName = LibraryName(['core']); + const LibraryName localLibraryName = LibraryName(['local']); + const LibraryName remoteLibraryName = LibraryName(['remote']); + final Runtime runtime = Runtime(); + final DynamicContent data = DynamicContent({'value': 0}); + final Finder textFinder = find.byType(Text); + + runtime.update(coreLibraryName, createCoreWidgets()); + runtime.update(localLibraryName, LocalWidgetLibrary( { + 'IntToString': (BuildContext context, DataSource source) { + final int value = source.v(['value'])!; + final String result = value.toString(); + return source.builder(['builder'], {'result': result}); + }, + })); + runtime.update(remoteLibraryName, parseLibraryFile(''' + import core; + import local; + + widget test = IntToString( + value: data.value, + builder: (result) => Text(text: result.result, textDirection: 'ltr'), + ); + ''')); + await tester.pumpWidget(RemoteWidget( + runtime: runtime, + data: data, + widget: const FullyQualifiedWidgetName(remoteLibraryName, 'test'), + )); + + expect(textFinder, findsOneWidget); + expect(tester.widget(textFinder).data, '0'); + + data.update('value', 1); + await tester.pump(); + expect(tester.widget(textFinder).data, '1'); + }); + + testWidgets('Widget builders - work with events', (WidgetTester tester) async { + const LibraryName coreLibraryName = LibraryName(['core']); + const LibraryName localLibraryName = LibraryName(['local']); + const LibraryName remoteLibraryName = LibraryName(['remote']); + final Runtime runtime = Runtime(); + final DynamicContent data = DynamicContent(); + final List dispatchedEvents = []; + final Finder textFinder = find.byType(Text); + + runtime.update(coreLibraryName, createCoreWidgets()); + runtime.update(localLibraryName, LocalWidgetLibrary( { + 'Zero': (BuildContext context, DataSource source) { + return source.builder(['builder'], {'result': 0}); + }, + })); + runtime.update(remoteLibraryName, parseLibraryFile(''' + import core; + import local; + + widget test = Zero( + builder: (result) => GestureDetector( + onTap: event 'works' {number: result.result}, + child: Text(text: 'Tap to trigger an event.', textDirection: 'ltr'), + ), + ); + ''')); + await tester.pumpWidget(RemoteWidget( + runtime: runtime, + data: data, + widget: const FullyQualifiedWidgetName(remoteLibraryName, 'test'), + onEvent: (String eventName, DynamicMap eventArguments) => + dispatchedEvents.add(RfwEvent(eventName, eventArguments)), + )); + + await tester.tap(textFinder); + await tester.pump(); + expect(dispatchedEvents, hasLength(1)); + expect(dispatchedEvents.single.name, 'works'); + expect(dispatchedEvents.single.arguments['number'], 0); + }); + + testWidgets('Widget builders - works nested', (WidgetTester tester) async { + const LibraryName coreLibraryName = LibraryName(['core']); + const LibraryName localLibraryName = LibraryName(['local']); + const LibraryName remoteLibraryName = LibraryName(['remote']); + final Runtime runtime = Runtime(); + final DynamicContent data = DynamicContent(); + final Finder textFinder = find.byType(Text); + runtime.update(coreLibraryName, createCoreWidgets()); + runtime.update(localLibraryName, LocalWidgetLibrary( { + 'Sum': (BuildContext context, DataSource source) { + final int operand1 = source.v(['operand1'])!; + final int operand2 = source.v(['operand2'])!; + final int result = operand1 + operand2; + return source.builder(['builder'], {'result': result}); + }, + 'IntToString': (BuildContext context, DataSource source) { + final int value = source.v(['value'])!; + final String result = value.toString(); + return source.builder(['builder'], {'result': result}); + }, + })); + runtime.update(remoteLibraryName, parseLibraryFile(''' + import core; + import local; + + widget test = Sum( + operand1: 1, + operand2: 2, + builder: (result1) => IntToString( + value: result1.result, + builder: (result2) => Text(text: ['1 + 2 = ', result2.result], textDirection: 'ltr'), + ), + ); + ''')); + await tester.pumpWidget(RemoteWidget( + runtime: runtime, + data: data, + widget: const FullyQualifiedWidgetName(remoteLibraryName, 'test'), + )); + + expect(textFinder, findsOneWidget); + expect(tester.widget(textFinder).data, '1 + 2 = 3'); + }); + + testWidgets('Widget builders - works nested dynamically', (WidgetTester tester) async { + const LibraryName coreLibraryName = LibraryName(['core']); + const LibraryName localLibraryName = LibraryName(['local']); + const LibraryName remoteLibraryName = LibraryName(['remote']); + final Map handlers = {}; + final Runtime runtime = Runtime(); + final DynamicContent data = DynamicContent({ + 'a1': 'apricot', + 'b1': 'blueberry', + }); + final Finder textFinder = find.byType(Text); + + runtime.update(coreLibraryName, createCoreWidgets()); + runtime.update(localLibraryName, LocalWidgetLibrary( { + 'Builder': (BuildContext context, DataSource source) { + final String? id = source.v(['id']); + if (id != null) { + handlers[id] = source.voidHandler(['handler'])!; + } + return source.builder(['builder'], { + 'param1': source.v(['arg1']), + 'param2': source.v(['arg2']), + }); + }, + })); + runtime.update(remoteLibraryName, parseLibraryFile(''' + import core; + import local; + + widget test { state1: 'strawberry' } = Builder( + arg1: data.a1, + arg2: 'apple', + id: 'A', + handler: set state.state1 = 'STRAWBERRY', + builder: (builder1) => Builder( + arg1: data.b1, + arg2: 'banana', + builder: (builder2) => Text( + textDirection: 'ltr', + text: [ + state.state1, ' ', builder1.param1, ' ', builder1.param2, ' ', builder2.param1, ' ', builder2.param2, + ], + ), + ), + ); + ''')); + await tester.pumpWidget(RemoteWidget( + runtime: runtime, + data: data, + widget: const FullyQualifiedWidgetName(remoteLibraryName, 'test'), + )); + + expect(tester.widget(textFinder).data, 'strawberry apricot apple blueberry banana'); + + data.update('a1', 'APRICOT'); + await tester.pump(); + expect(tester.widget(textFinder).data, 'strawberry APRICOT apple blueberry banana'); + + data.update('b1', 'BLUEBERRY'); + await tester.pump(); + expect(tester.widget(textFinder).data, 'strawberry APRICOT apple BLUEBERRY banana'); + + handlers['A']!(); + await tester.pump(); + expect(tester.widget(textFinder).data, 'STRAWBERRY APRICOT apple BLUEBERRY banana'); + }); + + testWidgets('Widget builders - switch works with builder', (WidgetTester tester) async { + const LibraryName coreLibraryName = LibraryName(['core']); + const LibraryName localLibraryName = LibraryName(['local']); + const LibraryName remoteLibraryName = LibraryName(['remote']); + final Runtime runtime = Runtime(); + final DynamicContent data = DynamicContent(); + final Finder textFinder = find.byType(Text); + + runtime.update(coreLibraryName, createCoreWidgets()); + runtime.update(localLibraryName, LocalWidgetLibrary( { + 'Builder': (BuildContext context, DataSource source) { + return source.builder(['builder'], {}); + }, + })); + runtime.update(remoteLibraryName, parseLibraryFile(''' + import core; + import local; + + widget test {enabled: false} = Builder( + value: state.value, + builder: switch state.enabled { + true: (scope) => GestureDetector( + onTap: set state.enabled = false, + child: Text(text: 'The builder is enabled.', textDirection: 'ltr'), + ), + false: (scope) => GestureDetector( + onTap: set state.enabled = true, + child: Text(text: 'The builder is disabled.', textDirection: 'ltr'), + ), + }, + ); + ''')); + await tester.pumpWidget(RemoteWidget( + runtime: runtime, + data: data, + widget: const FullyQualifiedWidgetName(remoteLibraryName, 'test'), + )); + + + expect(textFinder, findsOneWidget); + expect(tester.widget(textFinder).data, 'The builder is disabled.'); + + await tester.tap(textFinder); + await tester.pump(); + expect(textFinder, findsOneWidget); + expect(tester.widget(textFinder).data, 'The builder is enabled.'); + }); + + testWidgets('Widget builders - builder works with switch', (WidgetTester tester) async { + const LibraryName coreLibraryName = LibraryName(['core']); + const LibraryName localLibraryName = LibraryName(['local']); + const LibraryName remoteLibraryName = LibraryName(['remote']); + final Runtime runtime = Runtime(); + final DynamicContent data = DynamicContent(); + final Finder textFinder = find.byType(Text); + runtime.update(coreLibraryName, createCoreWidgets()); + runtime.update(localLibraryName, LocalWidgetLibrary( { + 'Inverter': (BuildContext context, DataSource source) { + final bool value = source.v(['value'])!; + return source.builder(['builder'], {'result': !value}); + }, + })); + runtime.update(remoteLibraryName, parseLibraryFile(''' + import core; + import local; + + widget test {value: false} = Inverter( + value: state.value, + builder: (result) => switch result.result { + true: GestureDetector( + onTap: set state.value = switch state.value { + true: false, + false: true, + }, + child: Text(text: 'The input is false, the output is true', textDirection: 'ltr'), + ), + false: GestureDetector( + onTap: set state.value = switch state.value { + true: false, + false: true, + }, + child: Text(text: 'The input is true, the output is false', textDirection: 'ltr'), + ), + }, + ); + ''')); + await tester.pumpWidget(RemoteWidget( + runtime: runtime, + data: data, + widget: const FullyQualifiedWidgetName(remoteLibraryName, 'test'), + )); + + expect(textFinder, findsOneWidget); + expect(tester.widget(textFinder).data, 'The input is false, the output is true'); + + await tester.tap(textFinder); + await tester.pump(); + expect(textFinder, findsOneWidget); + expect(tester.widget(textFinder).data, 'The input is true, the output is false'); + }); +} + +final class RfwEvent { + RfwEvent(this.name, this.arguments); + + final String name; + final DynamicMap arguments; } diff --git a/packages/rfw/test/text_test.dart b/packages/rfw/test/text_test.dart index 8fe51d75a3d9..2dac169fb874 100644 --- a/packages/rfw/test/text_test.dart +++ b/packages/rfw/test/text_test.dart @@ -340,4 +340,145 @@ void main() { final RemoteWidgetLibrary result = parseLibraryFile('widget a {b: 0} = c();'); expect(result.widgets.single.initialState, {'b': 0}); }); + + testWidgets('parseLibraryFile: widgetBuilders work', (WidgetTester tester) async { + final RemoteWidgetLibrary libraryFile = parseLibraryFile(''' + widget a = Builder(builder: (scope) => Container()); + '''); + expect(libraryFile.toString(), 'widget a = Builder({builder: (scope) => Container({})});'); + }); + + testWidgets('parseLibraryFile: widgetBuilders work with arguments', (WidgetTester tester) async { + final RemoteWidgetLibrary libraryFile = parseLibraryFile(''' + widget a = Builder(builder: (scope) => Container(width: scope.width)); + '''); + expect(libraryFile.toString(), 'widget a = Builder({builder: (scope) => Container({width: scope.width})});'); + }); + + testWidgets('parseLibraryFile: widgetBuilder arguments are lexical scoped', (WidgetTester tester) async { + final RemoteWidgetLibrary libraryFile = parseLibraryFile(''' + widget a = A( + a: (s1) => B( + b: (s2) => T(s1: s1.s1, s2: s2.s2), + ), + ); + '''); + expect(libraryFile.toString(), 'widget a = A({a: (s1) => B({b: (s2) => T({s1: s1.s1, s2: s2.s2})})});'); + }); + + testWidgets('parseLibraryFile: widgetBuilder arguments can be shadowed', (WidgetTester tester) async { + final RemoteWidgetLibrary libraryFile = parseLibraryFile(''' + widget a = A( + a: (s1) => B( + b: (s1) => T(t: s1.foo), + ), + ); + '''); + expect(libraryFile.toString(), 'widget a = A({a: (s1) => B({b: (s1) => T({t: s1.foo})})});'); + }); + + testWidgets('parseLibraryFile: widgetBuilders check the returned value', (WidgetTester tester) async { + void test(String input, String expectedMessage) { + try { + parseLibraryFile(input); + fail('parsing `$input` did not result in an error (expected "$expectedMessage").'); + } on ParserException catch (e) { + expect('$e', expectedMessage); + } + } + + const String expectedErrorMessage = + 'Expecting a switch or constructor call got 1 at line 1 column 27.'; + test('widget a = B(b: (foo) => 1);', expectedErrorMessage); + }); + + testWidgets('parseLibraryFile: widgetBuilders check reserved words', (WidgetTester tester) async { + void test(String input, String expectedMessage) { + try { + parseLibraryFile(input); + fail('parsing `$input` did not result in an error (expected "$expectedMessage").'); + } on ParserException catch (e) { + expect('$e', expectedMessage); + } + } + + const String expectedErrorMessage = + 'args is a reserved word at line 1 column 34.'; + test('widget a = Builder(builder: (args) => Container(width: args.width));', expectedErrorMessage); + }); + + testWidgets('parseLibraryFile: widgetBuilders check reserved words', (WidgetTester tester) async { + void test(String input, String expectedMessage) { + try { + parseDataFile(input); + fail('parsing `$input` did not result in an error (expected "$expectedMessage").'); + } on ParserException catch (e) { + expect('$e', expectedMessage); + } + } + + const String expectedErrorMessage = + 'Expected symbol "{" but found widget at line 1 column 7.'; + test('widget a = Builder(builder: (args) => Container(width: args.width));', expectedErrorMessage); + }); + + testWidgets('parseLibraryFile: switch works with widgetBuilders', (WidgetTester tester) async { + final RemoteWidgetLibrary libraryFile = parseLibraryFile(''' + widget a = A( + b: switch args.down { + true: (foo) => B(), + false: (bar) => C(), + } + ); + '''); + expect(libraryFile.toString(), 'widget a = A({b: switch args.down {true: (foo) => B({}), false: (bar) => C({})}});'); + }); + + testWidgets('parseLibraryFile: widgetBuilders work with switch', (WidgetTester tester) async { + final RemoteWidgetLibrary libraryFile = parseLibraryFile(''' + widget a = A( + b: (foo) => switch foo.letter { + 'a': A(), + 'b': B(), + }, + ); + '''); + expect(libraryFile.toString(), 'widget a = A({b: (foo) => switch foo.letter {a: A({}), b: B({})}});'); + }); + + testWidgets('parseLibraryFile: widgetBuilders work with lists', (WidgetTester tester) async { + final RemoteWidgetLibrary libraryFile = parseLibraryFile(''' + widget a = A( + b: (s1) => B(c: [s1.c]), + ); + '''); + expect(libraryFile.toString(), 'widget a = A({b: (s1) => B({c: [s1.c]})});' ); + }); + + testWidgets('parseLibraryFile: widgetBuilders work with maps', (WidgetTester tester) async { + final RemoteWidgetLibrary libraryFile = parseLibraryFile(''' + widget a = A( + b: (s1) => B(c: {d: s1.d}), + ); + '''); + expect(libraryFile.toString(), 'widget a = A({b: (s1) => B({c: {d: s1.d}})});'); + }); + + testWidgets('parseLibraryFile: widgetBuilders work with setters', (WidgetTester tester) async { + final RemoteWidgetLibrary libraryFile = parseLibraryFile(''' + widget a {foo: 0} = A( + b: (s1) => B(onTap: set state.foo = s1.foo), + ); + '''); + expect(libraryFile.toString(), 'widget a = A({b: (s1) => B({onTap: set state.foo = s1.foo})});'); + }); + + testWidgets('parseLibraryFile: widgetBuilders work with events', (WidgetTester tester) async { + final RemoteWidgetLibrary libraryFile = parseLibraryFile(''' + widget a {foo: 0} = A( + b: (s1) => B(onTap: event "foo" {result: s1.result}) + ); + '''); + expect(libraryFile.toString(), 'widget a = A({b: (s1) => B({onTap: event foo {result: s1.result}})});'); + }); } From 903f03add169c39d3f19bc70980af3f0862869a9 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 7 Mar 2024 12:18:07 -0500 Subject: [PATCH 064/126] Roll Flutter from 8f84f3f32ddc to 471a82856d86 (27 revisions) (#6281) https://github.com/flutter/flutter/compare/8f84f3f32ddc...471a82856d86 2024-03-07 leroux_bruno@yahoo.fr [flutter_test] Use defaultTargetPlatform for key events simulation (flutter/flutter#143579) 2024-03-07 engine-flutter-autoroll@skia.org Roll Packages from 9b88dbc53992 to 6701c9e61804 (3 revisions) (flutter/flutter#144772) 2024-03-07 matej.knopp@gmail.com Fix frameworks added to bundle multiple times instead of lipo (flutter/flutter#144688) 2024-03-07 engine-flutter-autoroll@skia.org Roll Flutter Engine from 40a42796b129 to f8c3b2db8cd1 (1 revision) (flutter/flutter#144766) 2024-03-07 engine-flutter-autoroll@skia.org Roll Flutter Engine from 0246484d2bae to 40a42796b129 (1 revision) (flutter/flutter#144765) 2024-03-07 engine-flutter-autoroll@skia.org Roll Flutter Engine from 6c1751bd774e to 0246484d2bae (1 revision) (flutter/flutter#144756) 2024-03-07 leroux_bruno@yahoo.fr [flutter_test] Change KeyEventSimulator default transit mode (flutter/flutter#143847) 2024-03-07 engine-flutter-autoroll@skia.org Roll Flutter Engine from 03ebd6460b83 to 6c1751bd774e (2 revisions) (flutter/flutter#144747) 2024-03-07 engine-flutter-autoroll@skia.org Roll Flutter Engine from 8a859c5b3a2d to 03ebd6460b83 (2 revisions) (flutter/flutter#144746) 2024-03-07 engine-flutter-autoroll@skia.org Roll Flutter Engine from 4f6ea31d1f25 to 8a859c5b3a2d (2 revisions) (flutter/flutter#144743) 2024-03-07 32538273+ValentinVignal@users.noreply.github.com Fix memory leak in `editable_gesture_test.dart` (flutter/flutter#144691) 2024-03-07 engine-flutter-autoroll@skia.org Roll Flutter Engine from 53ddbdfc24e5 to 4f6ea31d1f25 (2 revisions) (flutter/flutter#144741) 2024-03-07 engine-flutter-autoroll@skia.org Roll Flutter Engine from b2adf7471d3d to 53ddbdfc24e5 (1 revision) (flutter/flutter#144735) 2024-03-06 engine-flutter-autoroll@skia.org Roll Flutter Engine from 31bbe61dfa0d to b2adf7471d3d (1 revision) (flutter/flutter#144732) 2024-03-06 engine-flutter-autoroll@skia.org Roll Flutter Engine from 5bbac1a5c576 to 31bbe61dfa0d (3 revisions) (flutter/flutter#144724) 2024-03-06 magder@google.com Run macOS test on `dev/integration_tests/ui` (flutter/flutter#142735) 2024-03-06 kustermann@google.com Use wasm-compatible conditional import in timeline.dart, avoid emitting timeline events in SchedulerBinding (flutter/flutter#144682) 2024-03-06 engine-flutter-autoroll@skia.org Roll Flutter Engine from 44405aedba13 to 5bbac1a5c576 (2 revisions) (flutter/flutter#144714) 2024-03-06 54558023+keyonghan@users.noreply.github.com Bring back firebase tests to prod (flutter/flutter#144703) 2024-03-06 engine-flutter-autoroll@skia.org Roll Flutter Engine from 20037e385bda to 44405aedba13 (3 revisions) (flutter/flutter#144710) 2024-03-06 goderbauer@google.com Fix code sample failing in smoke test (flutter/flutter#144709) 2024-03-06 36861262+QuncCccccc@users.noreply.github.com Remove deprecated `errorColor` from `ThemeData` (flutter/flutter#144078) 2024-03-06 andrewrkolos@gmail.com make DevFSContent descendants immutable (flutter/flutter#144664) 2024-03-06 jonahwilliams@google.com [Impeller] measure GPU memory usage. (flutter/flutter#144575) 2024-03-06 engine-flutter-autoroll@skia.org Roll Flutter Engine from 9aad0e93899b to 20037e385bda (1 revision) (flutter/flutter#144707) 2024-03-06 engine-flutter-autoroll@skia.org Roll Flutter Engine from b6efe0dd88fe to 9aad0e93899b (2 revisions) (flutter/flutter#144702) 2024-03-06 34871572+gmackall@users.noreply.github.com Update android templates to use target sdk 34 (flutter/flutter#144641) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index db88c8b3da7e..d0cb6f552506 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -8f84f3f32ddcd271b603f429fd0a4946ac0d7351 +471a82856d860d0115e0b1d3ce594373287fcc2c From 0badb4356021085b02e5a4a788ce2c1d4e237b0d Mon Sep 17 00:00:00 2001 From: Kate Lovett Date: Thu, 7 Mar 2024 16:35:15 -0600 Subject: [PATCH 065/126] [two_dimensional_scrollables] Fix another combo of pinned/unpinned merged TableViewCells (#6283) Fixes https://github.com/flutter/flutter/issues/144100 Picks up doc fix from https://github.com/flutter/packages/pull/6154 as well --- .../two_dimensional_scrollables/CHANGELOG.md | 5 ++ .../lib/src/table_view/table.dart | 22 +++--- .../two_dimensional_scrollables/pubspec.yaml | 2 +- .../test/table_view/table_test.dart | 68 +++++++++++++++++++ 4 files changed, 86 insertions(+), 11 deletions(-) diff --git a/packages/two_dimensional_scrollables/CHANGELOG.md b/packages/two_dimensional_scrollables/CHANGELOG.md index 64d1a0a6a552..222468fa1776 100644 --- a/packages/two_dimensional_scrollables/CHANGELOG.md +++ b/packages/two_dimensional_scrollables/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.1.2 + +* Fixes a layout issue for unpinned merged cells that follow pinned table spans. +* Updates outdated sample code. + ## 0.1.1 * Fixes a layout issue when pinned cells are merged. diff --git a/packages/two_dimensional_scrollables/lib/src/table_view/table.dart b/packages/two_dimensional_scrollables/lib/src/table_view/table.dart index 917881a61c29..506204f917a7 100644 --- a/packages/two_dimensional_scrollables/lib/src/table_view/table.dart +++ b/packages/two_dimensional_scrollables/lib/src/table_view/table.dart @@ -49,8 +49,10 @@ import 'table_span.dart'; /// ```dart /// TableView.builder( /// cellBuilder: (BuildContext context, TableVicinity vicinity) { -/// return Center( -/// child: Text('Cell ${vicinity.column} : ${vicinity.row}'), +/// return TableViewCell( +/// child: Center( +/// child: Text('Cell ${vicinity.column} : ${vicinity.row}'), +/// ), /// ); /// }, /// columnCount: 10, @@ -809,10 +811,10 @@ class RenderTableViewport extends RenderTwoDimensionalViewport { switch ((rowIsInPinnedColumn, rowIsPinned)) { // Both row and column are pinned at this cell, or just pinned row. (true, true) || (false, true) => 0.0, - // Cell is within a pinned column - (true, false) => _pinnedRowsExtent - verticalOffset.pixels, - // Cell is within a pinned row, or no pinned portion. - (false, false) => -verticalOffset.pixels, + // Cell is within a pinned column, or no pinned area at all. + (true, false) || + (false, false) => + _pinnedRowsExtent - verticalOffset.pixels, }; mergedRowOffset = baseRowOffset + _rowMetrics[firstRow]!.leadingOffset + @@ -830,10 +832,10 @@ class RenderTableViewport extends RenderTwoDimensionalViewport { switch ((columnIsInPinnedRow, columnIsPinned)) { // Both row and column are pinned at this cell, or just pinned column. (true, true) || (false, true) => 0.0, - // Cell is within a pinned row. - (true, false) => _pinnedColumnsExtent - horizontalOffset.pixels, - // No pinned portion. - (false, false) => -horizontalOffset.pixels, + // Cell is within a pinned row, or no pinned area at all. + (true, false) || + (false, false) => + _pinnedColumnsExtent - horizontalOffset.pixels, }; mergedColumnOffset = baseColumnOffset + _columnMetrics[firstColumn]!.leadingOffset + diff --git a/packages/two_dimensional_scrollables/pubspec.yaml b/packages/two_dimensional_scrollables/pubspec.yaml index 8d04d42f6455..45f1d77ea6e1 100644 --- a/packages/two_dimensional_scrollables/pubspec.yaml +++ b/packages/two_dimensional_scrollables/pubspec.yaml @@ -1,6 +1,6 @@ name: two_dimensional_scrollables description: Widgets that scroll using the two dimensional scrolling foundation. -version: 0.1.1 +version: 0.1.2 repository: https://github.com/flutter/packages/tree/main/packages/two_dimensional_scrollables issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+two_dimensional_scrollables%22+ diff --git a/packages/two_dimensional_scrollables/test/table_view/table_test.dart b/packages/two_dimensional_scrollables/test/table_view/table_test.dart index c24d1927405e..de08873cfb2a 100644 --- a/packages/two_dimensional_scrollables/test/table_view/table_test.dart +++ b/packages/two_dimensional_scrollables/test/table_view/table_test.dart @@ -2207,6 +2207,74 @@ void main() { }); }); }); + + testWidgets( + 'Merged unpinned cells following pinned cells are laid out correctly', + (WidgetTester tester) async { + final ScrollController verticalController = ScrollController(); + final ScrollController horizontalController = ScrollController(); + final Set mergedCell = { + const TableVicinity(row: 2, column: 2), + const TableVicinity(row: 3, column: 2), + const TableVicinity(row: 2, column: 3), + const TableVicinity(row: 3, column: 3), + }; + final TableView tableView = TableView.builder( + columnCount: 10, + rowCount: 10, + columnBuilder: (_) => const TableSpan(extent: FixedTableSpanExtent(100)), + rowBuilder: (_) => const TableSpan(extent: FixedTableSpanExtent(100)), + cellBuilder: (BuildContext context, TableVicinity vicinity) { + if (mergedCell.contains(vicinity)) { + return const TableViewCell( + rowMergeStart: 2, + rowMergeSpan: 2, + columnMergeStart: 2, + columnMergeSpan: 2, + child: Text('Tile c: 2, r: 2'), + ); + } + return TableViewCell( + child: Text('Tile c: ${vicinity.column}, r: ${vicinity.row}'), + ); + }, + pinnedRowCount: 1, + pinnedColumnCount: 1, + verticalDetails: ScrollableDetails.vertical( + controller: verticalController, + ), + horizontalDetails: ScrollableDetails.horizontal( + controller: horizontalController, + ), + ); + await tester.pumpWidget(MaterialApp(home: tableView)); + await tester.pumpAndSettle(); + + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect( + tester.getRect(find.text('Tile c: 2, r: 2')), + const Rect.fromLTWH(200.0, 200.0, 200.0, 200.0), + ); + + verticalController.jumpTo(10.0); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 10.0); + expect(horizontalController.position.pixels, 0.0); + expect( + tester.getRect(find.text('Tile c: 2, r: 2')), + const Rect.fromLTWH(200.0, 190.0, 200.0, 200.0), + ); + + horizontalController.jumpTo(10.0); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 10.0); + expect(horizontalController.position.pixels, 10.0); + expect( + tester.getRect(find.text('Tile c: 2, r: 2')), + const Rect.fromLTWH(190.0, 190.0, 200.0, 200.0), + ); + }); } class _NullBuildContext implements BuildContext, TwoDimensionalChildManager { From 6860fdf8a753a5fa1726e2ac5814b8bb06a43b4b Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 8 Mar 2024 10:43:20 -0500 Subject: [PATCH 066/126] Roll Flutter (stable) from 7482962148e8 to ba3931984302 (2 revisions) (#6287) https://github.com/flutter/flutter/compare/7482962148e8...ba3931984302 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-stable-packages Please CC rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter (stable): https://github.com/flutter/flutter/issues/new/choose To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_stable.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_stable.version b/.ci/flutter_stable.version index 67aeb76b7edf..7f39f1a61c63 100644 --- a/.ci/flutter_stable.version +++ b/.ci/flutter_stable.version @@ -1 +1 @@ -7482962148e8d758338d8a28f589f317e1e42ba4 +ba393198430278b6595976de84fe170f553cc728 From a10b360a21531d03fb3795b7c083dda5c4796f96 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 8 Mar 2024 10:44:51 -0500 Subject: [PATCH 067/126] Roll Flutter from 471a82856d86 to 7c89ec8bbc6d (15 revisions) (#6288) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/flutter/flutter/compare/471a82856d86...7c89ec8bbc6d 2024-03-08 kustermann@google.com Remove `toString()` overrides in `dart:ui`/`package:flutter` in profile/release mode on wasm/vm targets (flutter/flutter#144763) 2024-03-08 engine-flutter-autoroll@skia.org Roll Flutter Engine from 80cd7981043f to bbb1ed00af49 (3 revisions) (flutter/flutter#144813) 2024-03-08 65758246+Nenuphar12@users.noreply.github.com Update documentation of `AlertDialog`'s default `TextStyle` for Material 3 (flutter/flutter#144697) 2024-03-08 engine-flutter-autoroll@skia.org Roll Flutter Engine from 175069397a40 to 80cd7981043f (5 revisions) (flutter/flutter#144804) 2024-03-07 andrewrkolos@gmail.com Enable asset transformation for `flutter run -d ` and `flutter test` (flutter/flutter#144734) 2024-03-07 engine-flutter-autoroll@skia.org Roll Flutter Engine from ec4f9af3d53b to 175069397a40 (2 revisions) (flutter/flutter#144799) 2024-03-07 git@reb0.org refactor: Remove `throwOnPluginPubspecError` flag for plugin validation (flutter/flutter#144214) 2024-03-07 engine-flutter-autoroll@skia.org Roll Flutter Engine from 305333c50191 to ec4f9af3d53b (1 revision) (flutter/flutter#144797) 2024-03-07 engine-flutter-autoroll@skia.org Roll Flutter Engine from c2863530ce39 to 305333c50191 (1 revision) (flutter/flutter#144793) 2024-03-07 faisal.hiyaz@gmail.com Fixed -> DropdownMenu throws exception when it is in any scrollable l… (flutter/flutter#140566) 2024-03-07 engine-flutter-autoroll@skia.org Roll Flutter Engine from 9e8ccaa3842e to c2863530ce39 (2 revisions) (flutter/flutter#144792) 2024-03-07 engine-flutter-autoroll@skia.org Roll Flutter Engine from 12828950890a to 9e8ccaa3842e (1 revision) (flutter/flutter#144784) 2024-03-07 fluttergithubbot@gmail.com Marks Mac_x64 module_test_ios to be flaky (flutter/flutter#144681) 2024-03-07 christopherfujino@gmail.com [flutter_tools] add custom tool analysis to analyze.dart, lint Future.catchError (flutter/flutter#140122) 2024-03-07 engine-flutter-autoroll@skia.org Roll Flutter Engine from f8c3b2db8cd1 to 12828950890a (1 revision) (flutter/flutter#144775) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index d0cb6f552506..801c9866ec53 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -471a82856d860d0115e0b1d3ce594373287fcc2c +7c89ec8bbc6da2b8c59d18615a2c31c5b404d77b From d489d84c359b5ad6849ece9b97c75e1925ed762d Mon Sep 17 00:00:00 2001 From: Reid Baker Date: Fri, 8 Mar 2024 17:48:39 -0500 Subject: [PATCH 068/126] [in_app_purchase_android] Add UserChoiceBilling mode. (#6162) Add UserChoiceBilling billing mode option. Fixes flutter/flutter/issues/143004 Left in draft until: This does not have an End to end working example with play integration. I am currently stuck at the server side play integration part. --- .../in_app_purchase_android/CHANGELOG.md | 3 +- .../inapppurchase/BillingClientFactory.java | 7 +- .../BillingClientFactoryImpl.java | 34 ++++- .../inapppurchase/MethodCallHandlerImpl.java | 21 ++- .../plugins/inapppurchase/Translator.java | 30 ++++ .../inapppurchase/MethodCallHandlerTest.java | 126 ++++++++++++++++- .../in_app_purchase_test.dart | 3 +- .../example/lib/main.dart | 47 +++++++ .../lib/billing_client_wrappers.dart | 1 + .../billing_client_manager.dart | 26 +++- .../billing_client_wrapper.dart | 41 +++++- .../billing_client_wrapper.g.dart | 1 + .../user_choice_details_wrapper.dart | 131 ++++++++++++++++++ .../user_choice_details_wrapper.g.dart | 45 ++++++ ...pp_purchase_android_platform_addition.dart | 17 ++- .../google_play_user_choice_details.dart | 101 ++++++++++++++ .../lib/src/types/translator.dart | 47 +++++++ .../lib/src/types/types.dart | 1 + .../in_app_purchase_android/pubspec.yaml | 2 +- .../billing_client_manager_test.dart | 27 ++++ .../billing_client_wrapper_test.dart | 92 +++++++++++- ...rchase_android_platform_addition_test.dart | 25 ++++ .../test/types/translator_test.dart | 71 ++++++++++ 23 files changed, 874 insertions(+), 25 deletions(-) create mode 100644 packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/user_choice_details_wrapper.dart create mode 100644 packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/user_choice_details_wrapper.g.dart create mode 100644 packages/in_app_purchase/in_app_purchase_android/lib/src/types/google_play_user_choice_details.dart create mode 100644 packages/in_app_purchase/in_app_purchase_android/lib/src/types/translator.dart create mode 100644 packages/in_app_purchase/in_app_purchase_android/test/types/translator_test.dart diff --git a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md index 896e9f958995..223d0441a788 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.3.2 +* Adds UserChoiceBilling APIs to platform addition. * Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. ## 0.3.1 diff --git a/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/BillingClientFactory.java b/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/BillingClientFactory.java index 9324c9367ee8..10ce08d63fc8 100644 --- a/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/BillingClientFactory.java +++ b/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/BillingClientFactory.java @@ -6,7 +6,9 @@ import android.content.Context; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.billingclient.api.BillingClient; +import com.android.billingclient.api.UserChoiceBillingListener; import io.flutter.plugin.common.MethodChannel; /** Responsible for creating a {@link BillingClient} object. */ @@ -22,5 +24,8 @@ interface BillingClientFactory { * @return The {@link BillingClient} object that is created. */ BillingClient createBillingClient( - @NonNull Context context, @NonNull MethodChannel channel, int billingChoiceMode); + @NonNull Context context, + @NonNull MethodChannel channel, + int billingChoiceMode, + @Nullable UserChoiceBillingListener userChoiceBillingListener); } diff --git a/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/BillingClientFactoryImpl.java b/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/BillingClientFactoryImpl.java index c6911f8314a3..4c53951a495a 100644 --- a/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/BillingClientFactoryImpl.java +++ b/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/BillingClientFactoryImpl.java @@ -6,7 +6,10 @@ import android.content.Context; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.billingclient.api.BillingClient; +import com.android.billingclient.api.UserChoiceBillingListener; +import io.flutter.Log; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugins.inapppurchase.MethodCallHandlerImpl.BillingChoiceMode; @@ -15,11 +18,34 @@ final class BillingClientFactoryImpl implements BillingClientFactory { @Override public BillingClient createBillingClient( - @NonNull Context context, @NonNull MethodChannel channel, int billingChoiceMode) { + @NonNull Context context, + @NonNull MethodChannel channel, + int billingChoiceMode, + @Nullable UserChoiceBillingListener userChoiceBillingListener) { BillingClient.Builder builder = BillingClient.newBuilder(context).enablePendingPurchases(); - if (billingChoiceMode == BillingChoiceMode.ALTERNATIVE_BILLING_ONLY) { - // https://developer.android.com/google/play/billing/alternative/alternative-billing-without-user-choice-in-app - builder.enableAlternativeBillingOnly(); + switch (billingChoiceMode) { + case BillingChoiceMode.ALTERNATIVE_BILLING_ONLY: + // https://developer.android.com/google/play/billing/alternative/alternative-billing-without-user-choice-in-app + builder.enableAlternativeBillingOnly(); + break; + case BillingChoiceMode.USER_CHOICE_BILLING: + if (userChoiceBillingListener != null) { + // https://developer.android.com/google/play/billing/alternative/alternative-billing-with-user-choice-in-app + builder.enableUserChoiceBilling(userChoiceBillingListener); + } else { + Log.e( + "BillingClientFactoryImpl", + "userChoiceBillingListener null when USER_CHOICE_BILLING set. Defaulting to PLAY_BILLING_ONLY"); + } + break; + case BillingChoiceMode.PLAY_BILLING_ONLY: + // Do nothing. + break; + default: + Log.e( + "BillingClientFactoryImpl", + "Unknown BillingChoiceMode " + billingChoiceMode + ", Defaulting to PLAY_BILLING_ONLY"); + break; } return builder.setListener(new PluginPurchaseListener(channel)).build(); } diff --git a/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java b/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java index ecf88747779a..0343a8a9d40b 100644 --- a/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java +++ b/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java @@ -10,6 +10,7 @@ import static io.flutter.plugins.inapppurchase.Translator.fromProductDetailsList; import static io.flutter.plugins.inapppurchase.Translator.fromPurchaseHistoryRecordList; import static io.flutter.plugins.inapppurchase.Translator.fromPurchasesList; +import static io.flutter.plugins.inapppurchase.Translator.fromUserChoiceDetails; import static io.flutter.plugins.inapppurchase.Translator.toProductList; import android.app.Activity; @@ -33,6 +34,7 @@ import com.android.billingclient.api.QueryProductDetailsParams.Product; import com.android.billingclient.api.QueryPurchaseHistoryParams; import com.android.billingclient.api.QueryPurchasesParams; +import com.android.billingclient.api.UserChoiceBillingListener; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import java.util.ArrayList; @@ -72,6 +74,8 @@ static final class MethodNames { "BillingClient#createAlternativeBillingOnlyReportingDetails()"; static final String SHOW_ALTERNATIVE_BILLING_ONLY_INFORMATION_DIALOG = "BillingClient#showAlternativeBillingOnlyInformationDialog()"; + static final String USER_SELECTED_ALTERNATIVE_BILLING = + "UserChoiceBillingListener#userSelectedAlternativeBilling(UserChoiceDetails)"; private MethodNames() {} } @@ -94,6 +98,7 @@ private MethodArgs() {} static final class BillingChoiceMode { static final int PLAY_BILLING_ONLY = 0; static final int ALTERNATIVE_BILLING_ONLY = 1; + static final int USER_CHOICE_BILLING = 2; } // TODO(gmackall): Replace uses of deprecated ProrationMode enum values with new @@ -507,9 +512,10 @@ private void getConnectionState(final MethodChannel.Result result) { private void startConnection( final int handle, final MethodChannel.Result result, int billingChoiceMode) { if (billingClient == null) { + UserChoiceBillingListener listener = getUserChoiceBillingListener(billingChoiceMode); billingClient = billingClientFactory.createBillingClient( - applicationContext, methodChannel, billingChoiceMode); + applicationContext, methodChannel, billingChoiceMode, listener); } billingClient.startConnection( @@ -537,6 +543,19 @@ public void onBillingServiceDisconnected() { }); } + @Nullable + private UserChoiceBillingListener getUserChoiceBillingListener(int billingChoiceMode) { + UserChoiceBillingListener listener = null; + if (billingChoiceMode == BillingChoiceMode.USER_CHOICE_BILLING) { + listener = + userChoiceDetails -> { + final Map arguments = fromUserChoiceDetails(userChoiceDetails); + methodChannel.invokeMethod(MethodNames.USER_SELECTED_ALTERNATIVE_BILLING, arguments); + }; + } + return listener; + } + private void acknowledgePurchase(String purchaseToken, final MethodChannel.Result result) { if (billingClientError(result)) { return; diff --git a/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/Translator.java b/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/Translator.java index 8c80b1797eda..f9e91659bc23 100644 --- a/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/Translator.java +++ b/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/Translator.java @@ -14,6 +14,8 @@ import com.android.billingclient.api.Purchase; import com.android.billingclient.api.PurchaseHistoryRecord; import com.android.billingclient.api.QueryProductDetailsParams; +import com.android.billingclient.api.UserChoiceDetails; +import com.android.billingclient.api.UserChoiceDetails.Product; import java.util.ArrayList; import java.util.Collections; import java.util.Currency; @@ -233,6 +235,34 @@ static HashMap fromBillingResult(BillingResult billingResult) { return info; } + static HashMap fromUserChoiceDetails(UserChoiceDetails userChoiceDetails) { + HashMap info = new HashMap<>(); + info.put("externalTransactionToken", userChoiceDetails.getExternalTransactionToken()); + info.put("originalExternalTransactionId", userChoiceDetails.getOriginalExternalTransactionId()); + info.put("products", fromProductsList(userChoiceDetails.getProducts())); + return info; + } + + static List> fromProductsList(List productsList) { + if (productsList.isEmpty()) { + return Collections.emptyList(); + } + ArrayList> output = new ArrayList<>(); + for (Product product : productsList) { + output.add(fromProduct(product)); + } + return output; + } + + static HashMap fromProduct(Product product) { + HashMap info = new HashMap<>(); + info.put("id", product.getId()); + info.put("offerToken", product.getOfferToken()); + info.put("productType", product.getType()); + + return info; + } + /** Converter from {@link BillingResult} and {@link BillingConfig} to map. */ static HashMap fromBillingConfig( BillingResult result, BillingConfig billingConfig) { diff --git a/packages/in_app_purchase/in_app_purchase_android/android/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java b/packages/in_app_purchase/in_app_purchase_android/android/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java index bfdf928ca511..42acbf5f8642 100644 --- a/packages/in_app_purchase/in_app_purchase_android/android/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java +++ b/packages/in_app_purchase/in_app_purchase_android/android/src/test/java/io/flutter/plugins/inapppurchase/MethodCallHandlerTest.java @@ -20,6 +20,7 @@ import static io.flutter.plugins.inapppurchase.MethodCallHandlerImpl.MethodNames.QUERY_PURCHASE_HISTORY_ASYNC; import static io.flutter.plugins.inapppurchase.MethodCallHandlerImpl.MethodNames.SHOW_ALTERNATIVE_BILLING_ONLY_INFORMATION_DIALOG; import static io.flutter.plugins.inapppurchase.MethodCallHandlerImpl.MethodNames.START_CONNECTION; +import static io.flutter.plugins.inapppurchase.MethodCallHandlerImpl.MethodNames.USER_SELECTED_ALTERNATIVE_BILLING; import static io.flutter.plugins.inapppurchase.PluginPurchaseListener.ON_PURCHASES_UPDATED; import static io.flutter.plugins.inapppurchase.Translator.fromAlternativeBillingOnlyReportingDetails; import static io.flutter.plugins.inapppurchase.Translator.fromBillingConfig; @@ -27,6 +28,7 @@ import static io.flutter.plugins.inapppurchase.Translator.fromProductDetailsList; import static io.flutter.plugins.inapppurchase.Translator.fromPurchaseHistoryRecordList; import static io.flutter.plugins.inapppurchase.Translator.fromPurchasesList; +import static io.flutter.plugins.inapppurchase.Translator.fromUserChoiceDetails; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static java.util.Collections.unmodifiableList; @@ -73,6 +75,8 @@ import com.android.billingclient.api.QueryProductDetailsParams; import com.android.billingclient.api.QueryPurchaseHistoryParams; import com.android.billingclient.api.QueryPurchasesParams; +import com.android.billingclient.api.UserChoiceBillingListener; +import com.android.billingclient.api.UserChoiceDetails; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; @@ -82,6 +86,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -92,6 +97,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.Spy; import org.mockito.stubbing.Answer; @@ -107,15 +113,23 @@ public class MethodCallHandlerTest { @Mock ActivityPluginBinding mockActivityPluginBinding; @Captor ArgumentCaptor> resultCaptor; + private final int DEFAULT_HANDLE = 1; + @Before public void setUp() { MockitoAnnotations.openMocks(this); // Use the same client no matter if alternative billing is enabled or not. when(factory.createBillingClient( - context, mockMethodChannel, BillingChoiceMode.PLAY_BILLING_ONLY)) + context, mockMethodChannel, BillingChoiceMode.PLAY_BILLING_ONLY, null)) + .thenReturn(mockBillingClient); + when(factory.createBillingClient( + context, mockMethodChannel, BillingChoiceMode.ALTERNATIVE_BILLING_ONLY, null)) .thenReturn(mockBillingClient); when(factory.createBillingClient( - context, mockMethodChannel, BillingChoiceMode.ALTERNATIVE_BILLING_ONLY)) + any(Context.class), + any(MethodChannel.class), + eq(BillingChoiceMode.USER_CHOICE_BILLING), + any(UserChoiceBillingListener.class))) .thenReturn(mockBillingClient); methodChannelHandler = new MethodCallHandlerImpl(activity, context, mockMethodChannel, factory); when(mockActivityPluginBinding.getActivity()).thenReturn(activity); @@ -164,7 +178,7 @@ public void startConnection() { mockStartConnection(BillingChoiceMode.PLAY_BILLING_ONLY); verify(result, never()).success(any()); verify(factory, times(1)) - .createBillingClient(context, mockMethodChannel, BillingChoiceMode.PLAY_BILLING_ONLY); + .createBillingClient(context, mockMethodChannel, BillingChoiceMode.PLAY_BILLING_ONLY, null); BillingResult billingResult = BillingResult.newBuilder() @@ -183,7 +197,7 @@ public void startConnectionAlternativeBillingOnly() { verify(result, never()).success(any()); verify(factory, times(1)) .createBillingClient( - context, mockMethodChannel, BillingChoiceMode.ALTERNATIVE_BILLING_ONLY); + context, mockMethodChannel, BillingChoiceMode.ALTERNATIVE_BILLING_ONLY, null); BillingResult billingResult = BillingResult.newBuilder() @@ -209,7 +223,7 @@ public void startConnectionAlternativeBillingUnset() { methodChannelHandler.onMethodCall(call, result); verify(result, never()).success(any()); verify(factory, times(1)) - .createBillingClient(context, mockMethodChannel, BillingChoiceMode.PLAY_BILLING_ONLY); + .createBillingClient(context, mockMethodChannel, BillingChoiceMode.PLAY_BILLING_ONLY, null); BillingResult billingResult = BillingResult.newBuilder() @@ -221,6 +235,106 @@ public void startConnectionAlternativeBillingUnset() { verify(result, times(1)).success(fromBillingResult(billingResult)); } + @Test + public void startConnectionUserChoiceBilling() { + ArgumentCaptor captor = + mockStartConnection(BillingChoiceMode.USER_CHOICE_BILLING); + ArgumentCaptor billingCaptor = + ArgumentCaptor.forClass(UserChoiceBillingListener.class); + verify(result, never()).success(any()); + verify(factory, times(1)) + .createBillingClient( + any(Context.class), + any(MethodChannel.class), + eq(BillingChoiceMode.USER_CHOICE_BILLING), + billingCaptor.capture()); + + BillingResult billingResult = + BillingResult.newBuilder() + .setResponseCode(100) + .setDebugMessage("dummy debug message") + .build(); + captor.getValue().onBillingSetupFinished(billingResult); + + verify(result, times(1)).success(fromBillingResult(billingResult)); + UserChoiceDetails details = mock(UserChoiceDetails.class); + final String externalTransactionToken = "someLongTokenId1234"; + final String originalTransactionId = "originalTransactionId123456"; + when(details.getExternalTransactionToken()).thenReturn(externalTransactionToken); + when(details.getOriginalExternalTransactionId()).thenReturn(originalTransactionId); + when(details.getProducts()).thenReturn(Collections.emptyList()); + billingCaptor.getValue().userSelectedAlternativeBilling(details); + + verify(mockMethodChannel, times(1)) + .invokeMethod(USER_SELECTED_ALTERNATIVE_BILLING, fromUserChoiceDetails(details)); + } + + @Test + public void userChoiceBillingOnSecondConnection() { + // First connection. + ArgumentCaptor captor1 = + mockStartConnection(BillingChoiceMode.PLAY_BILLING_ONLY); + verify(result, never()).success(any()); + verify(factory, times(1)) + .createBillingClient(context, mockMethodChannel, BillingChoiceMode.PLAY_BILLING_ONLY, null); + + BillingResult billingResult1 = + BillingResult.newBuilder() + .setResponseCode(100) + .setDebugMessage("dummy debug message") + .build(); + final BillingClientStateListener stateListener = captor1.getValue(); + stateListener.onBillingSetupFinished(billingResult1); + verify(result, times(1)).success(fromBillingResult(billingResult1)); + Mockito.reset(result, mockMethodChannel, mockBillingClient); + + // Disconnect + MethodCall disconnectCall = new MethodCall(END_CONNECTION, null); + methodChannelHandler.onMethodCall(disconnectCall, result); + + // Verify that the client is disconnected and that the OnDisconnect callback has + // been triggered + verify(result, times(1)).success(any()); + verify(mockBillingClient, times(1)).endConnection(); + stateListener.onBillingServiceDisconnected(); + Map expectedInvocation = new HashMap<>(); + expectedInvocation.put("handle", DEFAULT_HANDLE); + verify(mockMethodChannel, times(1)).invokeMethod(ON_DISCONNECT, expectedInvocation); + Mockito.reset(result, mockMethodChannel, mockBillingClient); + + // Second connection. + ArgumentCaptor captor2 = + mockStartConnection(BillingChoiceMode.USER_CHOICE_BILLING); + ArgumentCaptor billingCaptor = + ArgumentCaptor.forClass(UserChoiceBillingListener.class); + verify(result, never()).success(any()); + verify(factory, times(1)) + .createBillingClient( + any(Context.class), + any(MethodChannel.class), + eq(BillingChoiceMode.USER_CHOICE_BILLING), + billingCaptor.capture()); + + BillingResult billingResult2 = + BillingResult.newBuilder() + .setResponseCode(100) + .setDebugMessage("dummy debug message") + .build(); + captor2.getValue().onBillingSetupFinished(billingResult2); + + verify(result, times(1)).success(fromBillingResult(billingResult2)); + UserChoiceDetails details = mock(UserChoiceDetails.class); + final String externalTransactionToken = "someLongTokenId1234"; + final String originalTransactionId = "originalTransactionId123456"; + when(details.getExternalTransactionToken()).thenReturn(externalTransactionToken); + when(details.getOriginalExternalTransactionId()).thenReturn(originalTransactionId); + when(details.getProducts()).thenReturn(Collections.emptyList()); + billingCaptor.getValue().userSelectedAlternativeBilling(details); + + verify(mockMethodChannel, times(1)) + .invokeMethod(USER_SELECTED_ALTERNATIVE_BILLING, fromUserChoiceDetails(details)); + } + @Test public void startConnection_multipleCalls() { Map arguments = new HashMap<>(); @@ -1071,7 +1185,7 @@ private ArgumentCaptor mockStartConnection() { */ private ArgumentCaptor mockStartConnection(int billingChoiceMode) { Map arguments = new HashMap<>(); - arguments.put(MethodArgs.HANDLE, 1); + arguments.put(MethodArgs.HANDLE, DEFAULT_HANDLE); arguments.put(MethodArgs.BILLING_CHOICE_MODE, billingChoiceMode); MethodCall call = new MethodCall(START_CONNECTION, arguments); ArgumentCaptor captor = diff --git a/packages/in_app_purchase/in_app_purchase_android/example/integration_test/in_app_purchase_test.dart b/packages/in_app_purchase/in_app_purchase_android/example/integration_test/in_app_purchase_test.dart index 568becd7188c..8ed5de2ce4b6 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/integration_test/in_app_purchase_test.dart +++ b/packages/in_app_purchase/in_app_purchase_android/example/integration_test/in_app_purchase_test.dart @@ -27,7 +27,8 @@ void main() { late final BillingClient billingClient; setUpAll(() { - billingClient = BillingClient((PurchasesResultWrapper _) {}); + billingClient = BillingClient( + (PurchasesResultWrapper _) {}, (UserChoiceDetailsWrapper _) {}); }); testWidgets('BillingClient.acknowledgePurchase', diff --git a/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart index fbae24a5dcfd..89883a1564ad 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_android/example/lib/main.dart @@ -44,6 +44,7 @@ class _MyAppState extends State<_MyApp> { final InAppPurchasePlatform _inAppPurchasePlatform = InAppPurchasePlatform.instance; late StreamSubscription> _subscription; + late StreamSubscription _userChoiceDetailsStream; List _notFoundIds = []; List _products = []; List _purchases = []; @@ -56,6 +57,7 @@ class _MyAppState extends State<_MyApp> { bool _purchasePending = false; bool _loading = true; String? _queryProductError; + final List _userChoiceDetailsList = []; @override void initState() { @@ -70,6 +72,19 @@ class _MyAppState extends State<_MyApp> { // handle error here. }); initStoreInfo(); + final InAppPurchaseAndroidPlatformAddition addition = + InAppPurchasePlatformAddition.instance! + as InAppPurchaseAndroidPlatformAddition; + final Stream userChoiceDetailsUpdated = + addition.userChoiceDetailsStream; + _userChoiceDetailsStream = + userChoiceDetailsUpdated.listen((GooglePlayUserChoiceDetails details) { + deliverUserChoiceDetails(details); + }, onDone: () { + _userChoiceDetailsStream.cancel(); + }, onError: (Object error) { + // handle error here. + }); super.initState(); } @@ -134,6 +149,8 @@ class _MyAppState extends State<_MyApp> { @override void dispose() { _subscription.cancel(); + _userChoiceDetailsStream.cancel(); + _userChoiceDetailsList.clear(); super.dispose(); } @@ -149,6 +166,7 @@ class _MyAppState extends State<_MyApp> { _buildConsumableBox(), const _FeatureCard(), _buildFetchButtons(), + _buildUserChoiceDetailsDisplay(), ], ), ); @@ -326,6 +344,26 @@ class _MyAppState extends State<_MyApp> { ); } + Card _buildUserChoiceDetailsDisplay() { + const ListTile header = ListTile(title: Text('UserChoiceDetails')); + final List entries = []; + for (final String item in _userChoiceDetailsList) { + entries.add(ListTile( + title: Text(item, + style: TextStyle(color: ThemeData.light().colorScheme.primary)), + subtitle: Text(_countryCode))); + } + return Card( + child: Column( + children: [ + header, + const Divider(), + ...entries, + ], + ), + ); + } + Card _buildProductList() { if (_loading) { return const Card( @@ -537,6 +575,15 @@ class _MyAppState extends State<_MyApp> { // handle invalid purchase here if _verifyPurchase` failed. } + Future deliverUserChoiceDetails( + GooglePlayUserChoiceDetails details) async { + final String detailDescription = + '${details.externalTransactionToken}, ${details.originalExternalTransactionId}, ${details.products.length}'; + setState(() { + _userChoiceDetailsList.add(detailDescription); + }); + } + Future _listenToPurchaseUpdated( List purchaseDetailsList) async { for (final PurchaseDetails purchaseDetails in purchaseDetailsList) { diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/billing_client_wrappers.dart b/packages/in_app_purchase/in_app_purchase_android/lib/billing_client_wrappers.dart index 8a53b95e9a7e..508b80e05f7d 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/billing_client_wrappers.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/billing_client_wrappers.dart @@ -11,3 +11,4 @@ export 'src/billing_client_wrappers/product_details_wrapper.dart'; export 'src/billing_client_wrappers/product_wrapper.dart'; export 'src/billing_client_wrappers/purchase_wrapper.dart'; export 'src/billing_client_wrappers/subscription_offer_details_wrapper.dart'; +export 'src/billing_client_wrappers/user_choice_details_wrapper.dart'; diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_manager.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_manager.dart index 789ba5e01cc3..e9a3b50ce5cb 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_manager.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_manager.dart @@ -8,6 +8,7 @@ import 'package:flutter/widgets.dart'; import 'billing_client_wrapper.dart'; import 'purchase_wrapper.dart'; +import 'user_choice_details_wrapper.dart'; /// Abstraction of result of [BillingClient] operation that includes /// a [BillingResponse]. @@ -37,6 +38,13 @@ class BillingClientManager { _connect(); } + /// Stream of `userSelectedAlternativeBilling` events from the [BillingClient]. + /// + /// This is a broadcast stream, so it can be listened to multiple times. + /// A "done" event will be sent after [dispose] is called. + late final Stream userChoiceDetailsStream = + _userChoiceAlternativeBillingController.stream; + /// Stream of `onPurchasesUpdated` events from the [BillingClient]. /// /// This is a broadcast stream, so it can be listened to multiple times. @@ -49,10 +57,14 @@ class BillingClientManager { /// In order to access the [BillingClient], use [runWithClient] /// and [runWithClientNonRetryable] methods. @visibleForTesting - late final BillingClient client = BillingClient(_onPurchasesUpdated); + late final BillingClient client = + BillingClient(_onPurchasesUpdated, onUserChoiceAlternativeBilling); final StreamController _purchasesUpdatedController = StreamController.broadcast(); + final StreamController + _userChoiceAlternativeBillingController = + StreamController.broadcast(); BillingChoiceMode _billingChoiceMode; bool _isConnecting = false; @@ -113,12 +125,14 @@ class BillingClientManager { /// After calling [dispose]: /// - Further connection attempts will not be made. /// - [purchasesUpdatedStream] will be closed. + /// - [userChoiceDetailsStream] will be closed. /// - Calls to [runWithClient] and [runWithClientNonRetryable] will throw. void dispose() { _debugAssertNotDisposed(); _isDisposed = true; client.endConnection(); _purchasesUpdatedController.close(); + _userChoiceAlternativeBillingController.close(); } /// Ends connection to [BillingClient] and reconnects with [billingChoiceMode]. @@ -168,4 +182,14 @@ class BillingClientManager { 'called dispose() on a BillingClientManager, it can no longer be used.', ); } + + /// Callback passed to [BillingClient] to use when customer chooses + /// alternative billing. + @visibleForTesting + void onUserChoiceAlternativeBilling(UserChoiceDetailsWrapper event) { + if (_isDisposed) { + return; + } + _userChoiceAlternativeBillingController.add(event); + } } diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart index 15dc4217fe69..b3684d8177f4 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.dart @@ -18,6 +18,12 @@ part 'billing_client_wrapper.g.dart'; @visibleForTesting const String kOnPurchasesUpdated = 'PurchasesUpdatedListener#onPurchasesUpdated(BillingResult, List)'; + +/// Method identifier for the userSelectedAlternativeBilling method channel method. +@visibleForTesting +const String kUserSelectedAlternativeBilling = + 'UserChoiceBillingListener#userSelectedAlternativeBilling(UserChoiceDetails)'; + const String _kOnBillingServiceDisconnected = 'BillingClientStateListener#onBillingServiceDisconnected()'; @@ -40,6 +46,10 @@ const String _kOnBillingServiceDisconnected = typedef PurchasesUpdatedListener = void Function( PurchasesResultWrapper purchasesResult); +/// Wraps a [UserChoiceBillingListener](https://developer.android.com/reference/com/android/billingclient/api/UserChoiceBillingListener) +typedef UserSelectedAlternativeBillingListener = void Function( + UserChoiceDetailsWrapper userChoiceDetailsWrapper); + /// This class can be used directly instead of [InAppPurchaseConnection] to call /// Play-specific billing APIs. /// @@ -60,11 +70,16 @@ typedef PurchasesUpdatedListener = void Function( /// transparently. class BillingClient { /// Creates a billing client. - BillingClient(PurchasesUpdatedListener onPurchasesUpdated) { + BillingClient(PurchasesUpdatedListener onPurchasesUpdated, + UserSelectedAlternativeBillingListener? alternativeBillingListener) { channel.setMethodCallHandler(callHandler); _callbacks[kOnPurchasesUpdated] = [ onPurchasesUpdated ]; + _callbacks[kUserSelectedAlternativeBilling] = alternativeBillingListener == + null + ? [] + : [alternativeBillingListener]; } // Occasionally methods in the native layer require a Dart callback to be @@ -114,7 +129,8 @@ class BillingClient { BillingChoiceMode.playBillingOnly}) async { final List disconnectCallbacks = _callbacks[_kOnBillingServiceDisconnected] ??= []; - disconnectCallbacks.add(onBillingServiceDisconnected); + _callbacks[_kOnBillingServiceDisconnected] + ?.add(onBillingServiceDisconnected); return BillingResultWrapper.fromJson((await channel .invokeMapMethod( 'BillingClient#startConnection(BillingClientStateListener)', @@ -412,6 +428,15 @@ class BillingClient { _callbacks[_kOnBillingServiceDisconnected]! .cast(); onDisconnected[handle](); + case kUserSelectedAlternativeBilling: + if (_callbacks[kUserSelectedAlternativeBilling]!.isNotEmpty) { + final UserSelectedAlternativeBillingListener listener = + _callbacks[kUserSelectedAlternativeBilling]!.first + as UserSelectedAlternativeBillingListener; + listener(UserChoiceDetailsWrapper.fromJson( + (call.arguments as Map) + .cast())); + } } } } @@ -443,7 +468,7 @@ enum BillingResponse { @JsonValue(-2) featureNotSupported, - /// The play Store service is not connected now - potentially transient state. + /// The Play Store service is not connected now - potentially transient state. @JsonValue(-1) serviceDisconnected, @@ -490,8 +515,8 @@ enum BillingResponse { /// Plugin concept to cover billing modes. /// -/// [playBillingOnly] (google play billing only). -/// [alternativeBillingOnly] (app provided billing with reporting to play). +/// [playBillingOnly] (google Play billing only). +/// [alternativeBillingOnly] (app provided billing with reporting to Play). @JsonEnum(alwaysCreate: true) enum BillingChoiceMode { // WARNING: Changes to this class need to be reflected in our generated code. @@ -500,13 +525,17 @@ enum BillingChoiceMode { // Values must match what is used in // in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/MethodCallHandlerImpl.java - /// Billing through google play. Default state. + /// Billing through google Play. Default state. @JsonValue(0) playBillingOnly, /// Billing through app provided flow. @JsonValue(1) alternativeBillingOnly, + + /// Users can choose Play billing or alternative billing. + @JsonValue(2) + userChoiceBilling, } /// Serializer for [BillingChoiceMode]. diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.g.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.g.dart index eb7b41afce14..0768c35deec8 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.g.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/billing_client_wrapper.g.dart @@ -25,6 +25,7 @@ const _$BillingResponseEnumMap = { const _$BillingChoiceModeEnumMap = { BillingChoiceMode.playBillingOnly: 0, BillingChoiceMode.alternativeBillingOnly: 1, + BillingChoiceMode.userChoiceBilling: 2, }; const _$ProductTypeEnumMap = { diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/user_choice_details_wrapper.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/user_choice_details_wrapper.dart new file mode 100644 index 000000000000..abdc31a178b0 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/user_choice_details_wrapper.dart @@ -0,0 +1,131 @@ +// 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/foundation.dart'; +import 'package:json_annotation/json_annotation.dart'; + +import '../../billing_client_wrappers.dart'; + +// WARNING: Changes to `@JsonSerializable` classes need to be reflected in the +// below generated file. Run `flutter packages pub run build_runner watch` to +// rebuild and watch for further changes. +part 'user_choice_details_wrapper.g.dart'; + +/// This wraps [`com.android.billingclient.api.UserChoiceDetails`](https://developer.android.com/reference/com/android/billingclient/api/UserChoiceDetails) +// See https://docs.flutter.dev/data-and-backend/serialization/json#generating-code-for-nested-classes +// for explination for why this uses explicitToJson. +@JsonSerializable(createToJson: true, explicitToJson: true) +@immutable +class UserChoiceDetailsWrapper { + /// Creates a purchase wrapper with the given purchase details. + @visibleForTesting + const UserChoiceDetailsWrapper({ + required this.originalExternalTransactionId, + required this.externalTransactionToken, + required this.products, + }); + + /// Factory for creating a [UserChoiceDetailsWrapper] from a [Map] with + /// the user choice details. + factory UserChoiceDetailsWrapper.fromJson(Map map) => + _$UserChoiceDetailsWrapperFromJson(map); + + /// Creates a JSON representation of this product. + Map toJson() => _$UserChoiceDetailsWrapperToJson(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is UserChoiceDetailsWrapper && + other.originalExternalTransactionId == originalExternalTransactionId && + other.externalTransactionToken == externalTransactionToken && + listEquals(other.products, products); + } + + @override + int get hashCode => Object.hash( + originalExternalTransactionId, + externalTransactionToken, + products.hashCode, + ); + + /// Returns the external transaction Id of the originating subscription, if + /// the purchase is a subscription upgrade/downgrade. + @JsonKey(defaultValue: '') + final String originalExternalTransactionId; + + /// Returns a token that represents the user's prospective purchase via + /// user choice alternative billing. + @JsonKey(defaultValue: '') + final String externalTransactionToken; + + /// Returns a list of [UserChoiceDetailsProductWrapper] to be purchased in + /// the user choice alternative billing flow. + @JsonKey(defaultValue: []) + final List products; +} + +/// Data structure representing a UserChoiceDetails product. +/// +/// This wraps [`com.android.billingclient.api.UserChoiceDetails.Product`](https://developer.android.com/reference/com/android/billingclient/api/UserChoiceDetails.Product) +// +// See https://docs.flutter.dev/data-and-backend/serialization/json#generating-code-for-nested-classes +// for explination for why this uses explicitToJson. +@JsonSerializable(createToJson: true, explicitToJson: true) +@ProductTypeConverter() +@immutable +class UserChoiceDetailsProductWrapper { + /// Creates a [UserChoiceDetailsProductWrapper] with the given record details. + @visibleForTesting + const UserChoiceDetailsProductWrapper({ + required this.id, + required this.offerToken, + required this.productType, + }); + + /// Factory for creating a [UserChoiceDetailsProductWrapper] from a [Map] with the record details. + factory UserChoiceDetailsProductWrapper.fromJson(Map map) => + _$UserChoiceDetailsProductWrapperFromJson(map); + + /// Creates a JSON representation of this product. + Map toJson() => + _$UserChoiceDetailsProductWrapperToJson(this); + + /// Returns the id of the product being purchased. + @JsonKey(defaultValue: '') + final String id; + + /// Returns the offer token that was passed in launchBillingFlow to purchase the product. + @JsonKey(defaultValue: '') + final String offerToken; + + /// Returns the [ProductType] of the product being purchased. + final ProductType productType; + + @override + bool operator ==(Object other) { + if (identical(other, this)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is UserChoiceDetailsProductWrapper && + other.id == id && + other.offerToken == offerToken && + other.productType == productType; + } + + @override + int get hashCode => Object.hash( + id, + offerToken, + productType, + ); +} diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/user_choice_details_wrapper.g.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/user_choice_details_wrapper.g.dart new file mode 100644 index 000000000000..669d263588d6 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/user_choice_details_wrapper.g.dart @@ -0,0 +1,45 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'user_choice_details_wrapper.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +UserChoiceDetailsWrapper _$UserChoiceDetailsWrapperFromJson(Map json) => + UserChoiceDetailsWrapper( + originalExternalTransactionId: + json['originalExternalTransactionId'] as String? ?? '', + externalTransactionToken: + json['externalTransactionToken'] as String? ?? '', + products: (json['products'] as List?) + ?.map((e) => UserChoiceDetailsProductWrapper.fromJson( + Map.from(e as Map))) + .toList() ?? + [], + ); + +Map _$UserChoiceDetailsWrapperToJson( + UserChoiceDetailsWrapper instance) => + { + 'originalExternalTransactionId': instance.originalExternalTransactionId, + 'externalTransactionToken': instance.externalTransactionToken, + 'products': instance.products.map((e) => e.toJson()).toList(), + }; + +UserChoiceDetailsProductWrapper _$UserChoiceDetailsProductWrapperFromJson( + Map json) => + UserChoiceDetailsProductWrapper( + id: json['id'] as String? ?? '', + offerToken: json['offerToken'] as String? ?? '', + productType: + const ProductTypeConverter().fromJson(json['productType'] as String?), + ); + +Map _$UserChoiceDetailsProductWrapperToJson( + UserChoiceDetailsProductWrapper instance) => + { + 'id': instance.id, + 'offerToken': instance.offerToken, + 'productType': const ProductTypeConverter().toJson(instance.productType), + }; diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform_addition.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform_addition.dart index 732840802d28..e8c6f07a0bc7 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform_addition.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform_addition.dart @@ -2,19 +2,34 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:async'; + import 'package:flutter/services.dart'; import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; import '../billing_client_wrappers.dart'; import '../in_app_purchase_android.dart'; import 'billing_client_wrappers/billing_config_wrapper.dart'; +import 'types/translator.dart'; /// Contains InApp Purchase features that are only available on PlayStore. class InAppPurchaseAndroidPlatformAddition extends InAppPurchasePlatformAddition { /// Creates a [InAppPurchaseAndroidPlatformAddition] which uses the supplied /// `BillingClientManager` to provide Android specific features. - InAppPurchaseAndroidPlatformAddition(this._billingClientManager); + InAppPurchaseAndroidPlatformAddition(this._billingClientManager) { + _billingClientManager.userChoiceDetailsStream + .map(Translator.convertToUserChoiceDetails) + .listen(_userChoiceDetailsStreamController.add); + } + + final StreamController + _userChoiceDetailsStreamController = + StreamController.broadcast(); + + /// [GooglePlayUserChoiceDetails] emits each time user selects alternative billing. + late final Stream userChoiceDetailsStream = + _userChoiceDetailsStreamController.stream; /// Whether pending purchase is enabled. /// diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/types/google_play_user_choice_details.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/types/google_play_user_choice_details.dart new file mode 100644 index 000000000000..97553b345759 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/types/google_play_user_choice_details.dart @@ -0,0 +1,101 @@ +// 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/foundation.dart'; + +/// Data structure representing a UserChoiceDetails. +/// +/// This wraps [`com.android.billingclient.api.UserChoiceDetails`](https://developer.android.com/reference/com/android/billingclient/api/UserChoiceDetails) +@immutable +class GooglePlayUserChoiceDetails { + /// Creates a new Google Play specific user choice billing details object with + /// the provided details. + const GooglePlayUserChoiceDetails({ + required this.originalExternalTransactionId, + required this.externalTransactionToken, + required this.products, + }); + + /// Returns the external transaction Id of the originating subscription, if + /// the purchase is a subscription upgrade/downgrade. + final String originalExternalTransactionId; + + /// Returns a token that represents the user's prospective purchase via + /// user choice alternative billing. + final String externalTransactionToken; + + /// Returns a list of [GooglePlayUserChoiceDetailsProduct] to be purchased in + /// the user choice alternative billing flow. + final List products; + + @override + bool operator ==(Object other) { + if (identical(other, this)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is GooglePlayUserChoiceDetails && + other.originalExternalTransactionId == originalExternalTransactionId && + other.externalTransactionToken == externalTransactionToken && + listEquals(other.products, products); + } + + @override + int get hashCode => Object.hash( + originalExternalTransactionId, + externalTransactionToken, + products.hashCode, + ); +} + +/// Data structure representing a UserChoiceDetails product. +/// +/// This wraps [`com.android.billingclient.api.UserChoiceDetails.Product`](https://developer.android.com/reference/com/android/billingclient/api/UserChoiceDetails.Product) +@immutable +class GooglePlayUserChoiceDetailsProduct { + /// Creates UserChoiceDetailsProduct. + const GooglePlayUserChoiceDetailsProduct( + {required this.id, required this.offerToken, required this.productType}); + + /// Returns the id of the product being purchased. + final String id; + + /// Returns the offer token that was passed in launchBillingFlow to purchase the product. + final String offerToken; + + /// Returns the [GooglePlayProductType] of the product being purchased. + final GooglePlayProductType productType; + + @override + bool operator ==(Object other) { + if (identical(other, this)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is GooglePlayUserChoiceDetailsProduct && + other.id == id && + other.offerToken == offerToken && + other.productType == productType; + } + + @override + int get hashCode => Object.hash( + id, + offerToken, + productType, + ); +} + +/// This wraps [`com.android.billingclient.api.BillingClient.ProductType`](https://developer.android.com/reference/com/android/billingclient/api/BillingClient.ProductType) +enum GooglePlayProductType { + /// A Product type for Android apps in-app products. + inapp, + + /// A Product type for Android apps subscriptions. + subs +} diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/types/translator.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/types/translator.dart new file mode 100644 index 000000000000..f6461afc2c27 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/types/translator.dart @@ -0,0 +1,47 @@ +// 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/foundation.dart'; + +import '../../billing_client_wrappers.dart'; +import 'google_play_user_choice_details.dart'; + +/// Class used to convert cross process object into api expose objects. +class Translator { + Translator._(); + + /// Converts from [UserChoiceDetailsWrapper] to [GooglePlayUserChoiceDetails]. + static GooglePlayUserChoiceDetails convertToUserChoiceDetails( + UserChoiceDetailsWrapper detailsWrapper) { + return GooglePlayUserChoiceDetails( + originalExternalTransactionId: + detailsWrapper.originalExternalTransactionId, + externalTransactionToken: detailsWrapper.externalTransactionToken, + products: detailsWrapper.products + .map((UserChoiceDetailsProductWrapper e) => + convertToUserChoiceDetailsProduct(e)) + .toList()); + } + + /// Converts from [UserChoiceDetailsProductWrapper] to [GooglePlayUserChoiceDetailsProduct]. + @visibleForTesting + static GooglePlayUserChoiceDetailsProduct convertToUserChoiceDetailsProduct( + UserChoiceDetailsProductWrapper productWrapper) { + return GooglePlayUserChoiceDetailsProduct( + id: productWrapper.id, + offerToken: productWrapper.offerToken, + productType: convertToPlayProductType(productWrapper.productType)); + } + + /// Coverts from [ProductType] to [GooglePlayProductType]. + @visibleForTesting + static GooglePlayProductType convertToPlayProductType(ProductType type) { + switch (type) { + case ProductType.inapp: + return GooglePlayProductType.inapp; + case ProductType.subs: + return GooglePlayProductType.subs; + } + } +} diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/types/types.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/types/types.dart index 0a43425f6e94..5116ac54e061 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/types/types.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/types/types.dart @@ -6,4 +6,5 @@ export 'change_subscription_param.dart'; export 'google_play_product_details.dart'; export 'google_play_purchase_details.dart'; export 'google_play_purchase_param.dart'; +export 'google_play_user_choice_details.dart'; export 'query_purchase_details_response.dart'; diff --git a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml index 7ce2e0080ce2..5f57552f2336 100644 --- a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase_android description: An implementation for the Android platform of the Flutter `in_app_purchase` plugin. This uses the Android BillingClient APIs. repository: https://github.com/flutter/packages/tree/main/packages/in_app_purchase/in_app_purchase_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.3.1 +version: 0.3.2 environment: sdk: ^3.1.0 diff --git a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_manager_test.dart b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_manager_test.dart index b53bb5c96c3f..81874d75d8f9 100644 --- a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_manager_test.dart +++ b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_manager_test.dart @@ -138,5 +138,32 @@ void main() { expect(stubPlatform.countPreviousCalls(startConnectionCall), equals(1)); expect(stubPlatform.countPreviousCalls(endConnectionCall), equals(1)); }); + + test( + 'Emits UserChoiceDetailsWrapper when onUserChoiceAlternativeBilling is called', + () async { + connectedCompleter.complete(); + // Ensures all asynchronous connected code finishes. + await manager.runWithClientNonRetryable((_) async {}); + + const UserChoiceDetailsWrapper expected = UserChoiceDetailsWrapper( + originalExternalTransactionId: 'TransactionId', + externalTransactionToken: 'TransactionToken', + products: [ + UserChoiceDetailsProductWrapper( + id: 'id1', + offerToken: 'offerToken1', + productType: ProductType.inapp), + UserChoiceDetailsProductWrapper( + id: 'id2', + offerToken: 'offerToken2', + productType: ProductType.inapp), + ], + ); + final Future detailsFuture = + manager.userChoiceDetailsStream.first; + manager.onUserChoiceAlternativeBilling(expected); + expect(await detailsFuture, expected); + }); }); } diff --git a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.dart b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.dart index 3ffcd8d5d08e..92f14e2958ac 100644 --- a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.dart +++ b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/billing_client_wrapper_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:async'; + import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:in_app_purchase_android/billing_client_wrappers.dart'; @@ -37,7 +39,8 @@ void main() { .setMockMethodCallHandler(channel, stubPlatform.fakeMethodCallHandler)); setUp(() { - billingClient = BillingClient((PurchasesResultWrapper _) {}); + billingClient = BillingClient( + (PurchasesResultWrapper _) {}, (UserChoiceDetailsWrapper _) {}); stubPlatform.reset(); }); @@ -114,7 +117,7 @@ void main() { })); }); - test('passes billingChoiceMode when set', () async { + test('passes billingChoiceMode alternativeBillingOnly when set', () async { const String debugMessage = 'dummy message'; const BillingResponse responseCode = BillingResponse.developerError; stubPlatform.addResponse( @@ -136,6 +139,91 @@ void main() { })); }); + test('passes billingChoiceMode userChoiceBilling when set', () async { + const String debugMessage = 'dummy message'; + const BillingResponse responseCode = BillingResponse.ok; + stubPlatform.addResponse( + name: methodName, + value: { + 'responseCode': const BillingResponseConverter().toJson(responseCode), + 'debugMessage': debugMessage, + }, + ); + final Completer completer = + Completer(); + + billingClient = BillingClient((PurchasesResultWrapper _) {}, + (UserChoiceDetailsWrapper details) => completer.complete(details)); + stubPlatform.reset(); + await billingClient.startConnection( + onBillingServiceDisconnected: () {}, + billingChoiceMode: BillingChoiceMode.userChoiceBilling); + final MethodCall call = stubPlatform.previousCallMatching(methodName); + expect( + call.arguments, + equals({ + 'handle': 0, + 'billingChoiceMode': 2, + })); + const UserChoiceDetailsWrapper expected = UserChoiceDetailsWrapper( + originalExternalTransactionId: 'TransactionId', + externalTransactionToken: 'TransactionToken', + products: [ + UserChoiceDetailsProductWrapper( + id: 'id1', + offerToken: 'offerToken1', + productType: ProductType.inapp), + UserChoiceDetailsProductWrapper( + id: 'id2', + offerToken: 'offerToken2', + productType: ProductType.inapp), + ], + ); + await billingClient.callHandler( + MethodCall(kUserSelectedAlternativeBilling, expected.toJson())); + expect(completer.isCompleted, isTrue); + expect(await completer.future, expected); + }); + + test('UserChoiceDetailsWrapper searilization check', () async { + // Test ensures that changes to UserChoiceDetailsWrapper#toJson are + // compatible with code in Translator.java. + const String transactionIdKey = 'originalExternalTransactionId'; + const String transactionTokenKey = 'externalTransactionToken'; + const String productsKey = 'products'; + const String productIdKey = 'id'; + const String productOfferTokenKey = 'offerToken'; + const String productTypeKey = 'productType'; + + const UserChoiceDetailsProductWrapper expectedProduct1 = + UserChoiceDetailsProductWrapper( + id: 'id1', + offerToken: 'offerToken1', + productType: ProductType.inapp); + const UserChoiceDetailsProductWrapper expectedProduct2 = + UserChoiceDetailsProductWrapper( + id: 'id2', + offerToken: 'offerToken2', + productType: ProductType.inapp); + const UserChoiceDetailsWrapper expected = UserChoiceDetailsWrapper( + originalExternalTransactionId: 'TransactionId', + externalTransactionToken: 'TransactionToken', + products: [ + expectedProduct1, + expectedProduct2, + ], + ); + final Map detailsJson = expected.toJson(); + expect(detailsJson.keys, contains(transactionIdKey)); + expect(detailsJson.keys, contains(transactionTokenKey)); + expect(detailsJson.keys, contains(productsKey)); + + final Map productJson = expectedProduct1.toJson(); + expect(productJson, contains(productIdKey)); + expect(productJson, contains(productOfferTokenKey)); + expect(productJson, contains(productTypeKey)); + }); + test('handles method channel returning null', () async { stubPlatform.addResponse( name: methodName, diff --git a/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_addition_test.dart b/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_addition_test.dart index 718bd3cdde1c..bf6612542bd5 100644 --- a/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_addition_test.dart +++ b/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_addition_test.dart @@ -9,6 +9,7 @@ import 'package:in_app_purchase_android/billing_client_wrappers.dart'; import 'package:in_app_purchase_android/in_app_purchase_android.dart'; import 'package:in_app_purchase_android/src/billing_client_wrappers/billing_config_wrapper.dart'; import 'package:in_app_purchase_android/src/channel.dart'; +import 'package:in_app_purchase_android/src/types/translator.dart'; import 'billing_client_wrappers/billing_client_wrapper_test.dart'; import 'billing_client_wrappers/purchase_wrapper_test.dart'; @@ -281,4 +282,28 @@ void main() { expect(arguments['feature'], equals('subscriptions')); }); }); + + group('userChoiceDetails', () { + test('called', () async { + final Future futureDetails = + iapAndroidPlatformAddition.userChoiceDetailsStream.first; + const UserChoiceDetailsWrapper expected = UserChoiceDetailsWrapper( + originalExternalTransactionId: 'TransactionId', + externalTransactionToken: 'TransactionToken', + products: [ + UserChoiceDetailsProductWrapper( + id: 'id1', + offerToken: 'offerToken1', + productType: ProductType.inapp), + UserChoiceDetailsProductWrapper( + id: 'id2', + offerToken: 'offerToken2', + productType: ProductType.inapp), + ], + ); + manager.onUserChoiceAlternativeBilling(expected); + expect( + await futureDetails, Translator.convertToUserChoiceDetails(expected)); + }); + }); } diff --git a/packages/in_app_purchase/in_app_purchase_android/test/types/translator_test.dart b/packages/in_app_purchase/in_app_purchase_android/test/types/translator_test.dart new file mode 100644 index 000000000000..7e19c4b41572 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_android/test/types/translator_test.dart @@ -0,0 +1,71 @@ +// 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:in_app_purchase_android/billing_client_wrappers.dart'; +import 'package:in_app_purchase_android/src/types/google_play_user_choice_details.dart'; +import 'package:in_app_purchase_android/src/types/translator.dart'; +import 'package:test/test.dart'; + +void main() { + group('Translator ', () { + test('convertToPlayProductType', () { + expect(Translator.convertToPlayProductType(ProductType.inapp), + GooglePlayProductType.inapp); + expect(Translator.convertToPlayProductType(ProductType.subs), + GooglePlayProductType.subs); + expect(GooglePlayProductType.values.length, ProductType.values.length); + }); + + test('convertToUserChoiceDetailsProduct', () { + const GooglePlayUserChoiceDetailsProduct expected = + GooglePlayUserChoiceDetailsProduct( + id: 'id', + offerToken: 'offerToken', + productType: GooglePlayProductType.inapp); + expect( + Translator.convertToUserChoiceDetailsProduct( + UserChoiceDetailsProductWrapper( + id: expected.id, + offerToken: expected.offerToken, + productType: ProductType.inapp)), + expected); + }); + test('convertToUserChoiceDetailsProduct', () { + const GooglePlayUserChoiceDetailsProduct expectedProduct1 = + GooglePlayUserChoiceDetailsProduct( + id: 'id1', + offerToken: 'offerToken1', + productType: GooglePlayProductType.inapp); + const GooglePlayUserChoiceDetailsProduct expectedProduct2 = + GooglePlayUserChoiceDetailsProduct( + id: 'id2', + offerToken: 'offerToken2', + productType: GooglePlayProductType.subs); + const GooglePlayUserChoiceDetails expected = GooglePlayUserChoiceDetails( + originalExternalTransactionId: 'originalExternalTransactionId', + externalTransactionToken: 'externalTransactionToken', + products: [ + expectedProduct1, + expectedProduct2 + ]); + + expect( + Translator.convertToUserChoiceDetails(UserChoiceDetailsWrapper( + originalExternalTransactionId: + expected.originalExternalTransactionId, + externalTransactionToken: expected.externalTransactionToken, + products: [ + UserChoiceDetailsProductWrapper( + id: expectedProduct1.id, + offerToken: expectedProduct1.offerToken, + productType: ProductType.inapp), + UserChoiceDetailsProductWrapper( + id: expectedProduct2.id, + offerToken: expectedProduct2.offerToken, + productType: ProductType.subs), + ])), + expected); + }); + }); +} From 3eb794bde9e6d02730f0c4a8bd1df6d34e3636fd Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 11 Mar 2024 11:23:21 -0400 Subject: [PATCH 069/126] Manual roll Flutter from 7c89ec8bbc6d to 3bb2e5948e7a (31 revisions) (#6299) Manual roll Flutter from 7c89ec8bbc6d to 3bb2e5948e7a (31 revisions) Manual roll requested by tarrinneal@google.com https://github.com/flutter/flutter/compare/7c89ec8bbc6d...3bb2e5948e7a 2024-03-11 engine-flutter-autoroll@skia.org Roll Flutter Engine from 210f84eb818b to 3b0b59bb224d (1 revision) (flutter/flutter#144923) 2024-03-11 engine-flutter-autoroll@skia.org Roll Flutter Engine from d13999c0ca79 to 210f84eb818b (1 revision) (flutter/flutter#144920) 2024-03-11 engine-flutter-autoroll@skia.org Roll Flutter Engine from 7c2a56a44b41 to d13999c0ca79 (1 revision) (flutter/flutter#144907) 2024-03-10 ian@hixie.ch Rename isAvailableForEnvironment to isForEnvironment (#143176) (flutter/flutter#144858) 2024-03-10 engine-flutter-autoroll@skia.org Roll Flutter Engine from f77664330122 to 7c2a56a44b41 (1 revision) (flutter/flutter#144894) 2024-03-10 engine-flutter-autoroll@skia.org Roll Flutter Engine from 51fefde43cb3 to f77664330122 (1 revision) (flutter/flutter#144891) 2024-03-09 engine-flutter-autoroll@skia.org Roll Flutter Engine from 49f01510e4e2 to 51fefde43cb3 (1 revision) (flutter/flutter#144889) 2024-03-09 engine-flutter-autoroll@skia.org Roll Flutter Engine from 958d7cd560d2 to 49f01510e4e2 (1 revision) (flutter/flutter#144887) 2024-03-09 engine-flutter-autoroll@skia.org Roll Flutter Engine from fc06f5a99377 to 958d7cd560d2 (2 revisions) (flutter/flutter#144884) 2024-03-09 engine-flutter-autoroll@skia.org Roll Flutter Engine from 6f7fdc533490 to fc06f5a99377 (1 revision) (flutter/flutter#144876) 2024-03-09 engine-flutter-autoroll@skia.org Roll Flutter Engine from 196132ffe2f8 to 6f7fdc533490 (1 revision) (flutter/flutter#144875) 2024-03-09 ybz975218925@gmail.com Fix multiple calls to Slider's onChanged. (flutter/flutter#143680) 2024-03-09 engine-flutter-autoroll@skia.org Roll Flutter Engine from d4e3d964dc8a to 196132ffe2f8 (1 revision) (flutter/flutter#144870) 2024-03-09 engine-flutter-autoroll@skia.org Roll Flutter Engine from e79a72d362a7 to d4e3d964dc8a (1 revision) (flutter/flutter#144866) 2024-03-09 engine-flutter-autoroll@skia.org Roll Flutter Engine from c3b70321be79 to e79a72d362a7 (1 revision) (flutter/flutter#144865) 2024-03-09 engine-flutter-autoroll@skia.org Roll Flutter Engine from 79536627661a to c3b70321be79 (1 revision) (flutter/flutter#144864) 2024-03-09 engine-flutter-autoroll@skia.org Roll Flutter Engine from 7541e902f1b4 to 79536627661a (2 revisions) (flutter/flutter#144863) 2024-03-09 engine-flutter-autoroll@skia.org Roll Flutter Engine from 953927eae78b to 7541e902f1b4 (7 revisions) (flutter/flutter#144862) 2024-03-09 polinach@google.com Upgrade vm_service. (flutter/flutter#144845) 2024-03-09 450961837@qq.com Replace dead links (flutter/flutter#144827) 2024-03-09 engine-flutter-autoroll@skia.org Roll Flutter Engine from e099277ce061 to 953927eae78b (2 revisions) (flutter/flutter#144849) 2024-03-09 zanderso@users.noreply.github.com Mark Mac_arm64_ios module_test_ios bringup: true (flutter/flutter#144861) 2024-03-08 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Rename isAvailableForEnvironment to isForEnvironment (#143176)" (flutter/flutter#144855) 2024-03-08 engine-flutter-autoroll@skia.org Roll Flutter Engine from 05fdf947f81b to e099277ce061 (1 revision) (flutter/flutter#144844) 2024-03-08 ian@hixie.ch Rename isAvailableForEnvironment to isForEnvironment (flutter/flutter#143176) 2024-03-08 engine-flutter-autoroll@skia.org Roll Flutter Engine from bc4abcda6357 to 05fdf947f81b (9 revisions) (flutter/flutter#144839) 2024-03-08 58190796+MitchellGoodwin@users.noreply.github.com Fixes the transition builder changing the Cupertino transition on Android. (flutter/flutter#134790) 2024-03-08 jacksongardner@google.com Copy over source maps from dart2js target when they are enabled. (flutter/flutter#144832) 2024-03-08 fluttergithubbot@gmail.com Marks Linux_android new_gallery_opengles_impeller__transition_perf to be unflaky (flutter/flutter#144677) 2024-03-08 engine-flutter-autoroll@skia.org Roll Flutter Engine from bbb1ed00af49 to bc4abcda6357 (2 revisions) (flutter/flutter#144817) 2024-03-08 engine-flutter-autoroll@skia.org Roll Packages from 6701c9e61804 to 0badb4356021 (2 revisions) (flutter/flutter#144831) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC rmistry@google.com,stuartmorgan@google.com,tarrinneal@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: ... --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 801c9866ec53..63cd339c5937 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -7c89ec8bbc6da2b8c59d18615a2c31c5b404d77b +3bb2e5948e7a4766e919b604c54a6abc575338fe From 1e46a6a2fb31ed8c45159d7a41fdd4ea6ea595c7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 11 Mar 2024 12:05:20 -0400 Subject: [PATCH 070/126] Roll Flutter from 7c89ec8bbc6d to 3bb2e5948e7a (31 revisions) (#6300) https://github.com/flutter/flutter/compare/7c89ec8bbc6d...3bb2e5948e7a 2024-03-11 engine-flutter-autoroll@skia.org Roll Flutter Engine from 210f84eb818b to 3b0b59bb224d (1 revision) (flutter/flutter#144923) 2024-03-11 engine-flutter-autoroll@skia.org Roll Flutter Engine from d13999c0ca79 to 210f84eb818b (1 revision) (flutter/flutter#144920) 2024-03-11 engine-flutter-autoroll@skia.org Roll Flutter Engine from 7c2a56a44b41 to d13999c0ca79 (1 revision) (flutter/flutter#144907) 2024-03-10 ian@hixie.ch Rename isAvailableForEnvironment to isForEnvironment (#143176) (flutter/flutter#144858) 2024-03-10 engine-flutter-autoroll@skia.org Roll Flutter Engine from f77664330122 to 7c2a56a44b41 (1 revision) (flutter/flutter#144894) 2024-03-10 engine-flutter-autoroll@skia.org Roll Flutter Engine from 51fefde43cb3 to f77664330122 (1 revision) (flutter/flutter#144891) 2024-03-09 engine-flutter-autoroll@skia.org Roll Flutter Engine from 49f01510e4e2 to 51fefde43cb3 (1 revision) (flutter/flutter#144889) 2024-03-09 engine-flutter-autoroll@skia.org Roll Flutter Engine from 958d7cd560d2 to 49f01510e4e2 (1 revision) (flutter/flutter#144887) 2024-03-09 engine-flutter-autoroll@skia.org Roll Flutter Engine from fc06f5a99377 to 958d7cd560d2 (2 revisions) (flutter/flutter#144884) 2024-03-09 engine-flutter-autoroll@skia.org Roll Flutter Engine from 6f7fdc533490 to fc06f5a99377 (1 revision) (flutter/flutter#144876) 2024-03-09 engine-flutter-autoroll@skia.org Roll Flutter Engine from 196132ffe2f8 to 6f7fdc533490 (1 revision) (flutter/flutter#144875) 2024-03-09 ybz975218925@gmail.com Fix multiple calls to Slider's onChanged. (flutter/flutter#143680) 2024-03-09 engine-flutter-autoroll@skia.org Roll Flutter Engine from d4e3d964dc8a to 196132ffe2f8 (1 revision) (flutter/flutter#144870) 2024-03-09 engine-flutter-autoroll@skia.org Roll Flutter Engine from e79a72d362a7 to d4e3d964dc8a (1 revision) (flutter/flutter#144866) 2024-03-09 engine-flutter-autoroll@skia.org Roll Flutter Engine from c3b70321be79 to e79a72d362a7 (1 revision) (flutter/flutter#144865) 2024-03-09 engine-flutter-autoroll@skia.org Roll Flutter Engine from 79536627661a to c3b70321be79 (1 revision) (flutter/flutter#144864) 2024-03-09 engine-flutter-autoroll@skia.org Roll Flutter Engine from 7541e902f1b4 to 79536627661a (2 revisions) (flutter/flutter#144863) 2024-03-09 engine-flutter-autoroll@skia.org Roll Flutter Engine from 953927eae78b to 7541e902f1b4 (7 revisions) (flutter/flutter#144862) 2024-03-09 polinach@google.com Upgrade vm_service. (flutter/flutter#144845) 2024-03-09 450961837@qq.com Replace dead links (flutter/flutter#144827) 2024-03-09 engine-flutter-autoroll@skia.org Roll Flutter Engine from e099277ce061 to 953927eae78b (2 revisions) (flutter/flutter#144849) 2024-03-09 zanderso@users.noreply.github.com Mark Mac_arm64_ios module_test_ios bringup: true (flutter/flutter#144861) 2024-03-08 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Rename isAvailableForEnvironment to isForEnvironment (#143176)" (flutter/flutter#144855) 2024-03-08 engine-flutter-autoroll@skia.org Roll Flutter Engine from 05fdf947f81b to e099277ce061 (1 revision) (flutter/flutter#144844) 2024-03-08 ian@hixie.ch Rename isAvailableForEnvironment to isForEnvironment (flutter/flutter#143176) 2024-03-08 engine-flutter-autoroll@skia.org Roll Flutter Engine from bc4abcda6357 to 05fdf947f81b (9 revisions) (flutter/flutter#144839) 2024-03-08 58190796+MitchellGoodwin@users.noreply.github.com Fixes the transition builder changing the Cupertino transition on Android. (flutter/flutter#134790) 2024-03-08 jacksongardner@google.com Copy over source maps from dart2js target when they are enabled. (flutter/flutter#144832) 2024-03-08 fluttergithubbot@gmail.com Marks Linux_android new_gallery_opengles_impeller__transition_perf to be unflaky (flutter/flutter#144677) 2024-03-08 engine-flutter-autoroll@skia.org Roll Flutter Engine from bbb1ed00af49 to bc4abcda6357 (2 revisions) (flutter/flutter#144817) 2024-03-08 engine-flutter-autoroll@skia.org Roll Packages from 6701c9e61804 to 0badb4356021 (2 revisions) (flutter/flutter#144831) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC rmistry@google.com,stuartmorgan@google.com,tarrinneal@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md From d1aeb26e1b4319e30f2c9c2c1e1d6d1f44660331 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 12 Mar 2024 10:28:29 -0400 Subject: [PATCH 071/126] Manual roll Flutter from 3bb2e5948e7a to 1ca88730a0c1 (11 revisions) (#6304) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Manual roll requested by tarrinneal@google.com https://github.com/flutter/flutter/compare/3bb2e5948e7a...1ca88730a0c1 2024-03-12 godofredoc@google.com Update integration tests regexes. (flutter/flutter#144847) 2024-03-11 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Fail tests on exceptions raised after test completed (#144706)" (flutter/flutter#144970) 2024-03-11 polinach@google.com Make TabController communicating creation in constructor. (flutter/flutter#144912) 2024-03-11 goderbauer@google.com Fail tests on exceptions raised after test completed (flutter/flutter#144706) 2024-03-11 nate.w5687@gmail.com Refactoring `if` chains into `switch` statements (flutter/flutter#144905) 2024-03-11 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Expose build mode in environment of asset transformer processes (#144752)" (flutter/flutter#144957) 2024-03-11 andrewrkolos@gmail.com Expose build mode in environment of asset transformer processes (flutter/flutter#144752) 2024-03-11 engine-flutter-autoroll@skia.org Roll Flutter Engine from 9196947bc687 to 6745955bb49e (2 revisions) (flutter/flutter#144946) 2024-03-11 vtrgalo@gmail.com Skip test temporarily until headingLevel is added in engine (issue 41… (flutter/flutter#135077) 2024-03-11 engine-flutter-autoroll@skia.org Roll Flutter Engine from 3b0b59bb224d to 9196947bc687 (1 revision) (flutter/flutter#144934) 2024-03-11 engine-flutter-autoroll@skia.org Roll Packages from 0badb4356021 to d489d84c359b (3 revisions) (flutter/flutter#144931) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC rmistry@google.com,stuartmorgan@google.com,tarrinneal@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 63cd339c5937..ca143f98efcf 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -3bb2e5948e7a4766e919b604c54a6abc575338fe +1ca88730a0c13968e7923f1562e44792dae18ea2 From 38711aca66fa91cff1815839f99e9819f3c27d60 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 12 Mar 2024 12:24:05 -0400 Subject: [PATCH 072/126] Roll Flutter from 1ca88730a0c1 to 61812ca3eb13 (1 revision) (#6305) https://github.com/flutter/flutter/compare/1ca88730a0c1...61812ca3eb13 2024-03-12 nate.w5687@gmail.com Add platform check to `FocusManager` app lifecycle listener (flutter/flutter#144718) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC rmistry@google.com,stuartmorgan@google.com,tarrinneal@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index ca143f98efcf..3b34cd66097b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -1ca88730a0c13968e7923f1562e44792dae18ea2 +61812ca3eb1348b8d8192cab5d8abc2fc72931fa From 142a604d2af96b49cabc75b14c589f46708667e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Mar 2024 23:25:49 +0000 Subject: [PATCH 073/126] Bump github/codeql-action from 3.24.6 to 3.24.7 (#6310) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.24.6 to 3.24.7.
Changelog

Sourced from github/codeql-action's changelog.

CodeQL Action Changelog

See the releases page for the relevant changes to the CodeQL CLI and language packs.

Note that the only difference between v2 and v3 of the CodeQL Action is the node version they support, with v3 running on node 20 while we continue to release v2 to support running on node 16. For example 3.22.11 was the first v3 release and is functionally identical to 2.22.11. This approach ensures an easy way to track exactly which features are included in different versions, indicated by the minor and patch version numbers.

[UNRELEASED]

No user facing changes.

3.24.7 - 12 Mar 2024

  • Update default CodeQL bundle version to 2.16.4. #2185

3.24.6 - 29 Feb 2024

No user facing changes.

3.24.5 - 23 Feb 2024

  • Update default CodeQL bundle version to 2.16.3. #2156

3.24.4 - 21 Feb 2024

  • Fix an issue where an existing, but empty, /sys/fs/cgroup/cpuset.cpus file always resulted in a single-threaded run. #2151

3.24.3 - 15 Feb 2024

  • Fix an issue where the CodeQL Action would fail to load a configuration specified by the config input to the init Action. #2147

3.24.2 - 15 Feb 2024

  • Enable improved multi-threaded performance on larger runners for GitHub Enterprise Server users. This feature is already available to GitHub.com users. #2141

3.24.1 - 13 Feb 2024

  • Update default CodeQL bundle version to 2.16.2. #2124
  • The CodeQL action no longer fails if it can't write to the telemetry api endpoint. #2121

3.24.0 - 02 Feb 2024

  • CodeQL Python analysis will no longer install dependencies on GitHub Enterprise Server, as is already the case for GitHub.com. See release notes for 3.23.0 for more details. #2106

3.23.2 - 26 Jan 2024

  • On Linux, the maximum possible value for the --threads option now respects the CPU count as specified in cgroup files to more accurately reflect the number of available cores when running in containers. #2083
  • Update default CodeQL bundle version to 2.16.1. #2096

3.23.1 - 17 Jan 2024

... (truncated)

Commits
  • 3ab4101 Merge pull request #2192 from github/update-v3.24.7-5e882999f
  • a006adf Update changelog for v3.24.7
  • 5e88299 Bump the npm group with 2 updates (#2190)
  • 69e120d Merge pull request #2191 from github/henrymercer/use-include-query-help-flag
  • 5ec06c7 Use the --sarif-include-query-help option when supported
  • caf3779 Update default bundle to 2.16.4 (#2185)
  • 532ca54 Fail analyze step by passing an invalid option to database finalize (#2189)
  • 2fa207a Merge pull request #2188 from github/henrymercer/prepare-build-mode-help
  • 24c3eda Escape named value in input description
  • 27a6cd0 Remove experimental qualifiers from build mode input
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github/codeql-action&package-manager=github_actions&previous-version=3.24.6&new-version=3.24.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- .github/workflows/scorecards-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index cd08fedd157d..ddcdb5a2941c 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -49,6 +49,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v1.0.26 + uses: github/codeql-action/upload-sarif@3ab4101902695724f9365a384f86c1074d94e18c # v1.0.26 with: sarif_file: results.sarif From 131033e6358c1da8a4402a658562ded93cf77357 Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Wed, 13 Mar 2024 16:25:51 -0700 Subject: [PATCH 074/126] Replace deprecated UIGraphicsBeginImageContextWithOptions with UIGraphicsImageRenderer (#6285) `UIGraphicsBeginImageContextWithOptions` isn't technically deprecated, but `API_TO_BE_DEPRECATED`. However, according to reports it's crashing in iOS 17. Replace usage with `UIGraphicsImageRenderer`, available in iOS 10. I kept introducing horizontal flipping bugs when I was writing this, but it wasn't causing test failures. Changed the test images from a single color to a gradient with different colors in each corner, then updated the tests to confirm the orientation is still correct. Generated the test image with ImageMagick: ``` magick \( xc:red xc:blue +append \) \( xc:yellow xc:cyan +append \) -append -filter triangle -resize 12x7\! jpgImage.jpg ``` Also avoid doing extra work if there's nothing to scale, and return the original image. Fixes https://github.com/flutter/flutter/issues/144602 --- .../image_picker_ios/CHANGELOG.md | 3 +- .../ios/RunnerTests/ImagePickerTestImages.m | 160 +++++------------- .../example/ios/RunnerTests/ImageUtilTests.m | 99 +++++++++-- .../example/ios/TestImages/bmpImage.bmp | Bin 92 -> 390 bytes .../example/ios/TestImages/heicImage.heic | Bin 3207 -> 472 bytes .../example/ios/TestImages/jpgImage.jpg | Bin 3290 -> 852 bytes .../example/ios/TestImages/jpgImageTall.jpg | Bin 3390 -> 328 bytes .../example/ios/TestImages/pngImage.png | Bin 593 -> 1085 bytes .../example/ios/TestImages/tiffImage.tiff | Bin 4192 -> 768 bytes .../example/ios/TestImages/webpImage.webp | Bin 2622 -> 106 bytes .../ios/Classes/FLTImagePickerImageUtil.m | 43 +++-- .../image_picker_ios/pubspec.yaml | 2 +- 12 files changed, 161 insertions(+), 146 deletions(-) diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index d58ce03e7c9f..566aab0f686e 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 0.8.9+2 * Updates minimum iOS version to 12.0 and minimum Flutter version to 3.16.6. +* Replaces deprecated UIGraphicsBeginImageContextWithOptions with UIGraphicsImageRenderer. ## 0.8.9+1 diff --git a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerTestImages.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerTestImages.m index b91eec293028..da07b71fd292 100644 --- a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerTestImages.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerTestImages.m @@ -15,59 +15,23 @@ + (NSData *)JPGTestData { // embedded in the test bundle. Fall back to the base64 string representation of the jpg. data = [[NSData alloc] initWithBase64EncodedString: - @"/9j/4AAQSkZJRgABAQAALgAuAAD/4QCMRXhpZgAATU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABA" - "AAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAIdpAAQAAAABAAAAWgAAAAAAAAAuAAAAAQAAAC4AAAABAAOg" - "AQADAAAAAQABAACgAgAEAAAAAQAAAAygAwAEAAAAAQAAAAcAAAAA/+EJc2h0dHA6Ly9ucy5hZG9iZS5jb20" - "veGFwLzEuMC8APD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz" - "4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiP" - "iA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucy" - "MiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZ" - "G9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHBob3Rvc2hvcDpDcmVkaXQ9IsKpIEdvb2dsZSIvPiA8L3JkZjpSR" - "EY+IDwveDp4bXBtZXRhPiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - "CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDw/eHBhY2tldCBlbmQ9In" - "ciPz4A/+0AVlBob3Rvc2hvcCAzLjAAOEJJTQQEAAAAAAAdHAFaAAMbJUccAgAAAgACHAJuAAnCqSBHb29nbG" - "UAOEJJTQQlAAAAAAAQmkt2IF3PgNJVMGnV2zijEf/AABEIAAcADAMBIgACEQEDEQH/xAAfAAABBQEBAQEBAQA" - "AAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQ" - "gjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h" - "5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp" - "6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAAB" - "AncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0R" - "FRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tr" - "e4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2wBDAAQDAwMDAgQDAwMEBAQFBgoGBg" - "UFBgwICQcKDgwPDg4MDQ0PERYTDxAVEQ0NExoTFRcYGRkZDxIbHRsYHRYYGRj/2wBDAQQEBAYFBgsGBgsYEA0" - "QGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBj/3QAEAAH/2gAMAwEA" - "AhEDEQA/AMWiiivzk/qo/9k=" + @"/9j/4AAQSkZJRgABAQAASABIAAD/" + @"4QCMRXhpZgAATU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABA" + @"AIAAIdpAAQAAAABAAAAWgAAAAAAAABIAAAAAQAAAEgAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQAAAAygAw" + @"AEAAAAAQAAAAcAAAAA/8AAEQgABwAMAwERAAIRAQMRAf/" + @"EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//" + @"EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBka" + @"JSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio" + @"6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/" + @"EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//" + @"EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEX" + @"GBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZm" + @"qKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/" + @"bAEMAAgICAgICAwICAwUDAwMFBgUFBQUGCAYGBgYGCAoICAgICAgKCgoKCgoKCgwMDAwMDA4ODg4ODw8PDw8P" + @"Dw8PD//" + @"bAEMBAgMDBAQEBwQEBxALCQsQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ" + @"EBAQEP/dAAQAAv/aAAwDAQACEQMRAD8A9S8ZfsFeG/sH/IUboe7V/JHgR4t4v+2/4XVdj908aPHLG/2P/" + @"B6PsfO5/YL8O7m/4mjdT3av966fi1ivZw/ddF2P8VZ+OeN9pP8Ac9X2P//Z" options:0]; } return data; @@ -82,64 +46,14 @@ + (NSData *)JPGTallTestData { // embedded in the test bundle. Fall back to the base64 string representation of the jpg. data = [[NSData alloc] initWithBase64EncodedString: - @"/9j/4AAQSkZJRgABAQAALgAuAAD/" - @"4QCMRXhpZgAATU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABA" - @"AIAAIdpAAQAAAABAAAAWgAAAAAAAAAuAAAAAQAAAC4AAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQAAAASgAw" - @"AEAAAAAQAAAAcAAAAA/" - @"+EJ12h0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8APD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTX" - @"BDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB" - @"0az0iWE1QIENvcmUgNi4wLjAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkv" - @"MDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczpwaG90b" - @"3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC" - @"5vcmcvZGMvZWxlbWVudHMvMS4xLyIgcGhvdG9zaG9wOkNyZWRpdD0iwqkgR29vZ2xlIj4gPGRjOnN1YmplY3Q" - @"+IDxyZGY6QmFnLz4gPC9kYzpzdWJqZWN0PiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1w" - @"bWV0YT4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - @"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC" - @"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA" - @"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" - @"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - @"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC" - @"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA" - @"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" - @"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - @"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC" - @"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA" - @"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" - @"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - @"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC" - @"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA" - @"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" - @"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - @"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC" - @"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA" - @"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" - @"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - @"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC" - @"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA" - @"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" - @"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - @"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC" - @"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA" - @"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" - @"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI" - @"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC" - @"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA" - @"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" - @"ICAgICAgICAgICAgICA8P3hwYWNrZXQgZW5kPSJ3Ij8+AP/" - @"tAFZQaG90b3Nob3AgMy4wADhCSU0EBAAAAAAAHRwBWgADGyVHHAIAAAIAAhwCbgAJwqkgR29vZ2xlADhCSU0E" - @"JQAAAAAAEJpLdiBdz4DSVTBp1ds4oxH/wAARCAAHAAQDASIAAhEBAxEB/" - @"8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/" - @"8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGR" - @"olJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqK" - @"jpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/" - @"8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/" - @"8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8R" - @"cYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJm" - @"aoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/" - @"9sAQwAEAwMDAwIEAwMDBAQEBQYKBgYFBQYMCAkHCg4MDw4ODA0NDxEWEw8QFRENDRMaExUXGBkZGQ8SGx0bGB" - @"0WGBkY/" - @"9sAQwEEBAQGBQYLBgYLGBANEBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGB" - @"gYGBgY/90ABAAB/9oADAMBAAIRAxEAPwDFooor85P6qP/Z" + @"/9j/4AAQSkZJRgABAQAAAAAAAAD/" + @"2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQE" + @"w8QEBD/" + @"2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE" + @"BAQEBD/wAARCAAHAAQDAREAAhEBAxEB/8QAFAABAAAAAAAAAAAAAAAAAAAABP/" + @"EAB0QAAECBwAAAAAAAAAAAAAAAAIABgQIFiVBQlH/xAAUAQEAAAAAAAAAAAAAAAAAAAAH/" + @"8QAHBEAAQQDAQAAAAAAAAAAAAAABAAHI1EIFlIX/9oADAMBAAIRAxEAPwA76kLbdSxV/" + @"PGxcTHjm7hXngUfVWgp+n0N3suLmqX/2Q==" options:0]; } return data; @@ -154,10 +68,26 @@ + (NSData *)PNGTestData { // embedded in the test bundle. Fall back to the base64 string representation of the png. data = [[NSData alloc] initWithBase64EncodedString: - @"iVBORw0KGgoAAAAEQ2dCSVAAIAYsuHdmAAAADUlIRFIAAAAMAAAABwgGAAAAPLKsJAAAAARnQU1BAACxjwv8Y" - "QUAAAABc1JHQgCuzhzpAAAAIGNIUk0AAHomAACAhAAA+" - "gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAJcEh" - "ZcwAABxMAAAcTAc4gDwgAAAAOSURBVGMwdX71nxTMMKqBCAwAsfuEYQAAAABJRU5ErkJggg==" + @"iVBORw0KGgoAAAANSUhEUgAAAAwAAAAHEAIAAADjQOcwAAAABGdBTUEAALGPC/" + @"xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAhGVYSWZNTQAqAAAACAA" + @"FARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAA" + @"AAAAAEgAAAABAAAASAAAAAEAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADKADAAQAAAABAAAABwAAAACX5qZPA" + @"AAACXBIWXMAAAsTAAALEwEAmpwYAAACx2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bW" + @"xuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4bWx" + @"uczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRm" + @"OkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvY" + @"mUuY29tL3RpZmYvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leG" + @"lmLzEuMC8iPgogICAgICAgICA8dGlmZjpZUmVzb2x1dGlvbj43MjwvdGlmZjpZUmVzb2x1dGlvbj4KICAgICA" + @"gICAgPHRpZmY6UmVzb2x1dGlvblVuaXQ+" + @"MjwvdGlmZjpSZXNvbHV0aW9uVW5pdD4KICAgICAgICAgPHRpZmY6WFJlc29sdXRpb24+" + @"NzI8L3RpZmY6WFJlc29sdXRpb24+" + @"CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+" + @"CiAgICAgICAgIDxleGlmOlBpeGVsWERpbWVuc2lvbj4xMjwvZXhpZjpQaXhlbFhEaW1lbnNpb24+" + @"CiAgICAgICAgIDxleGlmOkNvbG9yU3BhY2U+" + @"MTwvZXhpZjpDb2xvclNwYWNlPgogICAgICAgICA8ZXhpZjpQaXhlbFlEaW1lbnNpb24+" + @"NzwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+" + @"CjwveDp4bXBtZXRhPgqm8GvmAAAAUElEQVQYGWP4/58BCJDJV6sYGERD9wPFHRimhjIwZK3KAopMDXUAqtv/" + @"XxQoAlKBquf/fyaQEDUACwOD2W0qGeSlSiWDPFWoZBB1vEa1wAYAWgsa+7/A1uQAAAAASUVORK5CYII=" options:0]; } return data; diff --git a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImageUtilTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImageUtilTests.m index ddd4087a9f4b..5566e1d92223 100644 --- a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImageUtilTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImageUtilTests.m @@ -8,31 +8,106 @@ @import image_picker_ios.Test; @import XCTest; +// Corner colors of test image scaled to 3x2. Format is "R G B A". +static NSString *const kColorRepresentation3x2BottomLeftYellow = @"1 0.776471 0 1"; +static NSString *const kColorRepresentation3x2TopLeftRed = @"1 0.0666667 0 1"; +static NSString *const kColorRepresentation3x2BottomRightCyan = @"0 0.772549 1 1"; +static NSString *const kColorRepresentation3x2TopRightBlue = @"0 0.0705882 0.996078 1"; + @interface ImageUtilTests : XCTestCase @end @implementation ImageUtilTests +static NSString *ColorStringAtPixel(UIImage *image, int pixelX, int pixelY) { + CGImageRef cgImage = image.CGImage; + + uint32_t argb; + CGContextRef context1 = CGBitmapContextCreate( + &argb, 1, 1, CGImageGetBitsPerComponent(cgImage), CGImageGetBytesPerRow(cgImage), + CGColorSpaceCreateDeviceRGB(), CGImageGetBitmapInfo(cgImage)); + CGContextDrawImage( + context1, CGRectMake(-pixelX, -pixelY, CGImageGetWidth(cgImage), CGImageGetHeight(cgImage)), + cgImage); + CGContextRelease(context1); + int blue = argb & 0xff; + int green = argb >> 8 & 0xff; + int red = argb >> 16 & 0xff; + int alpha = argb >> 24 & 0xff; + + return [CIColor colorWithRed:red / 255.f + green:green / 255.f + blue:blue / 255.f + alpha:alpha / 255.f] + .stringRepresentation; +} + +- (void)testScaledImage_EqualSizeReturnsSameImage { + UIImage *image = [UIImage imageWithData:ImagePickerTestImages.JPGTestData]; + UIImage *scaledImage = [FLTImagePickerImageUtil scaledImage:image + maxWidth:@(image.size.width) + maxHeight:@(image.size.height) + isMetadataAvailable:YES]; + + // Assert the same bytes pointer (not just equal objects). + XCTAssertEqual(image, scaledImage); +} + +- (void)testScaledImage_NilSizeReturnsSameImage { + UIImage *image = [UIImage imageWithData:ImagePickerTestImages.JPGTestData]; + UIImage *scaledImage = [FLTImagePickerImageUtil scaledImage:image + maxWidth:nil + maxHeight:nil + isMetadataAvailable:YES]; + + // Assert the same bytes pointer (not just equal objects). + XCTAssertEqual(image, scaledImage); +} + - (void)testScaledImage_ShouldBeScaled { UIImage *image = [UIImage imageWithData:ImagePickerTestImages.JPGTestData]; - UIImage *newImage = [FLTImagePickerImageUtil scaledImage:image - maxWidth:@3 - maxHeight:@2 - isMetadataAvailable:YES]; - XCTAssertEqual(newImage.size.width, 3); - XCTAssertEqual(newImage.size.height, 2); + CGFloat scaledWidth = 3; + CGFloat scaledHeight = 2; + UIImage *scaledImage = [FLTImagePickerImageUtil scaledImage:image + maxWidth:@(scaledWidth) + maxHeight:@(scaledHeight) + isMetadataAvailable:YES]; + XCTAssertEqual(scaledImage.size.width, scaledWidth); + XCTAssertEqual(scaledImage.size.height, scaledHeight); + + // Check the corners to make sure nothing has been rotated. + XCTAssertEqualObjects(ColorStringAtPixel(scaledImage, 0, 0), + kColorRepresentation3x2BottomLeftYellow); + XCTAssertEqualObjects(ColorStringAtPixel(scaledImage, 0, scaledHeight - 1), + kColorRepresentation3x2TopLeftRed); + XCTAssertEqualObjects(ColorStringAtPixel(scaledImage, scaledWidth - 1, 0), + kColorRepresentation3x2BottomRightCyan); + XCTAssertEqualObjects(ColorStringAtPixel(scaledImage, scaledWidth - 1, scaledHeight - 1), + kColorRepresentation3x2TopRightBlue); } - (void)testScaledImage_ShouldBeScaledWithNoMetadata { UIImage *image = [UIImage imageWithData:ImagePickerTestImages.JPGTestData]; - UIImage *newImage = [FLTImagePickerImageUtil scaledImage:image - maxWidth:@3 - maxHeight:@2 - isMetadataAvailable:NO]; - XCTAssertEqual(newImage.size.width, 3); - XCTAssertEqual(newImage.size.height, 2); + CGFloat scaledWidth = 3; + CGFloat scaledHeight = 2; + UIImage *scaledImage = [FLTImagePickerImageUtil scaledImage:image + maxWidth:@(scaledWidth) + maxHeight:@(scaledHeight) + isMetadataAvailable:NO]; + XCTAssertEqual(scaledImage.size.width, scaledWidth); + XCTAssertEqual(scaledImage.size.height, scaledHeight); + + // Check the corners to make sure nothing has been rotated. + XCTAssertEqualObjects(ColorStringAtPixel(scaledImage, 0, 0), + kColorRepresentation3x2BottomLeftYellow); + XCTAssertEqualObjects(ColorStringAtPixel(scaledImage, 0, scaledHeight - 1), + kColorRepresentation3x2TopLeftRed); + XCTAssertEqualObjects(ColorStringAtPixel(scaledImage, scaledWidth - 1, 0), + kColorRepresentation3x2BottomRightCyan); + XCTAssertEqualObjects(ColorStringAtPixel(scaledImage, scaledWidth - 1, scaledHeight - 1), + kColorRepresentation3x2TopRightBlue); } - (void)testScaledImage_ShouldBeCorrectRotation { diff --git a/packages/image_picker/image_picker_ios/example/ios/TestImages/bmpImage.bmp b/packages/image_picker/image_picker_ios/example/ios/TestImages/bmpImage.bmp index 553e765fb01836d630da6c16871d7711e7d96b70..eda28bc0d024a64f34d7d4d7ec4259f9ee69c8d6 100644 GIT binary patch literal 390 zcma)&p-u!r42J&=1Oma4Ca9C-G%H43(wspdK%-aGVi98wfjL#NZ*r~)YTkf%C>lpn z=5~%`gCI=4|4%z@+NA5dmk!f=t^QnfQFU6?;i@|DHTiD~ps_E+`u5>z`}VoK-z{jd z`RMw-zZ{0)y6R*c$FtS>`_b}odo!PMJWr-6f!IUg944U}V!>WN4wbs;A3Y^7LB+Y4 z6xAYD*b7BL134O$owG?+&0?87Q)D!dqCv?ynIzRDme>huc6#6trBzKH`M diff --git a/packages/image_picker/image_picker_ios/example/ios/TestImages/heicImage.heic b/packages/image_picker/image_picker_ios/example/ios/TestImages/heicImage.heic index 03f41f69cc82bcbf8dbdf4bca815de36783b05f3..b8099f2f8c0bd9502fdc1b6b9151a571e49f534c 100644 GIT binary patch literal 472 zcmZQzV30^FsVvAy%}izhg51nBLkOEOBR91q5hMl#iWw<6MGz(fLqTS835*S+`3f>i za={!%AgPp@lMgn|#esnl2!VjH1409t0YI#rnVFXc5(kTl0y(KnP+mq^vLT51BD0{V z07#$AEJ)4=(xpHNXGRtdkQ^AuePH+kH2!q&;n&l0q1FeEtykFjB#AfD y-9$q_t)t?^tcw>u{r@f8!hI*<^LB$A{RK~}tX_*93zGdi=K{zI^X*e*2pV3__?q zW6zSDf+YltY7J+45%L|<6kDV*SbR=a&5DT-!IHggS9Q0H^$d6iWK*G!SP!*4@c#IDdlR*74CXxZVSu#{pmsAJpeaJrtq04|M!LYRI z6$nC|t7vm;eI21aY!Sw^4KqQeT&4vqtpH>!>LlGyA=h=1aXbG>6kE6xrALiFI#(vr z-vfbm-!rIdNHQhS{5U+;4f9*DMHF`@pb&HbBqXUKML>N@ZM~;KA*Nh{-#HM38n9`& z2eg-92h#`}2+#rhFwwS86o~L&yUyOc<%dKJtA>00Ns61!1_=tF$AJOY^QA!FryoXh zzHhKin%bHydY57M2>AZWDWy{A2zUWh$2568(8YA1x=KG9VfI(;Q6}Woz+Sg8+U3oX^=di3vhJpAYAcg$*++^hTpmA<`%G_4PnSk2R~? zqSK>WUE33qiY1wt&e*@Z-MhHY3mgH(s>R@em@mJCg(riI>c1mITv zSi)3f41?ak;LaFER#kWbFw_4G1x?NB*AH^FRb!yWK#hSK12qO}4E%o#?7W{89Ue`2 zcpcGaRXW7HT>LQCacQwMvbOY~ekJki_1gT^t%nnnIpK3?ZL{3^Wn%r)-BK(&-+b_Q q$Jo#A3(ZX%@{QokyT`bHVdKv8x_jj(OJ5hV%Z145P@82hto{Y%%Lo1d diff --git a/packages/image_picker/image_picker_ios/example/ios/TestImages/jpgImage.jpg b/packages/image_picker/image_picker_ios/example/ios/TestImages/jpgImage.jpg index 12b2dc17624c028f8627b0aa72b82763cbab81e4..59997ece724f0d4da966d694c3879b0361bf46be 100644 GIT binary patch delta 292 zcmca5d4)~r|Be3-7zDiBJlz-=85tNn7(6BlMRIyDFfaglKu(E1_W=e$4hD7x9%e?t z&DC6c80(q9fSHMjnU$HDnU#$d2-rB-K!Agb0}QyJfCmitK!6_({@-SBW@KV!W?^Ar z2OkR1VRQz zDF#+Bn}NZLQ5wz;V$^`DVPart&tzbMs)+*9AOJKOq#H^zFJMHNxPS?+nr8tsoXrl> z_WvPgaYji=ft9{~Ua?+cN`6wRUUGh}ennz|zM-ChK7);YML}Y6c4~=2Qfhi;o~_dR z-TRdkGE;1o!cBb*d<&dYGcrA@ic*8C{6dnevXd=SljF4@-9<|J>PIC z)FAWfZHvW%{|_(-axkzn@GvtfF)#@-G7B>PKf)jnY#6dKf&o|?kYHqDW?^Mx=iubx z1}fMpz`(@F%*@2X%*qOK1Y<2wo`FS>RY=j$kxe)-kzJ`!#HexNLJno8jR!@8E`Crk zPAY2R|V^&07y2J$~}^+4C1KUw!=a`ODXD-+%o41@afLi3{--kc9XQ&0m5*e=)JJ zFtf0O{Kd#r4)VAl3#+0bn~-B5dt#xml2Idvh||P{8xL|S8wY(5O)9#`C8lEXQ1v6o zYha%d=dmWTdeO? z6Xq8X73Ad=mJ${Px>Zt=Ur1V3T0&M#LQ>*C&=5wTRyI~PZZV5{}uxeP=-m6S&+e=!TOcc?NXi4KOZ+18a|%ABYYmK Td}xN|uUg)Fr@Ln@{eKexu{bWW literal 3390 zcmeHKNpRa_6n?)pFNqT?Zi2H7s?tCy6U%nm5_{@q8mCDU#!Q>8WQKz+E4G?gawVm9 zGs6rmOQFN1l(na{Wofw;DC+@QTDA+*Lg_Zl5O$bhXenzi;+NtmloN2pPczb!{_j1# z@B4N5WS%!a1OM>A&;TF=5W)wTGw|^0vOETmO2HBUa087RfW#TX2cQ<9?HphjwL19- zT3o>p0J~+NDwt7xwE}mxsw-D=8qIf{CLA<1O**;DDl=z1=kkV8j0=LIhtgR!BZWk@ zAe7TZArcA;u%fqIOpD`^!DXbJtR(m!-*}Vf!54Xm3NK zK(=-cwqY|#0MSM$;BhR)BDzj|PB&Cu}r4I4LY z-g4)tn3cwId3mdDOl~igcTC-T-~A8le(<3^kL-K&vB#fy@~Qm?4?X?Nv(Ftq^85=g zzI5#6S6+SX^%HNOJoV1I@4f%QnU6j``^mZU7cS0x{>7JHef`b1-+h1Or>j5z^6PKE z|8dRYg>CdKEqtD3_BUQE=0#8xNx3Xuh$vaaS&Cj5VH#GgbEUTh_~=geg1)^6jvn`P zbdOwa%xI^*O^cS!bY8Kj+06bKv0eX(nL})z*Cj~e{lkH`gCr>m?~|8dXqu^YdtBbS z+WNY>TA#0;Z3@);8(E((&=P3Gvo<%^H?+352HTs0%|R14LAaEmnHq+v3Hp8h;J*&@ eQ~WoA%!^PG= diff --git a/packages/image_picker/image_picker_ios/example/ios/TestImages/pngImage.png b/packages/image_picker/image_picker_ios/example/ios/TestImages/pngImage.png index d7ad7d3968e943996da6cc5b18a6ba14e0552578..724e5e2acb5178c12dee5e237239673765620e79 100644 GIT binary patch literal 1085 zcmeAS@N?(olHy`uVBq!ia0vp^JV4AYzyu^8J3Kc4QY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fI1AfjnEKjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(y zU7!lx;>x^|#0uTKVr7USFmqf|i<65o3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c0 z6+^uR^q@XSM&D4+Kp&g7)QU_T+CVZ8ZD4;vtg`_bW)&HfTAZI#3UZd6xseUn1{6Vr z`yd(+>O=D~OYC4;(1g)-MIhu6)*uU_Yw$11Oa%rc*j5WH7CL) zGdDG_7~~g2pu@0Aplfu_&&e+eE=WvHwFBA%)`BF2t{QAjB*GeVWLuELq55p}K~aqq z^^kN076m3OJ1!f2cp|mqTJ|CP88F=jc)B=-NJu9C_&=YKW5UVs)e;ge&fl4j%1SJ2 zGcrh7dy1*chqshr)$RZBA{tCVjjNvjuUDHOV9LPF+2;piJTE>gTe~DWM4fAr>lZ delta 234 zcmdnXagk+$ggyrw0|SH2rZp-+im^Dz-HBn{IhmJ04rhT!WHAE+yD$hdo>SoG04lgN znT;`k*;LPDawKDeZb3$VNq%ugeu1r0MoCG5mA-ynv0h?Iep0Gla(=EpTw32y&p?0j zQAP_3J1zwnuz^ckITxj-WR}<+TB+ckpP!zSYJ<=Um79Eq$zt+iW<8U~6>@8Vj+5|o vaSV}=Os;vF^J{)1%K?|Bs126{S28k~Tx7~mh`+x9sD{DQ)z4*}Q$iB})B#DZ diff --git a/packages/image_picker/image_picker_ios/example/ios/TestImages/tiffImage.tiff b/packages/image_picker/image_picker_ios/example/ios/TestImages/tiffImage.tiff index 2431333c02e78de9435639a29ab4405e787cfbc3..6d7cc0bb6b9f85d5dac84a00f49f276f857efbe4 100644 GIT binary patch literal 768 zcmbV~F;2rk5JhKg2q;1#AQ>b?LE#4IO$TWsrJ?X8a*AybB~^M#v`3%`g31z{z&9`r zO(eSI&1MyED6>&!zF%g){v?@9u0%2s?}d|A*NN1HUx3H;T*S@X3^JE7bBnrwFtK^Z zbN;h!ecDRB`|7&3(mW9Iw3X(8kf*IQ4}?5u67-cP8#1mdQ#R~n!(KM*Wy4-J>}A7V zHf*x4-j|K~^wo7`qdpMwl#Tj8$Wu1z10fHZJbhh#LuqxL_v*tOKHjSj1RtjGf#8$g zP_1W^Zs&6-VsbJ3Xa~Ga)O+X*ozq|WcjS@l+#qVO3=@82c7o47{5au7)arE-wfdjN z`Cm-Wlle=udXq+O&2G>?fIIX6`nLK|M&b4FeGjwpv3$`h+1KY(yIFNB@i}{XUn(En OoIPvLt{;cA3;6^4cM3xQ literal 4192 zcmeH}cUTkI7QpXJdI&WkbQpRw383`eK|rb?*a%602q}hyViyq=T)T*1MOH;!bae&9 zjs**gy_ZFKh^Xu0DmGkwcR+#nzHj%vZ@>5FyYtQDH|L&v&bjBDn`yF$AQ+ilZfn(GV|1j7Qk9`^vcA zgc0(@0mkFpfF8m72N@5!Wx-*9cZ><-+C}1Tz5sPm9xi7@06^5oI|>diyy3d9pHwh- z;k^hiVo7zEGAS z@`8+uc^24!iZaL7VnR$D7A#YUFgLCf*U7?@d5G*_m`;yHVIdh(oHcrGp+sKLoAk}COy9BH2AppkEOLPU82g;=f>%cK}?m!Bb1 zDSa(0a7RO9Fiv^4OexFDmdS_QN%Ob^k#hwy32$hc=gf8D4S9h^e1@jHf)yg6Sm`&E z4WHp<-{!u0_Tk~-j0kUV?*me}c|W=jZzz#|bXW>TM6_5cL$v^qNR^6&@E~kLY8pmq zhb5o^GH?cfFUXU}B!(qHWe^z}jO9TkoQKETn*&t^*EkVz7&a{buSi=US14gy#6j)R zLYP;`C6IFqlyY3&3z?OXgEI^_Firr1W%440I4fI;*$M11*i;@^gs31_q*QX^_<|h1LJ0d)BIiqs02tge zz6$8zxy4|T-JCt$+&C^=nEs*hmy<(Ue+_tT2Q^;D57Z1{w++jN*AB~MH2`?@!Q8AE zmSwC5pm8Ap`gezAR$BnjECHb5(un=A@qQU`$iyN67fT%hntu)>{Bd!AL%G93 zM45b5t`ftODv;&MR0=Fl&KHO<&c8SD-(EN(tPydHN)Tm=6e1~fl?=N~EX{)Dmck>9 ze_x2De^%kYxokuXI1ctT1ZM4Lz?#YhT9;UW@M{|&GSvWK(k3{A4Am`~mJINdmtxjE z*!Pg({J`^H9_VuTisp&4FkBp*kc0_TihP{mnZO?;KnI$D1q^^GumpC116+VR@CE@O z6pRJ2APJ;`DL??SfdnW(0hk5mgHliqR)Dpj3Ty_opbpf7{h$#X0Vly(a1mSvH^4p6 z0lL6*&J)xPJgCaIH*Nsc5hQY2{t zDT`D)G%rqRZ1y}oZ zHdEVKJ62nwy;OUb_Id56IusonolqU2PKnMool`oUOajxA8O#(g7cgs?XP8|qGRu|~ z$(qJ0XZ^rxXZ7mp=#J4%)-BYn);+4*!6vY6*pcj9_Hy=q_D%LjJrlh^J(1oLy?VW? zdhhg&^aJ!o`epii^>65ZG{6i(4RQ=t8Z;U_FeDl}7{(hG7;Z8=WB9^|W#nxnFe)?J zZ*s0G>&soQLtaGt*v-9sR4lV+hYM1My)klYoo;$i}^s6zB zV?<*%jk)d0bd7OckoqA?=|$q2oiVLLY`%gk^>84(knf z4WAi)B7zhV6|pknR-{R!AhIs9cdYx^*<(*fF`^Qqs-hl8J47p@n`6+Ju`#P-?vJw` zCmq)q3t}TTue1g6{j|(QPPsrwx{(?44t@kV%H?sNu`r+PPUy~ zIQje(!zsB_j!xB_%Aa~5os^!EzAOC`KbBv^@5_kH*pSgH2o|gpJQoHC*9dz=zM@s4 z?o8jzRhdt-e6v<(^Pet;nHhNm2KVuo>UaB+eAfY?);~YyPZ9vjb;uorBEDnA0-Xa&F1ou6g0}cFw2I z&zb*ou~YG?;x`LY7BrWbl*}vXTo}G^cd16HqV(1x??syz6BmmYw=WsJWc`xQWx}!x z`T5q*}#rjWG*;O~HgR1v#Fy63a!@G^bjaN4XY^vXE zvblWo$1UP5x3`9GZK$!Ssn|x|mcQ*uZE|hv_a5KZZ8zLrzWt9K(j6T;6Ly}ibFZu0 zWxQ+oZgjV5ch8y|Fq#C z^I%y6(oodU+nC+h(UjVB<J(9r244t(dEbJ$4ZWUIX>ff-wFAN z7bmk$cD77yxp!*9sq3fXPq&|mI&7Cp=ukPmG?Y}qgKIwkh1FZ)Y4~-wz{9^yh-VV2pqmM!!wLMOJe6Lf``TQSw|M=8Z z@DhvPz00ICsC9nc?x&iV5N(1Zy zxBw486Tm}%3KD|x1@3G9se)^ODm3nUdUtZo%Z^!;KUf?2)qyOnLJK`KDc+me_ Mu)VH_cl>JL055MV#sB~S literal 2622 zcmeHJF>ljA6n>XbQXmr}WjI}hget}722v5LZl$IPw31PjA{wzd_BFAR_$>P>n@kKy z42&HZ5K>mQ&LFiCzkq*HHY6sH5al^C?vKD{o!xR%wtz?d!#H?p%> zo|7xZc`2{1&dWKjq_n=(r3W_vHn)?&cE&={ff#ze$-aL+XVh~|cG$3VD`<(4_hc4| zgW1z(&TQ;7UADbNHxnb722-JInoK8tWF#iDT{#d2;`Ep^x+qa&lci6!WGHAud#LFQ z*`a%6^maQL7c-NMlnM;a<2csh2bv6re7)Iha=p&$^%_>xqL;q1lbRo`{!`c$krR4> z@}y5wMSCERmC5ksMN7d*DjAJrkc<0~wrEmwRDE`tp%JmU1`| qjAPF{YdVGb-*3@k6%G^*6b=*)6b=*){C^Hy%}>I|p()1c2>t?)0hB5L diff --git a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerImageUtil.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerImageUtil.m index 245a66177930..547632871d17 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerImageUtil.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerImageUtil.m @@ -28,16 +28,37 @@ - (instancetype)initWithImages:(NSArray *)images interval:(NSTimeInte @implementation FLTImagePickerImageUtil : NSObject +static UIImage *FLTImagePickerDrawScaledImage(UIImage *imageToScale, double width, double height) { + UIGraphicsImageRenderer *imageRenderer = + [[UIGraphicsImageRenderer alloc] initWithSize:CGSizeMake(width, height) + format:imageToScale.imageRendererFormat]; + return [imageRenderer imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) { + CGContextRef cgContext = rendererContext.CGContext; + + // Flip vertically to translate between UIKit and Quartz. + CGContextTranslateCTM(cgContext, 0, height); + CGContextScaleCTM(cgContext, 1, -1); + CGContextDrawImage(cgContext, CGRectMake(0, 0, width, height), imageToScale.CGImage); + }]; +} + + (UIImage *)scaledImage:(UIImage *)image maxWidth:(NSNumber *)maxWidth maxHeight:(NSNumber *)maxHeight isMetadataAvailable:(BOOL)isMetadataAvailable { double originalWidth = image.size.width; double originalHeight = image.size.height; - double aspectRatio = originalWidth / originalHeight; - bool hasMaxWidth = maxWidth != nil; - bool hasMaxHeight = maxHeight != nil; + BOOL hasMaxWidth = maxWidth != nil; + BOOL hasMaxHeight = maxHeight != nil; + + if ((originalWidth == maxWidth.doubleValue && originalHeight == maxHeight.doubleValue) || + (!hasMaxWidth && !hasMaxHeight)) { + // Nothing to scale. + return image; + } + + double aspectRatio = originalWidth / originalHeight; double width = hasMaxWidth ? MIN(round([maxWidth doubleValue]), originalWidth) : originalWidth; double height = @@ -62,13 +83,7 @@ + (UIImage *)scaledImage:(UIImage *)image UIImage *imageToScale = [UIImage imageWithCGImage:image.CGImage scale:1 orientation:image.imageOrientation]; - - UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), NO, 1.0); - [imageToScale drawInRect:CGRectMake(0, 0, width, height)]; - - UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - return scaledImage; + return FLTImagePickerDrawScaledImage(imageToScale, width, height); } // Scaling the image always rotate itself based on the current imageOrientation of the original @@ -91,13 +106,7 @@ + (UIImage *)scaledImage:(UIImage *)image width = height; height = temp; } - - UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), NO, 1.0); - [imageToScale drawInRect:CGRectMake(0, 0, width, height)]; - - UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - return scaledImage; + return FLTImagePickerDrawScaledImage(imageToScale, width, height); } + (GIFInfo *)scaledGIFImage:(NSData *)data diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index 46e57768fea5..c1b3c8d62217 100755 --- a/packages/image_picker/image_picker_ios/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_ios description: iOS implementation of the image_picker plugin. repository: https://github.com/flutter/packages/tree/main/packages/image_picker/image_picker_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.9+1 +version: 0.8.9+2 environment: sdk: ^3.2.3 From be216b63aee5f60b231d249668a46f3cc1f41faa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Mar 2024 23:32:07 +0000 Subject: [PATCH 075/126] Bump actions/checkout from 4.1.1 to 4.1.2 (#6309) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.1 to 4.1.2.
Release notes

Sourced from actions/checkout's releases.

v4.1.2

We are investigating the following issue with this release and have rolled-back the v4 tag to point to v4.1.1

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v4.1.1...v4.1.2

Changelog

Sourced from actions/checkout's changelog.

Changelog

v4.1.2

v4.1.1

v4.1.0

v4.0.0

v3.6.0

v3.5.3

v3.5.2

v3.5.1

v3.5.0

v3.4.0

v3.3.0

v3.2.0

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=4.1.1&new-version=4.1.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- .github/workflows/release.yml | 2 +- .github/workflows/scorecards-analysis.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index adf983fdc5e5..977f7f5f1525 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,7 +36,7 @@ jobs: cd $GITHUB_WORKSPACE # Checks out a copy of the repo. - name: Check out code - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 with: fetch-depth: 0 # Fetch all history so the tool can get all the tags to determine version. - name: Set up tools diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index ddcdb5a2941c..d8b96951cc50 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -21,7 +21,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v2.4.0 + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v2.4.0 with: persist-credentials: false From 1601b4ba4305bd163ed2f3859cd0473e1e5c2817 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 13 Mar 2024 19:37:51 -0400 Subject: [PATCH 076/126] Manual roll Flutter from 61812ca3eb13 to 394269f9ea2e (6 revisions) (#6320) Manual roll requested by tarrinneal@google.com https://github.com/flutter/flutter/compare/61812ca3eb13...394269f9ea2e 2024-03-12 engine-flutter-autoroll@skia.org Manual roll Flutter Engine from 6cefbe183546 to 67713071563e (5 revisions) (flutter/flutter#145002) 2024-03-12 goderbauer@google.com add missing `await`s (flutter/flutter#144978) 2024-03-12 Moelfarri@gmail.com Write unit tests for API Examples of checkbox.0, checkbox.1 (flutter/flutter#144888) 2024-03-12 goderbauer@google.com Fix typing to unblock pub roll (flutter/flutter#144968) 2024-03-12 engine-flutter-autoroll@skia.org Manual roll Flutter Engine from 6745955bb49e to 6cefbe183546 (6 revisions) (flutter/flutter#144996) 2024-03-12 engine-flutter-autoroll@skia.org Roll Packages from d489d84c359b to d1aeb26e1b43 (3 revisions) (flutter/flutter#144994) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC rmistry@google.com,stuartmorgan@google.com,tarrinneal@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 3b34cd66097b..878321c64055 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -61812ca3eb1348b8d8192cab5d8abc2fc72931fa +394269f9ea2e6ada1ba69b798791d6c3bec51168 From b21c5424783a9e74cb841a8df7a15f26eba04a7b Mon Sep 17 00:00:00 2001 From: Peixin Li Date: Wed, 13 Mar 2024 17:10:07 -0700 Subject: [PATCH 077/126] Support overriding `ErrorWidget.builder` (#6302) *List which issues are fixed by this PR. You must list at least one issue.* Issue: https://github.com/flutter/flutter/issues/144960 --- packages/rfw/.gitignore | 3 +++ packages/rfw/CHANGELOG.md | 3 +++ packages/rfw/lib/src/flutter/runtime.dart | 9 ++++--- packages/rfw/pubspec.yaml | 2 +- packages/rfw/test/runtime_test.dart | 32 +++++++++++++++++++---- 5 files changed, 39 insertions(+), 10 deletions(-) diff --git a/packages/rfw/.gitignore b/packages/rfw/.gitignore index 01f61108a475..0b90ed48e5ab 100644 --- a/packages/rfw/.gitignore +++ b/packages/rfw/.gitignore @@ -76,3 +76,6 @@ build/ !**/ios/**/default.mode2v3 !**/ios/**/default.pbxuser !**/ios/**/default.perspectivev3 + +# Coverage +coverage/ diff --git a/packages/rfw/CHANGELOG.md b/packages/rfw/CHANGELOG.md index b34abdb06489..7f1da5e2f35b 100644 --- a/packages/rfw/CHANGELOG.md +++ b/packages/rfw/CHANGELOG.md @@ -1,3 +1,6 @@ +## 1.0.26 +* Supports overriding the error widget builder. + ## 1.0.25 * Adds support for wildget builders. diff --git a/packages/rfw/lib/src/flutter/runtime.dart b/packages/rfw/lib/src/flutter/runtime.dart index f66b7cd22547..732f345525ab 100644 --- a/packages/rfw/lib/src/flutter/runtime.dart +++ b/packages/rfw/lib/src/flutter/runtime.dart @@ -1559,11 +1559,12 @@ class _Subscription { } } -ErrorWidget _buildErrorWidget(String message) { - FlutterError.reportError(FlutterErrorDetails( +Widget _buildErrorWidget(String message) { + final FlutterErrorDetails detail = FlutterErrorDetails( exception: message, stack: StackTrace.current, library: 'Remote Flutter Widgets', - )); - return ErrorWidget(message); + ); + FlutterError.reportError(detail); + return ErrorWidget.builder(detail); } diff --git a/packages/rfw/pubspec.yaml b/packages/rfw/pubspec.yaml index 310d7bd6535c..a83be027aafe 100644 --- a/packages/rfw/pubspec.yaml +++ b/packages/rfw/pubspec.yaml @@ -2,7 +2,7 @@ name: rfw description: "Remote Flutter widgets: a library for rendering declarative widget description files at runtime." repository: https://github.com/flutter/packages/tree/main/packages/rfw issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+rfw%22 -version: 1.0.25 +version: 1.0.26 environment: sdk: ^3.2.0 diff --git a/packages/rfw/test/runtime_test.dart b/packages/rfw/test/runtime_test.dart index f081df3e8126..05eeb05a1bac 100644 --- a/packages/rfw/test/runtime_test.dart +++ b/packages/rfw/test/runtime_test.dart @@ -244,7 +244,7 @@ void main() { ), ); expect(tester.takeException().toString(), contains('Could not find remote widget named')); - expect(tester.widget(find.byType(ErrorWidget)).message, 'Could not find remote widget named widget in a, possibly because some dependencies were missing: b'); + expect(tester.widget(find.byType(ErrorWidget)).message, contains('Could not find remote widget named widget in a, possibly because some dependencies were missing: b')); }); testWidgets('Missing libraries in specified widget', (WidgetTester tester) async { @@ -258,7 +258,7 @@ void main() { ), ); expect(tester.takeException().toString(), contains('Could not find remote widget named')); - expect(tester.widget(find.byType(ErrorWidget)).message, 'Could not find remote widget named widget in a, possibly because some dependencies were missing: a'); + expect(tester.widget(find.byType(ErrorWidget)).message, contains('Could not find remote widget named widget in a, possibly because some dependencies were missing: a')); }); testWidgets('Missing libraries in import via dependency', (WidgetTester tester) async { @@ -276,7 +276,7 @@ void main() { ), ); expect(tester.takeException().toString(), contains('Could not find remote widget named')); - expect(tester.widget(find.byType(ErrorWidget)).message, 'Could not find remote widget named test in a, possibly because some dependencies were missing: b'); + expect(tester.widget(find.byType(ErrorWidget)).message, contains('Could not find remote widget named test in a, possibly because some dependencies were missing: b')); }); testWidgets('Missing widget', (WidgetTester tester) async { @@ -291,7 +291,7 @@ void main() { ), ); expect(tester.takeException().toString(), contains('Could not find remote widget named')); - expect(tester.widget(find.byType(ErrorWidget)).message, 'Could not find remote widget named widget in a.'); + expect(tester.widget(find.byType(ErrorWidget)).message, contains('Could not find remote widget named widget in a.')); }); testWidgets('Runtime', (WidgetTester tester) async { @@ -1152,7 +1152,29 @@ void main() { expect(tester.takeException().toString(), contains(expectedErrorMessage)); expect(find.byType(ErrorWidget), findsOneWidget); - expect(tester.widget(find.byType(ErrorWidget)).message, expectedErrorMessage); + expect(tester.widget(find.byType(ErrorWidget)).message, contains(expectedErrorMessage)); + }); + + + testWidgets('Customized error widget', (WidgetTester tester) async { + final ErrorWidgetBuilder oldBuilder = ErrorWidget.builder; + ErrorWidget.builder = (FlutterErrorDetails details) { + return const Text('oopsie!', textDirection: TextDirection.ltr); + }; + final Runtime runtime = Runtime() + ..update(const LibraryName(['a']), parseLibraryFile('')); + final DynamicContent data = DynamicContent(); + await tester.pumpWidget( + RemoteWidget( + runtime: runtime, + data: data, + widget: const FullyQualifiedWidgetName(LibraryName(['a']), 'widget'), + ), + ); + expect(tester.takeException().toString(), contains('Could not find remote widget named')); + expect(find.text('oopsie!'), findsOneWidget); + expect(find.byType(ErrorWidget), findsNothing); + ErrorWidget.builder = oldBuilder; }); testWidgets('Widget builders - work when scope is not used', (WidgetTester tester) async { From 221830014758bc4fb7d09411b0c0de9c9964a4d9 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Thu, 14 Mar 2024 10:35:11 -0700 Subject: [PATCH 078/126] [google_maps_flutter] Started dispatching platform messages from platform thread (#6069) Issue: https://github.com/flutter/flutter/issues/135252 Testing: This adds no new functionality and thus should be covered in tests. The problem is that we are permissive to unsafe platform channel usage in the release flutter engine. We should maybe consider running tests against a debug version of the engine. --- .../google_maps_flutter_ios/CHANGELOG.md | 4 ++ .../ios/Runner.xcodeproj/project.pbxproj | 4 ++ .../FLTTileProviderControllerTests.m | 32 ++++++++++ .../FLTGoogleMapTileOverlayController.m | 63 ++++++++++--------- .../google_maps_flutter_ios/pubspec.yaml | 2 +- 5 files changed, 73 insertions(+), 32 deletions(-) create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios12/ios/RunnerTests/FLTTileProviderControllerTests.m diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md index 6778f3b8e1ee..96468dc393fc 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.5.1 + +* Makes the tile overlay callback invoke the platform channel on the platform thread. + ## 2.5.0 * Adds support for `MapConfiguration.style`. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios12/ios/Runner.xcodeproj/project.pbxproj b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios12/ios/Runner.xcodeproj/project.pbxproj index 6ae6a4f3333d..432bdd7b15f6 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios12/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios12/ios/Runner.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 0DD7B6C32B744EEF00E857FD /* FLTTileProviderControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DD7B6C22B744EEF00E857FD /* FLTTileProviderControllerTests.m */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 4510D964F3B1259FEDD3ABA6 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7755F8F4BABC3D6A0BD4048B /* libPods-Runner.a */; }; @@ -54,6 +55,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 0DD7B6C22B744EEF00E857FD /* FLTTileProviderControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTTileProviderControllerTests.m; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; @@ -201,6 +203,7 @@ 982F2A6A27BADE17003C81F4 /* PartiallyMockedMapView.h */, 982F2A6B27BADE17003C81F4 /* PartiallyMockedMapView.m */, F7151F14265D7ED70028CB91 /* Info.plist */, + 0DD7B6C22B744EEF00E857FD /* FLTTileProviderControllerTests.m */, ); path = RunnerTests; sourceTree = ""; @@ -460,6 +463,7 @@ F7151F13265D7ED70028CB91 /* GoogleMapsTests.m in Sources */, 6851F3562835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m in Sources */, 982F2A6C27BADE17003C81F4 /* PartiallyMockedMapView.m in Sources */, + 0DD7B6C32B744EEF00E857FD /* FLTTileProviderControllerTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios12/ios/RunnerTests/FLTTileProviderControllerTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios12/ios/RunnerTests/FLTTileProviderControllerTests.m new file mode 100644 index 000000000000..d42174a906f5 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios12/ios/RunnerTests/FLTTileProviderControllerTests.m @@ -0,0 +1,32 @@ +// 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 GoogleMaps; +@import google_maps_flutter_ios; + +#import + +@interface FLTTileProviderControllerTests : XCTestCase +@end + +@implementation FLTTileProviderControllerTests + +- (void)testCallChannelOnPlatformThread { + id channel = OCMClassMock(FlutterMethodChannel.class); + FLTTileProviderController *controller = [[FLTTileProviderController alloc] init:channel + withTileOverlayIdentifier:@"foo"]; + XCTAssertNotNil(controller); + XCTestExpectation *expectation = [self expectationWithDescription:@"invokeMethod"]; + OCMStub([channel invokeMethod:[OCMArg any] arguments:[OCMArg any] result:[OCMArg any]]) + .andDo(^(NSInvocation *invocation) { + XCTAssertTrue([[NSThread currentThread] isMainThread]); + [expectation fulfill]; + }); + id receiver = OCMProtocolMock(@protocol(GMSTileReceiver)); + [controller requestTileForX:0 y:0 zoom:0 receiver:receiver]; + [self waitForExpectations:@[ expectation ] timeout:10.0]; +} + +@end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m index 5863697d7b9b..ffa646bf870f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m @@ -122,37 +122,38 @@ - (void)requestTileForX:(NSUInteger)x y:(NSUInteger)y zoom:(NSUInteger)zoom receiver:(id)receiver { - [self.methodChannel - invokeMethod:@"tileOverlay#getTile" - arguments:@{ - @"tileOverlayId" : self.tileOverlayIdentifier, - @"x" : @(x), - @"y" : @(y), - @"zoom" : @(zoom) - } - result:^(id _Nullable result) { - UIImage *tileImage; - if ([result isKindOfClass:[NSDictionary class]]) { - FlutterStandardTypedData *typedData = (FlutterStandardTypedData *)result[@"data"]; - if (typedData == nil) { - tileImage = kGMSTileLayerNoTile; - } else { - tileImage = [UIImage imageWithData:typedData.data]; - } - } else { - if ([result isKindOfClass:[FlutterError class]]) { - FlutterError *error = (FlutterError *)result; - NSLog(@"Can't get tile: errorCode = %@, errorMessage = %@, details = %@", - [error code], [error message], [error details]); - } - if ([result isKindOfClass:[FlutterMethodNotImplemented class]]) { - NSLog(@"Can't get tile: notImplemented"); - } - tileImage = kGMSTileLayerNoTile; - } - - [receiver receiveTileWithX:x y:y zoom:zoom image:tileImage]; - }]; + dispatch_async(dispatch_get_main_queue(), ^{ + [self.methodChannel invokeMethod:@"tileOverlay#getTile" + arguments:@{ + @"tileOverlayId" : self.tileOverlayIdentifier, + @"x" : @(x), + @"y" : @(y), + @"zoom" : @(zoom) + } + result:^(id _Nullable result) { + UIImage *tileImage; + if ([result isKindOfClass:[NSDictionary class]]) { + FlutterStandardTypedData *typedData = (FlutterStandardTypedData *)result[@"data"]; + if (typedData == nil) { + tileImage = kGMSTileLayerNoTile; + } else { + tileImage = [UIImage imageWithData:typedData.data]; + } + } else { + if ([result isKindOfClass:[FlutterError class]]) { + FlutterError *error = (FlutterError *)result; + NSLog(@"Can't get tile: errorCode = %@, errorMessage = %@, details = %@", + [error code], [error message], [error details]); + } + if ([result isKindOfClass:[FlutterMethodNotImplemented class]]) { + NSLog(@"Can't get tile: notImplemented"); + } + tileImage = kGMSTileLayerNoTile; + } + + [receiver receiveTileWithX:x y:y zoom:zoom image:tileImage]; + }]; + }); } @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml index a90927475205..c1ebb4e2cea2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_ios description: iOS implementation of the google_maps_flutter plugin. repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.5.0 +version: 2.5.1 environment: sdk: ^3.2.3 From e8ab6323e918511d887d197025aaa6649cb8ce7a Mon Sep 17 00:00:00 2001 From: Gray Mackall <34871572+gmackall@users.noreply.github.com> Date: Thu, 14 Mar 2024 10:55:20 -0700 Subject: [PATCH 079/126] [image_picker_android] Fix deprecation warnings by branching based on build version, and suppressing only when needed (#6233) Fixes https://github.com/flutter/flutter/issues/121816. --- .../image_picker_android/CHANGELOG.md | 3 ++- .../imagepicker/ImagePickerDelegate.java | 18 +++++++++++++---- .../plugins/imagepicker/ImagePickerUtils.java | 20 +++++++++++++++---- .../image_picker_android/pubspec.yaml | 2 +- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md index b06dcfa43c85..08b563a98856 100644 --- a/packages/image_picker/image_picker_android/CHANGELOG.md +++ b/packages/image_picker/image_picker_android/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.8.9+4 +* Minimizes scope of deprecation warning suppression to only the versions where it is required. * Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. * Updates compileSdk version to 34. diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java index cf45f1ba3f0d..b50a1b20cd1e 100644 --- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java +++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java @@ -550,10 +550,14 @@ private File createTemporaryWritableFile(String suffix) { private void grantUriPermissions(Intent intent, Uri imageUri) { PackageManager packageManager = activity.getPackageManager(); - // TODO(stuartmorgan): Add new codepath: https://github.com/flutter/flutter/issues/121816 - @SuppressWarnings("deprecation") - List compatibleActivities = - packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); + List compatibleActivities; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + compatibleActivities = + packageManager.queryIntentActivities( + intent, PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY)); + } else { + compatibleActivities = queryIntentActivitiesPreApi33(packageManager, intent); + } for (ResolveInfo info : compatibleActivities) { activity.grantUriPermission( @@ -563,6 +567,12 @@ private void grantUriPermissions(Intent intent, Uri imageUri) { } } + @SuppressWarnings("deprecation") + private static List queryIntentActivitiesPreApi33( + PackageManager packageManager, Intent intent) { + return packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); + } + @Override public boolean onRequestPermissionsResult( int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java index 5e80258a00d5..6a93c69feb49 100644 --- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java +++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java @@ -16,10 +16,15 @@ final class ImagePickerUtils { private static boolean isPermissionPresentInManifest(Context context, String permissionName) { try { PackageManager packageManager = context.getPackageManager(); - // TODO(stuartmorgan): Add new codepath: https://github.com/flutter/flutter/issues/121816 - @SuppressWarnings("deprecation") - PackageInfo packageInfo = - packageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS); + PackageInfo packageInfo; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + packageInfo = + packageManager.getPackageInfo( + context.getPackageName(), + PackageManager.PackageInfoFlags.of(PackageManager.GET_PERMISSIONS)); + } else { + packageInfo = getPermissionsPackageInfoPreApi33(packageManager, context.getPackageName()); + } String[] requestedPermissions = packageInfo.requestedPermissions; return Arrays.asList(requestedPermissions).contains(permissionName); @@ -29,6 +34,13 @@ private static boolean isPermissionPresentInManifest(Context context, String per } } + @SuppressWarnings("deprecation") + private static PackageInfo getPermissionsPackageInfoPreApi33( + PackageManager packageManager, String packageName) + throws PackageManager.NameNotFoundException { + return packageManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS); + } + /** * Camera permission need request if it present in manifest, because for M or great for take Photo * ar Video by intent need it permission, even if the camera permission is not used. diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index 5e575a36b937..a2e8ac5eaefe 100755 --- a/packages/image_picker/image_picker_android/pubspec.yaml +++ b/packages/image_picker/image_picker_android/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_android description: Android implementation of the image_picker plugin. repository: https://github.com/flutter/packages/tree/main/packages/image_picker/image_picker_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.9+3 +version: 0.8.9+4 environment: sdk: ^3.1.0 From 92a8b7a0b9356452890b15e611df277915027d08 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 14 Mar 2024 15:03:08 -0400 Subject: [PATCH 080/126] [webview_flutter_android][webview_flutter_wkwebview] Adds platform implementations for onHttpError (#6149) Copy of https://github.com/flutter/packages/pull/3695 since it doesn't contain permission to edit from contributors. Part of https://github.com/flutter/flutter/issues/39502 Full PR https://github.com/flutter/packages/pull/3278 --- .../webview_flutter_android/CHANGELOG.md | 4 +- .../GeneratedAndroidWebView.java | 74 ++ .../WebViewClientFlutterApiImpl.java | 30 + .../WebViewClientHostApiImpl.java | 18 + .../webviewflutter/WebViewClientTest.java | 25 + .../webview_flutter_test.dart | 75 ++ .../example/lib/main.dart | 5 + .../lib/src/android_proxy.dart | 5 + .../lib/src/android_webview.dart | 25 + .../lib/src/android_webview.g.dart | 61 ++ .../lib/src/android_webview_api_impls.dart | 35 + .../lib/src/android_webview_controller.dart | 27 + .../pigeons/android_webview.dart | 15 + .../webview_flutter_android/pubspec.yaml | 4 +- .../android_navigation_delegate_test.dart | 24 + .../test/android_webview_controller_test.dart | 5 + ...android_webview_controller_test.mocks.dart | 53 +- .../test/android_webview_test.dart | 31 + .../test/android_webview_test.mocks.dart | 6 + .../webview_android_widget_test.mocks.dart | 3 + .../webview_flutter_wkwebview/CHANGELOG.md | 5 + .../webview_flutter_test.dart | 75 ++ .../ios/RunnerTests/FWFDataConvertersTests.m | 30 + .../FWFNavigationDelegateHostApiTests.m | 42 + .../example/lib/main.dart | 3 + .../ios/Classes/FWFDataConverters.h | 53 +- .../ios/Classes/FWFDataConverters.m | 30 + .../ios/Classes/FWFGeneratedWebKitApis.h | 49 +- .../ios/Classes/FWFGeneratedWebKitApis.m | 919 ++++++++++------- .../Classes/FWFNavigationDelegateHostApi.m | 38 + .../lib/src/common/web_kit.g.dart | 959 ++++++++++-------- .../lib/src/foundation/foundation.dart | 15 + .../lib/src/web_kit/web_kit.dart | 44 + .../lib/src/web_kit/web_kit_api_impls.dart | 44 + .../lib/src/webkit_proxy.dart | 4 + .../lib/src/webkit_webview_controller.dart | 22 + .../pigeons/web_kit.dart | 53 +- .../webview_flutter_wkwebview/pubspec.yaml | 4 +- .../web_kit_cookie_manager_test.mocks.dart | 7 - .../web_kit_webview_widget_test.mocks.dart | 71 -- .../test/src/common/test_web_kit.g.dart | 70 +- .../src/foundation/foundation_test.mocks.dart | 2 - .../test/src/ui_kit/ui_kit_test.mocks.dart | 28 - .../test/src/web_kit/web_kit_test.dart | 28 + .../test/src/web_kit/web_kit_test.mocks.dart | 32 - .../test/webkit_navigation_delegate_test.dart | 55 + .../test/webkit_webview_controller_test.dart | 1 + .../webkit_webview_controller_test.mocks.dart | 63 -- ...kit_webview_cookie_manager_test.mocks.dart | 7 - .../webkit_webview_widget_test.mocks.dart | 10 - 50 files changed, 2196 insertions(+), 1092 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 403294ee39fe..44f60d72f469 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,5 +1,7 @@ -## NEXT +## 3.16.0 +* Adds onReceivedHttpError WebViewClient callback to support + `PlatformNavigationDelegate.onHttpError`. * Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. * Updates compileSdk to 34. diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java index 0d0d37e5d3da..cfdadec40144 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java @@ -316,6 +316,58 @@ ArrayList toList() { } } + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class WebResourceResponseData { + private @NonNull Long statusCode; + + public @NonNull Long getStatusCode() { + return statusCode; + } + + public void setStatusCode(@NonNull Long setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"statusCode\" is null."); + } + this.statusCode = setterArg; + } + + /** Constructor is non-public to enforce null safety; use Builder. */ + WebResourceResponseData() {} + + public static final class Builder { + + private @Nullable Long statusCode; + + public @NonNull Builder setStatusCode(@NonNull Long setterArg) { + this.statusCode = setterArg; + return this; + } + + public @NonNull WebResourceResponseData build() { + WebResourceResponseData pigeonReturn = new WebResourceResponseData(); + pigeonReturn.setStatusCode(statusCode); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(1); + toListResult.add(statusCode); + return toListResult; + } + + static @NonNull WebResourceResponseData fromList(@NonNull ArrayList list) { + WebResourceResponseData pigeonResult = new WebResourceResponseData(); + Object statusCode = list.get(0); + pigeonResult.setStatusCode( + (statusCode == null) + ? null + : ((statusCode instanceof Integer) ? (Integer) statusCode : (Long) statusCode)); + return pigeonResult; + } + } + /** Generated class from Pigeon that represents data sent in messages. */ public static final class WebResourceErrorData { private @NonNull Long errorCode; @@ -2388,6 +2440,8 @@ protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { return WebResourceErrorData.fromList((ArrayList) readValue(buffer)); case (byte) 129: return WebResourceRequestData.fromList((ArrayList) readValue(buffer)); + case (byte) 130: + return WebResourceResponseData.fromList((ArrayList) readValue(buffer)); default: return super.readValueOfType(type, buffer); } @@ -2401,6 +2455,9 @@ protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { } else if (value instanceof WebResourceRequestData) { stream.write(129); writeValue(stream, ((WebResourceRequestData) value).toList()); + } else if (value instanceof WebResourceResponseData) { + stream.write(130); + writeValue(stream, ((WebResourceResponseData) value).toList()); } else { super.writeValue(stream, value); } @@ -2455,6 +2512,23 @@ public void onPageFinished( channelReply -> callback.reply(null)); } + public void onReceivedHttpError( + @NonNull Long instanceIdArg, + @NonNull Long webViewInstanceIdArg, + @NonNull WebResourceRequestData requestArg, + @NonNull WebResourceResponseData responseArg, + @NonNull Reply callback) { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.webview_flutter_android.WebViewClientFlutterApi.onReceivedHttpError", + getCodec()); + channel.send( + new ArrayList( + Arrays.asList(instanceIdArg, webViewInstanceIdArg, requestArg, responseArg)), + channelReply -> callback.reply(null)); + } + public void onReceivedRequestError( @NonNull Long instanceIdArg, @NonNull Long webViewInstanceIdArg, diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientFlutterApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientFlutterApiImpl.java index 7a5a057cf115..0bd280d705b2 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientFlutterApiImpl.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientFlutterApiImpl.java @@ -9,6 +9,7 @@ import android.webkit.HttpAuthHandler; import android.webkit.WebResourceError; import android.webkit.WebResourceRequest; +import android.webkit.WebResourceResponse; import android.webkit.WebView; import android.webkit.WebViewClient; import androidx.annotation.NonNull; @@ -70,6 +71,16 @@ static GeneratedAndroidWebView.WebResourceRequestData createWebResourceRequestDa return requestData.build(); } + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + static GeneratedAndroidWebView.WebResourceResponseData createWebResourceResponseData( + WebResourceResponse response) { + final GeneratedAndroidWebView.WebResourceResponseData.Builder responseData = + new GeneratedAndroidWebView.WebResourceResponseData.Builder() + .setStatusCode((long) response.getStatusCode()); + + return responseData.build(); + } + /** * Creates a Flutter api that sends messages to Dart. * @@ -110,6 +121,25 @@ public void onPageFinished( onPageFinished(getIdentifierForClient(webViewClient), webViewIdentifier, urlArg, callback); } + /** Passes arguments from {@link WebViewClient#onReceivedHttpError} to Dart. */ + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public void onReceivedHttpError( + @NonNull WebViewClient webViewClient, + @NonNull WebView webView, + @NonNull WebResourceRequest request, + @NonNull WebResourceResponse response, + @NonNull Reply callback) { + webViewFlutterApi.create(webView, reply -> {}); + + final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView); + onReceivedHttpError( + getIdentifierForClient(webViewClient), + webViewIdentifier, + createWebResourceRequestData(request), + createWebResourceResponseData(response), + callback); + } + /** * Passes arguments from {@link WebViewClient#onReceivedError(WebView, WebResourceRequest, * WebResourceError)} to Dart. diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientHostApiImpl.java index 1ace7bfe0729..a2ed499629cb 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientHostApiImpl.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientHostApiImpl.java @@ -12,6 +12,7 @@ import android.webkit.HttpAuthHandler; import android.webkit.WebResourceError; import android.webkit.WebResourceRequest; +import android.webkit.WebResourceResponse; import android.webkit.WebView; import android.webkit.WebViewClient; import androidx.annotation.NonNull; @@ -55,6 +56,14 @@ public void onPageFinished(@NonNull WebView view, @NonNull String url) { flutterApi.onPageFinished(this, view, url, reply -> {}); } + @Override + public void onReceivedHttpError( + @NonNull WebView view, + @NonNull WebResourceRequest request, + @NonNull WebResourceResponse response) { + flutterApi.onReceivedHttpError(this, view, request, response, reply -> {}); + } + @Override public void onReceivedError( @NonNull WebView view, @@ -140,6 +149,15 @@ public void onPageFinished(@NonNull WebView view, @NonNull String url) { flutterApi.onPageFinished(this, view, url, reply -> {}); } + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + @Override + public void onReceivedHttpError( + @NonNull WebView view, + @NonNull WebResourceRequest request, + @NonNull WebResourceResponse response) { + flutterApi.onReceivedHttpError(this, view, request, response, reply -> {}); + } + // This method is only called when the WebViewFeature.RECEIVE_WEB_RESOURCE_ERROR feature is // enabled. The deprecated method is called when a device doesn't support this. @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java index 8e6b58149d05..ea40742465f2 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java @@ -13,6 +13,7 @@ import android.net.Uri; import android.webkit.WebResourceRequest; +import android.webkit.WebResourceResponse; import android.webkit.WebView; import android.webkit.WebViewClient; import androidx.annotation.NonNull; @@ -136,4 +137,28 @@ public void doUpdateVisitedHistory() { .doUpdateVisitedHistory( eq(webViewClient), eq(mockWebView), eq("https://www.google.com"), eq(true), any()); } + + @Test + public void onReceivedHttpError() { + final Uri mockUri = mock(Uri.class); + when(mockUri.toString()).thenReturn(""); + + final WebResourceRequest mockRequest = mock(WebResourceRequest.class); + when(mockRequest.getMethod()).thenReturn("method"); + when(mockRequest.getUrl()).thenReturn(mockUri); + when(mockRequest.isForMainFrame()).thenReturn(true); + when(mockRequest.getRequestHeaders()).thenReturn(null); + + final WebResourceResponse mockResponse = mock(WebResourceResponse.class); + when(mockResponse.getStatusCode()).thenReturn(404); + + webViewClient.onReceivedHttpError(mockWebView, mockRequest, mockResponse); + verify(mockFlutterApi) + .onReceivedHttpError( + eq(webViewClient), + eq(mockWebView), + any(WebResourceRequest.class), + any(WebResourceResponse.class), + any()); + } } 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 f599f07cc3a7..cee7d65f33fe 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 @@ -978,6 +978,81 @@ Future main() async { await pageFinishCompleter.future; }); + testWidgets('onHttpError', (WidgetTester tester) async { + final Completer errorCompleter = + Completer(); + + final PlatformWebViewController controller = PlatformWebViewController( + const PlatformWebViewControllerCreationParams(), + ); + unawaited(controller.setJavaScriptMode(JavaScriptMode.unrestricted)); + final PlatformNavigationDelegate delegate = PlatformNavigationDelegate( + const PlatformNavigationDelegateCreationParams(), + ); + unawaited(delegate.setOnHttpError((HttpResponseError error) { + errorCompleter.complete(error); + })); + unawaited(controller.setPlatformNavigationDelegate(delegate)); + unawaited(controller.loadRequest( + LoadRequestParams(uri: Uri.parse('$prefixUrl/favicon.ico')), + )); + + await tester.pumpWidget(Builder( + builder: (BuildContext context) { + return PlatformWebViewWidget( + PlatformWebViewWidgetCreationParams(controller: controller), + ).build(context); + }, + )); + + final HttpResponseError error = await errorCompleter.future; + + expect(error, isNotNull); + expect(error.response?.statusCode, 404); + }); + + testWidgets('onHttpError is not called when no HTTP error is received', + (WidgetTester tester) async { + const String testPage = ''' + + + + + + '''; + + final Completer errorCompleter = + Completer(); + final Completer pageFinishCompleter = Completer(); + + final PlatformWebViewController controller = PlatformWebViewController( + const PlatformWebViewControllerCreationParams(), + ); + unawaited(controller.setJavaScriptMode(JavaScriptMode.unrestricted)); + final PlatformNavigationDelegate delegate = PlatformNavigationDelegate( + const PlatformNavigationDelegateCreationParams(), + ); + unawaited(delegate.setOnHttpError((HttpResponseError error) { + errorCompleter.complete(error); + })); + unawaited(delegate.setOnPageFinished( + (_) => pageFinishCompleter.complete(), + )); + unawaited(controller.setPlatformNavigationDelegate(delegate)); + unawaited(controller.loadHtmlString(testPage)); + + await tester.pumpWidget(Builder( + builder: (BuildContext context) { + return PlatformWebViewWidget( + PlatformWebViewWidgetCreationParams(controller: controller), + ).build(context); + }, + )); + + expect(errorCompleter.future, doesNotComplete); + await pageFinishCompleter.future; + }); + testWidgets('can block requests', (WidgetTester tester) async { Completer pageLoaded = Completer(); 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 addccb00cde6..0f2972f23ef1 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart @@ -173,6 +173,11 @@ class _WebViewExampleState extends State { ..setOnPageFinished((String url) { debugPrint('Page finished loading: $url'); }) + ..setOnHttpError((HttpResponseError error) { + debugPrint( + 'HTTP error occured on page: ${error.response?.statusCode}', + ); + }) ..setOnWebResourceError((WebResourceError error) { debugPrint(''' Page resource error: diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_proxy.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_proxy.dart index 9f02d1b5f93f..78089d714b38 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_proxy.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_proxy.dart @@ -65,6 +65,11 @@ class AndroidWebViewProxy { final android_webview.WebViewClient Function({ void Function(android_webview.WebView webView, String url)? onPageStarted, void Function(android_webview.WebView webView, String url)? onPageFinished, + void Function( + android_webview.WebView webView, + android_webview.WebResourceRequest request, + android_webview.WebResourceResponse response, + )? onReceivedHttpError, void Function( android_webview.WebView webView, android_webview.WebResourceRequest request, diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart index 0803198a176b..61174ccbf639 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart @@ -775,6 +775,7 @@ class WebViewClient extends JavaObject { WebViewClient({ this.onPageStarted, this.onPageFinished, + this.onReceivedHttpError, this.onReceivedRequestError, @Deprecated('Only called on Android version < 23.') this.onReceivedError, this.requestLoading, @@ -796,6 +797,7 @@ class WebViewClient extends JavaObject { WebViewClient.detached({ this.onPageStarted, this.onPageFinished, + this.onReceivedHttpError, this.onReceivedRequestError, @Deprecated('Only called on Android version < 23.') this.onReceivedError, this.requestLoading, @@ -908,6 +910,15 @@ class WebViewClient extends JavaObject { /// reflect the state of the DOM at this point. final void Function(WebView webView, String url)? onPageFinished; + /// Notify the host application that an HTTP error has been received from the + /// server while loading a resource. + /// + /// HTTP errors have status codes >= 400. This callback will be called for any + /// resource (iframe, image, etc.), not just for the main page. Thus, it is + /// recommended to perform minimum required work in this callback. + final void Function(WebView webView, WebResourceRequest request, + WebResourceResponse response)? onReceivedHttpError; + /// Report web resource loading error to the host application. /// /// These errors usually indicate inability to connect to the server. Note @@ -981,6 +992,7 @@ class WebViewClient extends JavaObject { return WebViewClient.detached( onPageStarted: onPageStarted, onPageFinished: onPageFinished, + onReceivedHttpError: onReceivedHttpError, onReceivedRequestError: onReceivedRequestError, onReceivedError: onReceivedError, requestLoading: requestLoading, @@ -1470,6 +1482,19 @@ class WebResourceRequest { final Map requestHeaders; } +/// Encapsulates information about the web resource response. +/// +/// See [WebViewClient.onReceivedHttpError]. +class WebResourceResponse { + /// Constructs a [WebResourceResponse]. + WebResourceResponse({ + required this.statusCode, + }); + + /// The HTTP status code associated with the response. + final int statusCode; +} + /// Encapsulates information about errors occurred during loading of web resources. /// /// See [WebViewClient.onReceivedRequestError]. diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.g.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.g.dart index e7ece16f722b..5d2707f9472f 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.g.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.g.dart @@ -115,6 +115,27 @@ class WebResourceRequestData { } } +class WebResourceResponseData { + WebResourceResponseData({ + required this.statusCode, + }); + + int statusCode; + + Object encode() { + return [ + statusCode, + ]; + } + + static WebResourceResponseData decode(Object result) { + result as List; + return WebResourceResponseData( + statusCode: result[0]! as int, + ); + } +} + class WebResourceErrorData { WebResourceErrorData({ required this.errorCode, @@ -1700,6 +1721,9 @@ class _WebViewClientFlutterApiCodec extends StandardMessageCodec { } else if (value is WebResourceRequestData) { buffer.putUint8(129); writeValue(buffer, value.encode()); + } else if (value is WebResourceResponseData) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -1712,6 +1736,8 @@ class _WebViewClientFlutterApiCodec extends StandardMessageCodec { return WebResourceErrorData.decode(readValue(buffer)!); case 129: return WebResourceRequestData.decode(readValue(buffer)!); + case 130: + return WebResourceResponseData.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -1725,6 +1751,9 @@ abstract class WebViewClientFlutterApi { void onPageFinished(int instanceId, int webViewInstanceId, String url); + void onReceivedHttpError(int instanceId, int webViewInstanceId, + WebResourceRequestData request, WebResourceResponseData response); + void onReceivedRequestError(int instanceId, int webViewInstanceId, WebResourceRequestData request, WebResourceErrorData error); @@ -1796,6 +1825,38 @@ abstract class WebViewClientFlutterApi { }); } } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.webview_flutter_android.WebViewClientFlutterApi.onReceivedHttpError', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.webview_flutter_android.WebViewClientFlutterApi.onReceivedHttpError was null.'); + final List args = (message as List?)!; + final int? arg_instanceId = (args[0] as int?); + assert(arg_instanceId != null, + 'Argument for dev.flutter.pigeon.webview_flutter_android.WebViewClientFlutterApi.onReceivedHttpError was null, expected non-null int.'); + final int? arg_webViewInstanceId = (args[1] as int?); + assert(arg_webViewInstanceId != null, + 'Argument for dev.flutter.pigeon.webview_flutter_android.WebViewClientFlutterApi.onReceivedHttpError was null, expected non-null int.'); + final WebResourceRequestData? arg_request = + (args[2] as WebResourceRequestData?); + assert(arg_request != null, + 'Argument for dev.flutter.pigeon.webview_flutter_android.WebViewClientFlutterApi.onReceivedHttpError was null, expected non-null WebResourceRequestData.'); + final WebResourceResponseData? arg_response = + (args[3] as WebResourceResponseData?); + assert(arg_response != null, + 'Argument for dev.flutter.pigeon.webview_flutter_android.WebViewClientFlutterApi.onReceivedHttpError was null, expected non-null WebResourceResponseData.'); + api.onReceivedHttpError(arg_instanceId!, arg_webViewInstanceId!, + arg_request!, arg_response!); + return; + }); + } + } { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.webview_flutter_android.WebViewClientFlutterApi.onReceivedRequestError', diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart index f157c01a7424..c6f3c29c1b83 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart @@ -25,6 +25,13 @@ WebResourceRequest _toWebResourceRequest(WebResourceRequestData data) { ); } +/// Converts [WebResourceResponseData] to [WebResourceResponse] +WebResourceResponse _toWebResourceResponse(WebResourceResponseData data) { + return WebResourceResponse( + statusCode: data.statusCode, + ); +} + /// Converts [WebResourceErrorData] to [WebResourceError]. WebResourceError _toWebResourceError(WebResourceErrorData data) { return WebResourceError( @@ -700,6 +707,34 @@ class WebViewClientFlutterApiImpl extends WebViewClientFlutterApi { } } + @override + void onReceivedHttpError( + int instanceId, + int webViewInstanceId, + WebResourceRequestData request, + WebResourceResponseData response, + ) { + final WebViewClient? instance = instanceManager + .getInstanceWithWeakReference(instanceId) as WebViewClient?; + final WebView? webViewInstance = instanceManager + .getInstanceWithWeakReference(webViewInstanceId) as WebView?; + assert( + instance != null, + 'InstanceManager does not contain an WebViewClient with instanceId: $instanceId', + ); + assert( + webViewInstance != null, + 'InstanceManager does not contain an WebView with instanceId: $webViewInstanceId', + ); + if (instance!.onReceivedHttpError != null) { + instance.onReceivedHttpError!( + webViewInstance!, + _toWebResourceRequest(request), + _toWebResourceResponse(response), + ); + } + } + @override void onReceivedError( int instanceId, 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 b9dfe24a7603..f353532bc5dd 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 @@ -1297,6 +1297,25 @@ class AndroidNavigationDelegate extends PlatformNavigationDelegate { callback(url); } }, + onReceivedHttpError: ( + android_webview.WebView webView, + android_webview.WebResourceRequest request, + android_webview.WebResourceResponse response, + ) { + if (weakThis.target?._onHttpError != null) { + weakThis.target!._onHttpError!( + HttpResponseError( + request: WebResourceRequest( + uri: Uri.parse(request.url), + ), + response: WebResourceResponse( + uri: null, + statusCode: response.statusCode, + ), + ), + ); + } + }, onReceivedRequestError: ( android_webview.WebView webView, android_webview.WebResourceRequest request, @@ -1429,6 +1448,7 @@ class AndroidNavigationDelegate extends PlatformNavigationDelegate { PageEventCallback? _onPageFinished; PageEventCallback? _onPageStarted; + HttpResponseErrorCallback? _onHttpError; ProgressCallback? _onProgress; WebResourceErrorCallback? _onWebResourceError; NavigationRequestCallback? _onNavigationRequest; @@ -1503,6 +1523,13 @@ class AndroidNavigationDelegate extends PlatformNavigationDelegate { _onPageFinished = onPageFinished; } + @override + Future setOnHttpError( + HttpResponseErrorCallback onHttpError, + ) async { + _onHttpError = onHttpError; + } + @override Future setOnProgress( ProgressCallback onProgress, diff --git a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart index 8f40d9120ce7..73b8093d05eb 100644 --- a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart @@ -111,6 +111,14 @@ class WebResourceRequestData { Map requestHeaders; } +class WebResourceResponseData { + WebResourceResponseData( + this.statusCode, + ); + + int statusCode; +} + class WebResourceErrorData { WebResourceErrorData(this.errorCode, this.description); @@ -337,6 +345,13 @@ abstract class WebViewClientFlutterApi { void onPageFinished(int instanceId, int webViewInstanceId, String url); + void onReceivedHttpError( + int instanceId, + int webViewInstanceId, + WebResourceRequestData request, + WebResourceResponseData response, + ); + void onReceivedRequestError( int instanceId, int webViewInstanceId, diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 9f3fd0602a1f..01add9158426 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.15.0 +version: 3.16.0 environment: sdk: ^3.1.0 @@ -20,7 +20,7 @@ flutter: dependencies: flutter: sdk: flutter - webview_flutter_platform_interface: ^2.9.0 + webview_flutter_platform_interface: ^2.10.0 dev_dependencies: build_runner: ^2.1.4 diff --git a/packages/webview_flutter/webview_flutter_android/test/android_navigation_delegate_test.dart b/packages/webview_flutter/webview_flutter_android/test/android_navigation_delegate_test.dart index 36681d092164..85b99dec2327 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_navigation_delegate_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_navigation_delegate_test.dart @@ -59,6 +59,29 @@ void main() { expect(callbackUrl, 'https://www.google.com'); }); + test('onHttpError from onReceivedHttpError', () { + final AndroidNavigationDelegate androidNavigationDelegate = + AndroidNavigationDelegate(_buildCreationParams()); + + late final HttpResponseError callbackError; + androidNavigationDelegate.setOnHttpError( + (HttpResponseError httpError) => callbackError = httpError); + + CapturingWebViewClient.lastCreatedDelegate.onReceivedHttpError!( + android_webview.WebView.detached(), + android_webview.WebResourceRequest( + url: 'https://www.google.com', + isForMainFrame: false, + isRedirect: true, + hasGesture: true, + method: 'GET', + requestHeaders: {'X-Mock': 'mocking'}, + ), + android_webview.WebResourceResponse(statusCode: 401)); + + expect(callbackError.response?.statusCode, 401); + }); + test('onWebResourceError from onReceivedRequestError', () { final AndroidNavigationDelegate androidNavigationDelegate = AndroidNavigationDelegate(_buildCreationParams()); @@ -532,6 +555,7 @@ class CapturingWebViewClient extends android_webview.WebViewClient { CapturingWebViewClient({ super.onPageFinished, super.onPageStarted, + super.onReceivedHttpError, super.onReceivedError, super.onReceivedHttpAuthRequest, super.onReceivedRequestError, diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.dart index f912e8d5e64a..dcc5b27abcd8 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.dart @@ -140,6 +140,11 @@ void main() { onPageFinished, void Function(android_webview.WebView webView, String url)? onPageStarted, + void Function( + android_webview.WebView webView, + android_webview.WebResourceRequest request, + android_webview.WebResourceResponse response)? + onReceivedHttpError, @Deprecated('Only called on Android version < 23.') void Function( android_webview.WebView webView, diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.mocks.dart index a7abc51261db..73c513c8af33 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.mocks.dart @@ -343,6 +343,17 @@ class MockAndroidNavigationDelegate extends _i1.Mock returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override + _i9.Future setOnHttpError(_i3.HttpResponseErrorCallback? onHttpError) => + (super.noSuchMethod( + Invocation.method( + #setOnHttpError, + [onHttpError], + ), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value(), + ) as _i9.Future); + @override _i9.Future setOnProgress(_i3.ProgressCallback? onProgress) => (super.noSuchMethod( @@ -388,17 +399,6 @@ class MockAndroidNavigationDelegate extends _i1.Mock returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); - - @override - _i9.Future setOnHttpError(_i3.HttpResponseErrorCallback? onHttpError) => - (super.noSuchMethod( - Invocation.method( - #setOnHttpError, - [onHttpError], - ), - returnValue: _i9.Future.value(), - returnValueForMissingStub: _i9.Future.value(), - ) as _i9.Future); } /// A class which mocks [AndroidWebViewController]. @@ -892,7 +892,7 @@ class MockAndroidWebViewProxy extends _i1.Mock implements _i10.AndroidWebViewProxy { @override _i2.WebView Function( - {dynamic Function( + {void Function( int, int, int, @@ -900,7 +900,7 @@ class MockAndroidWebViewProxy extends _i1.Mock )? onScrollChanged}) get createAndroidWebView => (super.noSuchMethod( Invocation.getter(#createAndroidWebView), returnValue: ( - {dynamic Function( + {void Function( int, int, int, @@ -911,7 +911,7 @@ class MockAndroidWebViewProxy extends _i1.Mock Invocation.getter(#createAndroidWebView), ), returnValueForMissingStub: ( - {dynamic Function( + {void Function( int, int, int, @@ -922,7 +922,7 @@ class MockAndroidWebViewProxy extends _i1.Mock Invocation.getter(#createAndroidWebView), ), ) as _i2.WebView Function( - {dynamic Function( + {void Function( int, int, int, @@ -1137,6 +1137,11 @@ class MockAndroidWebViewProxy extends _i1.Mock String, String, )? onReceivedHttpAuthRequest, + void Function( + _i2.WebView, + _i2.WebResourceRequest, + _i2.WebResourceResponse, + )? onReceivedHttpError, void Function( _i2.WebView, _i2.WebResourceRequest, @@ -1178,6 +1183,11 @@ class MockAndroidWebViewProxy extends _i1.Mock String, String, )? onReceivedHttpAuthRequest, + void Function( + _i2.WebView, + _i2.WebResourceRequest, + _i2.WebResourceResponse, + )? onReceivedHttpError, void Function( _i2.WebView, _i2.WebResourceRequest, @@ -1222,6 +1232,11 @@ class MockAndroidWebViewProxy extends _i1.Mock String, String, )? onReceivedHttpAuthRequest, + void Function( + _i2.WebView, + _i2.WebResourceRequest, + _i2.WebResourceResponse, + )? onReceivedHttpError, void Function( _i2.WebView, _i2.WebResourceRequest, @@ -1266,6 +1281,11 @@ class MockAndroidWebViewProxy extends _i1.Mock String, String, )? onReceivedHttpAuthRequest, + void Function( + _i2.WebView, + _i2.WebResourceRequest, + _i2.WebResourceResponse, + )? onReceivedHttpError, void Function( _i2.WebView, _i2.WebResourceRequest, @@ -2117,6 +2137,7 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override _i9.Future setSynchronousReturnValueForOnJsConfirm(bool? value) => (super.noSuchMethod( @@ -2127,6 +2148,7 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override _i9.Future setSynchronousReturnValueForOnJsPrompt(bool? value) => (super.noSuchMethod( @@ -2137,6 +2159,7 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient { returnValue: _i9.Future.value(), returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); + @override _i2.WebChromeClient copy() => (super.noSuchMethod( Invocation.method( diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart index d542c498772c..36b9c0343578 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart @@ -642,6 +642,37 @@ void main() { expect(result, [mockWebView, 'https://www.google.com']); }); + test('onReceivedHttpError', () { + late final List result; + when(mockWebViewClient.onReceivedHttpError).thenReturn( + ( + WebView webView, + WebResourceRequest request, + WebResourceResponse response, + ) { + result = [webView, request, response]; + }, + ); + + flutterApi.onReceivedHttpError( + mockWebViewClientInstanceId, + mockWebViewInstanceId, + WebResourceRequestData( + url: 'https://www.google.com', + isForMainFrame: true, + hasGesture: true, + method: 'GET', + isRedirect: false, + requestHeaders: {}, + ), + WebResourceResponseData( + statusCode: 401, + ), + ); + + expect(result, [mockWebView, isNotNull, isNotNull]); + }); + test('onReceivedRequestError', () { late final List result; when(mockWebViewClient.onReceivedRequestError).thenReturn( diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart index 4a9ced6a07b8..54002135ab51 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart @@ -510,6 +510,7 @@ class MockTestWebChromeClientHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); + @override void setSynchronousReturnValueForOnJsAlert( int? instanceId, @@ -525,6 +526,7 @@ class MockTestWebChromeClientHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); + @override void setSynchronousReturnValueForOnJsConfirm( int? instanceId, @@ -540,6 +542,7 @@ class MockTestWebChromeClientHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); + @override void setSynchronousReturnValueForOnJsPrompt( int? instanceId, @@ -1340,6 +1343,7 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future setSynchronousReturnValueForOnJsConfirm(bool? value) => (super.noSuchMethod( @@ -1350,6 +1354,7 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future setSynchronousReturnValueForOnJsPrompt(bool? value) => (super.noSuchMethod( @@ -1360,6 +1365,7 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i2.WebChromeClient copy() => (super.noSuchMethod( Invocation.method( diff --git a/packages/webview_flutter/webview_flutter_android/test/legacy/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/legacy/webview_android_widget_test.mocks.dart index f7a11fb9e872..84a778786ecc 100644 --- a/packages/webview_flutter/webview_flutter_android/test/legacy/webview_android_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/legacy/webview_android_widget_test.mocks.dart @@ -880,6 +880,7 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future setSynchronousReturnValueForOnJsConfirm(bool? value) => (super.noSuchMethod( @@ -890,6 +891,7 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future setSynchronousReturnValueForOnJsPrompt(bool? value) => (super.noSuchMethod( @@ -900,6 +902,7 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i2.WebChromeClient copy() => (super.noSuchMethod( Invocation.method( diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index 8aa73ace0264..70ba80cee6ad 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.13.0 + +* Adds `decidePolicyForNavigationResponse` to internal WKNavigationDelegate to support the + `PlatformNavigationDelegate.onHttpError` callback. + ## 3.12.0 * Adds support for `setOnScrollPositionChange` method to the `WebKitWebViewController`. 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 91d9075cd471..80445b96fee0 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 @@ -987,6 +987,81 @@ Future main() async { }, ); + testWidgets('onHttpError', (WidgetTester tester) async { + final Completer errorCompleter = + Completer(); + + final PlatformWebViewController controller = PlatformWebViewController( + const PlatformWebViewControllerCreationParams(), + ); + unawaited(controller.setJavaScriptMode(JavaScriptMode.unrestricted)); + final PlatformNavigationDelegate delegate = PlatformNavigationDelegate( + const PlatformNavigationDelegateCreationParams(), + ); + unawaited(delegate.setOnHttpError((HttpResponseError error) { + errorCompleter.complete(error); + })); + unawaited(controller.setPlatformNavigationDelegate(delegate)); + unawaited(controller.loadRequest( + LoadRequestParams(uri: Uri.parse('$prefixUrl/favicon.ico')), + )); + + await tester.pumpWidget(Builder( + builder: (BuildContext context) { + return PlatformWebViewWidget( + PlatformWebViewWidgetCreationParams(controller: controller), + ).build(context); + }, + )); + + final HttpResponseError error = await errorCompleter.future; + + expect(error, isNotNull); + expect(error.response?.statusCode, 404); + }); + + testWidgets('onHttpError is not called when no HTTP error is received', + (WidgetTester tester) async { + const String testPage = ''' + + + + + + '''; + + final Completer errorCompleter = + Completer(); + final Completer pageFinishCompleter = Completer(); + + final PlatformWebViewController controller = PlatformWebViewController( + const PlatformWebViewControllerCreationParams(), + ); + unawaited(controller.setJavaScriptMode(JavaScriptMode.unrestricted)); + final PlatformNavigationDelegate delegate = PlatformNavigationDelegate( + const PlatformNavigationDelegateCreationParams(), + ); + unawaited(delegate.setOnHttpError((HttpResponseError error) { + errorCompleter.complete(error); + })); + unawaited(delegate.setOnPageFinished( + (_) => pageFinishCompleter.complete(), + )); + unawaited(controller.setPlatformNavigationDelegate(delegate)); + unawaited(controller.loadHtmlString(testPage)); + + await tester.pumpWidget(Builder( + builder: (BuildContext context) { + return PlatformWebViewWidget( + PlatformWebViewWidgetCreationParams(controller: controller), + ).build(context); + }, + )); + + expect(errorCompleter.future, doesNotComplete); + await pageFinishCompleter.future; + }); + testWidgets('can block requests', (WidgetTester tester) async { Completer pageLoaded = Completer(); 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 7613bf02f75c..a4ff58a47ea5 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 @@ -171,4 +171,34 @@ - (void)testNSKeyValueChangeKeyConversionReturnsUnknownIfUnrecognized { - (void)testWKNavigationTypeConversionReturnsUnknownIfUnrecognized { XCTAssertEqual(FWFWKNavigationTypeFromNativeWKNavigationType(-15), FWFWKNavigationTypeUnknown); } + +- (void)testFWFWKNavigationResponseDataFromNativeNavigationResponse { + WKNavigationResponse *mockResponse = OCMClassMock([WKNavigationResponse class]); + OCMStub([mockResponse isForMainFrame]).andReturn(YES); + + NSHTTPURLResponse *mockURLResponse = OCMClassMock([NSHTTPURLResponse class]); + OCMStub([mockURLResponse statusCode]).andReturn(1); + OCMStub([mockResponse response]).andReturn(mockURLResponse); + + FWFWKNavigationResponseData *data = + FWFWKNavigationResponseDataFromNativeNavigationResponse(mockResponse); + XCTAssertEqual(data.forMainFrame, YES); +} + +- (void)testFWFNSHttpUrlResponseDataFromNativeNSURLResponse { + NSHTTPURLResponse *mockResponse = OCMClassMock([NSHTTPURLResponse class]); + OCMStub([mockResponse statusCode]).andReturn(1); + + FWFNSHttpUrlResponseData *data = FWFNSHttpUrlResponseDataFromNativeNSURLResponse(mockResponse); + XCTAssertEqual(data.statusCode, 1); +} + +- (void)testFWFNativeWKNavigationResponsePolicyFromEnum { + XCTAssertEqual( + FWFNativeWKNavigationResponsePolicyFromEnum(FWFWKNavigationResponsePolicyEnumAllow), + WKNavigationResponsePolicyAllow); + XCTAssertEqual( + FWFNativeWKNavigationResponsePolicyFromEnum(FWFWKNavigationResponsePolicyEnumCancel), + WKNavigationResponsePolicyCancel); +} @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFNavigationDelegateHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFNavigationDelegateHostApiTests.m index dc4cb3cf8abe..829d27643bfb 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFNavigationDelegateHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFNavigationDelegateHostApiTests.m @@ -267,4 +267,46 @@ - (void)testDidReceiveAuthenticationChallenge { XCTAssertEqual(callbackDisposition, NSURLSessionAuthChallengeCancelAuthenticationChallenge); XCTAssertEqualObjects(callbackCredential, credential); } + +- (void)testDecidePolicyForNavigationResponse { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + + FWFNavigationDelegate *mockDelegate = [self mockNavigationDelegateWithManager:instanceManager + identifier:0]; + FWFNavigationDelegateFlutterApiImpl *mockFlutterAPI = + [self mockFlutterApiWithManager:instanceManager]; + + OCMStub([mockDelegate navigationDelegateAPI]).andReturn(mockFlutterAPI); + + WKWebView *mockWebView = OCMClassMock([WKWebView class]); + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:1]; + + WKNavigationResponse *mockNavigationResponse = OCMClassMock([WKNavigationResponse class]); + OCMStub([mockNavigationResponse isForMainFrame]).andReturn(YES); + + NSHTTPURLResponse *mockURLResponse = OCMClassMock([NSHTTPURLResponse class]); + OCMStub([mockURLResponse statusCode]).andReturn(1); + OCMStub([mockNavigationResponse response]).andReturn(mockURLResponse); + + OCMStub([mockFlutterAPI + decidePolicyForNavigationResponseForDelegateWithIdentifier:0 + webViewIdentifier:1 + navigationResponse:OCMOCK_ANY + completion: + ([OCMArg + invokeBlockWithArgs: + [[FWFWKNavigationResponsePolicyEnumBox + alloc] + initWithValue: + FWFWKNavigationResponsePolicyEnumAllow], + [NSNull null], nil])]); + + WKNavigationResponsePolicy __block callbackPolicy = -1; + [mockDelegate webView:mockWebView + decidePolicyForNavigationResponse:mockNavigationResponse + decisionHandler:^(WKNavigationResponsePolicy policy) { + callbackPolicy = policy; + }]; + XCTAssertEqual(callbackPolicy, WKNavigationResponsePolicyAllow); +} @end 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 36f3a1eb9ad4..68a9f1a0c4d8 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart @@ -174,6 +174,9 @@ class _WebViewExampleState extends State { ..setOnPageFinished((String url) { debugPrint('Page finished loading: $url'); }) + ..setOnHttpError((HttpResponseError error) { + debugPrint('Error occurred on page: ${error.response?.statusCode}'); + }) ..setOnWebResourceError((WebResourceError error) { debugPrint(''' Page resource error: diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h index c4d37571d62e..f97de9c8c19e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h @@ -84,11 +84,32 @@ extern FWFWKNavigationActionData *FWFWKNavigationActionDataFromNativeWKNavigatio /// @return A FWFNSUrlRequestData. extern FWFNSUrlRequestData *FWFNSUrlRequestDataFromNativeNSURLRequest(NSURLRequest *request); -/// Converts a WKFrameInfo to an FWFWKFrameInfoData. -/// -/// @param info The object containing information to create a FWFWKFrameInfoData. -/// -/// @return A FWFWKFrameInfoData. +/** + * Converts a WKNavigationResponse to an FWFWKNavigationResponseData. + * + * @param response The object containing information to create a WKNavigationResponseData. + * + * @return A FWFWKNavigationResponseData. + */ +extern FWFWKNavigationResponseData *FWFWKNavigationResponseDataFromNativeNavigationResponse( + WKNavigationResponse *response); +/** + * Converts a NSURLResponse to an FWFNSHttpUrlResponseData. + * + * @param response The object containing information to create a WKNavigationActionData. + * + * @return A FWFNSHttpUrlResponseData. + */ +extern FWFNSHttpUrlResponseData *FWFNSHttpUrlResponseDataFromNativeNSURLResponse( + NSURLResponse *response); + +/** + * Converts a WKFrameInfo to an FWFWKFrameInfoData. + * + * @param info The object containing information to create a FWFWKFrameInfoData. + * + * @return A FWFWKFrameInfoData. + */ extern FWFWKFrameInfoData *FWFWKFrameInfoDataFromNativeWKFrameInfo(WKFrameInfo *info); /// Converts an FWFWKNavigationActionPolicyEnumData to a WKNavigationActionPolicy. @@ -99,11 +120,23 @@ extern FWFWKFrameInfoData *FWFWKFrameInfoDataFromNativeWKFrameInfo(WKFrameInfo * extern WKNavigationActionPolicy FWFNativeWKNavigationActionPolicyFromEnumData( FWFWKNavigationActionPolicyEnumData *data); -/// Converts a NSError to an FWFNSErrorData. -/// -/// @param error The object containing information to create a FWFNSErrorData. -/// -/// @return A FWFNSErrorData. +/** + * Converts an FWFWKNavigationResponsePolicyEnumData to a WKNavigationResponsePolicy. + * + * @param policy The data object containing information to create a WKNavigationResponsePolicy. + * + * @return A WKNavigationResponsePolicy or -1 if data could not be converted. + */ +extern WKNavigationResponsePolicy FWFNativeWKNavigationResponsePolicyFromEnum( + FWFWKNavigationResponsePolicyEnum policy); + +/** + * Converts a NSError to an FWFNSErrorData. + * + * @param error The object containing information to create a FWFNSErrorData. + * + * @return A FWFNSErrorData. + */ extern FWFNSErrorData *FWFNSErrorDataFromNativeNSError(NSError *error); /// Converts an NSKeyValueChangeKey to a FWFNSKeyValueChangeKeyEnumData. 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 9a5cc86fe951..51a5ada5030b 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m @@ -181,6 +181,24 @@ WKAudiovisualMediaTypes FWFNativeWKAudiovisualMediaTypeFromEnumData( request:FWFNSUrlRequestDataFromNativeNSURLRequest(info.request)]; } +FWFWKNavigationResponseData *FWFWKNavigationResponseDataFromNativeNavigationResponse( + WKNavigationResponse *response) { + return [FWFWKNavigationResponseData + makeWithResponse:FWFNSHttpUrlResponseDataFromNativeNSURLResponse(response.response) + forMainFrame:response.forMainFrame]; +} + +/// Cast the NSURLResponse object to NSHTTPURLResponse. +/// +/// NSURLResponse doesn't contain the status code so it must be cast to NSHTTPURLResponse. +/// This cast will always succeed because the NSURLResponse object actually is an instance of +/// NSHTTPURLResponse. See: +/// https://developer.apple.com/documentation/foundation/nsurlresponse#overview +FWFNSHttpUrlResponseData *FWFNSHttpUrlResponseDataFromNativeNSURLResponse(NSURLResponse *response) { + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + return [FWFNSHttpUrlResponseData makeWithStatusCode:httpResponse.statusCode]; +} + WKNavigationActionPolicy FWFNativeWKNavigationActionPolicyFromEnumData( FWFWKNavigationActionPolicyEnumData *data) { switch (data.value) { @@ -209,6 +227,18 @@ WKNavigationActionPolicy FWFNativeWKNavigationActionPolicyFromEnumData( return [FWFNSErrorData makeWithCode:error.code domain:error.domain userInfo:userInfo]; } +WKNavigationResponsePolicy FWFNativeWKNavigationResponsePolicyFromEnum( + FWFWKNavigationResponsePolicyEnum policy) { + switch (policy) { + case FWFWKNavigationResponsePolicyEnumAllow: + return WKNavigationResponsePolicyAllow; + case FWFWKNavigationResponsePolicyEnumCancel: + return WKNavigationResponsePolicyCancel; + } + + return -1; +} + FWFNSKeyValueChangeKeyEnumData *FWFNSKeyValueChangeKeyEnumDataFromNativeNSKeyValueChangeKey( NSKeyValueChangeKey key) { if ([key isEqualToString:NSKeyValueChangeIndexesKey]) { 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 3a0fa3021d2f..984cfc93cc33 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h @@ -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 (v13.1.2), do not edit directly. +// Autogenerated from Pigeon (v13.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon #import @@ -130,6 +130,20 @@ typedef NS_ENUM(NSUInteger, FWFWKNavigationActionPolicyEnum) { - (instancetype)initWithValue:(FWFWKNavigationActionPolicyEnum)value; @end +/// Mirror of WKNavigationResponsePolicy. +/// +/// See https://developer.apple.com/documentation/webkit/wknavigationactionpolicy?language=objc. +typedef NS_ENUM(NSUInteger, FWFWKNavigationResponsePolicyEnum) { + FWFWKNavigationResponsePolicyEnumAllow = 0, + FWFWKNavigationResponsePolicyEnumCancel = 1, +}; + +/// Wrapper for FWFWKNavigationResponsePolicyEnum to allow for nullability. +@interface FWFWKNavigationResponsePolicyEnumBox : NSObject +@property(nonatomic, assign) FWFWKNavigationResponsePolicyEnum value; +- (instancetype)initWithValue:(FWFWKNavigationResponsePolicyEnum)value; +@end + /// Mirror of NSHTTPCookiePropertyKey. /// /// See https://developer.apple.com/documentation/foundation/nshttpcookiepropertykey. @@ -341,8 +355,10 @@ typedef NS_ENUM(NSUInteger, FWFNSUrlCredentialPersistence) { @class FWFWKPermissionDecisionData; @class FWFWKMediaCaptureTypeData; @class FWFNSUrlRequestData; +@class FWFNSHttpUrlResponseData; @class FWFWKUserScriptData; @class FWFWKNavigationActionData; +@class FWFWKNavigationResponseData; @class FWFWKFrameInfoData; @class FWFNSErrorData; @class FWFWKScriptMessageData; @@ -430,6 +446,16 @@ typedef NS_ENUM(NSUInteger, FWFNSUrlCredentialPersistence) { @property(nonatomic, copy) NSDictionary *allHttpHeaderFields; @end +/// Mirror of NSURLResponse. +/// +/// See https://developer.apple.com/documentation/foundation/nshttpurlresponse?language=objc. +@interface FWFNSHttpUrlResponseData : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithStatusCode:(NSInteger)statusCode; +@property(nonatomic, assign) NSInteger statusCode; +@end + /// Mirror of WKUserScript. /// /// See https://developer.apple.com/documentation/webkit/wkuserscript?language=objc. @@ -458,6 +484,18 @@ typedef NS_ENUM(NSUInteger, FWFNSUrlCredentialPersistence) { @property(nonatomic, assign) FWFWKNavigationType navigationType; @end +/// Mirror of WKNavigationResponse. +/// +/// See https://developer.apple.com/documentation/webkit/wknavigationresponse. +@interface FWFWKNavigationResponseData : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithResponse:(FWFNSHttpUrlResponseData *)response + forMainFrame:(BOOL)forMainFrame; +@property(nonatomic, strong) FWFNSHttpUrlResponseData *response; +@property(nonatomic, assign) BOOL forMainFrame; +@end + /// Mirror of WKFrameInfo. /// /// See https://developer.apple.com/documentation/webkit/wkframeinfo?language=objc. @@ -778,6 +816,15 @@ NSObject *FWFWKNavigationDelegateFlutterApiGetCodec(void); (void (^)(FWFWKNavigationActionPolicyEnumData *_Nullable, FlutterError *_Nullable))completion; +- (void)decidePolicyForNavigationResponseForDelegateWithIdentifier:(NSInteger)identifier + webViewIdentifier:(NSInteger)webViewIdentifier + navigationResponse:(FWFWKNavigationResponseData *) + navigationResponse + completion: + (void (^)( + FWFWKNavigationResponsePolicyEnumBox + *_Nullable, + FlutterError *_Nullable))completion; - (void)didFailNavigationForDelegateWithIdentifier:(NSInteger)identifier webViewIdentifier:(NSInteger)webViewIdentifier error:(FWFNSErrorData *)error 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 450efd648313..352f9b1c161d 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m @@ -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 (v13.1.2), do not edit directly. +// Autogenerated from Pigeon (v13.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon #import "FWFGeneratedWebKitApis.h" @@ -16,29 +16,6 @@ #error File requires ARC to be enabled. #endif -static NSArray *wrapResult(id result, FlutterError *error) { - if (error) { - return @[ - error.code ?: [NSNull null], error.message ?: [NSNull null], error.details ?: [NSNull null] - ]; - } - return @[ result ?: [NSNull null] ]; -} - -static FlutterError *createConnectionError(NSString *channelName) { - return [FlutterError - errorWithCode:@"channel-error" - message:[NSString stringWithFormat:@"%@/%@/%@", - @"Unable to establish connection on channel: '", - channelName, @"'."] - details:@""]; -} - -static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { - id result = array[key]; - return (result == [NSNull null]) ? nil : result; -} - /// Mirror of NSKeyValueObservingOptions. /// /// See @@ -133,6 +110,19 @@ - (instancetype)initWithValue:(FWFWKNavigationActionPolicyEnum)value { } @end +/// Mirror of WKNavigationResponsePolicy. +/// +/// See https://developer.apple.com/documentation/webkit/wknavigationactionpolicy?language=objc. +@implementation FWFWKNavigationResponsePolicyEnumBox +- (instancetype)initWithValue:(FWFWKNavigationResponsePolicyEnum)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} +@end + /// Mirror of NSHTTPCookiePropertyKey. /// /// See https://developer.apple.com/documentation/foundation/nshttpcookiepropertykey. @@ -212,6 +202,19 @@ - (instancetype)initWithValue:(FWFNSUrlCredentialPersistence)value { } @end +static NSArray *wrapResult(id result, FlutterError *error) { + if (error) { + return @[ + error.code ?: [NSNull null], error.message ?: [NSNull null], error.details ?: [NSNull null] + ]; + } + return @[ result ?: [NSNull null] ]; +} +static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { + id result = array[key]; + return (result == [NSNull null]) ? nil : result; +} + @interface FWFNSKeyValueObservingOptionsEnumData () + (FWFNSKeyValueObservingOptionsEnumData *)fromList:(NSArray *)list; + (nullable FWFNSKeyValueObservingOptionsEnumData *)nullableFromList:(NSArray *)list; @@ -272,6 +275,12 @@ + (nullable FWFNSUrlRequestData *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end +@interface FWFNSHttpUrlResponseData () ++ (FWFNSHttpUrlResponseData *)fromList:(NSArray *)list; ++ (nullable FWFNSHttpUrlResponseData *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + @interface FWFWKUserScriptData () + (FWFWKUserScriptData *)fromList:(NSArray *)list; + (nullable FWFWKUserScriptData *)nullableFromList:(NSArray *)list; @@ -284,6 +293,12 @@ + (nullable FWFWKNavigationActionData *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end +@interface FWFWKNavigationResponseData () ++ (FWFWKNavigationResponseData *)fromList:(NSArray *)list; ++ (nullable FWFWKNavigationResponseData *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + @interface FWFWKFrameInfoData () + (FWFWKFrameInfoData *)fromList:(NSArray *)list; + (nullable FWFWKFrameInfoData *)nullableFromList:(NSArray *)list; @@ -558,6 +573,27 @@ - (NSArray *)toList { } @end +@implementation FWFNSHttpUrlResponseData ++ (instancetype)makeWithStatusCode:(NSInteger)statusCode { + FWFNSHttpUrlResponseData *pigeonResult = [[FWFNSHttpUrlResponseData alloc] init]; + pigeonResult.statusCode = statusCode; + return pigeonResult; +} ++ (FWFNSHttpUrlResponseData *)fromList:(NSArray *)list { + FWFNSHttpUrlResponseData *pigeonResult = [[FWFNSHttpUrlResponseData alloc] init]; + pigeonResult.statusCode = [GetNullableObjectAtIndex(list, 0) integerValue]; + return pigeonResult; +} ++ (nullable FWFNSHttpUrlResponseData *)nullableFromList:(NSArray *)list { + return (list) ? [FWFNSHttpUrlResponseData fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + @(self.statusCode), + ]; +} +@end + @implementation FWFWKUserScriptData + (instancetype)makeWithSource:(NSString *)source injectionTime:(nullable FWFWKUserScriptInjectionTimeEnumData *)injectionTime @@ -618,6 +654,32 @@ - (NSArray *)toList { } @end +@implementation FWFWKNavigationResponseData ++ (instancetype)makeWithResponse:(FWFNSHttpUrlResponseData *)response + forMainFrame:(BOOL)forMainFrame { + FWFWKNavigationResponseData *pigeonResult = [[FWFWKNavigationResponseData alloc] init]; + pigeonResult.response = response; + pigeonResult.forMainFrame = forMainFrame; + return pigeonResult; +} ++ (FWFWKNavigationResponseData *)fromList:(NSArray *)list { + FWFWKNavigationResponseData *pigeonResult = [[FWFWKNavigationResponseData alloc] init]; + pigeonResult.response = + [FWFNSHttpUrlResponseData nullableFromList:(GetNullableObjectAtIndex(list, 0))]; + pigeonResult.forMainFrame = [GetNullableObjectAtIndex(list, 1) boolValue]; + return pigeonResult; +} ++ (nullable FWFWKNavigationResponseData *)nullableFromList:(NSArray *)list { + return (list) ? [FWFWKNavigationResponseData fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + (self.response ? [self.response toList] : [NSNull null]), + @(self.forMainFrame), + ]; +} +@end + @implementation FWFWKFrameInfoData + (instancetype)makeWithIsMainFrame:(BOOL)isMainFrame request:(FWFNSUrlRequestData *)request { FWFWKFrameInfoData *pigeonResult = [[FWFWKFrameInfoData alloc] init]; @@ -1323,26 +1385,28 @@ - (instancetype)initWithBinaryMessenger:(NSObject *)bina } - (void)createWithIdentifier:(NSInteger)arg_identifier completion:(void (^)(FlutterError *_Nullable))completion { - NSString *channelName = - @"dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewConfigurationFlutterApi.create"; FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:channelName + messageChannelWithName: + @"dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewConfigurationFlutterApi.create" binaryMessenger:self.binaryMessenger codec:FWFWKWebViewConfigurationFlutterApiGetCodec()]; - [channel sendMessage:@[ @(arg_identifier) ] - reply:^(NSArray *reply) { - if (reply != nil) { - if (reply.count > 1) { - completion([FlutterError errorWithCode:reply[0] - message:reply[1] - details:reply[2]]); - } else { - completion(nil); - } - } else { - completion(createConnectionError(channelName)); - } - }]; + [channel + sendMessage:@[ @(arg_identifier) ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion([FlutterError errorWithCode:@"channel-error" + message:@"Unable to establish connection on channel." + details:@""]); + } + }]; } @end @@ -1716,28 +1780,30 @@ - (instancetype)initWithBinaryMessenger:(NSObject *)bina userContentControllerIdentifier:(NSInteger)arg_userContentControllerIdentifier message:(FWFWKScriptMessageData *)arg_message completion:(void (^)(FlutterError *_Nullable))completion { - NSString *channelName = @"dev.flutter.pigeon.webview_flutter_wkwebview." - @"WKScriptMessageHandlerFlutterApi.didReceiveScriptMessage"; FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:channelName + messageChannelWithName:@"dev.flutter.pigeon.webview_flutter_wkwebview." + @"WKScriptMessageHandlerFlutterApi.didReceiveScriptMessage" binaryMessenger:self.binaryMessenger codec:FWFWKScriptMessageHandlerFlutterApiGetCodec()]; - [channel sendMessage:@[ - @(arg_identifier), @(arg_userContentControllerIdentifier), arg_message ?: [NSNull null] - ] - reply:^(NSArray *reply) { - if (reply != nil) { - if (reply.count > 1) { - completion([FlutterError errorWithCode:reply[0] - message:reply[1] - details:reply[2]]); - } else { - completion(nil); - } - } else { - completion(createConnectionError(channelName)); - } - }]; + [channel + sendMessage:@[ + @(arg_identifier), @(arg_userContentControllerIdentifier), arg_message ?: [NSNull null] + ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion([FlutterError errorWithCode:@"channel-error" + message:@"Unable to establish connection on channel." + details:@""]); + } + }]; } @end @@ -1782,13 +1848,17 @@ - (nullable id)readValueOfType:(UInt8)type { case 129: return [FWFNSErrorData fromList:[self readValue]]; case 130: - return [FWFNSUrlRequestData fromList:[self readValue]]; + return [FWFNSHttpUrlResponseData fromList:[self readValue]]; case 131: - return [FWFWKFrameInfoData fromList:[self readValue]]; + return [FWFNSUrlRequestData fromList:[self readValue]]; case 132: - return [FWFWKNavigationActionData fromList:[self readValue]]; + return [FWFWKFrameInfoData fromList:[self readValue]]; case 133: + return [FWFWKNavigationActionData fromList:[self readValue]]; + case 134: return [FWFWKNavigationActionPolicyEnumData fromList:[self readValue]]; + case 135: + return [FWFWKNavigationResponseData fromList:[self readValue]]; default: return [super readValueOfType:type]; } @@ -1805,18 +1875,24 @@ - (void)writeValue:(id)value { } else if ([value isKindOfClass:[FWFNSErrorData class]]) { [self writeByte:129]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FWFNSUrlRequestData class]]) { + } else if ([value isKindOfClass:[FWFNSHttpUrlResponseData class]]) { [self writeByte:130]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FWFWKFrameInfoData class]]) { + } else if ([value isKindOfClass:[FWFNSUrlRequestData class]]) { [self writeByte:131]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FWFWKNavigationActionData class]]) { + } else if ([value isKindOfClass:[FWFWKFrameInfoData class]]) { [self writeByte:132]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FWFWKNavigationActionPolicyEnumData class]]) { + } else if ([value isKindOfClass:[FWFWKNavigationActionData class]]) { [self writeByte:133]; [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FWFWKNavigationActionPolicyEnumData class]]) { + [self writeByte:134]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FWFWKNavigationResponseData class]]) { + [self writeByte:135]; + [self writeValue:[value toList]]; } else { [super writeValue:value]; } @@ -1862,52 +1938,56 @@ - (void)didFinishNavigationForDelegateWithIdentifier:(NSInteger)arg_identifier webViewIdentifier:(NSInteger)arg_webViewIdentifier URL:(nullable NSString *)arg_url completion:(void (^)(FlutterError *_Nullable))completion { - NSString *channelName = @"dev.flutter.pigeon.webview_flutter_wkwebview." - @"WKNavigationDelegateFlutterApi.didFinishNavigation"; FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:channelName + messageChannelWithName:@"dev.flutter.pigeon.webview_flutter_wkwebview." + @"WKNavigationDelegateFlutterApi.didFinishNavigation" binaryMessenger:self.binaryMessenger codec:FWFWKNavigationDelegateFlutterApiGetCodec()]; - [channel sendMessage:@[ @(arg_identifier), @(arg_webViewIdentifier), arg_url ?: [NSNull null] ] - reply:^(NSArray *reply) { - if (reply != nil) { - if (reply.count > 1) { - completion([FlutterError errorWithCode:reply[0] - message:reply[1] - details:reply[2]]); - } else { - completion(nil); - } - } else { - completion(createConnectionError(channelName)); - } - }]; + [channel + sendMessage:@[ @(arg_identifier), @(arg_webViewIdentifier), arg_url ?: [NSNull null] ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion([FlutterError errorWithCode:@"channel-error" + message:@"Unable to establish connection on channel." + details:@""]); + } + }]; } - (void)didStartProvisionalNavigationForDelegateWithIdentifier:(NSInteger)arg_identifier webViewIdentifier:(NSInteger)arg_webViewIdentifier URL:(nullable NSString *)arg_url completion:(void (^)(FlutterError *_Nullable)) completion { - NSString *channelName = @"dev.flutter.pigeon.webview_flutter_wkwebview." - @"WKNavigationDelegateFlutterApi.didStartProvisionalNavigation"; FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:channelName + messageChannelWithName:@"dev.flutter.pigeon.webview_flutter_wkwebview." + @"WKNavigationDelegateFlutterApi.didStartProvisionalNavigation" binaryMessenger:self.binaryMessenger codec:FWFWKNavigationDelegateFlutterApiGetCodec()]; - [channel sendMessage:@[ @(arg_identifier), @(arg_webViewIdentifier), arg_url ?: [NSNull null] ] - reply:^(NSArray *reply) { - if (reply != nil) { - if (reply.count > 1) { - completion([FlutterError errorWithCode:reply[0] - message:reply[1] - details:reply[2]]); - } else { - completion(nil); - } - } else { - completion(createConnectionError(channelName)); - } - }]; + [channel + sendMessage:@[ @(arg_identifier), @(arg_webViewIdentifier), arg_url ?: [NSNull null] ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion([FlutterError errorWithCode:@"channel-error" + message:@"Unable to establish connection on channel." + details:@""]); + } + }]; } - (void)decidePolicyForNavigationActionForDelegateWithIdentifier:(NSInteger)arg_identifier webViewIdentifier:(NSInteger)arg_webViewIdentifier @@ -1918,10 +1998,9 @@ - (void)decidePolicyForNavigationActionForDelegateWithIdentifier:(NSInteger)arg_ FWFWKNavigationActionPolicyEnumData *_Nullable, FlutterError *_Nullable))completion { - NSString *channelName = @"dev.flutter.pigeon.webview_flutter_wkwebview." - @"WKNavigationDelegateFlutterApi.decidePolicyForNavigationAction"; FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:channelName + messageChannelWithName:@"dev.flutter.pigeon.webview_flutter_wkwebview." + @"WKNavigationDelegateFlutterApi.decidePolicyForNavigationAction" binaryMessenger:self.binaryMessenger codec:FWFWKNavigationDelegateFlutterApiGetCodec()]; [channel sendMessage:@[ @@ -1939,60 +2018,107 @@ - (void)decidePolicyForNavigationActionForDelegateWithIdentifier:(NSInteger)arg_ completion(output, nil); } } else { - completion(nil, createConnectionError(channelName)); + completion(nil, [FlutterError + errorWithCode:@"channel-error" + message:@"Unable to establish connection on channel." + details:@""]); } }]; } -- (void)didFailNavigationForDelegateWithIdentifier:(NSInteger)arg_identifier - webViewIdentifier:(NSInteger)arg_webViewIdentifier - error:(FWFNSErrorData *)arg_error - completion:(void (^)(FlutterError *_Nullable))completion { - NSString *channelName = @"dev.flutter.pigeon.webview_flutter_wkwebview." - @"WKNavigationDelegateFlutterApi.didFailNavigation"; +- (void)decidePolicyForNavigationResponseForDelegateWithIdentifier:(NSInteger)arg_identifier + webViewIdentifier:(NSInteger)arg_webViewIdentifier + navigationResponse:(FWFWKNavigationResponseData *) + arg_navigationResponse + completion: + (void (^)( + FWFWKNavigationResponsePolicyEnumBox + *_Nullable, + FlutterError *_Nullable)) + completion { FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:channelName + messageChannelWithName:@"dev.flutter.pigeon.webview_flutter_wkwebview." + @"WKNavigationDelegateFlutterApi.decidePolicyForNavigationResponse" binaryMessenger:self.binaryMessenger codec:FWFWKNavigationDelegateFlutterApiGetCodec()]; - [channel sendMessage:@[ @(arg_identifier), @(arg_webViewIdentifier), arg_error ?: [NSNull null] ] + [channel sendMessage:@[ + @(arg_identifier), @(arg_webViewIdentifier), arg_navigationResponse ?: [NSNull null] + ] reply:^(NSArray *reply) { if (reply != nil) { if (reply.count > 1) { - completion([FlutterError errorWithCode:reply[0] - message:reply[1] - details:reply[2]]); + completion(nil, [FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); } else { - completion(nil); + NSNumber *outputAsNumber = reply[0] == [NSNull null] ? nil : reply[0]; + FWFWKNavigationResponsePolicyEnumBox *output = + outputAsNumber == nil ? nil + : [[FWFWKNavigationResponsePolicyEnumBox alloc] + initWithValue:[outputAsNumber integerValue]]; + completion(output, nil); } } else { - completion(createConnectionError(channelName)); + completion(nil, [FlutterError + errorWithCode:@"channel-error" + message:@"Unable to establish connection on channel." + details:@""]); } }]; } +- (void)didFailNavigationForDelegateWithIdentifier:(NSInteger)arg_identifier + webViewIdentifier:(NSInteger)arg_webViewIdentifier + error:(FWFNSErrorData *)arg_error + completion:(void (^)(FlutterError *_Nullable))completion { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.webview_flutter_wkwebview." + @"WKNavigationDelegateFlutterApi.didFailNavigation" + binaryMessenger:self.binaryMessenger + codec:FWFWKNavigationDelegateFlutterApiGetCodec()]; + [channel + sendMessage:@[ @(arg_identifier), @(arg_webViewIdentifier), arg_error ?: [NSNull null] ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion([FlutterError errorWithCode:@"channel-error" + message:@"Unable to establish connection on channel." + details:@""]); + } + }]; +} - (void)didFailProvisionalNavigationForDelegateWithIdentifier:(NSInteger)arg_identifier webViewIdentifier:(NSInteger)arg_webViewIdentifier error:(FWFNSErrorData *)arg_error completion:(void (^)(FlutterError *_Nullable)) completion { - NSString *channelName = @"dev.flutter.pigeon.webview_flutter_wkwebview." - @"WKNavigationDelegateFlutterApi.didFailProvisionalNavigation"; FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:channelName + messageChannelWithName:@"dev.flutter.pigeon.webview_flutter_wkwebview." + @"WKNavigationDelegateFlutterApi.didFailProvisionalNavigation" binaryMessenger:self.binaryMessenger codec:FWFWKNavigationDelegateFlutterApiGetCodec()]; - [channel sendMessage:@[ @(arg_identifier), @(arg_webViewIdentifier), arg_error ?: [NSNull null] ] - reply:^(NSArray *reply) { - if (reply != nil) { - if (reply.count > 1) { - completion([FlutterError errorWithCode:reply[0] - message:reply[1] - details:reply[2]]); - } else { - completion(nil); - } - } else { - completion(createConnectionError(channelName)); - } - }]; + [channel + sendMessage:@[ @(arg_identifier), @(arg_webViewIdentifier), arg_error ?: [NSNull null] ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion([FlutterError errorWithCode:@"channel-error" + message:@"Unable to establish connection on channel." + details:@""]); + } + }]; } - (void)webViewWebContentProcessDidTerminateForDelegateWithIdentifier:(NSInteger)arg_identifier webViewIdentifier: @@ -2000,40 +2126,41 @@ - (void)webViewWebContentProcessDidTerminateForDelegateWithIdentifier:(NSInteger completion: (void (^)(FlutterError *_Nullable)) completion { - NSString *channelName = @"dev.flutter.pigeon.webview_flutter_wkwebview." - @"WKNavigationDelegateFlutterApi.webViewWebContentProcessDidTerminate"; FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:channelName + messageChannelWithName:@"dev.flutter.pigeon.webview_flutter_wkwebview." + @"WKNavigationDelegateFlutterApi.webViewWebContentProcessDidTerminate" binaryMessenger:self.binaryMessenger codec:FWFWKNavigationDelegateFlutterApiGetCodec()]; - [channel sendMessage:@[ @(arg_identifier), @(arg_webViewIdentifier) ] - reply:^(NSArray *reply) { - if (reply != nil) { - if (reply.count > 1) { - completion([FlutterError errorWithCode:reply[0] - message:reply[1] - details:reply[2]]); - } else { - completion(nil); - } - } else { - completion(createConnectionError(channelName)); - } - }]; -} -- (void) - didReceiveAuthenticationChallengeForDelegateWithIdentifier:(NSInteger)arg_identifier - webViewIdentifier:(NSInteger)arg_webViewIdentifier - challengeIdentifier:(NSInteger)arg_challengeIdentifier - completion: - (void (^)(FWFAuthenticationChallengeResponse - *_Nullable, - FlutterError *_Nullable)) + [channel + sendMessage:@[ @(arg_identifier), @(arg_webViewIdentifier) ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion([FlutterError errorWithCode:@"channel-error" + message:@"Unable to establish connection on channel." + details:@""]); + } + }]; +} +- (void) + didReceiveAuthenticationChallengeForDelegateWithIdentifier:(NSInteger)arg_identifier + webViewIdentifier:(NSInteger)arg_webViewIdentifier + challengeIdentifier:(NSInteger)arg_challengeIdentifier + completion: + (void (^)(FWFAuthenticationChallengeResponse + *_Nullable, + FlutterError *_Nullable)) completion { - NSString *channelName = @"dev.flutter.pigeon.webview_flutter_wkwebview." - @"WKNavigationDelegateFlutterApi.didReceiveAuthenticationChallenge"; FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:channelName + messageChannelWithName:@"dev.flutter.pigeon.webview_flutter_wkwebview." + @"WKNavigationDelegateFlutterApi.didReceiveAuthenticationChallenge" binaryMessenger:self.binaryMessenger codec:FWFWKNavigationDelegateFlutterApiGetCodec()]; [channel sendMessage:@[ @(arg_identifier), @(arg_webViewIdentifier), @(arg_challengeIdentifier) ] @@ -2049,7 +2176,10 @@ - (void)webViewWebContentProcessDidTerminateForDelegateWithIdentifier:(NSInteger completion(output, nil); } } else { - completion(nil, createConnectionError(channelName)); + completion(nil, [FlutterError + errorWithCode:@"channel-error" + message:@"Unable to establish connection on channel." + details:@""]); } }]; } @@ -2261,52 +2391,56 @@ - (void)observeValueForObjectWithIdentifier:(NSInteger)arg_identifier (NSArray *)arg_changeKeys changeValues:(NSArray *)arg_changeValues completion:(void (^)(FlutterError *_Nullable))completion { - NSString *channelName = - @"dev.flutter.pigeon.webview_flutter_wkwebview.NSObjectFlutterApi.observeValue"; - FlutterBasicMessageChannel *channel = - [FlutterBasicMessageChannel messageChannelWithName:channelName - binaryMessenger:self.binaryMessenger - codec:FWFNSObjectFlutterApiGetCodec()]; - [channel sendMessage:@[ - @(arg_identifier), arg_keyPath ?: [NSNull null], @(arg_objectIdentifier), - arg_changeKeys ?: [NSNull null], arg_changeValues ?: [NSNull null] - ] - reply:^(NSArray *reply) { - if (reply != nil) { - if (reply.count > 1) { - completion([FlutterError errorWithCode:reply[0] - message:reply[1] - details:reply[2]]); - } else { - completion(nil); - } - } else { - completion(createConnectionError(channelName)); - } - }]; + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName: + @"dev.flutter.pigeon.webview_flutter_wkwebview.NSObjectFlutterApi.observeValue" + binaryMessenger:self.binaryMessenger + codec:FWFNSObjectFlutterApiGetCodec()]; + [channel + sendMessage:@[ + @(arg_identifier), arg_keyPath ?: [NSNull null], @(arg_objectIdentifier), + arg_changeKeys ?: [NSNull null], arg_changeValues ?: [NSNull null] + ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion([FlutterError errorWithCode:@"channel-error" + message:@"Unable to establish connection on channel." + details:@""]); + } + }]; } - (void)disposeObjectWithIdentifier:(NSInteger)arg_identifier completion:(void (^)(FlutterError *_Nullable))completion { - NSString *channelName = - @"dev.flutter.pigeon.webview_flutter_wkwebview.NSObjectFlutterApi.dispose"; - FlutterBasicMessageChannel *channel = - [FlutterBasicMessageChannel messageChannelWithName:channelName - binaryMessenger:self.binaryMessenger - codec:FWFNSObjectFlutterApiGetCodec()]; - [channel sendMessage:@[ @(arg_identifier) ] - reply:^(NSArray *reply) { - if (reply != nil) { - if (reply.count > 1) { - completion([FlutterError errorWithCode:reply[0] - message:reply[1] - details:reply[2]]); - } else { - completion(nil); - } - } else { - completion(createConnectionError(channelName)); - } - }]; + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName: + @"dev.flutter.pigeon.webview_flutter_wkwebview.NSObjectFlutterApi.dispose" + binaryMessenger:self.binaryMessenger + codec:FWFNSObjectFlutterApiGetCodec()]; + [channel + sendMessage:@[ @(arg_identifier) ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion([FlutterError errorWithCode:@"channel-error" + message:@"Unable to establish connection on channel." + details:@""]); + } + }]; } @end @@ -2324,34 +2458,38 @@ - (nullable id)readValueOfType:(UInt8)type { case 131: return [FWFNSHttpCookiePropertyKeyEnumData fromList:[self readValue]]; case 132: - return [FWFNSKeyValueChangeKeyEnumData fromList:[self readValue]]; + return [FWFNSHttpUrlResponseData fromList:[self readValue]]; case 133: - return [FWFNSKeyValueObservingOptionsEnumData fromList:[self readValue]]; + return [FWFNSKeyValueChangeKeyEnumData fromList:[self readValue]]; case 134: - return [FWFNSUrlRequestData fromList:[self readValue]]; + return [FWFNSKeyValueObservingOptionsEnumData fromList:[self readValue]]; case 135: - return [FWFObjectOrIdentifier fromList:[self readValue]]; + return [FWFNSUrlRequestData fromList:[self readValue]]; case 136: - return [FWFWKAudiovisualMediaTypeEnumData fromList:[self readValue]]; + return [FWFObjectOrIdentifier fromList:[self readValue]]; case 137: - return [FWFWKFrameInfoData fromList:[self readValue]]; + return [FWFWKAudiovisualMediaTypeEnumData fromList:[self readValue]]; case 138: - return [FWFWKMediaCaptureTypeData fromList:[self readValue]]; + return [FWFWKFrameInfoData fromList:[self readValue]]; case 139: - return [FWFWKNavigationActionData fromList:[self readValue]]; + return [FWFWKMediaCaptureTypeData fromList:[self readValue]]; case 140: - return [FWFWKNavigationActionPolicyEnumData fromList:[self readValue]]; + return [FWFWKNavigationActionData fromList:[self readValue]]; case 141: - return [FWFWKPermissionDecisionData fromList:[self readValue]]; + return [FWFWKNavigationActionPolicyEnumData fromList:[self readValue]]; case 142: - return [FWFWKScriptMessageData fromList:[self readValue]]; + return [FWFWKNavigationResponseData fromList:[self readValue]]; case 143: - return [FWFWKSecurityOriginData fromList:[self readValue]]; + return [FWFWKPermissionDecisionData fromList:[self readValue]]; case 144: - return [FWFWKUserScriptData fromList:[self readValue]]; + return [FWFWKScriptMessageData fromList:[self readValue]]; case 145: - return [FWFWKUserScriptInjectionTimeEnumData fromList:[self readValue]]; + return [FWFWKSecurityOriginData fromList:[self readValue]]; case 146: + return [FWFWKUserScriptData fromList:[self readValue]]; + case 147: + return [FWFWKUserScriptInjectionTimeEnumData fromList:[self readValue]]; + case 148: return [FWFWKWebsiteDataTypeEnumData fromList:[self readValue]]; default: return [super readValueOfType:type]; @@ -2375,51 +2513,57 @@ - (void)writeValue:(id)value { } else if ([value isKindOfClass:[FWFNSHttpCookiePropertyKeyEnumData class]]) { [self writeByte:131]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FWFNSKeyValueChangeKeyEnumData class]]) { + } else if ([value isKindOfClass:[FWFNSHttpUrlResponseData class]]) { [self writeByte:132]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FWFNSKeyValueObservingOptionsEnumData class]]) { + } else if ([value isKindOfClass:[FWFNSKeyValueChangeKeyEnumData class]]) { [self writeByte:133]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FWFNSUrlRequestData class]]) { + } else if ([value isKindOfClass:[FWFNSKeyValueObservingOptionsEnumData class]]) { [self writeByte:134]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FWFObjectOrIdentifier class]]) { + } else if ([value isKindOfClass:[FWFNSUrlRequestData class]]) { [self writeByte:135]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FWFWKAudiovisualMediaTypeEnumData class]]) { + } else if ([value isKindOfClass:[FWFObjectOrIdentifier class]]) { [self writeByte:136]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FWFWKFrameInfoData class]]) { + } else if ([value isKindOfClass:[FWFWKAudiovisualMediaTypeEnumData class]]) { [self writeByte:137]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FWFWKMediaCaptureTypeData class]]) { + } else if ([value isKindOfClass:[FWFWKFrameInfoData class]]) { [self writeByte:138]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FWFWKNavigationActionData class]]) { + } else if ([value isKindOfClass:[FWFWKMediaCaptureTypeData class]]) { [self writeByte:139]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FWFWKNavigationActionPolicyEnumData class]]) { + } else if ([value isKindOfClass:[FWFWKNavigationActionData class]]) { [self writeByte:140]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FWFWKPermissionDecisionData class]]) { + } else if ([value isKindOfClass:[FWFWKNavigationActionPolicyEnumData class]]) { [self writeByte:141]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FWFWKScriptMessageData class]]) { + } else if ([value isKindOfClass:[FWFWKNavigationResponseData class]]) { [self writeByte:142]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FWFWKSecurityOriginData class]]) { + } else if ([value isKindOfClass:[FWFWKPermissionDecisionData class]]) { [self writeByte:143]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FWFWKUserScriptData class]]) { + } else if ([value isKindOfClass:[FWFWKScriptMessageData class]]) { [self writeByte:144]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FWFWKUserScriptInjectionTimeEnumData class]]) { + } else if ([value isKindOfClass:[FWFWKSecurityOriginData class]]) { [self writeByte:145]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FWFWKWebsiteDataTypeEnumData class]]) { + } else if ([value isKindOfClass:[FWFWKUserScriptData class]]) { [self writeByte:146]; [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FWFWKUserScriptInjectionTimeEnumData class]]) { + [self writeByte:147]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FWFWKWebsiteDataTypeEnumData class]]) { + [self writeByte:148]; + [self writeValue:[value toList]]; } else { [super writeValue:value]; } @@ -3053,29 +3197,31 @@ - (void)onCreateWebViewForDelegateWithIdentifier:(NSInteger)arg_identifier configurationIdentifier:(NSInteger)arg_configurationIdentifier navigationAction:(FWFWKNavigationActionData *)arg_navigationAction completion:(void (^)(FlutterError *_Nullable))completion { - NSString *channelName = - @"dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateFlutterApi.onCreateWebView"; - FlutterBasicMessageChannel *channel = - [FlutterBasicMessageChannel messageChannelWithName:channelName - binaryMessenger:self.binaryMessenger - codec:FWFWKUIDelegateFlutterApiGetCodec()]; - [channel sendMessage:@[ - @(arg_identifier), @(arg_webViewIdentifier), @(arg_configurationIdentifier), - arg_navigationAction ?: [NSNull null] - ] - reply:^(NSArray *reply) { - if (reply != nil) { - if (reply.count > 1) { - completion([FlutterError errorWithCode:reply[0] - message:reply[1] - details:reply[2]]); - } else { - completion(nil); - } - } else { - completion(createConnectionError(channelName)); - } - }]; + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName: + @"dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateFlutterApi.onCreateWebView" + binaryMessenger:self.binaryMessenger + codec:FWFWKUIDelegateFlutterApiGetCodec()]; + [channel + sendMessage:@[ + @(arg_identifier), @(arg_webViewIdentifier), @(arg_configurationIdentifier), + arg_navigationAction ?: [NSNull null] + ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion([FlutterError errorWithCode:@"channel-error" + message:@"Unable to establish connection on channel." + details:@""]); + } + }]; } - (void)requestMediaCapturePermissionForDelegateWithIdentifier:(NSInteger)arg_identifier webViewIdentifier:(NSInteger)arg_webViewIdentifier @@ -3086,43 +3232,44 @@ - (void)requestMediaCapturePermissionForDelegateWithIdentifier:(NSInteger)arg_id (void (^)( FWFWKPermissionDecisionData *_Nullable, FlutterError *_Nullable))completion { - NSString *channelName = @"dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateFlutterApi." - @"requestMediaCapturePermission"; - FlutterBasicMessageChannel *channel = - [FlutterBasicMessageChannel messageChannelWithName:channelName - binaryMessenger:self.binaryMessenger - codec:FWFWKUIDelegateFlutterApiGetCodec()]; - [channel sendMessage:@[ - @(arg_identifier), @(arg_webViewIdentifier), arg_origin ?: [NSNull null], - arg_frame ?: [NSNull null], arg_type ?: [NSNull null] - ] - reply:^(NSArray *reply) { - if (reply != nil) { - if (reply.count > 1) { - completion(nil, [FlutterError errorWithCode:reply[0] - message:reply[1] - details:reply[2]]); - } else { - FWFWKPermissionDecisionData *output = - reply[0] == [NSNull null] ? nil : reply[0]; - completion(output, nil); - } - } else { - completion(nil, createConnectionError(channelName)); - } - }]; + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateFlutterApi." + @"requestMediaCapturePermission" + binaryMessenger:self.binaryMessenger + codec:FWFWKUIDelegateFlutterApiGetCodec()]; + [channel + sendMessage:@[ + @(arg_identifier), @(arg_webViewIdentifier), arg_origin ?: [NSNull null], + arg_frame ?: [NSNull null], arg_type ?: [NSNull null] + ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion(nil, [FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + FWFWKPermissionDecisionData *output = reply[0] == [NSNull null] ? nil : reply[0]; + completion(output, nil); + } + } else { + completion(nil, + [FlutterError errorWithCode:@"channel-error" + message:@"Unable to establish connection on channel." + details:@""]); + } + }]; } - (void)runJavaScriptAlertPanelForDelegateWithIdentifier:(NSInteger)arg_identifier message:(NSString *)arg_message frame:(FWFWKFrameInfoData *)arg_frame completion: (void (^)(FlutterError *_Nullable))completion { - NSString *channelName = @"dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateFlutterApi." - @"runJavaScriptAlertPanel"; - FlutterBasicMessageChannel *channel = - [FlutterBasicMessageChannel messageChannelWithName:channelName - binaryMessenger:self.binaryMessenger - codec:FWFWKUIDelegateFlutterApiGetCodec()]; + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateFlutterApi." + @"runJavaScriptAlertPanel" + binaryMessenger:self.binaryMessenger + codec:FWFWKUIDelegateFlutterApiGetCodec()]; [channel sendMessage:@[ @(arg_identifier), arg_message ?: [NSNull null], arg_frame ?: [NSNull null] ] reply:^(NSArray *reply) { @@ -3135,7 +3282,9 @@ - (void)runJavaScriptAlertPanelForDelegateWithIdentifier:(NSInteger)arg_identifi completion(nil); } } else { - completion(createConnectionError(channelName)); + completion([FlutterError errorWithCode:@"channel-error" + message:@"Unable to establish connection on channel." + details:@""]); } }]; } @@ -3145,12 +3294,11 @@ - (void)runJavaScriptConfirmPanelForDelegateWithIdentifier:(NSInteger)arg_identi completion: (void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion { - NSString *channelName = @"dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateFlutterApi." - @"runJavaScriptConfirmPanel"; - FlutterBasicMessageChannel *channel = - [FlutterBasicMessageChannel messageChannelWithName:channelName - binaryMessenger:self.binaryMessenger - codec:FWFWKUIDelegateFlutterApiGetCodec()]; + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateFlutterApi." + @"runJavaScriptConfirmPanel" + binaryMessenger:self.binaryMessenger + codec:FWFWKUIDelegateFlutterApiGetCodec()]; [channel sendMessage:@[ @(arg_identifier), arg_message ?: [NSNull null], arg_frame ?: [NSNull null] ] reply:^(NSArray *reply) { @@ -3164,7 +3312,10 @@ - (void)runJavaScriptConfirmPanelForDelegateWithIdentifier:(NSInteger)arg_identi completion(output, nil); } } else { - completion(nil, createConnectionError(channelName)); + completion(nil, + [FlutterError errorWithCode:@"channel-error" + message:@"Unable to establish connection on channel." + details:@""]); } }]; } @@ -3175,12 +3326,11 @@ - (void)runJavaScriptTextInputPanelForDelegateWithIdentifier:(NSInteger)arg_iden completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable)) completion { - NSString *channelName = @"dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateFlutterApi." - @"runJavaScriptTextInputPanel"; - FlutterBasicMessageChannel *channel = - [FlutterBasicMessageChannel messageChannelWithName:channelName - binaryMessenger:self.binaryMessenger - codec:FWFWKUIDelegateFlutterApiGetCodec()]; + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateFlutterApi." + @"runJavaScriptTextInputPanel" + binaryMessenger:self.binaryMessenger + codec:FWFWKUIDelegateFlutterApiGetCodec()]; [channel sendMessage:@[ @(arg_identifier), arg_prompt ?: [NSNull null], arg_defaultText ?: [NSNull null], arg_frame ?: [NSNull null] @@ -3196,7 +3346,10 @@ - (void)runJavaScriptTextInputPanelForDelegateWithIdentifier:(NSInteger)arg_iden completion(output, nil); } } else { - completion(nil, createConnectionError(channelName)); + completion(nil, [FlutterError + errorWithCode:@"channel-error" + message:@"Unable to establish connection on channel." + details:@""]); } }]; } @@ -3362,25 +3515,27 @@ - (instancetype)initWithBinaryMessenger:(NSObject *)bina } - (void)createWithIdentifier:(NSInteger)arg_identifier completion:(void (^)(FlutterError *_Nullable))completion { - NSString *channelName = @"dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlFlutterApi.create"; - FlutterBasicMessageChannel *channel = - [FlutterBasicMessageChannel messageChannelWithName:channelName - binaryMessenger:self.binaryMessenger - codec:FWFNSUrlFlutterApiGetCodec()]; - [channel sendMessage:@[ @(arg_identifier) ] - reply:^(NSArray *reply) { - if (reply != nil) { - if (reply.count > 1) { - completion([FlutterError errorWithCode:reply[0] - message:reply[1] - details:reply[2]]); - } else { - completion(nil); - } - } else { - completion(createConnectionError(channelName)); - } - }]; + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlFlutterApi.create" + binaryMessenger:self.binaryMessenger + codec:FWFNSUrlFlutterApiGetCodec()]; + [channel + sendMessage:@[ @(arg_identifier) ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion([FlutterError errorWithCode:@"channel-error" + message:@"Unable to establish connection on channel." + details:@""]); + } + }]; } @end @@ -3439,26 +3594,28 @@ - (void)scrollViewDidScrollWithIdentifier:(NSInteger)arg_identifier x:(double)arg_x y:(double)arg_y completion:(void (^)(FlutterError *_Nullable))completion { - NSString *channelName = @"dev.flutter.pigeon.webview_flutter_wkwebview." - @"UIScrollViewDelegateFlutterApi.scrollViewDidScroll"; FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:channelName + messageChannelWithName:@"dev.flutter.pigeon.webview_flutter_wkwebview." + @"UIScrollViewDelegateFlutterApi.scrollViewDidScroll" binaryMessenger:self.binaryMessenger codec:FWFUIScrollViewDelegateFlutterApiGetCodec()]; - [channel sendMessage:@[ @(arg_identifier), @(arg_uiScrollViewIdentifier), @(arg_x), @(arg_y) ] - reply:^(NSArray *reply) { - if (reply != nil) { - if (reply.count > 1) { - completion([FlutterError errorWithCode:reply[0] - message:reply[1] - details:reply[2]]); - } else { - completion(nil); - } - } else { - completion(createConnectionError(channelName)); - } - }]; + [channel + sendMessage:@[ @(arg_identifier), @(arg_uiScrollViewIdentifier), @(arg_x), @(arg_y) ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion([FlutterError errorWithCode:@"channel-error" + message:@"Unable to establish connection on channel." + details:@""]); + } + }]; } @end @@ -3527,29 +3684,31 @@ - (void)createWithIdentifier:(NSInteger)arg_identifier realm:(nullable NSString *)arg_realm authenticationMethod:(nullable NSString *)arg_authenticationMethod completion:(void (^)(FlutterError *_Nullable))completion { - NSString *channelName = - @"dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlProtectionSpaceFlutterApi.create"; FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:channelName + messageChannelWithName: + @"dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlProtectionSpaceFlutterApi.create" binaryMessenger:self.binaryMessenger codec:FWFNSUrlProtectionSpaceFlutterApiGetCodec()]; - [channel sendMessage:@[ - @(arg_identifier), arg_host ?: [NSNull null], arg_realm ?: [NSNull null], - arg_authenticationMethod ?: [NSNull null] - ] - reply:^(NSArray *reply) { - if (reply != nil) { - if (reply.count > 1) { - completion([FlutterError errorWithCode:reply[0] - message:reply[1] - details:reply[2]]); - } else { - completion(nil); - } - } else { - completion(createConnectionError(channelName)); - } - }]; + [channel + sendMessage:@[ + @(arg_identifier), arg_host ?: [NSNull null], arg_realm ?: [NSNull null], + arg_authenticationMethod ?: [NSNull null] + ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion([FlutterError errorWithCode:@"channel-error" + message:@"Unable to establish connection on channel." + details:@""]); + } + }]; } @end @@ -3575,25 +3734,27 @@ - (instancetype)initWithBinaryMessenger:(NSObject *)bina - (void)createWithIdentifier:(NSInteger)arg_identifier protectionSpaceIdentifier:(NSInteger)arg_protectionSpaceIdentifier completion:(void (^)(FlutterError *_Nullable))completion { - NSString *channelName = - @"dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlAuthenticationChallengeFlutterApi.create"; FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:channelName + messageChannelWithName:@"dev.flutter.pigeon.webview_flutter_wkwebview." + @"NSUrlAuthenticationChallengeFlutterApi.create" binaryMessenger:self.binaryMessenger codec:FWFNSUrlAuthenticationChallengeFlutterApiGetCodec()]; - [channel sendMessage:@[ @(arg_identifier), @(arg_protectionSpaceIdentifier) ] - reply:^(NSArray *reply) { - if (reply != nil) { - if (reply.count > 1) { - completion([FlutterError errorWithCode:reply[0] - message:reply[1] - details:reply[2]]); - } else { - completion(nil); - } - } else { - completion(createConnectionError(channelName)); - } - }]; + [channel + sendMessage:@[ @(arg_identifier), @(arg_protectionSpaceIdentifier) ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion([FlutterError errorWithCode:@"channel-error" + message:@"Unable to establish connection on channel." + details:@""]); + } + }]; } @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.m index db8c4562748f..2718702d4b93 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.m @@ -72,6 +72,24 @@ - (void)didStartProvisionalNavigationForDelegate:(FWFNavigationDelegate *)instan completion:completion]; } +- (void)decidePolicyForNavigationResponseForDelegate:(FWFNavigationDelegate *)instance + webView:(WKWebView *)webView + navigationResponse:(WKNavigationResponse *)navigationResponse + completion: + (void (^)(FWFWKNavigationResponsePolicyEnumBox *, + FlutterError *_Nullable))completion { + NSInteger webViewIdentifier = + [self.instanceManager identifierWithStrongReferenceForInstance:webView]; + FWFWKNavigationResponseData *navigationResponseData = + FWFWKNavigationResponseDataFromNativeNavigationResponse(navigationResponse); + [self + decidePolicyForNavigationResponseForDelegateWithIdentifier:[self + identifierForDelegate:instance] + webViewIdentifier:webViewIdentifier + navigationResponse:navigationResponseData + completion:completion]; +} + - (void)didFailNavigationForDelegate:(FWFNavigationDelegate *)instance webView:(WKWebView *)webView error:(NSError *)error @@ -190,6 +208,26 @@ - (void)webView:(WKWebView *)webView }]; } +- (void)webView:(WKWebView *)webView + decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse + decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler { + [self.navigationDelegateAPI + decidePolicyForNavigationResponseForDelegate:self + webView:webView + navigationResponse:navigationResponse + completion:^(FWFWKNavigationResponsePolicyEnumBox *policy, + FlutterError *error) { + NSAssert(!error, @"%@", error); + if (!error) { + decisionHandler( + FWFNativeWKNavigationResponsePolicyFromEnum( + policy.value)); + } else { + decisionHandler(WKNavigationResponsePolicyCancel); + } + }]; +} + - (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error { 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 6e1162a97caa..65f55dbbe3ff 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 @@ -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 (v13.1.2), do not edit directly. +// Autogenerated from Pigeon (v13.0.0), 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 @@ -11,13 +11,6 @@ import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; -PlatformException _createConnectionError(String channelName) { - return PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel: "$channelName".', - ); -} - List wrapResponse( {Object? result, PlatformException? error, bool empty = false}) { if (empty) { @@ -101,6 +94,14 @@ enum WKNavigationActionPolicyEnum { cancel, } +/// Mirror of WKNavigationResponsePolicy. +/// +/// See https://developer.apple.com/documentation/webkit/wknavigationactionpolicy?language=objc. +enum WKNavigationResponsePolicyEnum { + allow, + cancel, +} + /// Mirror of NSHTTPCookiePropertyKey. /// /// See https://developer.apple.com/documentation/foundation/nshttpcookiepropertykey. @@ -490,6 +491,30 @@ class NSUrlRequestData { } } +/// Mirror of NSURLResponse. +/// +/// See https://developer.apple.com/documentation/foundation/nshttpurlresponse?language=objc. +class NSHttpUrlResponseData { + NSHttpUrlResponseData({ + required this.statusCode, + }); + + int statusCode; + + Object encode() { + return [ + statusCode, + ]; + } + + static NSHttpUrlResponseData decode(Object result) { + result as List; + return NSHttpUrlResponseData( + statusCode: result[0]! as int, + ); + } +} + /// Mirror of WKUserScript. /// /// See https://developer.apple.com/documentation/webkit/wkuserscript?language=objc. @@ -561,6 +586,35 @@ class WKNavigationActionData { } } +/// Mirror of WKNavigationResponse. +/// +/// See https://developer.apple.com/documentation/webkit/wknavigationresponse. +class WKNavigationResponseData { + WKNavigationResponseData({ + required this.response, + required this.forMainFrame, + }); + + NSHttpUrlResponseData response; + + bool forMainFrame; + + Object encode() { + return [ + response.encode(), + forMainFrame, + ]; + } + + static WKNavigationResponseData decode(Object result) { + result as List; + return WKNavigationResponseData( + response: NSHttpUrlResponseData.decode(result[0]! as List), + forMainFrame: result[1]! as bool, + ); + } +} + /// Mirror of WKFrameInfo. /// /// See https://developer.apple.com/documentation/webkit/wkframeinfo?language=objc. @@ -813,18 +867,18 @@ class WKWebsiteDataStoreHostApi { Future createFromWebViewConfiguration( int arg_identifier, int arg_configurationIdentifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebsiteDataStoreHostApi.createFromWebViewConfiguration'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebsiteDataStoreHostApi.createFromWebViewConfiguration', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_configurationIdentifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -837,17 +891,17 @@ class WKWebsiteDataStoreHostApi { } Future createDefaultDataStore(int arg_identifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebsiteDataStoreHostApi.createDefaultDataStore'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebsiteDataStoreHostApi.createDefaultDataStore', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -863,20 +917,20 @@ class WKWebsiteDataStoreHostApi { int arg_identifier, List arg_dataTypes, double arg_modificationTimeInSecondsSinceEpoch) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebsiteDataStoreHostApi.removeDataOfTypes'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebsiteDataStoreHostApi.removeDataOfTypes', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([ arg_identifier, arg_dataTypes, arg_modificationTimeInSecondsSinceEpoch ]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -908,17 +962,17 @@ class UIViewHostApi { static const MessageCodec codec = StandardMessageCodec(); Future setBackgroundColor(int arg_identifier, int? arg_value) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.UIViewHostApi.setBackgroundColor'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.UIViewHostApi.setBackgroundColor', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_value]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -931,17 +985,17 @@ class UIViewHostApi { } Future setOpaque(int arg_identifier, bool arg_opaque) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.UIViewHostApi.setOpaque'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.UIViewHostApi.setOpaque', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_opaque]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -969,18 +1023,18 @@ class UIScrollViewHostApi { Future createFromWebView( int arg_identifier, int arg_webViewIdentifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.UIScrollViewHostApi.createFromWebView'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.UIScrollViewHostApi.createFromWebView', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier, arg_webViewIdentifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -993,17 +1047,17 @@ class UIScrollViewHostApi { } Future> getContentOffset(int arg_identifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.UIScrollViewHostApi.getContentOffset'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.UIScrollViewHostApi.getContentOffset', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -1021,17 +1075,17 @@ class UIScrollViewHostApi { } Future scrollBy(int arg_identifier, double arg_x, double arg_y) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.UIScrollViewHostApi.scrollBy'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.UIScrollViewHostApi.scrollBy', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_x, arg_y]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -1045,17 +1099,17 @@ class UIScrollViewHostApi { Future setContentOffset( int arg_identifier, double arg_x, double arg_y) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.UIScrollViewHostApi.setContentOffset'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.UIScrollViewHostApi.setContentOffset', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_x, arg_y]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -1069,18 +1123,18 @@ class UIScrollViewHostApi { Future setDelegate( int arg_identifier, int? arg_uiScrollViewDelegateIdentifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.UIScrollViewHostApi.setDelegate'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.UIScrollViewHostApi.setDelegate', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_uiScrollViewDelegateIdentifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -1131,17 +1185,17 @@ class WKWebViewConfigurationHostApi { _WKWebViewConfigurationHostApiCodec(); Future create(int arg_identifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewConfigurationHostApi.create'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewConfigurationHostApi.create', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -1155,18 +1209,18 @@ class WKWebViewConfigurationHostApi { Future createFromWebView( int arg_identifier, int arg_webViewIdentifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewConfigurationHostApi.createFromWebView'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewConfigurationHostApi.createFromWebView', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier, arg_webViewIdentifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -1180,17 +1234,17 @@ class WKWebViewConfigurationHostApi { Future setAllowsInlineMediaPlayback( int arg_identifier, bool arg_allow) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewConfigurationHostApi.setAllowsInlineMediaPlayback'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewConfigurationHostApi.setAllowsInlineMediaPlayback', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_allow]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -1204,17 +1258,17 @@ class WKWebViewConfigurationHostApi { Future setLimitsNavigationsToAppBoundDomains( int arg_identifier, bool arg_limit) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewConfigurationHostApi.setLimitsNavigationsToAppBoundDomains'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewConfigurationHostApi.setLimitsNavigationsToAppBoundDomains', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_limit]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -1228,17 +1282,17 @@ class WKWebViewConfigurationHostApi { Future setMediaTypesRequiringUserActionForPlayback(int arg_identifier, List arg_types) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewConfigurationHostApi.setMediaTypesRequiringUserActionForPlayback'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewConfigurationHostApi.setMediaTypesRequiringUserActionForPlayback', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_types]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -1335,18 +1389,18 @@ class WKUserContentControllerHostApi { Future createFromWebViewConfiguration( int arg_identifier, int arg_configurationIdentifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKUserContentControllerHostApi.createFromWebViewConfiguration'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKUserContentControllerHostApi.createFromWebViewConfiguration', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_configurationIdentifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -1360,18 +1414,18 @@ class WKUserContentControllerHostApi { Future addScriptMessageHandler( int arg_identifier, int arg_handlerIdentifier, String arg_name) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKUserContentControllerHostApi.addScriptMessageHandler'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKUserContentControllerHostApi.addScriptMessageHandler', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_handlerIdentifier, arg_name]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -1385,17 +1439,17 @@ class WKUserContentControllerHostApi { Future removeScriptMessageHandler( int arg_identifier, String arg_name) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKUserContentControllerHostApi.removeScriptMessageHandler'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKUserContentControllerHostApi.removeScriptMessageHandler', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_name]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -1408,17 +1462,17 @@ class WKUserContentControllerHostApi { } Future removeAllScriptMessageHandlers(int arg_identifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKUserContentControllerHostApi.removeAllScriptMessageHandlers'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKUserContentControllerHostApi.removeAllScriptMessageHandlers', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -1432,17 +1486,17 @@ class WKUserContentControllerHostApi { Future addUserScript( int arg_identifier, WKUserScriptData arg_userScript) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKUserContentControllerHostApi.addUserScript'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKUserContentControllerHostApi.addUserScript', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_userScript]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -1455,17 +1509,17 @@ class WKUserContentControllerHostApi { } Future removeAllUserScripts(int arg_identifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKUserContentControllerHostApi.removeAllUserScripts'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKUserContentControllerHostApi.removeAllUserScripts', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -1493,18 +1547,18 @@ class WKPreferencesHostApi { Future createFromWebViewConfiguration( int arg_identifier, int arg_configurationIdentifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKPreferencesHostApi.createFromWebViewConfiguration'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKPreferencesHostApi.createFromWebViewConfiguration', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_configurationIdentifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -1518,17 +1572,17 @@ class WKPreferencesHostApi { Future setJavaScriptEnabled( int arg_identifier, bool arg_enabled) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKPreferencesHostApi.setJavaScriptEnabled'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKPreferencesHostApi.setJavaScriptEnabled', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_enabled]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -1555,17 +1609,17 @@ class WKScriptMessageHandlerHostApi { static const MessageCodec codec = StandardMessageCodec(); Future create(int arg_identifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKScriptMessageHandlerHostApi.create'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKScriptMessageHandlerHostApi.create', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -1665,17 +1719,17 @@ class WKNavigationDelegateHostApi { static const MessageCodec codec = StandardMessageCodec(); Future create(int arg_identifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKNavigationDelegateHostApi.create'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKNavigationDelegateHostApi.create', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -1698,18 +1752,24 @@ class _WKNavigationDelegateFlutterApiCodec extends StandardMessageCodec { } else if (value is NSErrorData) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is NSUrlRequestData) { + } else if (value is NSHttpUrlResponseData) { buffer.putUint8(130); writeValue(buffer, value.encode()); - } else if (value is WKFrameInfoData) { + } else if (value is NSUrlRequestData) { buffer.putUint8(131); writeValue(buffer, value.encode()); - } else if (value is WKNavigationActionData) { + } else if (value is WKFrameInfoData) { buffer.putUint8(132); writeValue(buffer, value.encode()); - } else if (value is WKNavigationActionPolicyEnumData) { + } else if (value is WKNavigationActionData) { buffer.putUint8(133); writeValue(buffer, value.encode()); + } else if (value is WKNavigationActionPolicyEnumData) { + buffer.putUint8(134); + writeValue(buffer, value.encode()); + } else if (value is WKNavigationResponseData) { + buffer.putUint8(135); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -1723,13 +1783,17 @@ class _WKNavigationDelegateFlutterApiCodec extends StandardMessageCodec { case 129: return NSErrorData.decode(readValue(buffer)!); case 130: - return NSUrlRequestData.decode(readValue(buffer)!); + return NSHttpUrlResponseData.decode(readValue(buffer)!); case 131: - return WKFrameInfoData.decode(readValue(buffer)!); + return NSUrlRequestData.decode(readValue(buffer)!); case 132: - return WKNavigationActionData.decode(readValue(buffer)!); + return WKFrameInfoData.decode(readValue(buffer)!); case 133: + return WKNavigationActionData.decode(readValue(buffer)!); + case 134: return WKNavigationActionPolicyEnumData.decode(readValue(buffer)!); + case 135: + return WKNavigationResponseData.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -1753,6 +1817,11 @@ abstract class WKNavigationDelegateFlutterApi { int webViewIdentifier, WKNavigationActionData navigationAction); + Future decidePolicyForNavigationResponse( + int identifier, + int webViewIdentifier, + WKNavigationResponseData navigationResponse); + void didFailNavigation( int identifier, int webViewIdentifier, NSErrorData error); @@ -1867,6 +1936,42 @@ abstract class WKNavigationDelegateFlutterApi { }); } } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKNavigationDelegateFlutterApi.decidePolicyForNavigationResponse', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.WKNavigationDelegateFlutterApi.decidePolicyForNavigationResponse was null.'); + final List args = (message as List?)!; + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.WKNavigationDelegateFlutterApi.decidePolicyForNavigationResponse was null, expected non-null int.'); + final int? arg_webViewIdentifier = (args[1] as int?); + assert(arg_webViewIdentifier != null, + 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.WKNavigationDelegateFlutterApi.decidePolicyForNavigationResponse was null, expected non-null int.'); + final WKNavigationResponseData? arg_navigationResponse = + (args[2] as WKNavigationResponseData?); + assert(arg_navigationResponse != null, + 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.WKNavigationDelegateFlutterApi.decidePolicyForNavigationResponse was null, expected non-null WKNavigationResponseData.'); + try { + final WKNavigationResponsePolicyEnum output = + await api.decidePolicyForNavigationResponse(arg_identifier!, + arg_webViewIdentifier!, arg_navigationResponse!); + return wrapResponse(result: output.index); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.webview_flutter_wkwebview.WKNavigationDelegateFlutterApi.didFailNavigation', @@ -2041,17 +2146,17 @@ class NSObjectHostApi { static const MessageCodec codec = _NSObjectHostApiCodec(); Future dispose(int arg_identifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.NSObjectHostApi.dispose'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.NSObjectHostApi.dispose', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -2068,13 +2173,10 @@ class NSObjectHostApi { int arg_observerIdentifier, String arg_keyPath, List arg_options) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.NSObjectHostApi.addObserver'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.NSObjectHostApi.addObserver', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([ arg_identifier, arg_observerIdentifier, @@ -2082,7 +2184,10 @@ class NSObjectHostApi { arg_options ]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -2096,18 +2201,18 @@ class NSObjectHostApi { Future removeObserver(int arg_identifier, int arg_observerIdentifier, String arg_keyPath) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.NSObjectHostApi.removeObserver'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.NSObjectHostApi.removeObserver', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send( [arg_identifier, arg_observerIdentifier, arg_keyPath]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -2253,51 +2358,57 @@ class _WKWebViewHostApiCodec extends StandardMessageCodec { } else if (value is NSHttpCookiePropertyKeyEnumData) { buffer.putUint8(131); writeValue(buffer, value.encode()); - } else if (value is NSKeyValueChangeKeyEnumData) { + } else if (value is NSHttpUrlResponseData) { buffer.putUint8(132); writeValue(buffer, value.encode()); - } else if (value is NSKeyValueObservingOptionsEnumData) { + } else if (value is NSKeyValueChangeKeyEnumData) { buffer.putUint8(133); writeValue(buffer, value.encode()); - } else if (value is NSUrlRequestData) { + } else if (value is NSKeyValueObservingOptionsEnumData) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is ObjectOrIdentifier) { + } else if (value is NSUrlRequestData) { buffer.putUint8(135); writeValue(buffer, value.encode()); - } else if (value is WKAudiovisualMediaTypeEnumData) { + } else if (value is ObjectOrIdentifier) { buffer.putUint8(136); writeValue(buffer, value.encode()); - } else if (value is WKFrameInfoData) { + } else if (value is WKAudiovisualMediaTypeEnumData) { buffer.putUint8(137); writeValue(buffer, value.encode()); - } else if (value is WKMediaCaptureTypeData) { + } else if (value is WKFrameInfoData) { buffer.putUint8(138); writeValue(buffer, value.encode()); - } else if (value is WKNavigationActionData) { + } else if (value is WKMediaCaptureTypeData) { buffer.putUint8(139); writeValue(buffer, value.encode()); - } else if (value is WKNavigationActionPolicyEnumData) { + } else if (value is WKNavigationActionData) { buffer.putUint8(140); writeValue(buffer, value.encode()); - } else if (value is WKPermissionDecisionData) { + } else if (value is WKNavigationActionPolicyEnumData) { buffer.putUint8(141); writeValue(buffer, value.encode()); - } else if (value is WKScriptMessageData) { + } else if (value is WKNavigationResponseData) { buffer.putUint8(142); writeValue(buffer, value.encode()); - } else if (value is WKSecurityOriginData) { + } else if (value is WKPermissionDecisionData) { buffer.putUint8(143); writeValue(buffer, value.encode()); - } else if (value is WKUserScriptData) { + } else if (value is WKScriptMessageData) { buffer.putUint8(144); writeValue(buffer, value.encode()); - } else if (value is WKUserScriptInjectionTimeEnumData) { + } else if (value is WKSecurityOriginData) { buffer.putUint8(145); writeValue(buffer, value.encode()); - } else if (value is WKWebsiteDataTypeEnumData) { + } else if (value is WKUserScriptData) { buffer.putUint8(146); writeValue(buffer, value.encode()); + } else if (value is WKUserScriptInjectionTimeEnumData) { + buffer.putUint8(147); + writeValue(buffer, value.encode()); + } else if (value is WKWebsiteDataTypeEnumData) { + buffer.putUint8(148); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -2315,34 +2426,38 @@ class _WKWebViewHostApiCodec extends StandardMessageCodec { case 131: return NSHttpCookiePropertyKeyEnumData.decode(readValue(buffer)!); case 132: - return NSKeyValueChangeKeyEnumData.decode(readValue(buffer)!); + return NSHttpUrlResponseData.decode(readValue(buffer)!); case 133: - return NSKeyValueObservingOptionsEnumData.decode(readValue(buffer)!); + return NSKeyValueChangeKeyEnumData.decode(readValue(buffer)!); case 134: - return NSUrlRequestData.decode(readValue(buffer)!); + return NSKeyValueObservingOptionsEnumData.decode(readValue(buffer)!); case 135: - return ObjectOrIdentifier.decode(readValue(buffer)!); + return NSUrlRequestData.decode(readValue(buffer)!); case 136: - return WKAudiovisualMediaTypeEnumData.decode(readValue(buffer)!); + return ObjectOrIdentifier.decode(readValue(buffer)!); case 137: - return WKFrameInfoData.decode(readValue(buffer)!); + return WKAudiovisualMediaTypeEnumData.decode(readValue(buffer)!); case 138: - return WKMediaCaptureTypeData.decode(readValue(buffer)!); + return WKFrameInfoData.decode(readValue(buffer)!); case 139: - return WKNavigationActionData.decode(readValue(buffer)!); + return WKMediaCaptureTypeData.decode(readValue(buffer)!); case 140: - return WKNavigationActionPolicyEnumData.decode(readValue(buffer)!); + return WKNavigationActionData.decode(readValue(buffer)!); case 141: - return WKPermissionDecisionData.decode(readValue(buffer)!); + return WKNavigationActionPolicyEnumData.decode(readValue(buffer)!); case 142: - return WKScriptMessageData.decode(readValue(buffer)!); + return WKNavigationResponseData.decode(readValue(buffer)!); case 143: - return WKSecurityOriginData.decode(readValue(buffer)!); + return WKPermissionDecisionData.decode(readValue(buffer)!); case 144: - return WKUserScriptData.decode(readValue(buffer)!); + return WKScriptMessageData.decode(readValue(buffer)!); case 145: - return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!); + return WKSecurityOriginData.decode(readValue(buffer)!); case 146: + return WKUserScriptData.decode(readValue(buffer)!); + case 147: + return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!); + case 148: return WKWebsiteDataTypeEnumData.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -2365,18 +2480,18 @@ class WKWebViewHostApi { Future create( int arg_identifier, int arg_configurationIdentifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.create'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.create', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_configurationIdentifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -2390,18 +2505,18 @@ class WKWebViewHostApi { Future setUIDelegate( int arg_identifier, int? arg_uiDelegateIdentifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.setUIDelegate'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.setUIDelegate', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier, arg_uiDelegateIdentifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -2415,18 +2530,18 @@ class WKWebViewHostApi { Future setNavigationDelegate( int arg_identifier, int? arg_navigationDelegateIdentifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.setNavigationDelegate'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.setNavigationDelegate', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_navigationDelegateIdentifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -2439,17 +2554,17 @@ class WKWebViewHostApi { } Future getUrl(int arg_identifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.getUrl'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.getUrl', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -2462,17 +2577,17 @@ class WKWebViewHostApi { } Future getEstimatedProgress(int arg_identifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.getEstimatedProgress'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.getEstimatedProgress', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -2491,17 +2606,17 @@ class WKWebViewHostApi { Future loadRequest( int arg_identifier, NSUrlRequestData arg_request) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.loadRequest'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.loadRequest', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_request]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -2515,18 +2630,18 @@ class WKWebViewHostApi { Future loadHtmlString( int arg_identifier, String arg_string, String? arg_baseUrl) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.loadHtmlString'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.loadHtmlString', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier, arg_string, arg_baseUrl]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -2540,18 +2655,18 @@ class WKWebViewHostApi { Future loadFileUrl( int arg_identifier, String arg_url, String arg_readAccessUrl) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.loadFileUrl'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.loadFileUrl', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_url, arg_readAccessUrl]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -2564,17 +2679,17 @@ class WKWebViewHostApi { } Future loadFlutterAsset(int arg_identifier, String arg_key) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.loadFlutterAsset'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.loadFlutterAsset', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_key]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -2587,17 +2702,17 @@ class WKWebViewHostApi { } Future canGoBack(int arg_identifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.canGoBack'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.canGoBack', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -2615,17 +2730,17 @@ class WKWebViewHostApi { } Future canGoForward(int arg_identifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.canGoForward'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.canGoForward', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -2643,17 +2758,17 @@ class WKWebViewHostApi { } Future goBack(int arg_identifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.goBack'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.goBack', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -2666,17 +2781,17 @@ class WKWebViewHostApi { } Future goForward(int arg_identifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.goForward'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.goForward', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -2689,17 +2804,17 @@ class WKWebViewHostApi { } Future reload(int arg_identifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.reload'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.reload', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -2712,17 +2827,17 @@ class WKWebViewHostApi { } Future getTitle(int arg_identifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.getTitle'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.getTitle', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -2736,17 +2851,17 @@ class WKWebViewHostApi { Future setAllowsBackForwardNavigationGestures( int arg_identifier, bool arg_allow) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.setAllowsBackForwardNavigationGestures'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.setAllowsBackForwardNavigationGestures', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_allow]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -2760,17 +2875,17 @@ class WKWebViewHostApi { Future setCustomUserAgent( int arg_identifier, String? arg_userAgent) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.setCustomUserAgent'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.setCustomUserAgent', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_userAgent]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -2784,18 +2899,18 @@ class WKWebViewHostApi { Future evaluateJavaScript( int arg_identifier, String arg_javaScriptString) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.evaluateJavaScript'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.evaluateJavaScript', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier, arg_javaScriptString]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -2808,17 +2923,17 @@ class WKWebViewHostApi { } Future setInspectable(int arg_identifier, bool arg_inspectable) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.setInspectable'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.setInspectable', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_inspectable]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -2831,17 +2946,17 @@ class WKWebViewHostApi { } Future getCustomUserAgent(int arg_identifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.getCustomUserAgent'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.getCustomUserAgent', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -2868,17 +2983,17 @@ class WKUIDelegateHostApi { static const MessageCodec codec = StandardMessageCodec(); Future create(int arg_identifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateHostApi.create'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateHostApi.create', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -3202,18 +3317,18 @@ class WKHttpCookieStoreHostApi { Future createFromWebsiteDataStore( int arg_identifier, int arg_websiteDataStoreIdentifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKHttpCookieStoreHostApi.createFromWebsiteDataStore'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKHttpCookieStoreHostApi.createFromWebsiteDataStore', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_websiteDataStoreIdentifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -3227,17 +3342,17 @@ class WKHttpCookieStoreHostApi { Future setCookie( int arg_identifier, NSHttpCookieData arg_cookie) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.WKHttpCookieStoreHostApi.setCookie'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKHttpCookieStoreHostApi.setCookie', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel .send([arg_identifier, arg_cookie]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -3268,17 +3383,17 @@ class NSUrlHostApi { static const MessageCodec codec = StandardMessageCodec(); Future getAbsoluteString(int arg_identifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlHostApi.getAbsoluteString'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlHostApi.getAbsoluteString', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -3352,17 +3467,17 @@ class UIScrollViewDelegateHostApi { static const MessageCodec codec = StandardMessageCodec(); Future create(int arg_identifier) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.UIScrollViewDelegateHostApi.create'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.UIScrollViewDelegateHostApi.create', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([arg_identifier]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, @@ -3446,13 +3561,10 @@ class NSUrlCredentialHostApi { /// Create a new native instance and add it to the `InstanceManager`. Future createWithUser(int arg_identifier, String arg_user, String arg_password, NSUrlCredentialPersistence arg_persistence) async { - const String channelName = - 'dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlCredentialHostApi.createWithUser'; final BasicMessageChannel channel = BasicMessageChannel( - channelName, - codec, - binaryMessenger: _binaryMessenger, - ); + 'dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlCredentialHostApi.createWithUser', + codec, + binaryMessenger: _binaryMessenger); final List? replyList = await channel.send([ arg_identifier, arg_user, @@ -3460,7 +3572,10 @@ class NSUrlCredentialHostApi { arg_persistence.index ]) as List?; if (replyList == null) { - throw _createConnectionError(channelName); + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); } else if (replyList.length > 1) { throw PlatformException( code: replyList[0]! as String, 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 5c6d61aa8f7e..1dabbd0c24a5 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 @@ -221,6 +221,21 @@ class NSErrorUserInfoKey { 'NSErrorFailingURLStringKey'; } +/// The metadata associated with the response to an HTTP protocol URL load +/// request. +/// +/// Wraps [NSHttpUrlResponse](https://developer.apple.com/documentation/foundation/nshttpurlresponse?language=objc). +@immutable +class NSHttpUrlResponse { + /// Constructs an [NSHttpUrlResponse]. + const NSHttpUrlResponse({ + required this.statusCode, + }); + + /// The response’s HTTP status code. + final int statusCode; +} + /// Information about an error condition. /// /// Wraps [NSError](https://developer.apple.com/documentation/foundation/nserror?language=objc). diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart index fb99be0940ef..11065ad0805f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart @@ -97,6 +97,21 @@ enum WKNavigationActionPolicy { cancel, } +/// Indicate whether to allow or cancel navigation to a webpage. +/// +/// Wraps [WKNavigationResponsePolicy](https://developer.apple.com/documentation/webkit/wknavigationresponsepolicy?language=objc). +enum WKNavigationResponsePolicy { + /// Allow navigation to continue. + /// + /// See https://developer.apple.com/documentation/webkit/wknavigationresponsepolicy/wknavigationresponsepolicyallow?language=objc. + allow, + + /// Cancel navigation. + /// + /// See https://developer.apple.com/documentation/webkit/wknavigationresponsepolicy/wknavigationresponsepolicycancel?language=objc. + cancel, +} + /// Possible error values that WebKit APIs can return. /// /// See https://developer.apple.com/documentation/webkit/wkerrorcode. @@ -163,6 +178,24 @@ class WKNavigationAction { final WKNavigationType navigationType; } +/// An object that contains information about a response to a navigation request. +/// +/// Wraps [WKNavigationResponse](https://developer.apple.com/documentation/webkit/wknavigationresponse?language=objc). +@immutable +class WKNavigationResponse { + /// Constructs a [WKNavigationResponse]. + const WKNavigationResponse({ + required this.response, + required this.forMainFrame, + }); + + /// The URL request object associated with the navigation action. + final NSHttpUrlResponse response; + + /// The frame in which to display the new content. + final bool forMainFrame; +} + /// An object that contains information about a frame on a webpage. /// /// An instance of this class is a transient, data-only object; it does not @@ -858,6 +891,7 @@ class WKNavigationDelegate extends NSObject { this.didFinishNavigation, this.didStartProvisionalNavigation, this.decidePolicyForNavigationAction, + this.decidePolicyForNavigationResponse, this.didFailNavigation, this.didFailProvisionalNavigation, this.webViewWebContentProcessDidTerminate, @@ -884,6 +918,7 @@ class WKNavigationDelegate extends NSObject { this.didFinishNavigation, this.didStartProvisionalNavigation, this.decidePolicyForNavigationAction, + this.decidePolicyForNavigationResponse, this.didFailNavigation, this.didFailProvisionalNavigation, this.webViewWebContentProcessDidTerminate, @@ -918,6 +953,14 @@ class WKNavigationDelegate extends NSObject { WKNavigationAction navigationAction, )? decidePolicyForNavigationAction; + /// Called when permission is needed to navigate to new content. + /// + /// {@macro webview_flutter_wkwebview.foundation.callbacks} + final Future Function( + WKWebView webView, + WKNavigationResponse navigationResponse, + )? decidePolicyForNavigationResponse; + /// Called when an error occurred during navigation. /// /// {@macro webview_flutter_wkwebview.foundation.callbacks} @@ -950,6 +993,7 @@ class WKNavigationDelegate extends NSObject { didFinishNavigation: didFinishNavigation, didStartProvisionalNavigation: didStartProvisionalNavigation, decidePolicyForNavigationAction: decidePolicyForNavigationAction, + decidePolicyForNavigationResponse: decidePolicyForNavigationResponse, didFailNavigation: didFailNavigation, didFailProvisionalNavigation: didFailProvisionalNavigation, webViewWebContentProcessDidTerminate: 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 ac3bfc83f137..bdcc19eafd6b 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 @@ -70,6 +70,14 @@ extension _WKNavigationActionPolicyConverter on WKNavigationActionPolicy { } } +extension _WKNavigationResponsePolicyConverter on WKNavigationResponsePolicy { + WKNavigationResponsePolicyEnum toWKNavigationResponsePolicyEnumData() { + return WKNavigationResponsePolicyEnum.values.firstWhere( + (WKNavigationResponsePolicyEnum element) => element.name == name, + ); + } +} + extension _NSHttpCookiePropertyKeyConverter on NSHttpCookiePropertyKey { NSHttpCookiePropertyKeyEnumData toNSHttpCookiePropertyKeyEnumData() { late final NSHttpCookiePropertyKeyEnum value; @@ -153,6 +161,13 @@ extension _NavigationActionDataConverter on WKNavigationActionData { } } +extension _NavigationResponseDataConverter on WKNavigationResponseData { + WKNavigationResponse toNavigationResponse() { + return WKNavigationResponse( + response: response.toNSUrlResponse(), forMainFrame: forMainFrame); + } +} + extension _WKFrameInfoDataConverter on WKFrameInfoData { WKFrameInfo toWKFrameInfo() { return WKFrameInfo( @@ -173,6 +188,12 @@ extension _NSUrlRequestDataConverter on NSUrlRequestData { } } +extension _NSUrlResponseDataConverter on NSHttpUrlResponseData { + NSHttpUrlResponse toNSUrlResponse() { + return NSHttpUrlResponse(statusCode: statusCode); + } +} + extension _WKNSErrorDataConverter on NSErrorData { NSError toNSError() { return NSError( @@ -907,6 +928,29 @@ class WKNavigationDelegateFlutterApiImpl ); } + @override + Future decidePolicyForNavigationResponse( + int identifier, + int webViewIdentifier, + WKNavigationResponseData navigationResponse, + ) async { + final Future Function( + WKWebView, + WKNavigationResponse navigationResponse, + )? function = _getDelegate(identifier).decidePolicyForNavigationResponse; + + if (function == null) { + return WKNavigationResponsePolicyEnum.allow; + } + + final WKNavigationResponsePolicy policy = await function( + instanceManager.getInstanceWithWeakReference(webViewIdentifier)! + as WKWebView, + navigationResponse.toNavigationResponse(), + ); + return policy.toWKNavigationResponsePolicyEnumData(); + } + @override void webViewWebContentProcessDidTerminate( int identifier, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_proxy.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_proxy.dart index e4793d7e0ed2..ffd9b845eab6 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_proxy.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_proxy.dart @@ -69,6 +69,10 @@ class WebKitProxy { WKWebView webView, WKNavigationAction navigationAction, )? decidePolicyForNavigationAction, + Future Function( + WKWebView webView, + WKNavigationResponse navigationResponse, + )? decidePolicyForNavigationResponse, void Function(WKWebView webView, NSError error)? didFailNavigation, void Function(WKWebView webView, NSError error)? didFailProvisionalNavigation, 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 c4e38230de86..b9e9386239e7 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 @@ -986,6 +986,22 @@ class WebKitNavigationDelegate extends PlatformNavigationDelegate { weakThis.target!._onPageStarted!(url ?? ''); } }, + decidePolicyForNavigationResponse: + (WKWebView webView, WKNavigationResponse response) async { + if (weakThis.target?._onHttpError != null && + response.response.statusCode >= 400) { + weakThis.target!._onHttpError!( + HttpResponseError( + response: WebResourceResponse( + uri: null, + statusCode: response.response.statusCode, + ), + ), + ); + } + + return WKNavigationResponsePolicy.allow; + }, decidePolicyForNavigationAction: ( WKWebView webView, WKNavigationAction action, @@ -1100,6 +1116,7 @@ class WebKitNavigationDelegate extends PlatformNavigationDelegate { PageEventCallback? _onPageFinished; PageEventCallback? _onPageStarted; + HttpResponseErrorCallback? _onHttpError; ProgressCallback? _onProgress; WebResourceErrorCallback? _onWebResourceError; NavigationRequestCallback? _onNavigationRequest; @@ -1116,6 +1133,11 @@ class WebKitNavigationDelegate extends PlatformNavigationDelegate { _onPageStarted = onPageStarted; } + @override + Future setOnHttpError(HttpResponseErrorCallback onHttpError) async { + _onHttpError = onHttpError; + } + @override Future setOnProgress(ProgressCallback onProgress) async { _onProgress = onProgress; 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 5b0ea12db516..e660db3ef898 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart @@ -28,7 +28,7 @@ enum NSKeyValueObservingOptionsEnum { priorNotification, } -// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// TODO(bparrishMines): Enums need be wrapped in a data class because they can't // be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 class NSKeyValueObservingOptionsEnumData { late NSKeyValueObservingOptionsEnum value; @@ -44,7 +44,7 @@ enum NSKeyValueChangeEnum { replacement, } -// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// TODO(bparrishMines): Enums need be wrapped in a data class because they can't // be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 class NSKeyValueChangeEnumData { late NSKeyValueChangeEnum value; @@ -62,7 +62,7 @@ enum NSKeyValueChangeKeyEnum { unknown, } -// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// TODO(bparrishMines): Enums need be wrapped in a data class because they can't // be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 class NSKeyValueChangeKeyEnumData { late NSKeyValueChangeKeyEnum value; @@ -76,7 +76,7 @@ enum WKUserScriptInjectionTimeEnum { atDocumentEnd, } -// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// TODO(bparrishMines): Enums need be wrapped in a data class because they can't // be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 class WKUserScriptInjectionTimeEnumData { late WKUserScriptInjectionTimeEnum value; @@ -92,7 +92,7 @@ enum WKAudiovisualMediaTypeEnum { all, } -// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// TODO(bparrishMines): Enums need be wrapped in a data class because they can't // be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 class WKAudiovisualMediaTypeEnumData { late WKAudiovisualMediaTypeEnum value; @@ -112,7 +112,7 @@ enum WKWebsiteDataTypeEnum { indexedDBDatabases, } -// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// TODO(bparrishMines): Enums need be wrapped in a data class because they can't // be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 class WKWebsiteDataTypeEnumData { late WKWebsiteDataTypeEnum value; @@ -126,12 +126,20 @@ enum WKNavigationActionPolicyEnum { cancel, } -// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// TODO(bparrishMines): Enums need be wrapped in a data class because they can't // be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 class WKNavigationActionPolicyEnumData { late WKNavigationActionPolicyEnum value; } +/// Mirror of WKNavigationResponsePolicy. +/// +/// See https://developer.apple.com/documentation/webkit/wknavigationactionpolicy?language=objc. +enum WKNavigationResponsePolicyEnum { + allow, + cancel, +} + /// Mirror of NSHTTPCookiePropertyKey. /// /// See https://developer.apple.com/documentation/foundation/nshttpcookiepropertykey. @@ -152,7 +160,7 @@ enum NSHttpCookiePropertyKeyEnum { version, } -// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// TODO(bparrishMines): Enums need be wrapped in a data class because they can't // be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 class NSHttpCookiePropertyKeyEnumData { late NSHttpCookiePropertyKeyEnum value; @@ -220,7 +228,7 @@ enum WKPermissionDecision { prompt, } -// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// TODO(bparrishMines): Enums need be wrapped in a data class because they can't // be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 class WKPermissionDecisionData { late WKPermissionDecision value; @@ -252,7 +260,7 @@ enum WKMediaCaptureType { unknown, } -// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// TODO(bparrishMines): Enums need be wrapped in a data class because they can't // be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 class WKMediaCaptureTypeData { late WKMediaCaptureType value; @@ -320,6 +328,13 @@ class NSUrlRequestData { late Map allHttpHeaderFields; } +/// Mirror of NSURLResponse. +/// +/// See https://developer.apple.com/documentation/foundation/nshttpurlresponse?language=objc. +class NSHttpUrlResponseData { + late int statusCode; +} + /// Mirror of WKUserScript. /// /// See https://developer.apple.com/documentation/webkit/wkuserscript?language=objc. @@ -338,6 +353,14 @@ class WKNavigationActionData { late WKNavigationType navigationType; } +/// Mirror of WKNavigationResponse. +/// +/// See https://developer.apple.com/documentation/webkit/wknavigationresponse. +class WKNavigationResponseData { + late NSHttpUrlResponseData response; + late bool forMainFrame; +} + /// Mirror of WKFrameInfo. /// /// See https://developer.apple.com/documentation/webkit/wkframeinfo?language=objc. @@ -619,6 +642,16 @@ abstract class WKNavigationDelegateFlutterApi { WKNavigationActionData navigationAction, ); + @ObjCSelector( + 'decidePolicyForNavigationResponseForDelegateWithIdentifier:webViewIdentifier:navigationResponse:', + ) + @async + WKNavigationResponsePolicyEnum decidePolicyForNavigationResponse( + int identifier, + int webViewIdentifier, + WKNavigationResponseData navigationResponse, + ); + @ObjCSelector( 'didFailNavigationForDelegateWithIdentifier:webViewIdentifier:error:', ) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 18a0cef72e87..bffa7eaf66a5 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.12.0 +version: 3.13.0 environment: sdk: ^3.2.3 @@ -20,7 +20,7 @@ dependencies: flutter: sdk: flutter path: ^1.8.0 - webview_flutter_platform_interface: ^2.9.0 + webview_flutter_platform_interface: ^2.10.0 dev_dependencies: build_runner: ^2.1.5 diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/legacy/web_kit_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/legacy/web_kit_cookie_manager_test.mocks.dart index fe13a87048e5..eb58b7eb11e2 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/legacy/web_kit_cookie_manager_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/legacy/web_kit_cookie_manager_test.mocks.dart @@ -63,7 +63,6 @@ class MockWKHttpCookieStore extends _i1.Mock implements _i2.WKHttpCookieStore { returnValue: _i3.Future.value(), returnValueForMissingStub: _i3.Future.value(), ) as _i3.Future); - @override _i2.WKHttpCookieStore copy() => (super.noSuchMethod( Invocation.method( @@ -78,7 +77,6 @@ class MockWKHttpCookieStore extends _i1.Mock implements _i2.WKHttpCookieStore { ), ), ) as _i2.WKHttpCookieStore); - @override _i3.Future addObserver( _i4.NSObject? observer, { @@ -97,7 +95,6 @@ class MockWKHttpCookieStore extends _i1.Mock implements _i2.WKHttpCookieStore { returnValue: _i3.Future.value(), returnValueForMissingStub: _i3.Future.value(), ) as _i3.Future); - @override _i3.Future removeObserver( _i4.NSObject? observer, { @@ -132,7 +129,6 @@ class MockWKWebsiteDataStore extends _i1.Mock Invocation.getter(#httpCookieStore), ), ) as _i2.WKHttpCookieStore); - @override _i3.Future removeDataOfTypes( Set<_i2.WKWebsiteDataType>? dataTypes, @@ -148,7 +144,6 @@ class MockWKWebsiteDataStore extends _i1.Mock ), returnValue: _i3.Future.value(false), ) as _i3.Future); - @override _i2.WKWebsiteDataStore copy() => (super.noSuchMethod( Invocation.method( @@ -163,7 +158,6 @@ class MockWKWebsiteDataStore extends _i1.Mock ), ), ) as _i2.WKWebsiteDataStore); - @override _i3.Future addObserver( _i4.NSObject? observer, { @@ -182,7 +176,6 @@ class MockWKWebsiteDataStore extends _i1.Mock returnValue: _i3.Future.value(), returnValueForMissingStub: _i3.Future.value(), ) as _i3.Future); - @override _i3.Future removeObserver( _i4.NSObject? observer, { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/legacy/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/legacy/web_kit_webview_widget_test.mocks.dart index 411002e479ee..a33d5ac58302 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/legacy/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/legacy/web_kit_webview_widget_test.mocks.dart @@ -174,7 +174,6 @@ class MockUIScrollView extends _i1.Mock implements _i3.UIScrollView { ), )), ) as _i5.Future<_i2.Point>); - @override _i5.Future scrollBy(_i2.Point? offset) => (super.noSuchMethod( Invocation.method( @@ -184,7 +183,6 @@ class MockUIScrollView extends _i1.Mock implements _i3.UIScrollView { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future setContentOffset(_i2.Point? offset) => (super.noSuchMethod( @@ -195,7 +193,6 @@ class MockUIScrollView extends _i1.Mock implements _i3.UIScrollView { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future setDelegate(_i3.UIScrollViewDelegate? delegate) => (super.noSuchMethod( @@ -206,7 +203,6 @@ class MockUIScrollView extends _i1.Mock implements _i3.UIScrollView { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i3.UIScrollView copy() => (super.noSuchMethod( Invocation.method( @@ -221,7 +217,6 @@ class MockUIScrollView extends _i1.Mock implements _i3.UIScrollView { ), ), ) as _i3.UIScrollView); - @override _i5.Future setBackgroundColor(_i6.Color? color) => (super.noSuchMethod( Invocation.method( @@ -231,7 +226,6 @@ class MockUIScrollView extends _i1.Mock implements _i3.UIScrollView { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future setOpaque(bool? opaque) => (super.noSuchMethod( Invocation.method( @@ -241,7 +235,6 @@ class MockUIScrollView extends _i1.Mock implements _i3.UIScrollView { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future addObserver( _i7.NSObject? observer, { @@ -260,7 +253,6 @@ class MockUIScrollView extends _i1.Mock implements _i3.UIScrollView { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future removeObserver( _i7.NSObject? observer, { @@ -301,7 +293,6 @@ class MockWKNavigationDelegate extends _i1.Mock ), ), ) as _i4.WKNavigationDelegate); - @override _i5.Future addObserver( _i7.NSObject? observer, { @@ -320,7 +311,6 @@ class MockWKNavigationDelegate extends _i1.Mock returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future removeObserver( _i7.NSObject? observer, { @@ -355,7 +345,6 @@ class MockWKPreferences extends _i1.Mock implements _i4.WKPreferences { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i4.WKPreferences copy() => (super.noSuchMethod( Invocation.method( @@ -370,7 +359,6 @@ class MockWKPreferences extends _i1.Mock implements _i4.WKPreferences { ), ), ) as _i4.WKPreferences); - @override _i5.Future addObserver( _i7.NSObject? observer, { @@ -389,7 +377,6 @@ class MockWKPreferences extends _i1.Mock implements _i4.WKPreferences { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future removeObserver( _i7.NSObject? observer, { @@ -430,7 +417,6 @@ class MockWKScriptMessageHandler extends _i1.Mock _i4.WKUserContentController, _i4.WKScriptMessage, )); - @override _i4.WKScriptMessageHandler copy() => (super.noSuchMethod( Invocation.method( @@ -445,7 +431,6 @@ class MockWKScriptMessageHandler extends _i1.Mock ), ), ) as _i4.WKScriptMessageHandler); - @override _i5.Future addObserver( _i7.NSObject? observer, { @@ -464,7 +449,6 @@ class MockWKScriptMessageHandler extends _i1.Mock returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future removeObserver( _i7.NSObject? observer, { @@ -498,7 +482,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { Invocation.getter(#configuration), ), ) as _i4.WKWebViewConfiguration); - @override _i3.UIScrollView get scrollView => (super.noSuchMethod( Invocation.getter(#scrollView), @@ -507,7 +490,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { Invocation.getter(#scrollView), ), ) as _i3.UIScrollView); - @override _i5.Future setUIDelegate(_i4.WKUIDelegate? delegate) => (super.noSuchMethod( @@ -518,7 +500,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future setNavigationDelegate(_i4.WKNavigationDelegate? delegate) => (super.noSuchMethod( @@ -529,7 +510,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future getUrl() => (super.noSuchMethod( Invocation.method( @@ -538,7 +518,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { ), returnValue: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future getEstimatedProgress() => (super.noSuchMethod( Invocation.method( @@ -547,7 +526,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { ), returnValue: _i5.Future.value(0.0), ) as _i5.Future); - @override _i5.Future loadRequest(_i7.NSUrlRequest? request) => (super.noSuchMethod( @@ -558,7 +536,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future loadHtmlString( String? string, { @@ -573,7 +550,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future loadFileUrl( String? url, { @@ -588,7 +564,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future loadFlutterAsset(String? key) => (super.noSuchMethod( Invocation.method( @@ -598,7 +573,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future canGoBack() => (super.noSuchMethod( Invocation.method( @@ -607,7 +581,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { ), returnValue: _i5.Future.value(false), ) as _i5.Future); - @override _i5.Future canGoForward() => (super.noSuchMethod( Invocation.method( @@ -616,7 +589,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { ), returnValue: _i5.Future.value(false), ) as _i5.Future); - @override _i5.Future goBack() => (super.noSuchMethod( Invocation.method( @@ -626,7 +598,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future goForward() => (super.noSuchMethod( Invocation.method( @@ -636,7 +607,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future reload() => (super.noSuchMethod( Invocation.method( @@ -646,7 +616,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future getTitle() => (super.noSuchMethod( Invocation.method( @@ -655,7 +624,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { ), returnValue: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future setAllowsBackForwardNavigationGestures(bool? allow) => (super.noSuchMethod( @@ -666,7 +634,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future setCustomUserAgent(String? userAgent) => (super.noSuchMethod( Invocation.method( @@ -676,7 +643,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future evaluateJavaScript(String? javaScriptString) => (super.noSuchMethod( @@ -686,7 +652,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { ), returnValue: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future setInspectable(bool? inspectable) => (super.noSuchMethod( Invocation.method( @@ -696,7 +661,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future getCustomUserAgent() => (super.noSuchMethod( Invocation.method( @@ -705,7 +669,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { ), returnValue: _i5.Future.value(), ) as _i5.Future); - @override _i4.WKWebView copy() => (super.noSuchMethod( Invocation.method( @@ -720,7 +683,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { ), ), ) as _i4.WKWebView); - @override _i5.Future setBackgroundColor(_i6.Color? color) => (super.noSuchMethod( Invocation.method( @@ -730,7 +692,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future setOpaque(bool? opaque) => (super.noSuchMethod( Invocation.method( @@ -740,7 +701,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future addObserver( _i7.NSObject? observer, { @@ -759,7 +719,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future removeObserver( _i7.NSObject? observer, { @@ -794,7 +753,6 @@ class MockWKWebViewConfiguration extends _i1.Mock Invocation.getter(#userContentController), ), ) as _i4.WKUserContentController); - @override _i4.WKPreferences get preferences => (super.noSuchMethod( Invocation.getter(#preferences), @@ -803,7 +761,6 @@ class MockWKWebViewConfiguration extends _i1.Mock Invocation.getter(#preferences), ), ) as _i4.WKPreferences); - @override _i4.WKWebsiteDataStore get websiteDataStore => (super.noSuchMethod( Invocation.getter(#websiteDataStore), @@ -812,7 +769,6 @@ class MockWKWebViewConfiguration extends _i1.Mock Invocation.getter(#websiteDataStore), ), ) as _i4.WKWebsiteDataStore); - @override _i5.Future setAllowsInlineMediaPlayback(bool? allow) => (super.noSuchMethod( @@ -823,7 +779,6 @@ class MockWKWebViewConfiguration extends _i1.Mock returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future setLimitsNavigationsToAppBoundDomains(bool? limit) => (super.noSuchMethod( @@ -834,7 +789,6 @@ class MockWKWebViewConfiguration extends _i1.Mock returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future setMediaTypesRequiringUserActionForPlayback( Set<_i4.WKAudiovisualMediaType>? types) => @@ -846,7 +800,6 @@ class MockWKWebViewConfiguration extends _i1.Mock returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i4.WKWebViewConfiguration copy() => (super.noSuchMethod( Invocation.method( @@ -861,7 +814,6 @@ class MockWKWebViewConfiguration extends _i1.Mock ), ), ) as _i4.WKWebViewConfiguration); - @override _i5.Future addObserver( _i7.NSObject? observer, { @@ -880,7 +832,6 @@ class MockWKWebViewConfiguration extends _i1.Mock returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future removeObserver( _i7.NSObject? observer, { @@ -915,7 +866,6 @@ class MockWKWebsiteDataStore extends _i1.Mock Invocation.getter(#httpCookieStore), ), ) as _i4.WKHttpCookieStore); - @override _i5.Future removeDataOfTypes( Set<_i4.WKWebsiteDataType>? dataTypes, @@ -931,7 +881,6 @@ class MockWKWebsiteDataStore extends _i1.Mock ), returnValue: _i5.Future.value(false), ) as _i5.Future); - @override _i4.WKWebsiteDataStore copy() => (super.noSuchMethod( Invocation.method( @@ -946,7 +895,6 @@ class MockWKWebsiteDataStore extends _i1.Mock ), ), ) as _i4.WKWebsiteDataStore); - @override _i5.Future addObserver( _i7.NSObject? observer, { @@ -965,7 +913,6 @@ class MockWKWebsiteDataStore extends _i1.Mock returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future removeObserver( _i7.NSObject? observer, { @@ -1005,7 +952,6 @@ class MockWKUIDelegate extends _i1.Mock implements _i4.WKUIDelegate { ), ), ) as _i4.WKUIDelegate); - @override _i5.Future addObserver( _i7.NSObject? observer, { @@ -1024,7 +970,6 @@ class MockWKUIDelegate extends _i1.Mock implements _i4.WKUIDelegate { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future removeObserver( _i7.NSObject? observer, { @@ -1067,7 +1012,6 @@ class MockWKUserContentController extends _i1.Mock returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future removeScriptMessageHandler(String? name) => (super.noSuchMethod( @@ -1078,7 +1022,6 @@ class MockWKUserContentController extends _i1.Mock returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future removeAllScriptMessageHandlers() => (super.noSuchMethod( Invocation.method( @@ -1088,7 +1031,6 @@ class MockWKUserContentController extends _i1.Mock returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future addUserScript(_i4.WKUserScript? userScript) => (super.noSuchMethod( @@ -1099,7 +1041,6 @@ class MockWKUserContentController extends _i1.Mock returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future removeAllUserScripts() => (super.noSuchMethod( Invocation.method( @@ -1109,7 +1050,6 @@ class MockWKUserContentController extends _i1.Mock returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i4.WKUserContentController copy() => (super.noSuchMethod( Invocation.method( @@ -1124,7 +1064,6 @@ class MockWKUserContentController extends _i1.Mock ), ), ) as _i4.WKUserContentController); - @override _i5.Future addObserver( _i7.NSObject? observer, { @@ -1143,7 +1082,6 @@ class MockWKUserContentController extends _i1.Mock returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); - @override _i5.Future removeObserver( _i7.NSObject? observer, { @@ -1174,7 +1112,6 @@ class MockJavascriptChannelRegistry extends _i1.Mock Invocation.getter(#channels), returnValue: {}, ) as Map); - @override void onJavascriptChannelMessage( String? channel, @@ -1190,7 +1127,6 @@ class MockJavascriptChannelRegistry extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void updateJavascriptChannelsFromSet(Set<_i9.JavascriptChannel>? channels) => super.noSuchMethod( @@ -1227,7 +1163,6 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock ), returnValue: _i5.Future.value(false), ) as _i5.FutureOr); - @override void onPageStarted(String? url) => super.noSuchMethod( Invocation.method( @@ -1236,7 +1171,6 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void onPageFinished(String? url) => super.noSuchMethod( Invocation.method( @@ -1245,7 +1179,6 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void onProgress(int? progress) => super.noSuchMethod( Invocation.method( @@ -1254,7 +1187,6 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void onWebResourceError(_i10.WebResourceError? error) => super.noSuchMethod( Invocation.method( @@ -1298,7 +1230,6 @@ class MockWebViewWidgetProxy extends _i1.Mock ), ), ) as _i4.WKWebView); - @override _i4.WKScriptMessageHandler createScriptMessageHandler( {required void Function( @@ -1320,7 +1251,6 @@ class MockWebViewWidgetProxy extends _i1.Mock ), ), ) as _i4.WKScriptMessageHandler); - @override _i4.WKUIDelegate createUIDelgate( {void Function( @@ -1343,7 +1273,6 @@ class MockWebViewWidgetProxy extends _i1.Mock ), ), ) as _i4.WKUIDelegate); - @override _i4.WKNavigationDelegate createNavigationDelegate({ void Function( diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.g.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.g.dart index 82f754af224e..982b50810e6e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.g.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.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 (v13.1.2), do not edit directly. +// Autogenerated from Pigeon (v13.0.0), 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 @@ -1245,51 +1245,57 @@ class _TestWKWebViewHostApiCodec extends StandardMessageCodec { } else if (value is NSHttpCookiePropertyKeyEnumData) { buffer.putUint8(131); writeValue(buffer, value.encode()); - } else if (value is NSKeyValueChangeKeyEnumData) { + } else if (value is NSHttpUrlResponseData) { buffer.putUint8(132); writeValue(buffer, value.encode()); - } else if (value is NSKeyValueObservingOptionsEnumData) { + } else if (value is NSKeyValueChangeKeyEnumData) { buffer.putUint8(133); writeValue(buffer, value.encode()); - } else if (value is NSUrlRequestData) { + } else if (value is NSKeyValueObservingOptionsEnumData) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is ObjectOrIdentifier) { + } else if (value is NSUrlRequestData) { buffer.putUint8(135); writeValue(buffer, value.encode()); - } else if (value is WKAudiovisualMediaTypeEnumData) { + } else if (value is ObjectOrIdentifier) { buffer.putUint8(136); writeValue(buffer, value.encode()); - } else if (value is WKFrameInfoData) { + } else if (value is WKAudiovisualMediaTypeEnumData) { buffer.putUint8(137); writeValue(buffer, value.encode()); - } else if (value is WKMediaCaptureTypeData) { + } else if (value is WKFrameInfoData) { buffer.putUint8(138); writeValue(buffer, value.encode()); - } else if (value is WKNavigationActionData) { + } else if (value is WKMediaCaptureTypeData) { buffer.putUint8(139); writeValue(buffer, value.encode()); - } else if (value is WKNavigationActionPolicyEnumData) { + } else if (value is WKNavigationActionData) { buffer.putUint8(140); writeValue(buffer, value.encode()); - } else if (value is WKPermissionDecisionData) { + } else if (value is WKNavigationActionPolicyEnumData) { buffer.putUint8(141); writeValue(buffer, value.encode()); - } else if (value is WKScriptMessageData) { + } else if (value is WKNavigationResponseData) { buffer.putUint8(142); writeValue(buffer, value.encode()); - } else if (value is WKSecurityOriginData) { + } else if (value is WKPermissionDecisionData) { buffer.putUint8(143); writeValue(buffer, value.encode()); - } else if (value is WKUserScriptData) { + } else if (value is WKScriptMessageData) { buffer.putUint8(144); writeValue(buffer, value.encode()); - } else if (value is WKUserScriptInjectionTimeEnumData) { + } else if (value is WKSecurityOriginData) { buffer.putUint8(145); writeValue(buffer, value.encode()); - } else if (value is WKWebsiteDataTypeEnumData) { + } else if (value is WKUserScriptData) { buffer.putUint8(146); writeValue(buffer, value.encode()); + } else if (value is WKUserScriptInjectionTimeEnumData) { + buffer.putUint8(147); + writeValue(buffer, value.encode()); + } else if (value is WKWebsiteDataTypeEnumData) { + buffer.putUint8(148); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -1307,34 +1313,38 @@ class _TestWKWebViewHostApiCodec extends StandardMessageCodec { case 131: return NSHttpCookiePropertyKeyEnumData.decode(readValue(buffer)!); case 132: - return NSKeyValueChangeKeyEnumData.decode(readValue(buffer)!); + return NSHttpUrlResponseData.decode(readValue(buffer)!); case 133: - return NSKeyValueObservingOptionsEnumData.decode(readValue(buffer)!); + return NSKeyValueChangeKeyEnumData.decode(readValue(buffer)!); case 134: - return NSUrlRequestData.decode(readValue(buffer)!); + return NSKeyValueObservingOptionsEnumData.decode(readValue(buffer)!); case 135: - return ObjectOrIdentifier.decode(readValue(buffer)!); + return NSUrlRequestData.decode(readValue(buffer)!); case 136: - return WKAudiovisualMediaTypeEnumData.decode(readValue(buffer)!); + return ObjectOrIdentifier.decode(readValue(buffer)!); case 137: - return WKFrameInfoData.decode(readValue(buffer)!); + return WKAudiovisualMediaTypeEnumData.decode(readValue(buffer)!); case 138: - return WKMediaCaptureTypeData.decode(readValue(buffer)!); + return WKFrameInfoData.decode(readValue(buffer)!); case 139: - return WKNavigationActionData.decode(readValue(buffer)!); + return WKMediaCaptureTypeData.decode(readValue(buffer)!); case 140: - return WKNavigationActionPolicyEnumData.decode(readValue(buffer)!); + return WKNavigationActionData.decode(readValue(buffer)!); case 141: - return WKPermissionDecisionData.decode(readValue(buffer)!); + return WKNavigationActionPolicyEnumData.decode(readValue(buffer)!); case 142: - return WKScriptMessageData.decode(readValue(buffer)!); + return WKNavigationResponseData.decode(readValue(buffer)!); case 143: - return WKSecurityOriginData.decode(readValue(buffer)!); + return WKPermissionDecisionData.decode(readValue(buffer)!); case 144: - return WKUserScriptData.decode(readValue(buffer)!); + return WKScriptMessageData.decode(readValue(buffer)!); case 145: - return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!); + return WKSecurityOriginData.decode(readValue(buffer)!); case 146: + return WKUserScriptData.decode(readValue(buffer)!); + case 147: + return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!); + case 148: return WKWebsiteDataTypeEnumData.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart index df4e12e6082f..a34a190ac497 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart @@ -38,7 +38,6 @@ class MockTestNSObjectHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void addObserver( int? identifier, @@ -58,7 +57,6 @@ class MockTestNSObjectHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void removeObserver( int? identifier, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart index 66bbc7275e69..54e91efb6a66 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart @@ -40,7 +40,6 @@ class MockTestWKWebViewConfigurationHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void createFromWebView( int? identifier, @@ -56,7 +55,6 @@ class MockTestWKWebViewConfigurationHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void setAllowsInlineMediaPlayback( int? identifier, @@ -72,7 +70,6 @@ class MockTestWKWebViewConfigurationHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void setLimitsNavigationsToAppBoundDomains( int? identifier, @@ -88,7 +85,6 @@ class MockTestWKWebViewConfigurationHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void setMediaTypesRequiringUserActionForPlayback( int? identifier, @@ -130,7 +126,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void setUIDelegate( int? identifier, @@ -146,7 +141,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void setNavigationDelegate( int? identifier, @@ -162,13 +156,11 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override String? getUrl(int? identifier) => (super.noSuchMethod(Invocation.method( #getUrl, [identifier], )) as String?); - @override double getEstimatedProgress(int? identifier) => (super.noSuchMethod( Invocation.method( @@ -177,7 +169,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValue: 0.0, ) as double); - @override void loadRequest( int? identifier, @@ -193,7 +184,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void loadHtmlString( int? identifier, @@ -211,7 +201,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void loadFileUrl( int? identifier, @@ -229,7 +218,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void loadFlutterAsset( int? identifier, @@ -245,7 +233,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override bool canGoBack(int? identifier) => (super.noSuchMethod( Invocation.method( @@ -254,7 +241,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValue: false, ) as bool); - @override bool canGoForward(int? identifier) => (super.noSuchMethod( Invocation.method( @@ -263,7 +249,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValue: false, ) as bool); - @override void goBack(int? identifier) => super.noSuchMethod( Invocation.method( @@ -272,7 +257,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void goForward(int? identifier) => super.noSuchMethod( Invocation.method( @@ -281,7 +265,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void reload(int? identifier) => super.noSuchMethod( Invocation.method( @@ -290,13 +273,11 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override String? getTitle(int? identifier) => (super.noSuchMethod(Invocation.method( #getTitle, [identifier], )) as String?); - @override void setAllowsBackForwardNavigationGestures( int? identifier, @@ -312,7 +293,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void setCustomUserAgent( int? identifier, @@ -328,7 +308,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override _i4.Future evaluateJavaScript( int? identifier, @@ -344,7 +323,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValue: _i4.Future.value(), ) as _i4.Future); - @override void setInspectable( int? identifier, @@ -360,7 +338,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override String? getCustomUserAgent(int? identifier) => (super.noSuchMethod(Invocation.method( @@ -393,7 +370,6 @@ class MockTestUIScrollViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override List getContentOffset(int? identifier) => (super.noSuchMethod( Invocation.method( @@ -402,7 +378,6 @@ class MockTestUIScrollViewHostApi extends _i1.Mock ), returnValue: [], ) as List); - @override void scrollBy( int? identifier, @@ -420,7 +395,6 @@ class MockTestUIScrollViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void setContentOffset( int? identifier, @@ -438,7 +412,6 @@ class MockTestUIScrollViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void setDelegate( int? identifier, @@ -498,7 +471,6 @@ class MockTestUIViewHostApi extends _i1.Mock implements _i2.TestUIViewHostApi { ), returnValueForMissingStub: null, ); - @override void setOpaque( int? identifier, 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 e0eeb941cce9..d838bf996146 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 @@ -598,6 +598,34 @@ void main() { expect(policyData.value, WKNavigationActionPolicyEnum.cancel); }); + test('decidePolicyForNavigationResponse', () async { + WebKitFlutterApis.instance = WebKitFlutterApis( + instanceManager: instanceManager, + ); + + navigationDelegate = WKNavigationDelegate( + instanceManager: instanceManager, + decidePolicyForNavigationResponse: ( + WKWebView webView, + WKNavigationResponse navigationAction, + ) async { + return WKNavigationResponsePolicy.cancel; + }, + ); + + final WKNavigationResponsePolicyEnum policy = await WebKitFlutterApis + .instance.navigationDelegate + .decidePolicyForNavigationResponse( + instanceManager.getIdentifier(navigationDelegate)!, + instanceManager.getIdentifier(webView)!, + WKNavigationResponseData( + response: NSHttpUrlResponseData(statusCode: 401), + forMainFrame: true), + ); + + expect(policy, WKNavigationResponsePolicyEnum.cancel); + }); + test('didFailNavigation', () async { final Completer> argsCompleter = Completer>(); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart index d9a0ed01d50d..99aedf1659f7 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart @@ -47,7 +47,6 @@ class MockTestWKHttpCookieStoreHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override _i3.Future setCookie( int? identifier, @@ -109,7 +108,6 @@ class MockTestWKPreferencesHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void setJavaScriptEnabled( int? identifier, @@ -189,7 +187,6 @@ class MockTestWKUserContentControllerHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void addScriptMessageHandler( int? identifier, @@ -207,7 +204,6 @@ class MockTestWKUserContentControllerHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void removeScriptMessageHandler( int? identifier, @@ -223,7 +219,6 @@ class MockTestWKUserContentControllerHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void removeAllScriptMessageHandlers(int? identifier) => super.noSuchMethod( Invocation.method( @@ -232,7 +227,6 @@ class MockTestWKUserContentControllerHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void addUserScript( int? identifier, @@ -248,7 +242,6 @@ class MockTestWKUserContentControllerHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void removeAllUserScripts(int? identifier) => super.noSuchMethod( Invocation.method( @@ -276,7 +269,6 @@ class MockTestWKWebViewConfigurationHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void createFromWebView( int? identifier, @@ -292,7 +284,6 @@ class MockTestWKWebViewConfigurationHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void setAllowsInlineMediaPlayback( int? identifier, @@ -308,7 +299,6 @@ class MockTestWKWebViewConfigurationHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void setLimitsNavigationsToAppBoundDomains( int? identifier, @@ -324,7 +314,6 @@ class MockTestWKWebViewConfigurationHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void setMediaTypesRequiringUserActionForPlayback( int? identifier, @@ -366,7 +355,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void setUIDelegate( int? identifier, @@ -382,7 +370,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void setNavigationDelegate( int? identifier, @@ -398,13 +385,11 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override String? getUrl(int? identifier) => (super.noSuchMethod(Invocation.method( #getUrl, [identifier], )) as String?); - @override double getEstimatedProgress(int? identifier) => (super.noSuchMethod( Invocation.method( @@ -413,7 +398,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValue: 0.0, ) as double); - @override void loadRequest( int? identifier, @@ -429,7 +413,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void loadHtmlString( int? identifier, @@ -447,7 +430,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void loadFileUrl( int? identifier, @@ -465,7 +447,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void loadFlutterAsset( int? identifier, @@ -481,7 +462,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override bool canGoBack(int? identifier) => (super.noSuchMethod( Invocation.method( @@ -490,7 +470,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValue: false, ) as bool); - @override bool canGoForward(int? identifier) => (super.noSuchMethod( Invocation.method( @@ -499,7 +478,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValue: false, ) as bool); - @override void goBack(int? identifier) => super.noSuchMethod( Invocation.method( @@ -508,7 +486,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void goForward(int? identifier) => super.noSuchMethod( Invocation.method( @@ -517,7 +494,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void reload(int? identifier) => super.noSuchMethod( Invocation.method( @@ -526,13 +502,11 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override String? getTitle(int? identifier) => (super.noSuchMethod(Invocation.method( #getTitle, [identifier], )) as String?); - @override void setAllowsBackForwardNavigationGestures( int? identifier, @@ -548,7 +522,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void setCustomUserAgent( int? identifier, @@ -564,7 +537,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override _i3.Future evaluateJavaScript( int? identifier, @@ -580,7 +552,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValue: _i3.Future.value(), ) as _i3.Future); - @override void setInspectable( int? identifier, @@ -596,7 +567,6 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override String? getCustomUserAgent(int? identifier) => (super.noSuchMethod(Invocation.method( @@ -629,7 +599,6 @@ class MockTestWKWebsiteDataStoreHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override void createDefaultDataStore(int? identifier) => super.noSuchMethod( Invocation.method( @@ -638,7 +607,6 @@ class MockTestWKWebsiteDataStoreHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); - @override _i3.Future removeDataOfTypes( int? identifier, 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 171e4056feed..da8675886f7e 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 @@ -71,6 +71,60 @@ void main() { expect(callbackUrl, 'https://www.google.com'); }); + test('setOnHttpError from decidePolicyForNavigationResponse', () { + final WebKitNavigationDelegate webKitDelegate = WebKitNavigationDelegate( + const WebKitNavigationDelegateCreationParams( + webKitProxy: WebKitProxy( + createNavigationDelegate: CapturingNavigationDelegate.new, + createUIDelegate: CapturingUIDelegate.new, + ), + ), + ); + + late final HttpResponseError callbackError; + void onHttpError(HttpResponseError error) { + callbackError = error; + } + + webKitDelegate.setOnHttpError(onHttpError); + + CapturingNavigationDelegate + .lastCreatedDelegate.decidePolicyForNavigationResponse!( + WKWebView.detached(), + const WKNavigationResponse( + response: NSHttpUrlResponse(statusCode: 401), forMainFrame: true), + ); + + expect(callbackError.response?.statusCode, 401); + }); + + test('setOnHttpError is not called for error codes < 400', () { + final WebKitNavigationDelegate webKitDelegate = WebKitNavigationDelegate( + const WebKitNavigationDelegateCreationParams( + webKitProxy: WebKitProxy( + createNavigationDelegate: CapturingNavigationDelegate.new, + createUIDelegate: CapturingUIDelegate.new, + ), + ), + ); + + HttpResponseError? callbackError; + void onHttpError(HttpResponseError error) { + callbackError = error; + } + + webKitDelegate.setOnHttpError(onHttpError); + + CapturingNavigationDelegate + .lastCreatedDelegate.decidePolicyForNavigationResponse!( + WKWebView.detached(), + const WKNavigationResponse( + response: NSHttpUrlResponse(statusCode: 399), forMainFrame: true), + ); + + expect(callbackError, isNull); + }); + test('onWebResourceError from didFailNavigation', () { final WebKitNavigationDelegate webKitDelegate = WebKitNavigationDelegate( const WebKitNavigationDelegateCreationParams( @@ -263,6 +317,7 @@ class CapturingNavigationDelegate extends WKNavigationDelegate { CapturingNavigationDelegate({ super.didFinishNavigation, super.didStartProvisionalNavigation, + super.decidePolicyForNavigationResponse, super.didFailNavigation, super.didFailProvisionalNavigation, super.decidePolicyForNavigationAction, 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 a23eef8d8fbd..a28eca241ada 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 @@ -1538,6 +1538,7 @@ class CapturingNavigationDelegate extends WKNavigationDelegate { super.didFailNavigation, super.didFailProvisionalNavigation, super.decidePolicyForNavigationAction, + super.decidePolicyForNavigationResponse, super.webViewWebContentProcessDidTerminate, super.didReceiveAuthenticationChallenge, }) : super.detached() { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_controller_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_controller_test.mocks.dart index cc7b6932f66f..932c567fb1e5 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_controller_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_controller_test.mocks.dart @@ -159,7 +159,6 @@ class MockNSUrl extends _i1.Mock implements _i2.NSUrl { ), returnValue: _i6.Future.value(), ) as _i6.Future); - @override _i2.NSObject copy() => (super.noSuchMethod( Invocation.method( @@ -174,7 +173,6 @@ class MockNSUrl extends _i1.Mock implements _i2.NSUrl { ), ), ) as _i2.NSObject); - @override _i6.Future addObserver( _i2.NSObject? observer, { @@ -193,7 +191,6 @@ class MockNSUrl extends _i1.Mock implements _i2.NSUrl { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future removeObserver( _i2.NSObject? observer, { @@ -233,7 +230,6 @@ class MockUIScrollView extends _i1.Mock implements _i4.UIScrollView { ), )), ) as _i6.Future<_i3.Point>); - @override _i6.Future scrollBy(_i3.Point? offset) => (super.noSuchMethod( Invocation.method( @@ -243,7 +239,6 @@ class MockUIScrollView extends _i1.Mock implements _i4.UIScrollView { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future setContentOffset(_i3.Point? offset) => (super.noSuchMethod( @@ -254,7 +249,6 @@ class MockUIScrollView extends _i1.Mock implements _i4.UIScrollView { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future setDelegate(_i4.UIScrollViewDelegate? delegate) => (super.noSuchMethod( @@ -265,7 +259,6 @@ class MockUIScrollView extends _i1.Mock implements _i4.UIScrollView { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i4.UIScrollView copy() => (super.noSuchMethod( Invocation.method( @@ -280,7 +273,6 @@ class MockUIScrollView extends _i1.Mock implements _i4.UIScrollView { ), ), ) as _i4.UIScrollView); - @override _i6.Future setBackgroundColor(_i7.Color? color) => (super.noSuchMethod( Invocation.method( @@ -290,7 +282,6 @@ class MockUIScrollView extends _i1.Mock implements _i4.UIScrollView { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future setOpaque(bool? opaque) => (super.noSuchMethod( Invocation.method( @@ -300,7 +291,6 @@ class MockUIScrollView extends _i1.Mock implements _i4.UIScrollView { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future addObserver( _i2.NSObject? observer, { @@ -319,7 +309,6 @@ class MockUIScrollView extends _i1.Mock implements _i4.UIScrollView { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future removeObserver( _i2.NSObject? observer, { @@ -360,7 +349,6 @@ class MockUIScrollViewDelegate extends _i1.Mock ), ), ) as _i4.UIScrollViewDelegate); - @override _i6.Future addObserver( _i2.NSObject? observer, { @@ -379,7 +367,6 @@ class MockUIScrollViewDelegate extends _i1.Mock returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future removeObserver( _i2.NSObject? observer, { @@ -414,7 +401,6 @@ class MockWKPreferences extends _i1.Mock implements _i5.WKPreferences { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i5.WKPreferences copy() => (super.noSuchMethod( Invocation.method( @@ -429,7 +415,6 @@ class MockWKPreferences extends _i1.Mock implements _i5.WKPreferences { ), ), ) as _i5.WKPreferences); - @override _i6.Future addObserver( _i2.NSObject? observer, { @@ -448,7 +433,6 @@ class MockWKPreferences extends _i1.Mock implements _i5.WKPreferences { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future removeObserver( _i2.NSObject? observer, { @@ -491,7 +475,6 @@ class MockWKUserContentController extends _i1.Mock returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future removeScriptMessageHandler(String? name) => (super.noSuchMethod( @@ -502,7 +485,6 @@ class MockWKUserContentController extends _i1.Mock returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future removeAllScriptMessageHandlers() => (super.noSuchMethod( Invocation.method( @@ -512,7 +494,6 @@ class MockWKUserContentController extends _i1.Mock returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future addUserScript(_i5.WKUserScript? userScript) => (super.noSuchMethod( @@ -523,7 +504,6 @@ class MockWKUserContentController extends _i1.Mock returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future removeAllUserScripts() => (super.noSuchMethod( Invocation.method( @@ -533,7 +513,6 @@ class MockWKUserContentController extends _i1.Mock returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i5.WKUserContentController copy() => (super.noSuchMethod( Invocation.method( @@ -548,7 +527,6 @@ class MockWKUserContentController extends _i1.Mock ), ), ) as _i5.WKUserContentController); - @override _i6.Future addObserver( _i2.NSObject? observer, { @@ -567,7 +545,6 @@ class MockWKUserContentController extends _i1.Mock returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future removeObserver( _i2.NSObject? observer, { @@ -602,7 +579,6 @@ class MockWKWebsiteDataStore extends _i1.Mock Invocation.getter(#httpCookieStore), ), ) as _i5.WKHttpCookieStore); - @override _i6.Future removeDataOfTypes( Set<_i5.WKWebsiteDataType>? dataTypes, @@ -618,7 +594,6 @@ class MockWKWebsiteDataStore extends _i1.Mock ), returnValue: _i6.Future.value(false), ) as _i6.Future); - @override _i5.WKWebsiteDataStore copy() => (super.noSuchMethod( Invocation.method( @@ -633,7 +608,6 @@ class MockWKWebsiteDataStore extends _i1.Mock ), ), ) as _i5.WKWebsiteDataStore); - @override _i6.Future addObserver( _i2.NSObject? observer, { @@ -652,7 +626,6 @@ class MockWKWebsiteDataStore extends _i1.Mock returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future removeObserver( _i2.NSObject? observer, { @@ -686,7 +659,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { Invocation.getter(#configuration), ), ) as _i5.WKWebViewConfiguration); - @override _i4.UIScrollView get scrollView => (super.noSuchMethod( Invocation.getter(#scrollView), @@ -695,7 +667,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { Invocation.getter(#scrollView), ), ) as _i4.UIScrollView); - @override _i6.Future setUIDelegate(_i5.WKUIDelegate? delegate) => (super.noSuchMethod( @@ -706,7 +677,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future setNavigationDelegate(_i5.WKNavigationDelegate? delegate) => (super.noSuchMethod( @@ -717,7 +687,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future getUrl() => (super.noSuchMethod( Invocation.method( @@ -726,7 +695,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { ), returnValue: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future getEstimatedProgress() => (super.noSuchMethod( Invocation.method( @@ -735,7 +703,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { ), returnValue: _i6.Future.value(0.0), ) as _i6.Future); - @override _i6.Future loadRequest(_i2.NSUrlRequest? request) => (super.noSuchMethod( @@ -746,7 +713,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future loadHtmlString( String? string, { @@ -761,7 +727,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future loadFileUrl( String? url, { @@ -776,7 +741,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future loadFlutterAsset(String? key) => (super.noSuchMethod( Invocation.method( @@ -786,7 +750,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future canGoBack() => (super.noSuchMethod( Invocation.method( @@ -795,7 +758,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { ), returnValue: _i6.Future.value(false), ) as _i6.Future); - @override _i6.Future canGoForward() => (super.noSuchMethod( Invocation.method( @@ -804,7 +766,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { ), returnValue: _i6.Future.value(false), ) as _i6.Future); - @override _i6.Future goBack() => (super.noSuchMethod( Invocation.method( @@ -814,7 +775,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future goForward() => (super.noSuchMethod( Invocation.method( @@ -824,7 +784,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future reload() => (super.noSuchMethod( Invocation.method( @@ -834,7 +793,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future getTitle() => (super.noSuchMethod( Invocation.method( @@ -843,7 +801,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { ), returnValue: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future setAllowsBackForwardNavigationGestures(bool? allow) => (super.noSuchMethod( @@ -854,7 +811,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future setCustomUserAgent(String? userAgent) => (super.noSuchMethod( Invocation.method( @@ -864,7 +820,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future evaluateJavaScript(String? javaScriptString) => (super.noSuchMethod( @@ -874,7 +829,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { ), returnValue: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future setInspectable(bool? inspectable) => (super.noSuchMethod( Invocation.method( @@ -884,7 +838,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future getCustomUserAgent() => (super.noSuchMethod( Invocation.method( @@ -893,7 +846,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { ), returnValue: _i6.Future.value(), ) as _i6.Future); - @override _i5.WKWebView copy() => (super.noSuchMethod( Invocation.method( @@ -908,7 +860,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { ), ), ) as _i5.WKWebView); - @override _i6.Future setBackgroundColor(_i7.Color? color) => (super.noSuchMethod( Invocation.method( @@ -918,7 +869,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future setOpaque(bool? opaque) => (super.noSuchMethod( Invocation.method( @@ -928,7 +878,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future addObserver( _i2.NSObject? observer, { @@ -947,7 +896,6 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future removeObserver( _i2.NSObject? observer, { @@ -982,7 +930,6 @@ class MockWKWebViewConfiguration extends _i1.Mock Invocation.getter(#userContentController), ), ) as _i5.WKUserContentController); - @override _i5.WKPreferences get preferences => (super.noSuchMethod( Invocation.getter(#preferences), @@ -991,7 +938,6 @@ class MockWKWebViewConfiguration extends _i1.Mock Invocation.getter(#preferences), ), ) as _i5.WKPreferences); - @override _i5.WKWebsiteDataStore get websiteDataStore => (super.noSuchMethod( Invocation.getter(#websiteDataStore), @@ -1000,7 +946,6 @@ class MockWKWebViewConfiguration extends _i1.Mock Invocation.getter(#websiteDataStore), ), ) as _i5.WKWebsiteDataStore); - @override _i6.Future setAllowsInlineMediaPlayback(bool? allow) => (super.noSuchMethod( @@ -1011,7 +956,6 @@ class MockWKWebViewConfiguration extends _i1.Mock returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future setLimitsNavigationsToAppBoundDomains(bool? limit) => (super.noSuchMethod( @@ -1022,7 +966,6 @@ class MockWKWebViewConfiguration extends _i1.Mock returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future setMediaTypesRequiringUserActionForPlayback( Set<_i5.WKAudiovisualMediaType>? types) => @@ -1034,7 +977,6 @@ class MockWKWebViewConfiguration extends _i1.Mock returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i5.WKWebViewConfiguration copy() => (super.noSuchMethod( Invocation.method( @@ -1049,7 +991,6 @@ class MockWKWebViewConfiguration extends _i1.Mock ), ), ) as _i5.WKWebViewConfiguration); - @override _i6.Future addObserver( _i2.NSObject? observer, { @@ -1068,7 +1009,6 @@ class MockWKWebViewConfiguration extends _i1.Mock returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future removeObserver( _i2.NSObject? observer, { @@ -1109,7 +1049,6 @@ class MockWKScriptMessageHandler extends _i1.Mock _i5.WKUserContentController, _i5.WKScriptMessage, )); - @override _i5.WKScriptMessageHandler copy() => (super.noSuchMethod( Invocation.method( @@ -1124,7 +1063,6 @@ class MockWKScriptMessageHandler extends _i1.Mock ), ), ) as _i5.WKScriptMessageHandler); - @override _i6.Future addObserver( _i2.NSObject? observer, { @@ -1143,7 +1081,6 @@ class MockWKScriptMessageHandler extends _i1.Mock returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); - @override _i6.Future removeObserver( _i2.NSObject? observer, { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_cookie_manager_test.mocks.dart index bda15f7cec78..d9d21dab8db5 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_cookie_manager_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_cookie_manager_test.mocks.dart @@ -63,7 +63,6 @@ class MockWKWebsiteDataStore extends _i1.Mock Invocation.getter(#httpCookieStore), ), ) as _i2.WKHttpCookieStore); - @override _i3.Future removeDataOfTypes( Set<_i2.WKWebsiteDataType>? dataTypes, @@ -79,7 +78,6 @@ class MockWKWebsiteDataStore extends _i1.Mock ), returnValue: _i3.Future.value(false), ) as _i3.Future); - @override _i2.WKWebsiteDataStore copy() => (super.noSuchMethod( Invocation.method( @@ -94,7 +92,6 @@ class MockWKWebsiteDataStore extends _i1.Mock ), ), ) as _i2.WKWebsiteDataStore); - @override _i3.Future addObserver( _i4.NSObject? observer, { @@ -113,7 +110,6 @@ class MockWKWebsiteDataStore extends _i1.Mock returnValue: _i3.Future.value(), returnValueForMissingStub: _i3.Future.value(), ) as _i3.Future); - @override _i3.Future removeObserver( _i4.NSObject? observer, { @@ -148,7 +144,6 @@ class MockWKHttpCookieStore extends _i1.Mock implements _i2.WKHttpCookieStore { returnValue: _i3.Future.value(), returnValueForMissingStub: _i3.Future.value(), ) as _i3.Future); - @override _i2.WKHttpCookieStore copy() => (super.noSuchMethod( Invocation.method( @@ -163,7 +158,6 @@ class MockWKHttpCookieStore extends _i1.Mock implements _i2.WKHttpCookieStore { ), ), ) as _i2.WKHttpCookieStore); - @override _i3.Future addObserver( _i4.NSObject? observer, { @@ -182,7 +176,6 @@ class MockWKHttpCookieStore extends _i1.Mock implements _i2.WKHttpCookieStore { returnValue: _i3.Future.value(), returnValueForMissingStub: _i3.Future.value(), ) as _i3.Future); - @override _i3.Future removeObserver( _i4.NSObject? observer, { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_widget_test.mocks.dart index 2aff8e964b60..8c511d9f8025 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_widget_test.mocks.dart @@ -99,7 +99,6 @@ class MockWKUIDelegate extends _i1.Mock implements _i2.WKUIDelegate { ), ), ) as _i2.WKUIDelegate); - @override _i3.Future addObserver( _i4.NSObject? observer, { @@ -118,7 +117,6 @@ class MockWKUIDelegate extends _i1.Mock implements _i2.WKUIDelegate { returnValue: _i3.Future.value(), returnValueForMissingStub: _i3.Future.value(), ) as _i3.Future); - @override _i3.Future removeObserver( _i4.NSObject? observer, { @@ -153,7 +151,6 @@ class MockWKWebViewConfiguration extends _i1.Mock Invocation.getter(#userContentController), ), ) as _i2.WKUserContentController); - @override _i2.WKPreferences get preferences => (super.noSuchMethod( Invocation.getter(#preferences), @@ -162,7 +159,6 @@ class MockWKWebViewConfiguration extends _i1.Mock Invocation.getter(#preferences), ), ) as _i2.WKPreferences); - @override _i2.WKWebsiteDataStore get websiteDataStore => (super.noSuchMethod( Invocation.getter(#websiteDataStore), @@ -171,7 +167,6 @@ class MockWKWebViewConfiguration extends _i1.Mock Invocation.getter(#websiteDataStore), ), ) as _i2.WKWebsiteDataStore); - @override _i3.Future setAllowsInlineMediaPlayback(bool? allow) => (super.noSuchMethod( @@ -182,7 +177,6 @@ class MockWKWebViewConfiguration extends _i1.Mock returnValue: _i3.Future.value(), returnValueForMissingStub: _i3.Future.value(), ) as _i3.Future); - @override _i3.Future setLimitsNavigationsToAppBoundDomains(bool? limit) => (super.noSuchMethod( @@ -193,7 +187,6 @@ class MockWKWebViewConfiguration extends _i1.Mock returnValue: _i3.Future.value(), returnValueForMissingStub: _i3.Future.value(), ) as _i3.Future); - @override _i3.Future setMediaTypesRequiringUserActionForPlayback( Set<_i2.WKAudiovisualMediaType>? types) => @@ -205,7 +198,6 @@ class MockWKWebViewConfiguration extends _i1.Mock returnValue: _i3.Future.value(), returnValueForMissingStub: _i3.Future.value(), ) as _i3.Future); - @override _i2.WKWebViewConfiguration copy() => (super.noSuchMethod( Invocation.method( @@ -220,7 +212,6 @@ class MockWKWebViewConfiguration extends _i1.Mock ), ), ) as _i2.WKWebViewConfiguration); - @override _i3.Future addObserver( _i4.NSObject? observer, { @@ -239,7 +230,6 @@ class MockWKWebViewConfiguration extends _i1.Mock returnValue: _i3.Future.value(), returnValueForMissingStub: _i3.Future.value(), ) as _i3.Future); - @override _i3.Future removeObserver( _i4.NSObject? observer, { From 0895119f1dfb10c25c1fd438f6990094d3a7c35d Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 14 Mar 2024 15:35:23 -0400 Subject: [PATCH 081/126] [pigeon] Fixes double prefixes added to enum names for Objc HostApis and FlutterApis (#6263) In some areas of the Objc generator, the the name created from [_objcTypeForDartType](https://github.com/flutter/packages/blob/main/packages/pigeon/lib/objc_generator.dart#L1353) is being passed to [_enumName](https://github.com/flutter/packages/blob/main/packages/pigeon/lib/objc_generator.dart#L1196) and both of these methods add a prefix to enums. The locations are when they are used in `HostApi` or `FlutterApi` methods, but the name of the generated enum would be correct. e.g. `FLTEnumName` vs `FLTFLTEnumName`. This fixes the locations where this is happening by passing the AST name to `_enumName` instead. --- packages/pigeon/CHANGELOG.md | 4 + packages/pigeon/lib/generator_tools.dart | 2 +- packages/pigeon/lib/objc_generator.dart | 10 +- .../ios/RunnerTests/AllDatatypesTest.m | 20 +- .../ios/RunnerTests/AsyncHandlersTest.m | 24 +- .../example/ios/RunnerTests/ListTest.m | 12 +- .../ios/Classes/AlternateLanguageTestPlugin.h | 2 +- .../ios/Classes/AlternateLanguageTestPlugin.m | 108 +-- .../ios/Classes/CoreTests.gen.h | 185 ++-- .../ios/Classes/CoreTests.gen.m | 801 +++++++++--------- packages/pigeon/pubspec.yaml | 2 +- packages/pigeon/test/objc_generator_test.dart | 174 ++++ packages/pigeon/tool/shared/generation.dart | 1 + 13 files changed, 769 insertions(+), 576 deletions(-) diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md index 1fef8dbb39d1..1c775d236405 100644 --- a/packages/pigeon/CHANGELOG.md +++ b/packages/pigeon/CHANGELOG.md @@ -1,3 +1,7 @@ +## 17.1.3 + +* [objc] Fixes double prefixes added to enum names. + ## 17.1.2 * [swift] Separates message call code generation into separate methods. diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart index 8540fdaec151..47a9b6c76169 100644 --- a/packages/pigeon/lib/generator_tools.dart +++ b/packages/pigeon/lib/generator_tools.dart @@ -13,7 +13,7 @@ import 'ast.dart'; /// The current version of pigeon. /// /// This must match the version in pubspec.yaml. -const String pigeonVersion = '17.1.2'; +const String pigeonVersion = '17.1.3'; /// Read all the content from [stdin] to a String. String readStdin() { diff --git a/packages/pigeon/lib/objc_generator.dart b/packages/pigeon/lib/objc_generator.dart index bc26495e650b..d30f02d14719 100644 --- a/packages/pigeon/lib/objc_generator.dart +++ b/packages/pigeon/lib/objc_generator.dart @@ -359,7 +359,7 @@ class ObjcHeaderGenerator extends StructuredGenerator { String? lastArgType; String? returnType; final String enumReturnType = _enumName( - returnTypeName.baseName, + func.returnType.baseName, suffix: ' *_Nullable', prefix: generatorOptions.prefix, box: true, @@ -765,7 +765,7 @@ static FlutterError *createConnectionError(NSString *channelName) { } else if (arg.type.isEnum) { indent.writeln('NSNumber *${argName}AsNumber = $valueGetter;'); indent.writeln( - '${_enumName(arg.type.baseName, suffix: ' *', prefix: '', box: true)}$argName = ${argName}AsNumber == nil ? nil : [[${_enumName(arg.type.baseName, prefix: generatorOptions.prefix, box: true)} alloc] initWithValue:[${argName}AsNumber integerValue]];'); + '${_enumName(arg.type.baseName, suffix: ' *', prefix: generatorOptions.prefix, box: true)}$argName = ${argName}AsNumber == nil ? nil : [[${_enumName(arg.type.baseName, prefix: generatorOptions.prefix, box: true)} alloc] initWithValue:[${argName}AsNumber integerValue]];'); } else { indent.writeln('${objcArgType.beforeString}$argName = $valueGetter;'); } @@ -799,7 +799,7 @@ static FlutterError *createConnectionError(NSString *channelName) { if (func.returnType.isEnum) { returnTypeString = - '${_enumName(returnType.baseName, suffix: ' *_Nullable', prefix: generatorOptions.prefix, box: true)} enumValue'; + '${_enumName(func.returnType.baseName, suffix: ' *_Nullable', prefix: generatorOptions.prefix, box: true)} enumValue'; } if (func.parameters.isEmpty) { indent.writeScoped( @@ -1132,7 +1132,7 @@ static FlutterError *createConnectionError(NSString *channelName) { indent.writeln('completion(nil);'); } else { if (func.returnType.isEnum) { - final String enumName = _enumName(returnType.baseName, + final String enumName = _enumName(func.returnType.baseName, prefix: languageOptions.prefix, box: true); indent.writeln('NSNumber *outputAsNumber = $nullCheck;'); indent.writeln( @@ -1212,7 +1212,7 @@ String _callbackForType( if (type.isVoid) { return 'void (^)(FlutterError *_Nullable)'; } else if (type.isEnum) { - return 'void (^)(${_enumName(objcType.baseName, suffix: ' *_Nullable', prefix: options.prefix, box: true)}, FlutterError *_Nullable)'; + return 'void (^)(${_enumName(type.baseName, suffix: ' *_Nullable', prefix: options.prefix, box: true)}, FlutterError *_Nullable)'; } else { return 'void (^)(${objcType.beforeString}_Nullable, FlutterError *_Nullable)'; } diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/RunnerTests/AllDatatypesTest.m b/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/RunnerTests/AllDatatypesTest.m index 4ac3d8a6129a..bcc8c4972a7d 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/RunnerTests/AllDatatypesTest.m +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/RunnerTests/AllDatatypesTest.m @@ -17,14 +17,14 @@ @interface AllDatatypesTest : XCTestCase @implementation AllDatatypesTest - (void)testAllNull { - AllNullableTypes *everything = [[AllNullableTypes alloc] init]; + FLTAllNullableTypes *everything = [[FLTAllNullableTypes alloc] init]; EchoBinaryMessenger *binaryMessenger = - [[EchoBinaryMessenger alloc] initWithCodec:FlutterIntegrationCoreApiGetCodec()]; - FlutterIntegrationCoreApi *api = - [[FlutterIntegrationCoreApi alloc] initWithBinaryMessenger:binaryMessenger]; + [[EchoBinaryMessenger alloc] initWithCodec:FLTFlutterIntegrationCoreApiGetCodec()]; + FLTFlutterIntegrationCoreApi *api = + [[FLTFlutterIntegrationCoreApi alloc] initWithBinaryMessenger:binaryMessenger]; XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; [api echoAllNullableTypes:everything - completion:^(AllNullableTypes *_Nonnull result, FlutterError *_Nullable error) { + completion:^(FLTAllNullableTypes *_Nonnull result, FlutterError *_Nullable error) { XCTAssertNil(result.aNullableBool); XCTAssertNil(result.aNullableInt); XCTAssertNil(result.aNullableDouble); @@ -41,7 +41,7 @@ - (void)testAllNull { } - (void)testAllEquals { - AllNullableTypes *everything = [[AllNullableTypes alloc] init]; + FLTAllNullableTypes *everything = [[FLTAllNullableTypes alloc] init]; everything.aNullableBool = @NO; everything.aNullableInt = @(1); everything.aNullableDouble = @(2.0); @@ -58,12 +58,12 @@ - (void)testAllEquals { everything.aNullableMap = @{@"hello" : @(1234)}; everything.nullableMapWithObject = @{@"hello" : @(1234), @"goodbye" : @"world"}; EchoBinaryMessenger *binaryMessenger = - [[EchoBinaryMessenger alloc] initWithCodec:FlutterIntegrationCoreApiGetCodec()]; - FlutterIntegrationCoreApi *api = - [[FlutterIntegrationCoreApi alloc] initWithBinaryMessenger:binaryMessenger]; + [[EchoBinaryMessenger alloc] initWithCodec:FLTFlutterIntegrationCoreApiGetCodec()]; + FLTFlutterIntegrationCoreApi *api = + [[FLTFlutterIntegrationCoreApi alloc] initWithBinaryMessenger:binaryMessenger]; XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; [api echoAllNullableTypes:everything - completion:^(AllNullableTypes *_Nonnull result, FlutterError *_Nullable error) { + completion:^(FLTAllNullableTypes *_Nonnull result, FlutterError *_Nullable error) { XCTAssertEqual(result.aNullableBool, everything.aNullableBool); XCTAssertEqual(result.aNullableInt, everything.aNullableInt); XCTAssertEqual(result.aNullableDouble, everything.aNullableDouble); diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/RunnerTests/AsyncHandlersTest.m b/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/RunnerTests/AsyncHandlersTest.m index 7034618a2396..660911086c38 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/RunnerTests/AsyncHandlersTest.m +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/RunnerTests/AsyncHandlersTest.m @@ -10,7 +10,7 @@ #import "MockBinaryMessenger.h" /////////////////////////////////////////////////////////////////////////////////////////// -@interface MockHostSmallApi : NSObject +@interface MockHostSmallApi : NSObject @property(nonatomic, copy) NSString *output; @property(nonatomic, retain) FlutterError *voidVoidError; @end @@ -42,11 +42,11 @@ @implementation AsyncHandlersTest - (void)testAsyncHost2Flutter { MockBinaryMessenger *binaryMessenger = - [[MockBinaryMessenger alloc] initWithCodec:FlutterIntegrationCoreApiGetCodec()]; + [[MockBinaryMessenger alloc] initWithCodec:FLTFlutterIntegrationCoreApiGetCodec()]; NSString *value = @"Test"; binaryMessenger.result = value; - FlutterIntegrationCoreApi *flutterApi = - [[FlutterIntegrationCoreApi alloc] initWithBinaryMessenger:binaryMessenger]; + FLTFlutterIntegrationCoreApi *flutterApi = + [[FLTFlutterIntegrationCoreApi alloc] initWithBinaryMessenger:binaryMessenger]; XCTestExpectation *expectation = [self expectationWithDescription:@"echo callback"]; [flutterApi echoAsyncString:value completion:^(NSString *_Nonnull output, FlutterError *_Nullable error) { @@ -58,9 +58,9 @@ - (void)testAsyncHost2Flutter { - (void)testAsyncFlutter2HostVoidVoid { MockBinaryMessenger *binaryMessenger = - [[MockBinaryMessenger alloc] initWithCodec:HostSmallApiGetCodec()]; + [[MockBinaryMessenger alloc] initWithCodec:FLTHostSmallApiGetCodec()]; MockHostSmallApi *mockHostSmallApi = [[MockHostSmallApi alloc] init]; - SetUpHostSmallApi(binaryMessenger, mockHostSmallApi); + SetUpFLTHostSmallApi(binaryMessenger, mockHostSmallApi); NSString *channelName = @"dev.flutter.pigeon.pigeon_integration_tests.HostSmallApi.voidVoid"; XCTAssertNotNil(binaryMessenger.handlers[channelName]); @@ -75,12 +75,12 @@ - (void)testAsyncFlutter2HostVoidVoid { - (void)testAsyncFlutter2HostVoidVoidError { MockBinaryMessenger *binaryMessenger = - [[MockBinaryMessenger alloc] initWithCodec:HostSmallApiGetCodec()]; + [[MockBinaryMessenger alloc] initWithCodec:FLTHostSmallApiGetCodec()]; MockHostSmallApi *mockHostSmallApi = [[MockHostSmallApi alloc] init]; mockHostSmallApi.voidVoidError = [FlutterError errorWithCode:@"code" message:@"message" details:nil]; - SetUpHostSmallApi(binaryMessenger, mockHostSmallApi); + SetUpFLTHostSmallApi(binaryMessenger, mockHostSmallApi); NSString *channelName = @"dev.flutter.pigeon.pigeon_integration_tests.HostSmallApi.voidVoid"; XCTAssertNotNil(binaryMessenger.handlers[channelName]); @@ -96,11 +96,11 @@ - (void)testAsyncFlutter2HostVoidVoidError { - (void)testAsyncFlutter2Host { MockBinaryMessenger *binaryMessenger = - [[MockBinaryMessenger alloc] initWithCodec:HostSmallApiGetCodec()]; + [[MockBinaryMessenger alloc] initWithCodec:FLTHostSmallApiGetCodec()]; MockHostSmallApi *mockHostSmallApi = [[MockHostSmallApi alloc] init]; NSString *value = @"Test"; mockHostSmallApi.output = value; - SetUpHostSmallApi(binaryMessenger, mockHostSmallApi); + SetUpFLTHostSmallApi(binaryMessenger, mockHostSmallApi); NSString *channelName = @"dev.flutter.pigeon.pigeon_integration_tests.HostSmallApi.echo"; XCTAssertNotNil(binaryMessenger.handlers[channelName]); @@ -117,9 +117,9 @@ - (void)testAsyncFlutter2Host { - (void)testAsyncFlutter2HostError { MockBinaryMessenger *binaryMessenger = - [[MockBinaryMessenger alloc] initWithCodec:HostSmallApiGetCodec()]; + [[MockBinaryMessenger alloc] initWithCodec:FLTHostSmallApiGetCodec()]; MockHostSmallApi *mockHostSmallApi = [[MockHostSmallApi alloc] init]; - SetUpHostSmallApi(binaryMessenger, mockHostSmallApi); + SetUpFLTHostSmallApi(binaryMessenger, mockHostSmallApi); NSString *channelName = @"dev.flutter.pigeon.pigeon_integration_tests.HostSmallApi.echo"; XCTAssertNotNil(binaryMessenger.handlers[channelName]); diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/RunnerTests/ListTest.m b/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/RunnerTests/ListTest.m index 124de85973b2..54bea7f0e781 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/RunnerTests/ListTest.m +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/example/ios/RunnerTests/ListTest.m @@ -17,18 +17,18 @@ @interface ListTest : XCTestCase @implementation ListTest - (void)testListInList { - TestMessage *top = [[TestMessage alloc] init]; - TestMessage *inside = [[TestMessage alloc] init]; + FLTTestMessage *top = [[FLTTestMessage alloc] init]; + FLTTestMessage *inside = [[FLTTestMessage alloc] init]; inside.testList = @[ @1, @2, @3 ]; top.testList = @[ inside ]; EchoBinaryMessenger *binaryMessenger = - [[EchoBinaryMessenger alloc] initWithCodec:FlutterSmallApiGetCodec()]; - FlutterSmallApi *api = [[FlutterSmallApi alloc] initWithBinaryMessenger:binaryMessenger]; + [[EchoBinaryMessenger alloc] initWithCodec:FLTFlutterSmallApiGetCodec()]; + FLTFlutterSmallApi *api = [[FLTFlutterSmallApi alloc] initWithBinaryMessenger:binaryMessenger]; XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; [api echoWrappedList:top - completion:^(TestMessage *_Nonnull result, FlutterError *_Nullable err) { + completion:^(FLTTestMessage *_Nonnull result, FlutterError *_Nullable err) { XCTAssertEqual(1u, result.testList.count); - XCTAssertTrue([result.testList[0] isKindOfClass:[TestMessage class]]); + XCTAssertTrue([result.testList[0] isKindOfClass:[FLTTestMessage class]]); XCTAssertEqualObjects(inside.testList, [result.testList[0] testList]); [expectation fulfill]; }]; diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.h b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.h index 96ac05a7de1e..afb911d8e00d 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.h +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.h @@ -6,5 +6,5 @@ #import "CoreTests.gen.h" -@interface AlternateLanguageTestPlugin : NSObject +@interface AlternateLanguageTestPlugin : NSObject @end diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.m b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.m index e0feca739952..b10d8d99bcc0 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.m +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.m @@ -7,16 +7,16 @@ #import "CoreTests.gen.h" @interface AlternateLanguageTestPlugin () -@property(nonatomic) FlutterIntegrationCoreApi *flutterAPI; +@property(nonatomic) FLTFlutterIntegrationCoreApi *flutterAPI; @end /// This plugin handles the native side of the integration tests in example/integration_test/. @implementation AlternateLanguageTestPlugin + (void)registerWithRegistrar:(NSObject *)registrar { AlternateLanguageTestPlugin *plugin = [[AlternateLanguageTestPlugin alloc] init]; - SetUpHostIntegrationCoreApi([registrar messenger], plugin); + SetUpFLTHostIntegrationCoreApi([registrar messenger], plugin); plugin.flutterAPI = - [[FlutterIntegrationCoreApi alloc] initWithBinaryMessenger:[registrar messenger]]; + [[FLTFlutterIntegrationCoreApi alloc] initWithBinaryMessenger:[registrar messenger]]; } #pragma mark HostIntegrationCoreApi implementation @@ -24,13 +24,13 @@ + (void)registerWithRegistrar:(NSObject *)registrar { - (void)noopWithError:(FlutterError *_Nullable *_Nonnull)error { } -- (nullable AllTypes *)echoAllTypes:(AllTypes *)everything - error:(FlutterError *_Nullable *_Nonnull)error { +- (nullable FLTAllTypes *)echoAllTypes:(FLTAllTypes *)everything + error:(FlutterError *_Nullable *_Nonnull)error { return everything; } -- (nullable AllNullableTypes *)echoAllNullableTypes:(nullable AllNullableTypes *)everything - error:(FlutterError *_Nullable *_Nonnull)error { +- (nullable FLTAllNullableTypes *)echoAllNullableTypes:(nullable FLTAllNullableTypes *)everything + error:(FlutterError *_Nullable *_Nonnull)error { return everything; } @@ -84,13 +84,14 @@ - (nullable id)echoObject:(id)anObject error:(FlutterError *_Nullable *_Nonnull) return aMap; } -- (nullable AllClassesWrapper *)echoClassWrapper:(AllClassesWrapper *)wrapper - error:(FlutterError *_Nullable *_Nonnull)error { +- (nullable FLTAllClassesWrapper *)echoClassWrapper:(FLTAllClassesWrapper *)wrapper + error:(FlutterError *_Nullable *_Nonnull)error { return wrapper; } -- (AnEnumBox *_Nullable)echoEnum:(AnEnum)anEnum error:(FlutterError *_Nullable *_Nonnull)error { - return [[AnEnumBox alloc] initWithValue:anEnum]; +- (FLTAnEnumBox *_Nullable)echoEnum:(FLTAnEnum)anEnum + error:(FlutterError *_Nullable *_Nonnull)error { + return [[FLTAnEnumBox alloc] initWithValue:anEnum]; } - (nullable NSString *)echoNamedDefaultString:(NSString *)aString @@ -108,25 +109,25 @@ - (nullable NSNumber *)echoRequiredInt:(NSInteger)anInt return @(anInt); } -- (nullable NSString *)extractNestedNullableStringFrom:(AllClassesWrapper *)wrapper +- (nullable NSString *)extractNestedNullableStringFrom:(FLTAllClassesWrapper *)wrapper error:(FlutterError *_Nullable *_Nonnull)error { return wrapper.allNullableTypes.aNullableString; } -- (nullable AllClassesWrapper *) +- (nullable FLTAllClassesWrapper *) createNestedObjectWithNullableString:(nullable NSString *)nullableString error:(FlutterError *_Nullable *_Nonnull)error { - AllNullableTypes *innerObject = [[AllNullableTypes alloc] init]; + FLTAllNullableTypes *innerObject = [[FLTAllNullableTypes alloc] init]; innerObject.aNullableString = nullableString; - return [AllClassesWrapper makeWithAllNullableTypes:innerObject allTypes:nil]; + return [FLTAllClassesWrapper makeWithAllNullableTypes:innerObject allTypes:nil]; } -- (nullable AllNullableTypes *)sendMultipleNullableTypesABool:(nullable NSNumber *)aNullableBool - anInt:(nullable NSNumber *)aNullableInt - aString:(nullable NSString *)aNullableString - error:(FlutterError *_Nullable *_Nonnull) - error { - AllNullableTypes *someTypes = [[AllNullableTypes alloc] init]; +- (nullable FLTAllNullableTypes *) + sendMultipleNullableTypesABool:(nullable NSNumber *)aNullableBool + anInt:(nullable NSNumber *)aNullableInt + aString:(nullable NSString *)aNullableString + error:(FlutterError *_Nullable *_Nonnull)error { + FLTAllNullableTypes *someTypes = [[FLTAllNullableTypes alloc] init]; someTypes.aNullableBool = aNullableBool; someTypes.aNullableInt = aNullableInt; someTypes.aNullableString = aNullableString; @@ -175,8 +176,8 @@ - (nullable id)echoNullableObject:(nullable id)aNullableObject return aNullableMap; } -- (AnEnumBox *_Nullable)echoNullableEnum:(nullable AnEnumBox *)AnEnumBoxed - error:(FlutterError *_Nullable *_Nonnull)error { +- (FLTAnEnumBox *_Nullable)echoNullableEnum:(nullable FLTAnEnumBox *)AnEnumBoxed + error:(FlutterError *_Nullable *_Nonnull)error { return AnEnumBoxed; } @@ -207,13 +208,13 @@ - (void)throwAsyncFlutterErrorWithCompletion:(void (^)(id _Nullable, completion(nil, [FlutterError errorWithCode:@"code" message:@"message" details:@"details"]); } -- (void)echoAsyncAllTypes:(AllTypes *)everything - completion:(void (^)(AllTypes *_Nullable, FlutterError *_Nullable))completion { +- (void)echoAsyncAllTypes:(FLTAllTypes *)everything + completion:(void (^)(FLTAllTypes *_Nullable, FlutterError *_Nullable))completion { completion(everything, nil); } -- (void)echoAsyncNullableAllNullableTypes:(nullable AllNullableTypes *)everything - completion:(void (^)(AllNullableTypes *_Nullable, +- (void)echoAsyncNullableAllNullableTypes:(nullable FLTAllNullableTypes *)everything + completion:(void (^)(FLTAllNullableTypes *_Nullable, FlutterError *_Nullable))completion { completion(everything, nil); } @@ -260,9 +261,9 @@ - (void)echoAsyncMap:(NSDictionary *)aMap completion(aMap, nil); } -- (void)echoAsyncEnum:(AnEnum)anEnum - completion:(void (^)(AnEnumBox *_Nullable, FlutterError *_Nullable))completion { - completion([[AnEnumBox alloc] initWithValue:anEnum], nil); +- (void)echoAsyncEnum:(FLTAnEnum)anEnum + completion:(void (^)(FLTAnEnumBox *_Nullable, FlutterError *_Nullable))completion { + completion([[FLTAnEnumBox alloc] initWithValue:anEnum], nil); } - (void)echoAsyncNullableInt:(nullable NSNumber *)anInt @@ -308,8 +309,9 @@ - (void)echoAsyncNullableMap:(nullable NSDictionary *)aMap completion(aMap, nil); } -- (void)echoAsyncNullableEnum:(nullable AnEnumBox *)AnEnumBoxed - completion:(void (^)(AnEnumBox *_Nullable, FlutterError *_Nullable))completion { +- (void)echoAsyncNullableEnum:(nullable FLTAnEnumBox *)AnEnumBoxed + completion: + (void (^)(FLTAnEnumBox *_Nullable, FlutterError *_Nullable))completion { completion(AnEnumBoxed, nil); } @@ -332,10 +334,11 @@ - (void)callFlutterThrowErrorFromVoidWithCompletion:(void (^)(FlutterError *_Nul }]; } -- (void)callFlutterEchoAllTypes:(AllTypes *)everything - completion:(void (^)(AllTypes *_Nullable, FlutterError *_Nullable))completion { +- (void)callFlutterEchoAllTypes:(FLTAllTypes *)everything + completion: + (void (^)(FLTAllTypes *_Nullable, FlutterError *_Nullable))completion { [self.flutterAPI echoAllTypes:everything - completion:^(AllTypes *value, FlutterError *error) { + completion:^(FLTAllTypes *value, FlutterError *error) { completion(value, error); }]; } @@ -343,14 +346,15 @@ - (void)callFlutterEchoAllTypes:(AllTypes *)everything - (void)callFlutterSendMultipleNullableTypesABool:(nullable NSNumber *)aNullableBool anInt:(nullable NSNumber *)aNullableInt aString:(nullable NSString *)aNullableString - completion:(void (^)(AllNullableTypes *_Nullable, + completion:(void (^)(FLTAllNullableTypes *_Nullable, FlutterError *_Nullable))completion { - [self.flutterAPI sendMultipleNullableTypesABool:aNullableBool - anInt:aNullableInt - aString:aNullableString - completion:^(AllNullableTypes *value, FlutterError *error) { - completion(value, error); - }]; + [self.flutterAPI + sendMultipleNullableTypesABool:aNullableBool + anInt:aNullableInt + aString:aNullableString + completion:^(FLTAllNullableTypes *value, FlutterError *error) { + completion(value, error); + }]; } - (void)callFlutterEchoBool:(BOOL)aBool @@ -411,19 +415,19 @@ - (void)callFlutterEchoMap:(NSDictionary *)aMap }]; } -- (void)callFlutterEchoEnum:(AnEnum)anEnum - completion:(void (^)(AnEnumBox *_Nullable, FlutterError *_Nullable))completion { +- (void)callFlutterEchoEnum:(FLTAnEnum)anEnum + completion:(void (^)(FLTAnEnumBox *_Nullable, FlutterError *_Nullable))completion { [self.flutterAPI echoEnum:anEnum - completion:^(AnEnumBox *value, FlutterError *error) { + completion:^(FLTAnEnumBox *value, FlutterError *error) { completion(value, error); }]; } -- (void)callFlutterEchoAllNullableTypes:(nullable AllNullableTypes *)everything - completion:(void (^)(AllNullableTypes *_Nullable, +- (void)callFlutterEchoAllNullableTypes:(nullable FLTAllNullableTypes *)everything + completion:(void (^)(FLTAllNullableTypes *_Nullable, FlutterError *_Nullable))completion { [self.flutterAPI echoAllNullableTypes:everything - completion:^(AllNullableTypes *value, FlutterError *error) { + completion:^(FLTAllNullableTypes *value, FlutterError *error) { completion(value, error); }]; } @@ -491,11 +495,11 @@ - (void)callFlutterEchoNullableMap:(nullable NSDictionary *)aMap }]; } -- (void)callFlutterEchoNullableEnum:(nullable AnEnumBox *)AnEnumBoxed - completion: - (void (^)(AnEnumBox *_Nullable, FlutterError *_Nullable))completion { +- (void)callFlutterEchoNullableEnum:(nullable FLTAnEnumBox *)AnEnumBoxed + completion:(void (^)(FLTAnEnumBox *_Nullable, + FlutterError *_Nullable))completion { [self.flutterAPI echoNullableEnum:AnEnumBoxed - completion:^(AnEnumBox *value, FlutterError *error) { + completion:^(FLTAnEnumBox *value, FlutterError *error) { completion(value, error); }]; } diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.h b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.h index e7022baa9403..d3506f9d3e92 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.h +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.h @@ -14,27 +14,27 @@ NS_ASSUME_NONNULL_BEGIN -typedef NS_ENUM(NSUInteger, AnEnum) { - AnEnumOne = 0, - AnEnumTwo = 1, - AnEnumThree = 2, - AnEnumFortyTwo = 3, - AnEnumFourHundredTwentyTwo = 4, +typedef NS_ENUM(NSUInteger, FLTAnEnum) { + FLTAnEnumOne = 0, + FLTAnEnumTwo = 1, + FLTAnEnumThree = 2, + FLTAnEnumFortyTwo = 3, + FLTAnEnumFourHundredTwentyTwo = 4, }; -/// Wrapper for AnEnum to allow for nullability. -@interface AnEnumBox : NSObject -@property(nonatomic, assign) AnEnum value; -- (instancetype)initWithValue:(AnEnum)value; +/// Wrapper for FLTAnEnum to allow for nullability. +@interface FLTAnEnumBox : NSObject +@property(nonatomic, assign) FLTAnEnum value; +- (instancetype)initWithValue:(FLTAnEnum)value; @end -@class AllTypes; -@class AllNullableTypes; -@class AllClassesWrapper; -@class TestMessage; +@class FLTAllTypes; +@class FLTAllNullableTypes; +@class FLTAllClassesWrapper; +@class FLTTestMessage; /// A class containing all supported types. -@interface AllTypes : NSObject +@interface FLTAllTypes : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithABool:(BOOL)aBool @@ -47,7 +47,7 @@ typedef NS_ENUM(NSUInteger, AnEnum) { aFloatArray:(FlutterStandardTypedData *)aFloatArray aList:(NSArray *)aList aMap:(NSDictionary *)aMap - anEnum:(AnEnum)anEnum + anEnum:(FLTAnEnum)anEnum aString:(NSString *)aString anObject:(id)anObject; @property(nonatomic, assign) BOOL aBool; @@ -60,13 +60,13 @@ typedef NS_ENUM(NSUInteger, AnEnum) { @property(nonatomic, strong) FlutterStandardTypedData *aFloatArray; @property(nonatomic, copy) NSArray *aList; @property(nonatomic, copy) NSDictionary *aMap; -@property(nonatomic, assign) AnEnum anEnum; +@property(nonatomic, assign) FLTAnEnum anEnum; @property(nonatomic, copy) NSString *aString; @property(nonatomic, strong) id anObject; @end /// A class containing all supported nullable types. -@interface AllNullableTypes : NSObject +@interface FLTAllNullableTypes : NSObject + (instancetype)makeWithANullableBool:(nullable NSNumber *)aNullableBool aNullableInt:(nullable NSNumber *)aNullableInt aNullableInt64:(nullable NSNumber *)aNullableInt64 @@ -81,7 +81,7 @@ typedef NS_ENUM(NSUInteger, AnEnum) { nullableMapWithAnnotations: (nullable NSDictionary *)nullableMapWithAnnotations nullableMapWithObject:(nullable NSDictionary *)nullableMapWithObject - aNullableEnum:(nullable AnEnumBox *)aNullableEnum + aNullableEnum:(nullable FLTAnEnumBox *)aNullableEnum aNullableString:(nullable NSString *)aNullableString aNullableObject:(nullable id)aNullableObject; @property(nonatomic, strong, nullable) NSNumber *aNullableBool; @@ -98,7 +98,7 @@ typedef NS_ENUM(NSUInteger, AnEnum) { @property(nonatomic, copy, nullable) NSDictionary *nullableMapWithAnnotations; @property(nonatomic, copy, nullable) NSDictionary *nullableMapWithObject; -@property(nonatomic, strong, nullable) AnEnumBox *aNullableEnum; +@property(nonatomic, strong, nullable) FLTAnEnumBox *aNullableEnum; @property(nonatomic, copy, nullable) NSString *aNullableString; @property(nonatomic, strong, nullable) id aNullableObject; @end @@ -108,35 +108,35 @@ typedef NS_ENUM(NSUInteger, AnEnum) { /// This is needed to test nested nullable and non-nullable classes, /// `AllNullableTypes` is non-nullable here as it is easier to instantiate /// than `AllTypes` when testing doesn't require both (ie. testing null classes). -@interface AllClassesWrapper : NSObject +@interface FLTAllClassesWrapper : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithAllNullableTypes:(AllNullableTypes *)allNullableTypes - allTypes:(nullable AllTypes *)allTypes; -@property(nonatomic, strong) AllNullableTypes *allNullableTypes; -@property(nonatomic, strong, nullable) AllTypes *allTypes; ++ (instancetype)makeWithAllNullableTypes:(FLTAllNullableTypes *)allNullableTypes + allTypes:(nullable FLTAllTypes *)allTypes; +@property(nonatomic, strong) FLTAllNullableTypes *allNullableTypes; +@property(nonatomic, strong, nullable) FLTAllTypes *allTypes; @end /// A data class containing a List, used in unit tests. -@interface TestMessage : NSObject +@interface FLTTestMessage : NSObject + (instancetype)makeWithTestList:(nullable NSArray *)testList; @property(nonatomic, copy, nullable) NSArray *testList; @end -/// The codec used by HostIntegrationCoreApi. -NSObject *HostIntegrationCoreApiGetCodec(void); +/// The codec used by FLTHostIntegrationCoreApi. +NSObject *FLTHostIntegrationCoreApiGetCodec(void); /// The core interface that each host language plugin must implement in /// platform_test integration tests. -@protocol HostIntegrationCoreApi +@protocol FLTHostIntegrationCoreApi /// A no-op function taking no arguments and returning no value, to sanity /// test basic calling. - (void)noopWithError:(FlutterError *_Nullable *_Nonnull)error; /// Returns the passed object, to test serialization and deserialization. /// /// @return `nil` only when `error != nil`. -- (nullable AllTypes *)echoAllTypes:(AllTypes *)everything - error:(FlutterError *_Nullable *_Nonnull)error; +- (nullable FLTAllTypes *)echoAllTypes:(FLTAllTypes *)everything + error:(FlutterError *_Nullable *_Nonnull)error; /// Returns an error, to test error handling. - (nullable id)throwErrorWithError:(FlutterError *_Nullable *_Nonnull)error; /// Returns an error from a void function, to test error handling. @@ -182,12 +182,13 @@ NSObject *HostIntegrationCoreApiGetCodec(void); /// Returns the passed map to test nested class serialization and deserialization. /// /// @return `nil` only when `error != nil`. -- (nullable AllClassesWrapper *)echoClassWrapper:(AllClassesWrapper *)wrapper - error:(FlutterError *_Nullable *_Nonnull)error; +- (nullable FLTAllClassesWrapper *)echoClassWrapper:(FLTAllClassesWrapper *)wrapper + error:(FlutterError *_Nullable *_Nonnull)error; /// Returns the passed enum to test serialization and deserialization. /// /// @return `nil` only when `error != nil`. -- (AnEnumBox *_Nullable)echoEnum:(AnEnum)anEnum error:(FlutterError *_Nullable *_Nonnull)error; +- (FLTAnEnumBox *_Nullable)echoEnum:(FLTAnEnum)anEnum + error:(FlutterError *_Nullable *_Nonnull)error; /// Returns the default string. /// /// @return `nil` only when `error != nil`. @@ -204,27 +205,27 @@ NSObject *HostIntegrationCoreApiGetCodec(void); - (nullable NSNumber *)echoRequiredInt:(NSInteger)anInt error:(FlutterError *_Nullable *_Nonnull)error; /// Returns the passed object, to test serialization and deserialization. -- (nullable AllNullableTypes *)echoAllNullableTypes:(nullable AllNullableTypes *)everything - error:(FlutterError *_Nullable *_Nonnull)error; +- (nullable FLTAllNullableTypes *)echoAllNullableTypes:(nullable FLTAllNullableTypes *)everything + error:(FlutterError *_Nullable *_Nonnull)error; /// Returns the inner `aString` value from the wrapped object, to test /// sending of nested objects. -- (nullable NSString *)extractNestedNullableStringFrom:(AllClassesWrapper *)wrapper +- (nullable NSString *)extractNestedNullableStringFrom:(FLTAllClassesWrapper *)wrapper error:(FlutterError *_Nullable *_Nonnull)error; /// Returns the inner `aString` value from the wrapped object, to test /// sending of nested objects. /// /// @return `nil` only when `error != nil`. -- (nullable AllClassesWrapper *) +- (nullable FLTAllClassesWrapper *) createNestedObjectWithNullableString:(nullable NSString *)nullableString error:(FlutterError *_Nullable *_Nonnull)error; /// Returns passed in arguments of multiple types. /// /// @return `nil` only when `error != nil`. -- (nullable AllNullableTypes *)sendMultipleNullableTypesABool:(nullable NSNumber *)aNullableBool - anInt:(nullable NSNumber *)aNullableInt - aString:(nullable NSString *)aNullableString - error:(FlutterError *_Nullable *_Nonnull) - error; +- (nullable FLTAllNullableTypes *) + sendMultipleNullableTypesABool:(nullable NSNumber *)aNullableBool + anInt:(nullable NSNumber *)aNullableInt + aString:(nullable NSString *)aNullableString + error:(FlutterError *_Nullable *_Nonnull)error; /// Returns passed in int. - (nullable NSNumber *)echoNullableInt:(nullable NSNumber *)aNullableInt error:(FlutterError *_Nullable *_Nonnull)error; @@ -251,8 +252,8 @@ NSObject *HostIntegrationCoreApiGetCodec(void); - (nullable NSDictionary *)echoNullableMap: (nullable NSDictionary *)aNullableMap error:(FlutterError *_Nullable *_Nonnull)error; -- (AnEnumBox *_Nullable)echoNullableEnum:(nullable AnEnumBox *)anEnumBoxed - error:(FlutterError *_Nullable *_Nonnull)error; +- (FLTAnEnumBox *_Nullable)echoNullableEnum:(nullable FLTAnEnumBox *)anEnumBoxed + error:(FlutterError *_Nullable *_Nonnull)error; /// Returns passed in int. - (nullable NSNumber *)echoOptionalNullableInt:(nullable NSNumber *)aNullableInt error:(FlutterError *_Nullable *_Nonnull)error; @@ -289,8 +290,8 @@ NSObject *HostIntegrationCoreApiGetCodec(void); completion:(void (^)(NSDictionary *_Nullable, FlutterError *_Nullable))completion; /// Returns the passed enum, to test asynchronous serialization and deserialization. -- (void)echoAsyncEnum:(AnEnum)anEnum - completion:(void (^)(AnEnumBox *_Nullable, FlutterError *_Nullable))completion; +- (void)echoAsyncEnum:(FLTAnEnum)anEnum + completion:(void (^)(FLTAnEnumBox *_Nullable, FlutterError *_Nullable))completion; /// Responds with an error from an async function returning a value. - (void)throwAsyncErrorWithCompletion:(void (^)(id _Nullable, FlutterError *_Nullable))completion; /// Responds with an error from an async void function. @@ -299,11 +300,11 @@ NSObject *HostIntegrationCoreApiGetCodec(void); - (void)throwAsyncFlutterErrorWithCompletion:(void (^)(id _Nullable, FlutterError *_Nullable))completion; /// Returns the passed object, to test async serialization and deserialization. -- (void)echoAsyncAllTypes:(AllTypes *)everything - completion:(void (^)(AllTypes *_Nullable, FlutterError *_Nullable))completion; +- (void)echoAsyncAllTypes:(FLTAllTypes *)everything + completion:(void (^)(FLTAllTypes *_Nullable, FlutterError *_Nullable))completion; /// Returns the passed object, to test serialization and deserialization. -- (void)echoAsyncNullableAllNullableTypes:(nullable AllNullableTypes *)everything - completion:(void (^)(AllNullableTypes *_Nullable, +- (void)echoAsyncNullableAllNullableTypes:(nullable FLTAllNullableTypes *)everything + completion:(void (^)(FLTAllNullableTypes *_Nullable, FlutterError *_Nullable))completion; /// Returns passed in int asynchronously. - (void)echoAsyncNullableInt:(nullable NSNumber *)anInt @@ -332,21 +333,23 @@ NSObject *HostIntegrationCoreApiGetCodec(void); completion:(void (^)(NSDictionary *_Nullable, FlutterError *_Nullable))completion; /// Returns the passed enum, to test asynchronous serialization and deserialization. -- (void)echoAsyncNullableEnum:(nullable AnEnumBox *)anEnumBoxed - completion:(void (^)(AnEnumBox *_Nullable, FlutterError *_Nullable))completion; +- (void)echoAsyncNullableEnum:(nullable FLTAnEnumBox *)anEnumBoxed + completion: + (void (^)(FLTAnEnumBox *_Nullable, FlutterError *_Nullable))completion; - (void)callFlutterNoopWithCompletion:(void (^)(FlutterError *_Nullable))completion; - (void)callFlutterThrowErrorWithCompletion:(void (^)(id _Nullable, FlutterError *_Nullable))completion; - (void)callFlutterThrowErrorFromVoidWithCompletion:(void (^)(FlutterError *_Nullable))completion; -- (void)callFlutterEchoAllTypes:(AllTypes *)everything - completion:(void (^)(AllTypes *_Nullable, FlutterError *_Nullable))completion; -- (void)callFlutterEchoAllNullableTypes:(nullable AllNullableTypes *)everything - completion:(void (^)(AllNullableTypes *_Nullable, +- (void)callFlutterEchoAllTypes:(FLTAllTypes *)everything + completion: + (void (^)(FLTAllTypes *_Nullable, FlutterError *_Nullable))completion; +- (void)callFlutterEchoAllNullableTypes:(nullable FLTAllNullableTypes *)everything + completion:(void (^)(FLTAllNullableTypes *_Nullable, FlutterError *_Nullable))completion; - (void)callFlutterSendMultipleNullableTypesABool:(nullable NSNumber *)aNullableBool anInt:(nullable NSNumber *)aNullableInt aString:(nullable NSString *)aNullableString - completion:(void (^)(AllNullableTypes *_Nullable, + completion:(void (^)(FLTAllNullableTypes *_Nullable, FlutterError *_Nullable))completion; - (void)callFlutterEchoBool:(BOOL)aBool completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion; @@ -364,8 +367,8 @@ NSObject *HostIntegrationCoreApiGetCodec(void); - (void)callFlutterEchoMap:(NSDictionary *)aMap completion:(void (^)(NSDictionary *_Nullable, FlutterError *_Nullable))completion; -- (void)callFlutterEchoEnum:(AnEnum)anEnum - completion:(void (^)(AnEnumBox *_Nullable, FlutterError *_Nullable))completion; +- (void)callFlutterEchoEnum:(FLTAnEnum)anEnum + completion:(void (^)(FLTAnEnumBox *_Nullable, FlutterError *_Nullable))completion; - (void)callFlutterEchoNullableBool:(nullable NSNumber *)aBool completion: (void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion; @@ -387,20 +390,20 @@ NSObject *HostIntegrationCoreApiGetCodec(void); - (void)callFlutterEchoNullableMap:(nullable NSDictionary *)aMap completion:(void (^)(NSDictionary *_Nullable, FlutterError *_Nullable))completion; -- (void)callFlutterEchoNullableEnum:(nullable AnEnumBox *)anEnumBoxed +- (void)callFlutterEchoNullableEnum:(nullable FLTAnEnumBox *)anEnumBoxed completion: - (void (^)(AnEnumBox *_Nullable, FlutterError *_Nullable))completion; + (void (^)(FLTAnEnumBox *_Nullable, FlutterError *_Nullable))completion; @end -extern void SetUpHostIntegrationCoreApi(id binaryMessenger, - NSObject *_Nullable api); +extern void SetUpFLTHostIntegrationCoreApi(id binaryMessenger, + NSObject *_Nullable api); -/// The codec used by FlutterIntegrationCoreApi. -NSObject *FlutterIntegrationCoreApiGetCodec(void); +/// The codec used by FLTFlutterIntegrationCoreApi. +NSObject *FLTFlutterIntegrationCoreApiGetCodec(void); /// The core interface that the Dart platform_test code implements for host /// integration tests to call into. -@interface FlutterIntegrationCoreApi : NSObject +@interface FLTFlutterIntegrationCoreApi : NSObject - (instancetype)initWithBinaryMessenger:(id)binaryMessenger; /// A no-op function taking no arguments and returning no value, to sanity /// test basic calling. @@ -410,19 +413,19 @@ NSObject *FlutterIntegrationCoreApiGetCodec(void); /// Responds with an error from an async void function. - (void)throwErrorFromVoidWithCompletion:(void (^)(FlutterError *_Nullable))completion; /// Returns the passed object, to test serialization and deserialization. -- (void)echoAllTypes:(AllTypes *)everything - completion:(void (^)(AllTypes *_Nullable, FlutterError *_Nullable))completion; +- (void)echoAllTypes:(FLTAllTypes *)everything + completion:(void (^)(FLTAllTypes *_Nullable, FlutterError *_Nullable))completion; /// Returns the passed object, to test serialization and deserialization. -- (void)echoAllNullableTypes:(nullable AllNullableTypes *)everything +- (void)echoAllNullableTypes:(nullable FLTAllNullableTypes *)everything completion: - (void (^)(AllNullableTypes *_Nullable, FlutterError *_Nullable))completion; + (void (^)(FLTAllNullableTypes *_Nullable, FlutterError *_Nullable))completion; /// Returns passed in arguments of multiple types. /// /// Tests multiple-arity FlutterApi handling. - (void)sendMultipleNullableTypesABool:(nullable NSNumber *)aNullableBool anInt:(nullable NSNumber *)aNullableInt aString:(nullable NSString *)aNullableString - completion:(void (^)(AllNullableTypes *_Nullable, + completion:(void (^)(FLTAllNullableTypes *_Nullable, FlutterError *_Nullable))completion; /// Returns the passed boolean, to test serialization and deserialization. - (void)echoBool:(BOOL)aBool @@ -448,8 +451,8 @@ NSObject *FlutterIntegrationCoreApiGetCodec(void); completion: (void (^)(NSDictionary *_Nullable, FlutterError *_Nullable))completion; /// Returns the passed enum to test serialization and deserialization. -- (void)echoEnum:(AnEnum)anEnum - completion:(void (^)(AnEnumBox *_Nullable, FlutterError *_Nullable))completion; +- (void)echoEnum:(FLTAnEnum)anEnum + completion:(void (^)(FLTAnEnumBox *_Nullable, FlutterError *_Nullable))completion; /// Returns the passed boolean, to test serialization and deserialization. - (void)echoNullableBool:(nullable NSNumber *)aBool completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion; @@ -474,8 +477,8 @@ NSObject *FlutterIntegrationCoreApiGetCodec(void); completion:(void (^)(NSDictionary *_Nullable, FlutterError *_Nullable))completion; /// Returns the passed enum to test serialization and deserialization. -- (void)echoNullableEnum:(nullable AnEnumBox *)anEnumBoxed - completion:(void (^)(AnEnumBox *_Nullable, FlutterError *_Nullable))completion; +- (void)echoNullableEnum:(nullable FLTAnEnumBox *)anEnumBoxed + completion:(void (^)(FLTAnEnumBox *_Nullable, FlutterError *_Nullable))completion; /// A no-op function taking no arguments and returning no value, to sanity /// test basic asynchronous calling. - (void)noopAsyncWithCompletion:(void (^)(FlutterError *_Nullable))completion; @@ -484,38 +487,38 @@ NSObject *FlutterIntegrationCoreApiGetCodec(void); completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; @end -/// The codec used by HostTrivialApi. -NSObject *HostTrivialApiGetCodec(void); +/// The codec used by FLTHostTrivialApi. +NSObject *FLTHostTrivialApiGetCodec(void); /// An API that can be implemented for minimal, compile-only tests. -@protocol HostTrivialApi +@protocol FLTHostTrivialApi - (void)noopWithError:(FlutterError *_Nullable *_Nonnull)error; @end -extern void SetUpHostTrivialApi(id binaryMessenger, - NSObject *_Nullable api); +extern void SetUpFLTHostTrivialApi(id binaryMessenger, + NSObject *_Nullable api); -/// The codec used by HostSmallApi. -NSObject *HostSmallApiGetCodec(void); +/// The codec used by FLTHostSmallApi. +NSObject *FLTHostSmallApiGetCodec(void); /// A simple API implemented in some unit tests. -@protocol HostSmallApi +@protocol FLTHostSmallApi - (void)echoString:(NSString *)aString completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; - (void)voidVoidWithCompletion:(void (^)(FlutterError *_Nullable))completion; @end -extern void SetUpHostSmallApi(id binaryMessenger, - NSObject *_Nullable api); +extern void SetUpFLTHostSmallApi(id binaryMessenger, + NSObject *_Nullable api); -/// The codec used by FlutterSmallApi. -NSObject *FlutterSmallApiGetCodec(void); +/// The codec used by FLTFlutterSmallApi. +NSObject *FLTFlutterSmallApiGetCodec(void); /// A simple API called in some unit tests. -@interface FlutterSmallApi : NSObject +@interface FLTFlutterSmallApi : NSObject - (instancetype)initWithBinaryMessenger:(id)binaryMessenger; -- (void)echoWrappedList:(TestMessage *)msg - completion:(void (^)(TestMessage *_Nullable, FlutterError *_Nullable))completion; +- (void)echoWrappedList:(FLTTestMessage *)msg + completion:(void (^)(FLTTestMessage *_Nullable, FlutterError *_Nullable))completion; - (void)echoString:(NSString *)aString completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; @end diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.m b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.m index 0cc5b059c04f..d69afc01e134 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.m +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.m @@ -40,8 +40,8 @@ static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { return (result == [NSNull null]) ? nil : result; } -@implementation AnEnumBox -- (instancetype)initWithValue:(AnEnum)value { +@implementation FLTAnEnumBox +- (instancetype)initWithValue:(FLTAnEnum)value { self = [super init]; if (self) { _value = value; @@ -50,31 +50,31 @@ - (instancetype)initWithValue:(AnEnum)value { } @end -@interface AllTypes () -+ (AllTypes *)fromList:(NSArray *)list; -+ (nullable AllTypes *)nullableFromList:(NSArray *)list; +@interface FLTAllTypes () ++ (FLTAllTypes *)fromList:(NSArray *)list; ++ (nullable FLTAllTypes *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end -@interface AllNullableTypes () -+ (AllNullableTypes *)fromList:(NSArray *)list; -+ (nullable AllNullableTypes *)nullableFromList:(NSArray *)list; +@interface FLTAllNullableTypes () ++ (FLTAllNullableTypes *)fromList:(NSArray *)list; ++ (nullable FLTAllNullableTypes *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end -@interface AllClassesWrapper () -+ (AllClassesWrapper *)fromList:(NSArray *)list; -+ (nullable AllClassesWrapper *)nullableFromList:(NSArray *)list; +@interface FLTAllClassesWrapper () ++ (FLTAllClassesWrapper *)fromList:(NSArray *)list; ++ (nullable FLTAllClassesWrapper *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end -@interface TestMessage () -+ (TestMessage *)fromList:(NSArray *)list; -+ (nullable TestMessage *)nullableFromList:(NSArray *)list; +@interface FLTTestMessage () ++ (FLTTestMessage *)fromList:(NSArray *)list; ++ (nullable FLTTestMessage *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end -@implementation AllTypes +@implementation FLTAllTypes + (instancetype)makeWithABool:(BOOL)aBool anInt:(NSInteger)anInt anInt64:(NSInteger)anInt64 @@ -85,10 +85,10 @@ + (instancetype)makeWithABool:(BOOL)aBool aFloatArray:(FlutterStandardTypedData *)aFloatArray aList:(NSArray *)aList aMap:(NSDictionary *)aMap - anEnum:(AnEnum)anEnum + anEnum:(FLTAnEnum)anEnum aString:(NSString *)aString anObject:(id)anObject { - AllTypes *pigeonResult = [[AllTypes alloc] init]; + FLTAllTypes *pigeonResult = [[FLTAllTypes alloc] init]; pigeonResult.aBool = aBool; pigeonResult.anInt = anInt; pigeonResult.anInt64 = anInt64; @@ -104,8 +104,8 @@ + (instancetype)makeWithABool:(BOOL)aBool pigeonResult.anObject = anObject; return pigeonResult; } -+ (AllTypes *)fromList:(NSArray *)list { - AllTypes *pigeonResult = [[AllTypes alloc] init]; ++ (FLTAllTypes *)fromList:(NSArray *)list { + FLTAllTypes *pigeonResult = [[FLTAllTypes alloc] init]; pigeonResult.aBool = [GetNullableObjectAtIndex(list, 0) boolValue]; pigeonResult.anInt = [GetNullableObjectAtIndex(list, 1) integerValue]; pigeonResult.anInt64 = [GetNullableObjectAtIndex(list, 2) integerValue]; @@ -121,8 +121,8 @@ + (AllTypes *)fromList:(NSArray *)list { pigeonResult.anObject = GetNullableObjectAtIndex(list, 12); return pigeonResult; } -+ (nullable AllTypes *)nullableFromList:(NSArray *)list { - return (list) ? [AllTypes fromList:list] : nil; ++ (nullable FLTAllTypes *)nullableFromList:(NSArray *)list { + return (list) ? [FLTAllTypes fromList:list] : nil; } - (NSArray *)toList { return @[ @@ -143,7 +143,7 @@ - (NSArray *)toList { } @end -@implementation AllNullableTypes +@implementation FLTAllNullableTypes + (instancetype)makeWithANullableBool:(nullable NSNumber *)aNullableBool aNullableInt:(nullable NSNumber *)aNullableInt aNullableInt64:(nullable NSNumber *)aNullableInt64 @@ -158,10 +158,10 @@ + (instancetype)makeWithANullableBool:(nullable NSNumber *)aNullableBool nullableMapWithAnnotations: (nullable NSDictionary *)nullableMapWithAnnotations nullableMapWithObject:(nullable NSDictionary *)nullableMapWithObject - aNullableEnum:(nullable AnEnumBox *)aNullableEnum + aNullableEnum:(nullable FLTAnEnumBox *)aNullableEnum aNullableString:(nullable NSString *)aNullableString aNullableObject:(nullable id)aNullableObject { - AllNullableTypes *pigeonResult = [[AllNullableTypes alloc] init]; + FLTAllNullableTypes *pigeonResult = [[FLTAllNullableTypes alloc] init]; pigeonResult.aNullableBool = aNullableBool; pigeonResult.aNullableInt = aNullableInt; pigeonResult.aNullableInt64 = aNullableInt64; @@ -180,8 +180,8 @@ + (instancetype)makeWithANullableBool:(nullable NSNumber *)aNullableBool pigeonResult.aNullableObject = aNullableObject; return pigeonResult; } -+ (AllNullableTypes *)fromList:(NSArray *)list { - AllNullableTypes *pigeonResult = [[AllNullableTypes alloc] init]; ++ (FLTAllNullableTypes *)fromList:(NSArray *)list { + FLTAllNullableTypes *pigeonResult = [[FLTAllNullableTypes alloc] init]; pigeonResult.aNullableBool = GetNullableObjectAtIndex(list, 0); pigeonResult.aNullableInt = GetNullableObjectAtIndex(list, 1); pigeonResult.aNullableInt64 = GetNullableObjectAtIndex(list, 2); @@ -196,17 +196,17 @@ + (AllNullableTypes *)fromList:(NSArray *)list { pigeonResult.nullableMapWithAnnotations = GetNullableObjectAtIndex(list, 11); pigeonResult.nullableMapWithObject = GetNullableObjectAtIndex(list, 12); NSNumber *aNullableEnumAsNumber = GetNullableObjectAtIndex(list, 13); - AnEnumBox *aNullableEnum = + FLTAnEnumBox *aNullableEnum = aNullableEnumAsNumber == nil ? nil - : [[AnEnumBox alloc] initWithValue:[aNullableEnumAsNumber integerValue]]; + : [[FLTAnEnumBox alloc] initWithValue:[aNullableEnumAsNumber integerValue]]; pigeonResult.aNullableEnum = aNullableEnum; pigeonResult.aNullableString = GetNullableObjectAtIndex(list, 14); pigeonResult.aNullableObject = GetNullableObjectAtIndex(list, 15); return pigeonResult; } -+ (nullable AllNullableTypes *)nullableFromList:(NSArray *)list { - return (list) ? [AllNullableTypes fromList:list] : nil; ++ (nullable FLTAllNullableTypes *)nullableFromList:(NSArray *)list { + return (list) ? [FLTAllNullableTypes fromList:list] : nil; } - (NSArray *)toList { return @[ @@ -231,23 +231,23 @@ - (NSArray *)toList { } @end -@implementation AllClassesWrapper -+ (instancetype)makeWithAllNullableTypes:(AllNullableTypes *)allNullableTypes - allTypes:(nullable AllTypes *)allTypes { - AllClassesWrapper *pigeonResult = [[AllClassesWrapper alloc] init]; +@implementation FLTAllClassesWrapper ++ (instancetype)makeWithAllNullableTypes:(FLTAllNullableTypes *)allNullableTypes + allTypes:(nullable FLTAllTypes *)allTypes { + FLTAllClassesWrapper *pigeonResult = [[FLTAllClassesWrapper alloc] init]; pigeonResult.allNullableTypes = allNullableTypes; pigeonResult.allTypes = allTypes; return pigeonResult; } -+ (AllClassesWrapper *)fromList:(NSArray *)list { - AllClassesWrapper *pigeonResult = [[AllClassesWrapper alloc] init]; ++ (FLTAllClassesWrapper *)fromList:(NSArray *)list { + FLTAllClassesWrapper *pigeonResult = [[FLTAllClassesWrapper alloc] init]; pigeonResult.allNullableTypes = - [AllNullableTypes nullableFromList:(GetNullableObjectAtIndex(list, 0))]; - pigeonResult.allTypes = [AllTypes nullableFromList:(GetNullableObjectAtIndex(list, 1))]; + [FLTAllNullableTypes nullableFromList:(GetNullableObjectAtIndex(list, 0))]; + pigeonResult.allTypes = [FLTAllTypes nullableFromList:(GetNullableObjectAtIndex(list, 1))]; return pigeonResult; } -+ (nullable AllClassesWrapper *)nullableFromList:(NSArray *)list { - return (list) ? [AllClassesWrapper fromList:list] : nil; ++ (nullable FLTAllClassesWrapper *)nullableFromList:(NSArray *)list { + return (list) ? [FLTAllClassesWrapper fromList:list] : nil; } - (NSArray *)toList { return @[ @@ -257,19 +257,19 @@ - (NSArray *)toList { } @end -@implementation TestMessage +@implementation FLTTestMessage + (instancetype)makeWithTestList:(nullable NSArray *)testList { - TestMessage *pigeonResult = [[TestMessage alloc] init]; + FLTTestMessage *pigeonResult = [[FLTTestMessage alloc] init]; pigeonResult.testList = testList; return pigeonResult; } -+ (TestMessage *)fromList:(NSArray *)list { - TestMessage *pigeonResult = [[TestMessage alloc] init]; ++ (FLTTestMessage *)fromList:(NSArray *)list { + FLTTestMessage *pigeonResult = [[FLTTestMessage alloc] init]; pigeonResult.testList = GetNullableObjectAtIndex(list, 0); return pigeonResult; } -+ (nullable TestMessage *)nullableFromList:(NSArray *)list { - return (list) ? [TestMessage fromList:list] : nil; ++ (nullable FLTTestMessage *)nullableFromList:(NSArray *)list { + return (list) ? [FLTTestMessage fromList:list] : nil; } - (NSArray *)toList { return @[ @@ -278,39 +278,39 @@ - (NSArray *)toList { } @end -@interface HostIntegrationCoreApiCodecReader : FlutterStandardReader +@interface FLTHostIntegrationCoreApiCodecReader : FlutterStandardReader @end -@implementation HostIntegrationCoreApiCodecReader +@implementation FLTHostIntegrationCoreApiCodecReader - (nullable id)readValueOfType:(UInt8)type { switch (type) { case 128: - return [AllClassesWrapper fromList:[self readValue]]; + return [FLTAllClassesWrapper fromList:[self readValue]]; case 129: - return [AllNullableTypes fromList:[self readValue]]; + return [FLTAllNullableTypes fromList:[self readValue]]; case 130: - return [AllTypes fromList:[self readValue]]; + return [FLTAllTypes fromList:[self readValue]]; case 131: - return [TestMessage fromList:[self readValue]]; + return [FLTTestMessage fromList:[self readValue]]; default: return [super readValueOfType:type]; } } @end -@interface HostIntegrationCoreApiCodecWriter : FlutterStandardWriter +@interface FLTHostIntegrationCoreApiCodecWriter : FlutterStandardWriter @end -@implementation HostIntegrationCoreApiCodecWriter +@implementation FLTHostIntegrationCoreApiCodecWriter - (void)writeValue:(id)value { - if ([value isKindOfClass:[AllClassesWrapper class]]) { + if ([value isKindOfClass:[FLTAllClassesWrapper class]]) { [self writeByte:128]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[AllNullableTypes class]]) { + } else if ([value isKindOfClass:[FLTAllNullableTypes class]]) { [self writeByte:129]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[AllTypes class]]) { + } else if ([value isKindOfClass:[FLTAllTypes class]]) { [self writeByte:130]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[TestMessage class]]) { + } else if ([value isKindOfClass:[FLTTestMessage class]]) { [self writeByte:131]; [self writeValue:[value toList]]; } else { @@ -319,40 +319,40 @@ - (void)writeValue:(id)value { } @end -@interface HostIntegrationCoreApiCodecReaderWriter : FlutterStandardReaderWriter +@interface FLTHostIntegrationCoreApiCodecReaderWriter : FlutterStandardReaderWriter @end -@implementation HostIntegrationCoreApiCodecReaderWriter +@implementation FLTHostIntegrationCoreApiCodecReaderWriter - (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { - return [[HostIntegrationCoreApiCodecWriter alloc] initWithData:data]; + return [[FLTHostIntegrationCoreApiCodecWriter alloc] initWithData:data]; } - (FlutterStandardReader *)readerWithData:(NSData *)data { - return [[HostIntegrationCoreApiCodecReader alloc] initWithData:data]; + return [[FLTHostIntegrationCoreApiCodecReader alloc] initWithData:data]; } @end -NSObject *HostIntegrationCoreApiGetCodec(void) { +NSObject *FLTHostIntegrationCoreApiGetCodec(void) { static FlutterStandardMessageCodec *sSharedObject = nil; static dispatch_once_t sPred = 0; dispatch_once(&sPred, ^{ - HostIntegrationCoreApiCodecReaderWriter *readerWriter = - [[HostIntegrationCoreApiCodecReaderWriter alloc] init]; + FLTHostIntegrationCoreApiCodecReaderWriter *readerWriter = + [[FLTHostIntegrationCoreApiCodecReaderWriter alloc] init]; sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; }); return sSharedObject; } -void SetUpHostIntegrationCoreApi(id binaryMessenger, - NSObject *api) { +void SetUpFLTHostIntegrationCoreApi(id binaryMessenger, + NSObject *api) { /// A no-op function taking no arguments and returning no value, to sanity /// test basic calling. { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.noop" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(noopWithError:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(noopWithError:)", + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to @selector(noopWithError:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; @@ -369,17 +369,17 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAllTypes" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert( [api respondsToSelector:@selector(echoAllTypes:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(echoAllTypes:error:)", + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to @selector(echoAllTypes:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - AllTypes *arg_everything = GetNullableObjectAtIndex(args, 0); + FLTAllTypes *arg_everything = GetNullableObjectAtIndex(args, 0); FlutterError *error; - AllTypes *output = [api echoAllTypes:arg_everything error:&error]; + FLTAllTypes *output = [api echoAllTypes:arg_everything error:&error]; callback(wrapResult(output, error)); }]; } else { @@ -392,11 +392,11 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.throwError" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert( [api respondsToSelector:@selector(throwErrorWithError:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(throwErrorWithError:)", + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to @selector(throwErrorWithError:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; @@ -413,10 +413,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"throwErrorFromVoid" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(throwErrorFromVoidWithError:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(throwErrorFromVoidWithError:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -434,10 +434,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"throwFlutterError" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(throwFlutterErrorWithError:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(throwFlutterErrorWithError:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -455,10 +455,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoInt" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoInt:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(echoInt:error:)", + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to @selector(echoInt:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; @@ -477,11 +477,12 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoDouble" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(echoDouble:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(echoDouble:error:)", - api); + NSCAssert( + [api respondsToSelector:@selector(echoDouble:error:)], + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to @selector(echoDouble:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; double arg_aDouble = [GetNullableObjectAtIndex(args, 0) doubleValue]; @@ -499,10 +500,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoBool" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoBool:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(echoBool:error:)", + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to @selector(echoBool:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; @@ -521,11 +522,12 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoString" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(echoString:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(echoString:error:)", - api); + NSCAssert( + [api respondsToSelector:@selector(echoString:error:)], + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to @selector(echoString:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSString *arg_aString = GetNullableObjectAtIndex(args, 0); @@ -543,11 +545,11 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoUint8List" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert( [api respondsToSelector:@selector(echoUint8List:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(echoUint8List:error:)", + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to @selector(echoUint8List:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; @@ -566,11 +568,12 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoObject" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(echoObject:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(echoObject:error:)", - api); + NSCAssert( + [api respondsToSelector:@selector(echoObject:error:)], + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to @selector(echoObject:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; id arg_anObject = GetNullableObjectAtIndex(args, 0); @@ -588,10 +591,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoList" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoList:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(echoList:error:)", + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to @selector(echoList:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; @@ -610,10 +613,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoMap" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoMap:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(echoMap:error:)", + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to @selector(echoMap:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; @@ -632,17 +635,17 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoClassWrapper" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { - NSCAssert( - [api respondsToSelector:@selector(echoClassWrapper:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(echoClassWrapper:error:)", - api); + NSCAssert([api respondsToSelector:@selector(echoClassWrapper:error:)], + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " + @"@selector(echoClassWrapper:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - AllClassesWrapper *arg_wrapper = GetNullableObjectAtIndex(args, 0); + FLTAllClassesWrapper *arg_wrapper = GetNullableObjectAtIndex(args, 0); FlutterError *error; - AllClassesWrapper *output = [api echoClassWrapper:arg_wrapper error:&error]; + FLTAllClassesWrapper *output = [api echoClassWrapper:arg_wrapper error:&error]; callback(wrapResult(output, error)); }]; } else { @@ -655,16 +658,16 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoEnum" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoEnum:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(echoEnum:error:)", + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to @selector(echoEnum:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - AnEnum arg_anEnum = [GetNullableObjectAtIndex(args, 0) integerValue]; + FLTAnEnum arg_anEnum = [GetNullableObjectAtIndex(args, 0) integerValue]; FlutterError *error; - AnEnumBox *enumBox = [api echoEnum:arg_anEnum error:&error]; + FLTAnEnumBox *enumBox = [api echoEnum:arg_anEnum error:&error]; NSNumber *output = enumBox == nil ? nil : [NSNumber numberWithInteger:enumBox.value]; callback(wrapResult(output, error)); }]; @@ -678,10 +681,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoNamedDefaultString" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoNamedDefaultString:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoNamedDefaultString:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -701,10 +704,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoOptionalDefaultDouble" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoOptionalDefaultDouble:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoOptionalDefaultDouble:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -724,12 +727,12 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoRequiredInt" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { - NSCAssert( - [api respondsToSelector:@selector(echoRequiredInt:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(echoRequiredInt:error:)", - api); + NSCAssert([api respondsToSelector:@selector(echoRequiredInt:error:)], + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " + @"@selector(echoRequiredInt:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSInteger arg_anInt = [GetNullableObjectAtIndex(args, 0) integerValue]; @@ -747,17 +750,17 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoAllNullableTypes" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoAllNullableTypes:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoAllNullableTypes:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - AllNullableTypes *arg_everything = GetNullableObjectAtIndex(args, 0); + FLTAllNullableTypes *arg_everything = GetNullableObjectAtIndex(args, 0); FlutterError *error; - AllNullableTypes *output = [api echoAllNullableTypes:arg_everything error:&error]; + FLTAllNullableTypes *output = [api echoAllNullableTypes:arg_everything error:&error]; callback(wrapResult(output, error)); }]; } else { @@ -771,15 +774,15 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"extractNestedNullableString" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(extractNestedNullableStringFrom:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(extractNestedNullableStringFrom:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - AllClassesWrapper *arg_wrapper = GetNullableObjectAtIndex(args, 0); + FLTAllClassesWrapper *arg_wrapper = GetNullableObjectAtIndex(args, 0); FlutterError *error; NSString *output = [api extractNestedNullableStringFrom:arg_wrapper error:&error]; callback(wrapResult(output, error)); @@ -795,18 +798,18 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"createNestedNullableString" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(createNestedObjectWithNullableString:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(createNestedObjectWithNullableString:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSString *arg_nullableString = GetNullableObjectAtIndex(args, 0); FlutterError *error; - AllClassesWrapper *output = [api createNestedObjectWithNullableString:arg_nullableString - error:&error]; + FLTAllClassesWrapper *output = [api createNestedObjectWithNullableString:arg_nullableString + error:&error]; callback(wrapResult(output, error)); }]; } else { @@ -819,11 +822,11 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"sendMultipleNullableTypes" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(sendMultipleNullableTypesABool: anInt:aString:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(sendMultipleNullableTypesABool:anInt:aString:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -832,10 +835,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, NSNumber *arg_aNullableInt = GetNullableObjectAtIndex(args, 1); NSString *arg_aNullableString = GetNullableObjectAtIndex(args, 2); FlutterError *error; - AllNullableTypes *output = [api sendMultipleNullableTypesABool:arg_aNullableBool - anInt:arg_aNullableInt - aString:arg_aNullableString - error:&error]; + FLTAllNullableTypes *output = [api sendMultipleNullableTypesABool:arg_aNullableBool + anInt:arg_aNullableInt + aString:arg_aNullableString + error:&error]; callback(wrapResult(output, error)); }]; } else { @@ -848,12 +851,12 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoNullableInt" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { - NSCAssert( - [api respondsToSelector:@selector(echoNullableInt:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(echoNullableInt:error:)", - api); + NSCAssert([api respondsToSelector:@selector(echoNullableInt:error:)], + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " + @"@selector(echoNullableInt:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSNumber *arg_aNullableInt = GetNullableObjectAtIndex(args, 0); @@ -871,10 +874,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoNullableDouble" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoNullableDouble:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoNullableDouble:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -894,12 +897,12 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoNullableBool" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { - NSCAssert( - [api respondsToSelector:@selector(echoNullableBool:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(echoNullableBool:error:)", - api); + NSCAssert([api respondsToSelector:@selector(echoNullableBool:error:)], + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " + @"@selector(echoNullableBool:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSNumber *arg_aNullableBool = GetNullableObjectAtIndex(args, 0); @@ -917,10 +920,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoNullableString" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoNullableString:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoNullableString:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -940,10 +943,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoNullableUint8List" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoNullableUint8List:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoNullableUint8List:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -964,10 +967,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoNullableObject" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoNullableObject:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoNullableObject:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -987,12 +990,12 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoNullableList" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { - NSCAssert( - [api respondsToSelector:@selector(echoNullableList:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(echoNullableList:error:)", - api); + NSCAssert([api respondsToSelector:@selector(echoNullableList:error:)], + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " + @"@selector(echoNullableList:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSArray *arg_aNullableList = GetNullableObjectAtIndex(args, 0); @@ -1010,12 +1013,12 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoNullableMap" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { - NSCAssert( - [api respondsToSelector:@selector(echoNullableMap:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(echoNullableMap:error:)", - api); + NSCAssert([api respondsToSelector:@selector(echoNullableMap:error:)], + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " + @"@selector(echoNullableMap:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSDictionary *arg_aNullableMap = GetNullableObjectAtIndex(args, 0); @@ -1032,21 +1035,21 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoNullableEnum" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { - NSCAssert( - [api respondsToSelector:@selector(echoNullableEnum:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(echoNullableEnum:error:)", - api); + NSCAssert([api respondsToSelector:@selector(echoNullableEnum:error:)], + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " + @"@selector(echoNullableEnum:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSNumber *arg_anEnumAsNumber = GetNullableObjectAtIndex(args, 0); - AnEnumBox *arg_anEnum = + FLTAnEnumBox *arg_anEnum = arg_anEnumAsNumber == nil ? nil - : [[AnEnumBox alloc] initWithValue:[arg_anEnumAsNumber integerValue]]; + : [[FLTAnEnumBox alloc] initWithValue:[arg_anEnumAsNumber integerValue]]; FlutterError *error; - AnEnumBox *enumBox = [api echoNullableEnum:arg_anEnum error:&error]; + FLTAnEnumBox *enumBox = [api echoNullableEnum:arg_anEnum error:&error]; NSNumber *output = enumBox == nil ? nil : [NSNumber numberWithInteger:enumBox.value]; callback(wrapResult(output, error)); }]; @@ -1060,10 +1063,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoOptionalNullableInt" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoOptionalNullableInt:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoOptionalNullableInt:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1083,10 +1086,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoNamedNullableString" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoNamedNullableString:error:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoNamedNullableString:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1107,12 +1110,12 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.noopAsync" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { - NSCAssert( - [api respondsToSelector:@selector(noopAsyncWithCompletion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(noopAsyncWithCompletion:)", - api); + NSCAssert([api respondsToSelector:@selector(noopAsyncWithCompletion:)], + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " + @"@selector(noopAsyncWithCompletion:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { [api noopAsyncWithCompletion:^(FlutterError *_Nullable error) { callback(wrapResult(nil, error)); @@ -1128,12 +1131,12 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncInt" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { - NSCAssert( - [api respondsToSelector:@selector(echoAsyncInt:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(echoAsyncInt:completion:)", - api); + NSCAssert([api respondsToSelector:@selector(echoAsyncInt:completion:)], + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " + @"@selector(echoAsyncInt:completion:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSInteger arg_anInt = [GetNullableObjectAtIndex(args, 0) integerValue]; @@ -1152,10 +1155,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncDouble" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoAsyncDouble:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoAsyncDouble:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1176,10 +1179,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncBool" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoAsyncBool:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoAsyncBool:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1200,10 +1203,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncString" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoAsyncString:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoAsyncString:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1224,10 +1227,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoAsyncUint8List" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoAsyncUint8List:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoAsyncUint8List:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1249,10 +1252,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncObject" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoAsyncObject:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoAsyncObject:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1273,10 +1276,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncList" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoAsyncList:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoAsyncList:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1297,12 +1300,12 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncMap" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { - NSCAssert( - [api respondsToSelector:@selector(echoAsyncMap:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to @selector(echoAsyncMap:completion:)", - api); + NSCAssert([api respondsToSelector:@selector(echoAsyncMap:completion:)], + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " + @"@selector(echoAsyncMap:completion:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSDictionary *arg_aMap = GetNullableObjectAtIndex(args, 0); @@ -1322,17 +1325,17 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncEnum" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoAsyncEnum:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoAsyncEnum:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - AnEnum arg_anEnum = [GetNullableObjectAtIndex(args, 0) integerValue]; + FLTAnEnum arg_anEnum = [GetNullableObjectAtIndex(args, 0) integerValue]; [api echoAsyncEnum:arg_anEnum - completion:^(AnEnumBox *_Nullable enumValue, FlutterError *_Nullable error) { + completion:^(FLTAnEnumBox *_Nullable enumValue, FlutterError *_Nullable error) { NSNumber *output = enumValue == nil ? nil : [NSNumber numberWithInteger:enumValue.value]; callback(wrapResult(output, error)); @@ -1348,10 +1351,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.throwAsyncError" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(throwAsyncErrorWithCompletion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(throwAsyncErrorWithCompletion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1369,10 +1372,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"throwAsyncErrorFromVoid" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(throwAsyncErrorFromVoidWithCompletion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(throwAsyncErrorFromVoidWithCompletion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1390,10 +1393,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"throwAsyncFlutterError" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(throwAsyncFlutterErrorWithCompletion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(throwAsyncFlutterErrorWithCompletion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1412,17 +1415,17 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoAsyncAllTypes" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoAsyncAllTypes:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoAsyncAllTypes:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - AllTypes *arg_everything = GetNullableObjectAtIndex(args, 0); + FLTAllTypes *arg_everything = GetNullableObjectAtIndex(args, 0); [api echoAsyncAllTypes:arg_everything - completion:^(AllTypes *_Nullable output, FlutterError *_Nullable error) { + completion:^(FLTAllTypes *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; }]; @@ -1436,17 +1439,17 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoAsyncNullableAllNullableTypes" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoAsyncNullableAllNullableTypes:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoAsyncNullableAllNullableTypes:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - AllNullableTypes *arg_everything = GetNullableObjectAtIndex(args, 0); + FLTAllNullableTypes *arg_everything = GetNullableObjectAtIndex(args, 0); [api echoAsyncNullableAllNullableTypes:arg_everything - completion:^(AllNullableTypes *_Nullable output, + completion:^(FLTAllNullableTypes *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -1461,10 +1464,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoAsyncNullableInt" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoAsyncNullableInt:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoAsyncNullableInt:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1485,10 +1488,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoAsyncNullableDouble" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoAsyncNullableDouble:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoAsyncNullableDouble:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1509,10 +1512,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoAsyncNullableBool" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoAsyncNullableBool:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoAsyncNullableBool:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1533,10 +1536,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoAsyncNullableString" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoAsyncNullableString:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoAsyncNullableString:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1557,10 +1560,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoAsyncNullableUint8List" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoAsyncNullableUint8List:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoAsyncNullableUint8List:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1582,10 +1585,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoAsyncNullableObject" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoAsyncNullableObject:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoAsyncNullableObject:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1606,10 +1609,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoAsyncNullableList" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoAsyncNullableList:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoAsyncNullableList:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1630,10 +1633,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoAsyncNullableMap" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoAsyncNullableMap:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoAsyncNullableMap:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1655,26 +1658,26 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"echoAsyncNullableEnum" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoAsyncNullableEnum:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(echoAsyncNullableEnum:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSNumber *arg_anEnumAsNumber = GetNullableObjectAtIndex(args, 0); - AnEnumBox *arg_anEnum = + FLTAnEnumBox *arg_anEnum = arg_anEnumAsNumber == nil ? nil - : [[AnEnumBox alloc] initWithValue:[arg_anEnumAsNumber integerValue]]; - [api - echoAsyncNullableEnum:arg_anEnum - completion:^(AnEnumBox *_Nullable enumValue, FlutterError *_Nullable error) { - NSNumber *output = - enumValue == nil ? nil : [NSNumber numberWithInteger:enumValue.value]; - callback(wrapResult(output, error)); - }]; + : [[FLTAnEnumBox alloc] initWithValue:[arg_anEnumAsNumber integerValue]]; + [api echoAsyncNullableEnum:arg_anEnum + completion:^(FLTAnEnumBox *_Nullable enumValue, + FlutterError *_Nullable error) { + NSNumber *output = + enumValue == nil ? nil : [NSNumber numberWithInteger:enumValue.value]; + callback(wrapResult(output, error)); + }]; }]; } else { [channel setMessageHandler:nil]; @@ -1685,10 +1688,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName: @"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterNoop" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(callFlutterNoopWithCompletion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(callFlutterNoopWithCompletion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1705,10 +1708,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"callFlutterThrowError" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(callFlutterThrowErrorWithCompletion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(callFlutterThrowErrorWithCompletion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1726,10 +1729,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"callFlutterThrowErrorFromVoid" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(callFlutterThrowErrorFromVoidWithCompletion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(callFlutterThrowErrorFromVoidWithCompletion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1746,17 +1749,18 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"callFlutterEchoAllTypes" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(callFlutterEchoAllTypes:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(callFlutterEchoAllTypes:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - AllTypes *arg_everything = GetNullableObjectAtIndex(args, 0); + FLTAllTypes *arg_everything = GetNullableObjectAtIndex(args, 0); [api callFlutterEchoAllTypes:arg_everything - completion:^(AllTypes *_Nullable output, FlutterError *_Nullable error) { + completion:^(FLTAllTypes *_Nullable output, + FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; }]; @@ -1769,17 +1773,17 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"callFlutterEchoAllNullableTypes" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(callFlutterEchoAllNullableTypes:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(callFlutterEchoAllNullableTypes:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - AllNullableTypes *arg_everything = GetNullableObjectAtIndex(args, 0); + FLTAllNullableTypes *arg_everything = GetNullableObjectAtIndex(args, 0); [api callFlutterEchoAllNullableTypes:arg_everything - completion:^(AllNullableTypes *_Nullable output, + completion:^(FLTAllNullableTypes *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -1793,11 +1797,11 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"callFlutterSendMultipleNullableTypes" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector (callFlutterSendMultipleNullableTypesABool:anInt:aString:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(callFlutterSendMultipleNullableTypesABool:anInt:aString:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1808,7 +1812,7 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, [api callFlutterSendMultipleNullableTypesABool:arg_aNullableBool anInt:arg_aNullableInt aString:arg_aNullableString - completion:^(AllNullableTypes *_Nullable output, + completion:^(FLTAllNullableTypes *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -1822,10 +1826,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"callFlutterEchoBool" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(callFlutterEchoBool:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(callFlutterEchoBool:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1845,10 +1849,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"callFlutterEchoInt" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(callFlutterEchoInt:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(callFlutterEchoInt:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1868,10 +1872,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"callFlutterEchoDouble" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(callFlutterEchoDouble:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(callFlutterEchoDouble:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1891,10 +1895,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"callFlutterEchoString" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(callFlutterEchoString:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(callFlutterEchoString:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1914,10 +1918,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"callFlutterEchoUint8List" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(callFlutterEchoUint8List:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(callFlutterEchoUint8List:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1938,10 +1942,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"callFlutterEchoList" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(callFlutterEchoList:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(callFlutterEchoList:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1961,10 +1965,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"callFlutterEchoMap" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(callFlutterEchoMap:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(callFlutterEchoMap:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -1985,17 +1989,18 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"callFlutterEchoEnum" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(callFlutterEchoEnum:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(callFlutterEchoEnum:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - AnEnum arg_anEnum = [GetNullableObjectAtIndex(args, 0) integerValue]; + FLTAnEnum arg_anEnum = [GetNullableObjectAtIndex(args, 0) integerValue]; [api callFlutterEchoEnum:arg_anEnum - completion:^(AnEnumBox *_Nullable enumValue, FlutterError *_Nullable error) { + completion:^(FLTAnEnumBox *_Nullable enumValue, + FlutterError *_Nullable error) { NSNumber *output = enumValue == nil ? nil : [NSNumber numberWithInteger:enumValue.value]; callback(wrapResult(output, error)); @@ -2010,10 +2015,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"callFlutterEchoNullableBool" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(callFlutterEchoNullableBool:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(callFlutterEchoNullableBool:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -2034,10 +2039,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"callFlutterEchoNullableInt" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(callFlutterEchoNullableInt:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(callFlutterEchoNullableInt:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -2058,10 +2063,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"callFlutterEchoNullableDouble" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(callFlutterEchoNullableDouble:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(callFlutterEchoNullableDouble:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -2082,10 +2087,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"callFlutterEchoNullableString" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(callFlutterEchoNullableString:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(callFlutterEchoNullableString:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -2106,10 +2111,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"callFlutterEchoNullableUint8List" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(callFlutterEchoNullableUint8List:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(callFlutterEchoNullableUint8List:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -2130,10 +2135,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"callFlutterEchoNullableList" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(callFlutterEchoNullableList:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(callFlutterEchoNullableList:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -2154,10 +2159,10 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"callFlutterEchoNullableMap" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(callFlutterEchoNullableMap:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(callFlutterEchoNullableMap:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { @@ -2178,21 +2183,21 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @"callFlutterEchoNullableEnum" binaryMessenger:binaryMessenger - codec:HostIntegrationCoreApiGetCodec()]; + codec:FLTHostIntegrationCoreApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(callFlutterEchoNullableEnum:completion:)], - @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " @"@selector(callFlutterEchoNullableEnum:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSNumber *arg_anEnumAsNumber = GetNullableObjectAtIndex(args, 0); - AnEnumBox *arg_anEnum = + FLTAnEnumBox *arg_anEnum = arg_anEnumAsNumber == nil ? nil - : [[AnEnumBox alloc] initWithValue:[arg_anEnumAsNumber integerValue]]; + : [[FLTAnEnumBox alloc] initWithValue:[arg_anEnumAsNumber integerValue]]; [api callFlutterEchoNullableEnum:arg_anEnum - completion:^(AnEnumBox *_Nullable enumValue, + completion:^(FLTAnEnumBox *_Nullable enumValue, FlutterError *_Nullable error) { NSNumber *output = enumValue == nil ? nil @@ -2205,39 +2210,39 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, } } } -@interface FlutterIntegrationCoreApiCodecReader : FlutterStandardReader +@interface FLTFlutterIntegrationCoreApiCodecReader : FlutterStandardReader @end -@implementation FlutterIntegrationCoreApiCodecReader +@implementation FLTFlutterIntegrationCoreApiCodecReader - (nullable id)readValueOfType:(UInt8)type { switch (type) { case 128: - return [AllClassesWrapper fromList:[self readValue]]; + return [FLTAllClassesWrapper fromList:[self readValue]]; case 129: - return [AllNullableTypes fromList:[self readValue]]; + return [FLTAllNullableTypes fromList:[self readValue]]; case 130: - return [AllTypes fromList:[self readValue]]; + return [FLTAllTypes fromList:[self readValue]]; case 131: - return [TestMessage fromList:[self readValue]]; + return [FLTTestMessage fromList:[self readValue]]; default: return [super readValueOfType:type]; } } @end -@interface FlutterIntegrationCoreApiCodecWriter : FlutterStandardWriter +@interface FLTFlutterIntegrationCoreApiCodecWriter : FlutterStandardWriter @end -@implementation FlutterIntegrationCoreApiCodecWriter +@implementation FLTFlutterIntegrationCoreApiCodecWriter - (void)writeValue:(id)value { - if ([value isKindOfClass:[AllClassesWrapper class]]) { + if ([value isKindOfClass:[FLTAllClassesWrapper class]]) { [self writeByte:128]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[AllNullableTypes class]]) { + } else if ([value isKindOfClass:[FLTAllNullableTypes class]]) { [self writeByte:129]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[AllTypes class]]) { + } else if ([value isKindOfClass:[FLTAllTypes class]]) { [self writeByte:130]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[TestMessage class]]) { + } else if ([value isKindOfClass:[FLTTestMessage class]]) { [self writeByte:131]; [self writeValue:[value toList]]; } else { @@ -2246,33 +2251,33 @@ - (void)writeValue:(id)value { } @end -@interface FlutterIntegrationCoreApiCodecReaderWriter : FlutterStandardReaderWriter +@interface FLTFlutterIntegrationCoreApiCodecReaderWriter : FlutterStandardReaderWriter @end -@implementation FlutterIntegrationCoreApiCodecReaderWriter +@implementation FLTFlutterIntegrationCoreApiCodecReaderWriter - (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { - return [[FlutterIntegrationCoreApiCodecWriter alloc] initWithData:data]; + return [[FLTFlutterIntegrationCoreApiCodecWriter alloc] initWithData:data]; } - (FlutterStandardReader *)readerWithData:(NSData *)data { - return [[FlutterIntegrationCoreApiCodecReader alloc] initWithData:data]; + return [[FLTFlutterIntegrationCoreApiCodecReader alloc] initWithData:data]; } @end -NSObject *FlutterIntegrationCoreApiGetCodec(void) { +NSObject *FLTFlutterIntegrationCoreApiGetCodec(void) { static FlutterStandardMessageCodec *sSharedObject = nil; static dispatch_once_t sPred = 0; dispatch_once(&sPred, ^{ - FlutterIntegrationCoreApiCodecReaderWriter *readerWriter = - [[FlutterIntegrationCoreApiCodecReaderWriter alloc] init]; + FLTFlutterIntegrationCoreApiCodecReaderWriter *readerWriter = + [[FLTFlutterIntegrationCoreApiCodecReaderWriter alloc] init]; sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; }); return sSharedObject; } -@interface FlutterIntegrationCoreApi () +@interface FLTFlutterIntegrationCoreApi () @property(nonatomic, strong) NSObject *binaryMessenger; @end -@implementation FlutterIntegrationCoreApi +@implementation FLTFlutterIntegrationCoreApi - (instancetype)initWithBinaryMessenger:(NSObject *)binaryMessenger { self = [super init]; @@ -2287,7 +2292,7 @@ - (void)noopWithCompletion:(void (^)(FlutterError *_Nullable))completion { FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterIntegrationCoreApiGetCodec()]; + codec:FLTFlutterIntegrationCoreApiGetCodec()]; [channel sendMessage:nil reply:^(NSArray *reply) { if (reply != nil) { @@ -2309,7 +2314,7 @@ - (void)throwErrorWithCompletion:(void (^)(id _Nullable, FlutterError *_Nullable FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterIntegrationCoreApiGetCodec()]; + codec:FLTFlutterIntegrationCoreApiGetCodec()]; [channel sendMessage:nil reply:^(NSArray *reply) { if (reply != nil) { @@ -2332,7 +2337,7 @@ - (void)throwErrorFromVoidWithCompletion:(void (^)(FlutterError *_Nullable))comp FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterIntegrationCoreApiGetCodec()]; + codec:FLTFlutterIntegrationCoreApiGetCodec()]; [channel sendMessage:nil reply:^(NSArray *reply) { if (reply != nil) { @@ -2348,14 +2353,14 @@ - (void)throwErrorFromVoidWithCompletion:(void (^)(FlutterError *_Nullable))comp } }]; } -- (void)echoAllTypes:(AllTypes *)arg_everything - completion:(void (^)(AllTypes *_Nullable, FlutterError *_Nullable))completion { +- (void)echoAllTypes:(FLTAllTypes *)arg_everything + completion:(void (^)(FLTAllTypes *_Nullable, FlutterError *_Nullable))completion { NSString *channelName = @"dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAllTypes"; FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterIntegrationCoreApiGetCodec()]; + codec:FLTFlutterIntegrationCoreApiGetCodec()]; [channel sendMessage:@[ arg_everything ?: [NSNull null] ] reply:^(NSArray *reply) { if (reply != nil) { @@ -2364,7 +2369,7 @@ - (void)echoAllTypes:(AllTypes *)arg_everything message:reply[1] details:reply[2]]); } else { - AllTypes *output = reply[0] == [NSNull null] ? nil : reply[0]; + FLTAllTypes *output = reply[0] == [NSNull null] ? nil : reply[0]; completion(output, nil); } } else { @@ -2372,15 +2377,15 @@ - (void)echoAllTypes:(AllTypes *)arg_everything } }]; } -- (void)echoAllNullableTypes:(nullable AllNullableTypes *)arg_everything - completion: - (void (^)(AllNullableTypes *_Nullable, FlutterError *_Nullable))completion { +- (void)echoAllNullableTypes:(nullable FLTAllNullableTypes *)arg_everything + completion:(void (^)(FLTAllNullableTypes *_Nullable, + FlutterError *_Nullable))completion { NSString *channelName = @"dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAllNullableTypes"; FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterIntegrationCoreApiGetCodec()]; + codec:FLTFlutterIntegrationCoreApiGetCodec()]; [channel sendMessage:@[ arg_everything ?: [NSNull null] ] reply:^(NSArray *reply) { if (reply != nil) { @@ -2389,7 +2394,7 @@ - (void)echoAllNullableTypes:(nullable AllNullableTypes *)arg_everything message:reply[1] details:reply[2]]); } else { - AllNullableTypes *output = reply[0] == [NSNull null] ? nil : reply[0]; + FLTAllNullableTypes *output = reply[0] == [NSNull null] ? nil : reply[0]; completion(output, nil); } } else { @@ -2400,14 +2405,14 @@ - (void)echoAllNullableTypes:(nullable AllNullableTypes *)arg_everything - (void)sendMultipleNullableTypesABool:(nullable NSNumber *)arg_aNullableBool anInt:(nullable NSNumber *)arg_aNullableInt aString:(nullable NSString *)arg_aNullableString - completion:(void (^)(AllNullableTypes *_Nullable, + completion:(void (^)(FLTAllNullableTypes *_Nullable, FlutterError *_Nullable))completion { NSString *channelName = @"dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." @"sendMultipleNullableTypes"; FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterIntegrationCoreApiGetCodec()]; + codec:FLTFlutterIntegrationCoreApiGetCodec()]; [channel sendMessage:@[ arg_aNullableBool ?: [NSNull null], arg_aNullableInt ?: [NSNull null], arg_aNullableString ?: [NSNull null] @@ -2419,7 +2424,7 @@ - (void)sendMultipleNullableTypesABool:(nullable NSNumber *)arg_aNullableBool message:reply[1] details:reply[2]]); } else { - AllNullableTypes *output = reply[0] == [NSNull null] ? nil : reply[0]; + FLTAllNullableTypes *output = reply[0] == [NSNull null] ? nil : reply[0]; completion(output, nil); } } else { @@ -2434,7 +2439,7 @@ - (void)echoBool:(BOOL)arg_aBool FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterIntegrationCoreApiGetCodec()]; + codec:FLTFlutterIntegrationCoreApiGetCodec()]; [channel sendMessage:@[ @(arg_aBool) ] reply:^(NSArray *reply) { if (reply != nil) { @@ -2458,7 +2463,7 @@ - (void)echoInt:(NSInteger)arg_anInt FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterIntegrationCoreApiGetCodec()]; + codec:FLTFlutterIntegrationCoreApiGetCodec()]; [channel sendMessage:@[ @(arg_anInt) ] reply:^(NSArray *reply) { if (reply != nil) { @@ -2482,7 +2487,7 @@ - (void)echoDouble:(double)arg_aDouble FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterIntegrationCoreApiGetCodec()]; + codec:FLTFlutterIntegrationCoreApiGetCodec()]; [channel sendMessage:@[ @(arg_aDouble) ] reply:^(NSArray *reply) { if (reply != nil) { @@ -2506,7 +2511,7 @@ - (void)echoString:(NSString *)arg_aString FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterIntegrationCoreApiGetCodec()]; + codec:FLTFlutterIntegrationCoreApiGetCodec()]; [channel sendMessage:@[ arg_aString ?: [NSNull null] ] reply:^(NSArray *reply) { if (reply != nil) { @@ -2531,7 +2536,7 @@ - (void)echoUint8List:(FlutterStandardTypedData *)arg_aList FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterIntegrationCoreApiGetCodec()]; + codec:FLTFlutterIntegrationCoreApiGetCodec()]; [channel sendMessage:@[ arg_aList ?: [NSNull null] ] reply:^(NSArray *reply) { if (reply != nil) { @@ -2556,7 +2561,7 @@ - (void)echoList:(NSArray *)arg_aList FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterIntegrationCoreApiGetCodec()]; + codec:FLTFlutterIntegrationCoreApiGetCodec()]; [channel sendMessage:@[ arg_aList ?: [NSNull null] ] reply:^(NSArray *reply) { if (reply != nil) { @@ -2581,7 +2586,7 @@ - (void)echoMap:(NSDictionary *)arg_aMap FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterIntegrationCoreApiGetCodec()]; + codec:FLTFlutterIntegrationCoreApiGetCodec()]; [channel sendMessage:@[ arg_aMap ?: [NSNull null] ] reply:^(NSArray *reply) { if (reply != nil) { @@ -2599,14 +2604,14 @@ - (void)echoMap:(NSDictionary *)arg_aMap } }]; } -- (void)echoEnum:(AnEnum)arg_anEnum - completion:(void (^)(AnEnumBox *_Nullable, FlutterError *_Nullable))completion { +- (void)echoEnum:(FLTAnEnum)arg_anEnum + completion:(void (^)(FLTAnEnumBox *_Nullable, FlutterError *_Nullable))completion { NSString *channelName = @"dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoEnum"; FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterIntegrationCoreApiGetCodec()]; + codec:FLTFlutterIntegrationCoreApiGetCodec()]; [channel sendMessage:@[ [NSNumber numberWithInteger:arg_anEnum] ] reply:^(NSArray *reply) { if (reply != nil) { @@ -2616,10 +2621,10 @@ - (void)echoEnum:(AnEnum)arg_anEnum details:reply[2]]); } else { NSNumber *outputAsNumber = reply[0] == [NSNull null] ? nil : reply[0]; - AnEnumBox *output = + FLTAnEnumBox *output = outputAsNumber == nil ? nil - : [[AnEnumBox alloc] initWithValue:[outputAsNumber integerValue]]; + : [[FLTAnEnumBox alloc] initWithValue:[outputAsNumber integerValue]]; completion(output, nil); } } else { @@ -2634,7 +2639,7 @@ - (void)echoNullableBool:(nullable NSNumber *)arg_aBool FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterIntegrationCoreApiGetCodec()]; + codec:FLTFlutterIntegrationCoreApiGetCodec()]; [channel sendMessage:@[ arg_aBool ?: [NSNull null] ] reply:^(NSArray *reply) { if (reply != nil) { @@ -2658,7 +2663,7 @@ - (void)echoNullableInt:(nullable NSNumber *)arg_anInt FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterIntegrationCoreApiGetCodec()]; + codec:FLTFlutterIntegrationCoreApiGetCodec()]; [channel sendMessage:@[ arg_anInt ?: [NSNull null] ] reply:^(NSArray *reply) { if (reply != nil) { @@ -2682,7 +2687,7 @@ - (void)echoNullableDouble:(nullable NSNumber *)arg_aDouble FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterIntegrationCoreApiGetCodec()]; + codec:FLTFlutterIntegrationCoreApiGetCodec()]; [channel sendMessage:@[ arg_aDouble ?: [NSNull null] ] reply:^(NSArray *reply) { if (reply != nil) { @@ -2706,7 +2711,7 @@ - (void)echoNullableString:(nullable NSString *)arg_aString FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterIntegrationCoreApiGetCodec()]; + codec:FLTFlutterIntegrationCoreApiGetCodec()]; [channel sendMessage:@[ arg_aString ?: [NSNull null] ] reply:^(NSArray *reply) { if (reply != nil) { @@ -2731,7 +2736,7 @@ - (void)echoNullableUint8List:(nullable FlutterStandardTypedData *)arg_aList FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterIntegrationCoreApiGetCodec()]; + codec:FLTFlutterIntegrationCoreApiGetCodec()]; [channel sendMessage:@[ arg_aList ?: [NSNull null] ] reply:^(NSArray *reply) { if (reply != nil) { @@ -2756,7 +2761,7 @@ - (void)echoNullableList:(nullable NSArray *)arg_aList FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterIntegrationCoreApiGetCodec()]; + codec:FLTFlutterIntegrationCoreApiGetCodec()]; [channel sendMessage:@[ arg_aList ?: [NSNull null] ] reply:^(NSArray *reply) { if (reply != nil) { @@ -2781,7 +2786,7 @@ - (void)echoNullableMap:(nullable NSDictionary *)arg_aMap FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterIntegrationCoreApiGetCodec()]; + codec:FLTFlutterIntegrationCoreApiGetCodec()]; [channel sendMessage:@[ arg_aMap ?: [NSNull null] ] reply:^(NSArray *reply) { if (reply != nil) { @@ -2799,14 +2804,14 @@ - (void)echoNullableMap:(nullable NSDictionary *)arg_aMap } }]; } -- (void)echoNullableEnum:(nullable AnEnumBox *)arg_anEnum - completion:(void (^)(AnEnumBox *_Nullable, FlutterError *_Nullable))completion { +- (void)echoNullableEnum:(nullable FLTAnEnumBox *)arg_anEnum + completion:(void (^)(FLTAnEnumBox *_Nullable, FlutterError *_Nullable))completion { NSString *channelName = @"dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableEnum"; FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterIntegrationCoreApiGetCodec()]; + codec:FLTFlutterIntegrationCoreApiGetCodec()]; [channel sendMessage:@[ arg_anEnum == nil ? [NSNull null] : [NSNumber numberWithInteger:arg_anEnum.value] ] reply:^(NSArray *reply) { @@ -2817,10 +2822,10 @@ - (void)echoNullableEnum:(nullable AnEnumBox *)arg_anEnum details:reply[2]]); } else { NSNumber *outputAsNumber = reply[0] == [NSNull null] ? nil : reply[0]; - AnEnumBox *output = + FLTAnEnumBox *output = outputAsNumber == nil ? nil - : [[AnEnumBox alloc] initWithValue:[outputAsNumber integerValue]]; + : [[FLTAnEnumBox alloc] initWithValue:[outputAsNumber integerValue]]; completion(output, nil); } } else { @@ -2834,7 +2839,7 @@ - (void)noopAsyncWithCompletion:(void (^)(FlutterError *_Nullable))completion { FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterIntegrationCoreApiGetCodec()]; + codec:FLTFlutterIntegrationCoreApiGetCodec()]; [channel sendMessage:nil reply:^(NSArray *reply) { if (reply != nil) { @@ -2857,7 +2862,7 @@ - (void)echoAsyncString:(NSString *)arg_aString FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterIntegrationCoreApiGetCodec()]; + codec:FLTFlutterIntegrationCoreApiGetCodec()]; [channel sendMessage:@[ arg_aString ?: [NSNull null] ] reply:^(NSArray *reply) { if (reply != nil) { @@ -2876,22 +2881,22 @@ - (void)echoAsyncString:(NSString *)arg_aString } @end -NSObject *HostTrivialApiGetCodec(void) { +NSObject *FLTHostTrivialApiGetCodec(void) { static FlutterStandardMessageCodec *sSharedObject = nil; sSharedObject = [FlutterStandardMessageCodec sharedInstance]; return sSharedObject; } -void SetUpHostTrivialApi(id binaryMessenger, - NSObject *api) { +void SetUpFLTHostTrivialApi(id binaryMessenger, + NSObject *api) { { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostTrivialApi.noop" binaryMessenger:binaryMessenger - codec:HostTrivialApiGetCodec()]; + codec:FLTHostTrivialApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(noopWithError:)], - @"HostTrivialApi api (%@) doesn't respond to @selector(noopWithError:)", api); + @"FLTHostTrivialApi api (%@) doesn't respond to @selector(noopWithError:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; [api noopWithError:&error]; @@ -2902,21 +2907,23 @@ void SetUpHostTrivialApi(id binaryMessenger, } } } -NSObject *HostSmallApiGetCodec(void) { +NSObject *FLTHostSmallApiGetCodec(void) { static FlutterStandardMessageCodec *sSharedObject = nil; sSharedObject = [FlutterStandardMessageCodec sharedInstance]; return sSharedObject; } -void SetUpHostSmallApi(id binaryMessenger, NSObject *api) { +void SetUpFLTHostSmallApi(id binaryMessenger, + NSObject *api) { { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostSmallApi.echo" binaryMessenger:binaryMessenger - codec:HostSmallApiGetCodec()]; + codec:FLTHostSmallApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(echoString:completion:)], - @"HostSmallApi api (%@) doesn't respond to @selector(echoString:completion:)", api); + @"FLTHostSmallApi api (%@) doesn't respond to @selector(echoString:completion:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSString *arg_aString = GetNullableObjectAtIndex(args, 0); @@ -2933,10 +2940,10 @@ void SetUpHostSmallApi(id binaryMessenger, NSObject binaryMessenger, NSObject *FlutterSmallApiGetCodec(void) { +NSObject *FLTFlutterSmallApiGetCodec(void) { static FlutterStandardMessageCodec *sSharedObject = nil; static dispatch_once_t sPred = 0; dispatch_once(&sPred, ^{ - FlutterSmallApiCodecReaderWriter *readerWriter = - [[FlutterSmallApiCodecReaderWriter alloc] init]; + FLTFlutterSmallApiCodecReaderWriter *readerWriter = + [[FLTFlutterSmallApiCodecReaderWriter alloc] init]; sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; }); return sSharedObject; } -@interface FlutterSmallApi () +@interface FLTFlutterSmallApi () @property(nonatomic, strong) NSObject *binaryMessenger; @end -@implementation FlutterSmallApi +@implementation FLTFlutterSmallApi - (instancetype)initWithBinaryMessenger:(NSObject *)binaryMessenger { self = [super init]; @@ -3009,14 +3016,14 @@ - (instancetype)initWithBinaryMessenger:(NSObject *)bina } return self; } -- (void)echoWrappedList:(TestMessage *)arg_msg - completion:(void (^)(TestMessage *_Nullable, FlutterError *_Nullable))completion { +- (void)echoWrappedList:(FLTTestMessage *)arg_msg + completion:(void (^)(FLTTestMessage *_Nullable, FlutterError *_Nullable))completion { NSString *channelName = @"dev.flutter.pigeon.pigeon_integration_tests.FlutterSmallApi.echoWrappedList"; FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterSmallApiGetCodec()]; + codec:FLTFlutterSmallApiGetCodec()]; [channel sendMessage:@[ arg_msg ?: [NSNull null] ] reply:^(NSArray *reply) { if (reply != nil) { @@ -3025,7 +3032,7 @@ - (void)echoWrappedList:(TestMessage *)arg_msg message:reply[1] details:reply[2]]); } else { - TestMessage *output = reply[0] == [NSNull null] ? nil : reply[0]; + FLTTestMessage *output = reply[0] == [NSNull null] ? nil : reply[0]; completion(output, nil); } } else { @@ -3039,7 +3046,7 @@ - (void)echoString:(NSString *)arg_aString FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:channelName binaryMessenger:self.binaryMessenger - codec:FlutterSmallApiGetCodec()]; + codec:FLTFlutterSmallApiGetCodec()]; [channel sendMessage:@[ arg_aString ?: [NSNull null] ] reply:^(NSArray *reply) { if (reply != nil) { diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml index a0cbdbd1f82a..c97ed7d51e10 100644 --- a/packages/pigeon/pubspec.yaml +++ b/packages/pigeon/pubspec.yaml @@ -2,7 +2,7 @@ name: pigeon description: Code generator tool to make communication between Flutter and the host platform type-safe and easier. repository: https://github.com/flutter/packages/tree/main/packages/pigeon issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+pigeon%22 -version: 17.1.2 # This must match the version in lib/generator_tools.dart +version: 17.1.3 # This must match the version in lib/generator_tools.dart environment: sdk: ^3.1.0 diff --git a/packages/pigeon/test/objc_generator_test.dart b/packages/pigeon/test/objc_generator_test.dart index 267747e1e913..7717f0ca8779 100644 --- a/packages/pigeon/test/objc_generator_test.dart +++ b/packages/pigeon/test/objc_generator_test.dart @@ -2931,4 +2931,178 @@ void main() { 'return [FlutterError errorWithCode:@"channel-error" message:[NSString stringWithFormat:@"%@/%@/%@", @"Unable to establish connection on channel: \'", channelName, @"\'."] details:@""]')); expect(code, contains('completion(createConnectionError(channelName))')); }); + + test('header of FlutterApi uses correct enum name with prefix', () { + final Enum enum1 = Enum( + name: 'Enum1', + members: [ + EnumMember(name: 'one'), + EnumMember(name: 'two'), + ], + ); + final Root root = Root(apis: [ + AstFlutterApi(name: 'Api', methods: [ + Method( + name: 'doSomething', + location: ApiLocation.flutter, + isAsynchronous: true, + parameters: [], + returnType: TypeDeclaration( + baseName: 'Enum1', + isNullable: false, + associatedEnum: enum1, + ), + ) + ]), + ], classes: [], enums: [ + enum1, + ]); + final StringBuffer sink = StringBuffer(); + const ObjcGenerator generator = ObjcGenerator(); + final OutputFileOptions generatorOptions = + OutputFileOptions( + fileType: FileType.header, + languageOptions: const ObjcOptions(prefix: 'FLT'), + ); + generator.generate( + generatorOptions, + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final String code = sink.toString(); + expect(code, isNot(contains('FLTFLT'))); + expect(code, contains('FLTEnum1Box')); + }); + + test('source of FlutterApi uses correct enum name with prefix', () { + final Enum enum1 = Enum( + name: 'Enum1', + members: [ + EnumMember(name: 'one'), + EnumMember(name: 'two'), + ], + ); + final Root root = Root(apis: [ + AstFlutterApi(name: 'Api', methods: [ + Method( + name: 'doSomething', + location: ApiLocation.flutter, + isAsynchronous: true, + parameters: [], + returnType: TypeDeclaration( + baseName: 'Enum1', + isNullable: false, + associatedEnum: enum1, + ), + ) + ]), + ], classes: [], enums: [ + enum1, + ]); + final StringBuffer sink = StringBuffer(); + const ObjcGenerator generator = ObjcGenerator(); + final OutputFileOptions generatorOptions = + OutputFileOptions( + fileType: FileType.source, + languageOptions: const ObjcOptions(prefix: 'FLT'), + ); + generator.generate( + generatorOptions, + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final String code = sink.toString(); + expect(code, isNot(contains('FLTFLT'))); + expect(code, contains('FLTEnum1Box')); + }); + + test('header of HostApi uses correct enum name with prefix', () { + final Enum enum1 = Enum( + name: 'Enum1', + members: [ + EnumMember(name: 'one'), + EnumMember(name: 'two'), + ], + ); + final TypeDeclaration enumType = TypeDeclaration( + baseName: 'Enum1', + isNullable: false, + associatedEnum: enum1, + ); + final Root root = Root(apis: [ + AstHostApi(name: 'Api', methods: [ + Method( + name: 'doSomething', + location: ApiLocation.host, + isAsynchronous: true, + parameters: [Parameter(name: 'value', type: enumType)], + returnType: enumType, + ) + ]), + ], classes: [], enums: [ + enum1, + ]); + final StringBuffer sink = StringBuffer(); + const ObjcGenerator generator = ObjcGenerator(); + final OutputFileOptions generatorOptions = + OutputFileOptions( + fileType: FileType.header, + languageOptions: const ObjcOptions(prefix: 'FLT'), + ); + generator.generate( + generatorOptions, + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final String code = sink.toString(); + expect(code, isNot(contains('FLTFLT'))); + expect(code, contains('FLTEnum1Box')); + }); + + test('source of HostApi uses correct enum name with prefix', () { + final Enum enum1 = Enum( + name: 'Enum1', + members: [ + EnumMember(name: 'one'), + EnumMember(name: 'two'), + ], + ); + final TypeDeclaration enumType = TypeDeclaration( + baseName: 'Enum1', + isNullable: false, + associatedEnum: enum1, + ); + final Root root = Root(apis: [ + AstHostApi(name: 'Api', methods: [ + Method( + name: 'doSomething', + location: ApiLocation.host, + isAsynchronous: true, + parameters: [Parameter(name: 'value', type: enumType)], + returnType: enumType, + ) + ]), + ], classes: [], enums: [ + enum1, + ]); + final StringBuffer sink = StringBuffer(); + const ObjcGenerator generator = ObjcGenerator(); + final OutputFileOptions generatorOptions = + OutputFileOptions( + fileType: FileType.source, + languageOptions: const ObjcOptions(prefix: 'FLT'), + ); + generator.generate( + generatorOptions, + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final String code = sink.toString(); + expect(code, isNot(contains('FLTFLT'))); + expect(code, contains('FLTEnum1Box')); + }); } diff --git a/packages/pigeon/tool/shared/generation.dart b/packages/pigeon/tool/shared/generation.dart index 68bcc10d6163..51f0e95e851f 100644 --- a/packages/pigeon/tool/shared/generation.dart +++ b/packages/pigeon/tool/shared/generation.dart @@ -151,6 +151,7 @@ Future generateTestPigeons({required String baseDir}) async { objcSourceOut: skipLanguages.contains(GeneratorLanguage.objc) ? null : '$alternateOutputBase/ios/Classes/$pascalCaseName.gen.m', + objcPrefix: input == 'core_tests' ? 'FLT' : '', suppressVersion: true, dartPackageName: 'pigeon_integration_tests', ); From 6db47f400af9c3721b04714d3e43cafc7f493284 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 14 Mar 2024 23:24:05 -0400 Subject: [PATCH 082/126] [camera_web][google_maps_flutter] Fix tests throwing errors after test completion with manual roll (#6318) It looks like https://github.com/flutter/flutter/commit/77651bc4969404dfa456b88ea08e052c47c5744f made errors thrown after a test to cause the test as failed. This PR tries to update the tests to not cause an error after the test completes. See https://logs.chromium.org/logs/flutter/buildbucket/cr-buildbucket/8753561699724872161/+/u/Run_package_tests/drive_examples/stdout?format=raw for original tests failures. See https://github.com/flutter/flutter/issues/145149 --- .ci/flutter_master.version | 2 +- .../integration_test/camera_web_test.dart | 21 ++++++++----------- .../integration_test/projection_test.dart | 5 ++++- script/configs/exclude_integration_web.yaml | 2 ++ 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 878321c64055..c8d242720482 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -394269f9ea2e6ada1ba69b798791d6c3bec51168 +c01d7f06986146646fb26470453b9a6eda033872 diff --git a/packages/camera/camera_web/example/integration_test/camera_web_test.dart b/packages/camera/camera_web/example/integration_test/camera_web_test.dart index 6c73bcf76bdc..56fb16cfab9a 100644 --- a/packages/camera/camera_web/example/integration_test/camera_web_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_web_test.dart @@ -2384,10 +2384,9 @@ void main() { testWidgets('onCameraResolutionChanged emits an empty stream', (WidgetTester tester) async { - expect( - CameraPlatform.instance.onCameraResolutionChanged(cameraId), - emits(isEmpty), - ); + final Stream stream = + CameraPlatform.instance.onCameraResolutionChanged(cameraId); + expect(await stream.isEmpty, isTrue); }); testWidgets( @@ -2968,20 +2967,18 @@ void main() { (WidgetTester tester) async { when(() => window.screen).thenReturn(null); - expect( - CameraPlatform.instance.onDeviceOrientationChanged(), - emits(isEmpty), - ); + final Stream stream = + CameraPlatform.instance.onDeviceOrientationChanged(); + expect(await stream.isEmpty, isTrue); }); testWidgets('when screen orientation is not supported', (WidgetTester tester) async { when(() => screen.orientation).thenReturn(null); - expect( - CameraPlatform.instance.onDeviceOrientationChanged(), - emits(isEmpty), - ); + final Stream stream = + CameraPlatform.instance.onDeviceOrientationChanged(); + expect(await stream.isEmpty, isTrue); }); }); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/projection_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/projection_test.dart index e64bd43561a2..c5d2ff7aba24 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/projection_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/projection_test.dart @@ -71,7 +71,10 @@ void main() { expect(await controller.getZoomLevel(), 12); expect(coords.latitude, closeTo(19, _acceptableLatLngDelta)); expect(coords.longitude, closeTo(26, _acceptableLatLngDelta)); - }); + }, + // TODO(bparrishMines): This is failing due to an error being thrown after + // completion. See https://github.com/flutter/flutter/issues/145149 + skip: true); testWidgets('addPadding', (WidgetTester tester) async { const LatLng initialMapCenter = LatLng(0, 0); diff --git a/script/configs/exclude_integration_web.yaml b/script/configs/exclude_integration_web.yaml index 6c0fc4efcb7a..59b7d8fae145 100644 --- a/script/configs/exclude_integration_web.yaml +++ b/script/configs/exclude_integration_web.yaml @@ -1,2 +1,4 @@ # Currently missing: https://github.com/flutter/flutter/issues/82211 - file_selector +# Waiting on https://github.com/flutter/flutter/issues/145149 +- google_maps_flutter From 756dcc11180b0de60087a72cf6702ec0dc61f877 Mon Sep 17 00:00:00 2001 From: Valentin Vignal <32538273+ValentinVignal@users.noreply.github.com> Date: Fri, 15 Mar 2024 21:56:45 +0800 Subject: [PATCH 083/126] [go_router] Use `leak_tracker_flutter_testing` (#6210) --- packages/go_router/CHANGELOG.md | 5 +++-- packages/go_router/example/pubspec.yaml | 4 ++-- packages/go_router/lib/src/builder.dart | 7 +++++++ packages/go_router/pubspec.yaml | 6 +++--- packages/go_router/test/builder_test.dart | 1 + .../test/custom_transition_page_test.dart | 5 +++++ packages/go_router/test/delegate_test.dart | 18 ++++++++++++++++-- packages/go_router/test/extension_test.dart | 1 + packages/go_router/test/extra_codec_test.dart | 2 ++ packages/go_router/test/go_router_test.dart | 18 ++++++++++++++++++ .../test/helpers/error_screen_helpers.dart | 1 + packages/go_router/test/inherited_test.dart | 1 + packages/go_router/test/name_case_test.dart | 1 + packages/go_router/test/parser_test.dart | 1 + packages/go_router/test/route_data_test.dart | 9 +++++++++ .../go_router/test/routing_config_test.dart | 6 ++++++ packages/go_router/test/test_helpers.dart | 8 ++++++++ 17 files changed, 85 insertions(+), 9 deletions(-) diff --git a/packages/go_router/CHANGELOG.md b/packages/go_router/CHANGELOG.md index 3b01fb1dbb49..73e6fe03ae84 100644 --- a/packages/go_router/CHANGELOG.md +++ b/packages/go_router/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 13.2.1 -* Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. +- Updates minimum supported SDK version to Flutter 3.16/Dart 3.2. +- Fixes memory leaks. ## 13.2.0 diff --git a/packages/go_router/example/pubspec.yaml b/packages/go_router/example/pubspec.yaml index 5eb27e8536b3..eb4fd432ea46 100644 --- a/packages/go_router/example/pubspec.yaml +++ b/packages/go_router/example/pubspec.yaml @@ -4,8 +4,8 @@ version: 3.0.1 publish_to: none environment: - sdk: ^3.1.0 - flutter: ">=3.13.0" + sdk: ^3.2.0 + flutter: ">=3.16.0" dependencies: adaptive_navigation: ^0.0.4 diff --git a/packages/go_router/lib/src/builder.dart b/packages/go_router/lib/src/builder.dart index bcd79c48c136..49b6372c6933 100644 --- a/packages/go_router/lib/src/builder.dart +++ b/packages/go_router/lib/src/builder.dart @@ -189,6 +189,13 @@ class _CustomNavigatorState extends State<_CustomNavigator> { _pages = null; } + @override + void dispose() { + _controller?.dispose(); + _registry.dispose(); + super.dispose(); + } + void _updatePages(BuildContext context) { assert(_pages == null); final List> pages = >[]; diff --git a/packages/go_router/pubspec.yaml b/packages/go_router/pubspec.yaml index b93509a97adf..3974ba276066 100644 --- a/packages/go_router/pubspec.yaml +++ b/packages/go_router/pubspec.yaml @@ -1,13 +1,13 @@ name: go_router description: A declarative router for Flutter based on Navigation 2 supporting deep linking, data-driven routes and more -version: 13.2.0 +version: 13.2.1 repository: https://github.com/flutter/packages/tree/main/packages/go_router issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+go_router%22 environment: - sdk: ">=3.1.0 <4.0.0" - flutter: ">=3.13.0" + sdk: ">=3.2.0 <4.0.0" + flutter: ">=3.16.0" dependencies: collection: ^1.15.0 diff --git a/packages/go_router/test/builder_test.dart b/packages/go_router/test/builder_test.dart index f03c090a7b44..7b260d5250ea 100644 --- a/packages/go_router/test/builder_test.dart +++ b/packages/go_router/test/builder_test.dart @@ -371,6 +371,7 @@ void main() { ), ], ); + addTearDown(goRouter.dispose); await tester.pumpWidget(MaterialApp.router( routerConfig: goRouter, diff --git a/packages/go_router/test/custom_transition_page_test.dart b/packages/go_router/test/custom_transition_page_test.dart index c0dac88d0cfb..710d9d19c673 100644 --- a/packages/go_router/test/custom_transition_page_test.dart +++ b/packages/go_router/test/custom_transition_page_test.dart @@ -22,6 +22,7 @@ void main() { ), ], ); + addTearDown(router.dispose); await tester.pumpWidget( MaterialApp.router( routerConfig: router, @@ -35,6 +36,7 @@ void main() { (WidgetTester tester) async { final ValueNotifier showHomeValueNotifier = ValueNotifier(false); + addTearDown(showHomeValueNotifier.dispose); await tester.pumpWidget( MaterialApp( home: ValueListenableBuilder( @@ -83,6 +85,7 @@ void main() { testWidgets('NoTransitionPage does not apply any reverse transition', (WidgetTester tester) async { final ValueNotifier showHomeValueNotifier = ValueNotifier(true); + addTearDown(showHomeValueNotifier.dispose); await tester.pumpWidget( MaterialApp( home: ValueListenableBuilder( @@ -139,6 +142,7 @@ void main() { ), ], ); + addTearDown(router.dispose); await tester.pumpWidget(MaterialApp.router(routerConfig: router)); expect(find.byKey(homeKey), findsOneWidget); router.push('/dismissible-modal'); @@ -176,6 +180,7 @@ void main() { ), ], ); + addTearDown(router.dispose); await tester.pumpWidget(MaterialApp.router(routerConfig: router)); expect(find.byKey(homeKey), findsOneWidget); diff --git a/packages/go_router/test/delegate_test.dart b/packages/go_router/test/delegate_test.dart index 9b7a99f13fd5..6513a1c9665f 100644 --- a/packages/go_router/test/delegate_test.dart +++ b/packages/go_router/test/delegate_test.dart @@ -12,6 +12,7 @@ import 'test_helpers.dart'; Future createGoRouter( WidgetTester tester, { Listenable? refreshListenable, + bool dispose = true, }) async { final GoRouter router = GoRouter( initialLocation: '/', @@ -25,6 +26,9 @@ Future createGoRouter( ], refreshListenable: refreshListenable, ); + if (dispose) { + addTearDown(router.dispose); + } await tester.pumpWidget(MaterialApp.router( routerConfig: router, )); @@ -65,6 +69,7 @@ Future createGoRouterWithStatefulShellRoute( ], builder: mockStackedShellBuilder), ], ); + addTearDown(router.dispose); await tester.pumpWidget(MaterialApp.router( routerConfig: router, )); @@ -287,6 +292,7 @@ void main() { GoRoute(path: '/page-1', builder: (_, __) => const SizedBox()), ], ); + addTearDown(goRouter.dispose); await tester.pumpWidget( MaterialApp.router( routerConfig: goRouter, @@ -369,6 +375,7 @@ void main() { builder: (_, __) => const SizedBox()), ], ); + addTearDown(goRouter.dispose); await tester.pumpWidget( MaterialApp.router( routerConfig: goRouter, @@ -418,6 +425,7 @@ void main() { GoRoute(path: '/page-1', builder: (_, __) => const SizedBox()), ], ); + addTearDown(goRouter.dispose); await tester.pumpWidget( MaterialApp.router( routerConfig: goRouter, @@ -535,6 +543,7 @@ void main() { ), ], ); + addTearDown(router.dispose); await tester.pumpWidget(MaterialApp.router( routerConfig: router, )); @@ -634,8 +643,13 @@ void main() { testWidgets('dispose unsubscribes from refreshListenable', (WidgetTester tester) async { final FakeRefreshListenable refreshListenable = FakeRefreshListenable(); - final GoRouter goRouter = - await createGoRouter(tester, refreshListenable: refreshListenable); + addTearDown(refreshListenable.dispose); + + final GoRouter goRouter = await createGoRouter( + tester, + refreshListenable: refreshListenable, + dispose: false, + ); await tester.pumpWidget(Container()); goRouter.dispose(); expect(refreshListenable.unsubscribed, true); diff --git a/packages/go_router/test/extension_test.dart b/packages/go_router/test/extension_test.dart index 97e5401bad6e..0f314edbf66e 100644 --- a/packages/go_router/test/extension_test.dart +++ b/packages/go_router/test/extension_test.dart @@ -26,6 +26,7 @@ void main() { builder: (_, __) => const SizedBox()) ], ); + addTearDown(router.dispose); await tester.pumpWidget(MaterialApp.router( routerConfig: router, )); diff --git a/packages/go_router/test/extra_codec_test.dart b/packages/go_router/test/extra_codec_test.dart index 3c858cad17d8..35291db978d8 100644 --- a/packages/go_router/test/extra_codec_test.dart +++ b/packages/go_router/test/extra_codec_test.dart @@ -32,6 +32,8 @@ void main() { return null; }, ); + + addTearDown(router.dispose); final SimpleDependency dependency = SimpleDependency(); addTearDown(() => dependency.dispose()); diff --git a/packages/go_router/test/go_router_test.dart b/packages/go_router/test/go_router_test.dart index 852e7d9fec6f..776e9a4d680e 100644 --- a/packages/go_router/test/go_router_test.dart +++ b/packages/go_router/test/go_router_test.dart @@ -968,6 +968,8 @@ void main() { testWidgets('does not crash when inherited widget changes', (WidgetTester tester) async { final ValueNotifier notifier = ValueNotifier('initial'); + + addTearDown(notifier.dispose); final List routes = [ GoRoute( path: '/', @@ -985,6 +987,7 @@ void main() { final GoRouter router = GoRouter( routes: routes, ); + addTearDown(router.dispose); await tester.pumpWidget( MaterialApp.router( routerConfig: router, @@ -3212,6 +3215,7 @@ void main() { (WidgetTester tester) async { final GoRouterNamedLocationSpy router = GoRouterNamedLocationSpy(routes: routes); + addTearDown(router.dispose); await tester.pumpWidget( MaterialApp.router( routerConfig: router, @@ -3230,6 +3234,7 @@ void main() { testWidgets('calls [go] on closest GoRouter', (WidgetTester tester) async { final GoRouterGoSpy router = GoRouterGoSpy(routes: routes); + addTearDown(router.dispose); await tester.pumpWidget( MaterialApp.router( routerConfig: router, @@ -3247,6 +3252,7 @@ void main() { testWidgets('calls [goNamed] on closest GoRouter', (WidgetTester tester) async { final GoRouterGoNamedSpy router = GoRouterGoNamedSpy(routes: routes); + addTearDown(router.dispose); await tester.pumpWidget( MaterialApp.router( routerConfig: router, @@ -3268,6 +3274,7 @@ void main() { testWidgets('calls [push] on closest GoRouter', (WidgetTester tester) async { final GoRouterPushSpy router = GoRouterPushSpy(routes: routes); + addTearDown(router.dispose); await tester.pumpWidget( MaterialApp.router( routerConfig: router, @@ -3285,6 +3292,7 @@ void main() { testWidgets('calls [push] on closest GoRouter and waits for result', (WidgetTester tester) async { final GoRouterPushSpy router = GoRouterPushSpy(routes: routes); + addTearDown(router.dispose); await tester.pumpWidget( MaterialApp.router( routeInformationProvider: router.routeInformationProvider, @@ -3305,6 +3313,7 @@ void main() { testWidgets('calls [pushNamed] on closest GoRouter', (WidgetTester tester) async { final GoRouterPushNamedSpy router = GoRouterPushNamedSpy(routes: routes); + addTearDown(router.dispose); await tester.pumpWidget( MaterialApp.router( routerConfig: router, @@ -3326,6 +3335,7 @@ void main() { testWidgets('calls [pushNamed] on closest GoRouter and waits for result', (WidgetTester tester) async { final GoRouterPushNamedSpy router = GoRouterPushNamedSpy(routes: routes); + addTearDown(router.dispose); await tester.pumpWidget( MaterialApp.router( routeInformationProvider: router.routeInformationProvider, @@ -3349,6 +3359,7 @@ void main() { testWidgets('calls [pop] on closest GoRouter', (WidgetTester tester) async { final GoRouterPopSpy router = GoRouterPopSpy(routes: routes); + addTearDown(router.dispose); await tester.pumpWidget( MaterialApp.router( routerConfig: router, @@ -3363,6 +3374,7 @@ void main() { testWidgets('calls [pop] on closest GoRouter with result', (WidgetTester tester) async { final GoRouterPopSpy router = GoRouterPopSpy(routes: routes); + addTearDown(router.dispose); await tester.pumpWidget( MaterialApp.router( routerConfig: router, @@ -4315,6 +4327,7 @@ void main() { GoRoute(path: '/a', builder: (_, __) => const DummyScreen()), ], ); + addTearDown(router.dispose); await tester.pumpWidget( MaterialApp.router( @@ -4377,6 +4390,7 @@ void main() { ), ], ); + addTearDown(router.dispose); await tester.pumpWidget( MaterialApp.router( @@ -4443,6 +4457,7 @@ void main() { ), ], ); + addTearDown(router.dispose); await tester.pumpWidget( MaterialApp.router( @@ -4499,6 +4514,7 @@ void main() { ), ], ); + addTearDown(router.dispose); await tester.pumpWidget(MaterialApp.router(routerConfig: router)); @@ -4568,6 +4584,7 @@ void main() { ), ], ); + addTearDown(router.dispose); await tester.pumpWidget( MaterialApp.router( @@ -4638,6 +4655,7 @@ void main() { ), ], ); + addTearDown(router.dispose); await tester.pumpWidget(MaterialApp.router(routerConfig: router)); diff --git a/packages/go_router/test/helpers/error_screen_helpers.dart b/packages/go_router/test/helpers/error_screen_helpers.dart index 26822e27333c..56f03e9fd38d 100644 --- a/packages/go_router/test/helpers/error_screen_helpers.dart +++ b/packages/go_router/test/helpers/error_screen_helpers.dart @@ -42,6 +42,7 @@ WidgetTesterCallback testClickingTheButtonRedirectsToRoot({ ), ], ); + addTearDown(router.dispose); await tester.pumpWidget(appRouterBuilder(router)); await tester.tap(buttonFinder); await tester.pumpAndSettle(); diff --git a/packages/go_router/test/inherited_test.dart b/packages/go_router/test/inherited_test.dart index 6c6d965ceb34..a5902d47dfee 100644 --- a/packages/go_router/test/inherited_test.dart +++ b/packages/go_router/test/inherited_test.dart @@ -97,6 +97,7 @@ void main() { ) ], ); + addTearDown(router.dispose); await tester.pumpWidget( MaterialApp.router( diff --git a/packages/go_router/test/name_case_test.dart b/packages/go_router/test/name_case_test.dart index 6e3f067197fe..d2323c346533 100644 --- a/packages/go_router/test/name_case_test.dart +++ b/packages/go_router/test/name_case_test.dart @@ -25,6 +25,7 @@ void main() { ), ], ); + addTearDown(router.dispose); // run MaterialApp, initial screen path is '/' -> ScreenA await tester.pumpWidget( diff --git a/packages/go_router/test/parser_test.dart b/packages/go_router/test/parser_test.dart index 47ea84c4f940..2d42de859e7e 100644 --- a/packages/go_router/test/parser_test.dart +++ b/packages/go_router/test/parser_test.dart @@ -27,6 +27,7 @@ void main() { redirectLimit: redirectLimit, redirect: redirect, ); + addTearDown(router.dispose); await tester.pumpWidget(MaterialApp.router( routerConfig: router, )); diff --git a/packages/go_router/test/route_data_test.dart b/packages/go_router/test/route_data_test.dart index dc75fecd1899..1308d42635d1 100644 --- a/packages/go_router/test/route_data_test.dart +++ b/packages/go_router/test/route_data_test.dart @@ -199,6 +199,7 @@ void main() { initialLocation: '/build', routes: _routes, ); + addTearDown(goRouter.dispose); await tester.pumpWidget(MaterialApp.router(routerConfig: goRouter)); expect(find.byKey(const Key('build')), findsOneWidget); expect(find.byKey(const Key('buildPage')), findsNothing); @@ -212,6 +213,7 @@ void main() { initialLocation: '/build-page', routes: _routes, ); + addTearDown(goRouter.dispose); await tester.pumpWidget(MaterialApp.router(routerConfig: goRouter)); expect(find.byKey(const Key('build')), findsNothing); expect(find.byKey(const Key('buildPage')), findsOneWidget); @@ -229,6 +231,7 @@ void main() { _shellRouteDataBuilder, ], ); + addTearDown(goRouter.dispose); await tester.pumpWidget(MaterialApp.router(routerConfig: goRouter)); expect(find.byKey(const Key('builder')), findsOneWidget); expect(find.byKey(const Key('page-builder')), findsNothing); @@ -272,6 +275,7 @@ void main() { ), ], ); + addTearDown(goRouter.dispose); await tester.pumpWidget(MaterialApp.router( routerConfig: goRouter, )); @@ -301,6 +305,7 @@ void main() { _shellRouteDataPageBuilder, ], ); + addTearDown(goRouter.dispose); await tester.pumpWidget(MaterialApp.router(routerConfig: goRouter)); expect(find.byKey(const Key('builder')), findsNothing); expect(find.byKey(const Key('page-builder')), findsOneWidget); @@ -318,6 +323,7 @@ void main() { _statefulShellRouteDataBuilder, ], ); + addTearDown(goRouter.dispose); await tester.pumpWidget(MaterialApp.router(routerConfig: goRouter)); expect(find.byKey(const Key('builder')), findsOneWidget); expect(find.byKey(const Key('page-builder')), findsNothing); @@ -333,6 +339,7 @@ void main() { _statefulShellRouteDataPageBuilder, ], ); + addTearDown(goRouter.dispose); await tester.pumpWidget(MaterialApp.router(routerConfig: goRouter)); expect(find.byKey(const Key('builder')), findsNothing); expect(find.byKey(const Key('page-builder')), findsOneWidget); @@ -367,6 +374,7 @@ void main() { initialLocation: '/redirect', routes: _routes, ); + addTearDown(goRouter.dispose); await tester.pumpWidget(MaterialApp.router(routerConfig: goRouter)); expect(find.byKey(const Key('build')), findsNothing); expect(find.byKey(const Key('buildPage')), findsOneWidget); @@ -380,6 +388,7 @@ void main() { initialLocation: '/redirect-with-state', routes: _routes, ); + addTearDown(goRouter.dispose); await tester.pumpWidget(MaterialApp.router(routerConfig: goRouter)); expect(find.byKey(const Key('build')), findsNothing); expect(find.byKey(const Key('buildPage')), findsNothing); diff --git a/packages/go_router/test/routing_config_test.dart b/packages/go_router/test/routing_config_test.dart index da36885e0037..949c25934c66 100644 --- a/packages/go_router/test/routing_config_test.dart +++ b/packages/go_router/test/routing_config_test.dart @@ -18,6 +18,7 @@ void main() { redirect: (_, __) => '/', ), ); + addTearDown(config.dispose); final GoRouter router = await createRouterWithRoutingConfig(config, tester); expect(find.text('home'), findsOneWidget); @@ -35,6 +36,7 @@ void main() { ], ), ); + addTearDown(config.dispose); await createRouterWithRoutingConfig(config, tester); expect(find.text('home'), findsOneWidget); @@ -56,6 +58,7 @@ void main() { ], ), ); + addTearDown(config.dispose); final GoRouter router = await createRouterWithRoutingConfig( config, tester, @@ -87,6 +90,7 @@ void main() { ], ), ); + addTearDown(config.dispose); final GoRouter router = await createRouterWithRoutingConfig( config, tester, @@ -120,11 +124,13 @@ void main() { ], ), ); + addTearDown(config.dispose); final GoRouter router = await createRouterWithRoutingConfig( config, tester, errorBuilder: (_, __) => const Text('error'), ); + expect(find.text('home'), findsOneWidget); // Sanity check. router.goNamed('abc'); diff --git a/packages/go_router/test/test_helpers.dart b/packages/go_router/test/test_helpers.dart index 112617832001..e6f69c507885 100644 --- a/packages/go_router/test/test_helpers.dart +++ b/packages/go_router/test/test_helpers.dart @@ -188,6 +188,7 @@ Future createRouter( requestFocus: requestFocus, overridePlatformDefaultLocation: overridePlatformDefaultLocation, ); + addTearDown(goRouter.dispose); await tester.pumpWidget( MaterialApp.router( restorationScopeId: @@ -221,6 +222,7 @@ Future createRouterWithRoutingConfig( requestFocus: requestFocus, overridePlatformDefaultLocation: overridePlatformDefaultLocation, ); + addTearDown(goRouter.dispose); await tester.pumpWidget( MaterialApp.router( restorationScopeId: @@ -335,6 +337,12 @@ class DummyRestorableStatefulWidgetState } } + @override + void dispose() { + _counter.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) => Container(); } From 0a49d2431cde1373ae74721a2617bb239af7d37b Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Fri, 15 Mar 2024 12:24:20 -0700 Subject: [PATCH 084/126] [pigeon]Add hellohuanlin to pigeon iOS for code reviews (#6333) So that I get notified for code reviews. I am mainly interested in Swift codegen. *Replace this paragraph with a description of what this PR is changing or adding, and why. Consider including before/after screenshots.* *List which issues are fixed by this PR. You must list at least one issue.* *If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].* --- CODEOWNERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 7ccddb125462..4d7c8b95b3f7 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -88,8 +88,9 @@ packages/google_sign_in/google_sign_in_ios/** @vashworth packages/image_picker/image_picker_ios/** @vashworth packages/in_app_purchase/in_app_purchase_storekit/** @louisehsu packages/ios_platform_images/** @jmagman -packages/local_auth/local_auth_darwin/** @louisehsu +packages/local_auth/local_auth_darwin/** @louisehsu packages/path_provider/path_provider_foundation/** @jmagman +packages/pigeon/**/ios/**/* @hellohuanlin packages/pointer_interceptor/pointer_interceptor_ios/** @ditman packages/quick_actions/quick_actions_ios/** @hellohuanlin packages/shared_preferences/shared_preferences_foundation/** @tarrinneal From 9aa208a218346a449a698114a20b2fb5add8ecf7 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Sat, 16 Mar 2024 13:38:08 -0400 Subject: [PATCH 085/126] [pointer_interceptor_web] Skip broken semantics tests and do a manual roll (#6342) Skipping broken semantics tests. See: https://github.com/flutter/flutter/issues/145238 --- .ci/flutter_master.version | 2 +- .../example/integration_test/widget_test.dart | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index c8d242720482..de0c690e098b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c01d7f06986146646fb26470453b9a6eda033872 +71606af8d82e7a630aaff4d57cd5e984fcc3ae02 diff --git a/packages/pointer_interceptor/pointer_interceptor_web/example/integration_test/widget_test.dart b/packages/pointer_interceptor/pointer_interceptor_web/example/integration_test/widget_test.dart index d712f6fbfa00..35fe738ecfed 100644 --- a/packages/pointer_interceptor/pointer_interceptor_web/example/integration_test/widget_test.dart +++ b/packages/pointer_interceptor/pointer_interceptor_web/example/integration_test/widget_test.dart @@ -75,7 +75,10 @@ void main() { expect(element.tagName.toLowerCase(), 'flt-semantics'); expect(element.getAttribute('aria-label'), 'Works As Expected'); - }); + }, + // TODO(bparrishMines): The semantics label is returning null. + // See https://github.com/flutter/flutter/issues/145238 + skip: true); testWidgets( 'finds semantics of wrapped widgets with intercepting set to false', @@ -88,7 +91,10 @@ void main() { expect(element.tagName.toLowerCase(), 'flt-semantics'); expect(element.getAttribute('aria-label'), 'Never calls onPressed transparent'); - }); + }, + // TODO(bparrishMines): The semantics label is returning null. + // See https://github.com/flutter/flutter/issues/145238 + skip: true); testWidgets('finds semantics of unwrapped elements', (WidgetTester tester) async { @@ -99,7 +105,10 @@ void main() { expect(element.tagName.toLowerCase(), 'flt-semantics'); expect(element.getAttribute('aria-label'), 'Never calls onPressed'); - }); + }, + // TODO(bparrishMines): The semantics label is returning null. + // See https://github.com/flutter/flutter/issues/145238 + skip: true); // Notice that, when hit-testing the background platform view, instead of // finding a semantics node, the platform view itself is found. This is From a757073ac4eaf05b7516d3d0488e5c98b221043f Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 17 Mar 2024 11:46:22 -0400 Subject: [PATCH 086/126] Roll Flutter from 71606af8d82e to 7c860ddf9705 (21 revisions) (#6345) https://github.com/flutter/flutter/compare/71606af8d82e...7c860ddf9705 2024-03-17 engine-flutter-autoroll@skia.org Roll Flutter Engine from cfbf11d757d1 to 4c420dd2dfd9 (2 revisions) (flutter/flutter#145280) 2024-03-17 ian@hixie.ch Factor out use of "print" in flutter_goldens (flutter/flutter#144846) 2024-03-16 engine-flutter-autoroll@skia.org Roll Flutter Engine from dd4050334ff3 to cfbf11d757d1 (1 revision) (flutter/flutter#145275) 2024-03-16 engine-flutter-autoroll@skia.org Roll Flutter Engine from 618ee6c8ba34 to dd4050334ff3 (1 revision) (flutter/flutter#145270) 2024-03-16 engine-flutter-autoroll@skia.org Roll Flutter Engine from e4602aa149d8 to 618ee6c8ba34 (1 revision) (flutter/flutter#145266) 2024-03-16 engine-flutter-autoroll@skia.org Roll Flutter Engine from 91b9cf24d328 to e4602aa149d8 (1 revision) (flutter/flutter#145259) 2024-03-16 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Reland #128236 "Improve build output for all platforms" (#143166)" (flutter/flutter#145261) 2024-03-16 6655696+guidezpl@users.noreply.github.com Reland #128236 "Improve build output for all platforms" (flutter/flutter#143166) 2024-03-16 6655696+guidezpl@users.noreply.github.com Introduce `Split` curve (flutter/flutter#143130) 2024-03-16 engine-flutter-autoroll@skia.org Roll Flutter Engine from 120e52b8a1c2 to 91b9cf24d328 (1 revision) (flutter/flutter#145256) 2024-03-16 engine-flutter-autoroll@skia.org Roll Flutter Engine from 3abc25139959 to 120e52b8a1c2 (1 revision) (flutter/flutter#145254) 2024-03-16 engine-flutter-autoroll@skia.org Roll Flutter Engine from 6f180615e6ac to 3abc25139959 (1 revision) (flutter/flutter#145253) 2024-03-16 magder@google.com Platform view devicelab ad banner scroll list real ads (flutter/flutter#145239) 2024-03-16 engine-flutter-autoroll@skia.org Roll Flutter Engine from 6ddaa40bf1a6 to 6f180615e6ac (1 revision) (flutter/flutter#145251) 2024-03-16 engine-flutter-autoroll@skia.org Roll Flutter Engine from d46c1fa17dda to 6ddaa40bf1a6 (1 revision) (flutter/flutter#145250) 2024-03-16 engine-flutter-autoroll@skia.org Roll Flutter Engine from 7f15dc1200bc to d46c1fa17dda (2 revisions) (flutter/flutter#145248) 2024-03-16 31859944+LongCatIsLooong@users.noreply.github.com Use a separate `TextPainter` for intrinsics calculation in `RenderEditable` and `RenderParagraph` (flutter/flutter#144577) 2024-03-16 engine-flutter-autoroll@skia.org Roll Flutter Engine from 2db915c64e6e to 7f15dc1200bc (1 revision) (flutter/flutter#145245) 2024-03-15 engine-flutter-autoroll@skia.org Roll Flutter Engine from ba1115c30381 to 2db915c64e6e (2 revisions) (flutter/flutter#145240) 2024-03-15 engine-flutter-autoroll@skia.org Roll Flutter Engine from c2fd5333a5d2 to ba1115c30381 (3 revisions) (flutter/flutter#145237) 2024-03-15 91688203+yusuf-goog@users.noreply.github.com Renaming the virtual device Nexus5 name to Nexus5.gce_x86 (flutter/flutter#145225) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC rmistry@google.com,stuartmorgan@google.com,tarrinneal@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index de0c690e098b..de6612a5dadb 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -71606af8d82e7a630aaff4d57cd5e984fcc3ae02 +7c860ddf970575d4ca6dd0ccc4bd823613a16359 From 1b9bab3c068d4180030f6b9400a652414e925644 Mon Sep 17 00:00:00 2001 From: leiatfly <16229121+leiatfly@users.noreply.github.com> Date: Mon, 18 Mar 2024 09:13:04 -0700 Subject: [PATCH 087/126] [flutter_markdown] Adds onSelectionChanged in Markdown (#6169) With this PR, a client can use 'onSelectionChanged' to handle selected text in Markdown. This provides another way to fix issue [#107073](https://github.com/flutter/flutter/issues/107073), along with the prior solution in [PR/6062](https://github.com/flutter/packages/pull/6062) --- packages/flutter_markdown/CHANGELOG.md | 4 ++ packages/flutter_markdown/README.md | 9 +++ .../flutter_markdown/lib/src/builder.dart | 7 +++ packages/flutter_markdown/lib/src/widget.dart | 19 +++++++ packages/flutter_markdown/pubspec.yaml | 2 +- packages/flutter_markdown/test/text_test.dart | 57 +++++++++++++++++++ packages/flutter_markdown/test/utils.dart | 46 +++++++++++++++ 7 files changed, 143 insertions(+), 1 deletion(-) diff --git a/packages/flutter_markdown/CHANGELOG.md b/packages/flutter_markdown/CHANGELOG.md index e0fe8c99c3f5..3ce5c52c1a7b 100644 --- a/packages/flutter_markdown/CHANGELOG.md +++ b/packages/flutter_markdown/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.21+1 + +* Adds `onSelectionChanged` to the constructors of `Markdown` and `MarkdownBody`. + ## 0.6.21 * Fixes support for `WidgetSpan` in `Text.rich` elements inside `MarkdownElementBuilder`. diff --git a/packages/flutter_markdown/README.md b/packages/flutter_markdown/README.md index 6a97d169e7fb..93cc6f50b8c3 100644 --- a/packages/flutter_markdown/README.md +++ b/packages/flutter_markdown/README.md @@ -72,6 +72,15 @@ but it's possible to create your own custom styling. Use the MarkdownStyle class to pass in your own style. If you don't want to use Markdown outside of material design, use the MarkdownRaw class. +## Selection + +By default, Markdown is not selectable. A caller may use the following ways to +customize the selection behavior of Markdown: + +* Set `selectable` to true, and use `onTapText` and `onSelectionChanged` to + handle tapping and selecting events. +* Set `selectable` to false, and wrap Markdown with [`SelectionArea`](https://api.flutter.dev/flutter/material/SelectionArea-class.html) or [`SelectionRegion`](https://api.flutter.dev/flutter/widgets/SelectableRegion-class.html). + ## Emoji Support Emoji glyphs can be included in the formatted text displayed by the Markdown diff --git a/packages/flutter_markdown/lib/src/builder.dart b/packages/flutter_markdown/lib/src/builder.dart index 6188099429f0..8ecdd7b9b3fb 100644 --- a/packages/flutter_markdown/lib/src/builder.dart +++ b/packages/flutter_markdown/lib/src/builder.dart @@ -112,6 +112,7 @@ class MarkdownBuilder implements md.NodeVisitor { required this.paddingBuilders, required this.listItemCrossAxisAlignment, this.fitContent = false, + this.onSelectionChanged, this.onTapText, this.softLineBreak = false, }); @@ -155,6 +156,9 @@ class MarkdownBuilder implements md.NodeVisitor { /// does not allow for intrinsic height measurements. final MarkdownListItemCrossAxisAlignment listItemCrossAxisAlignment; + /// Called when the user changes selection when [selectable] is set to true. + final MarkdownOnSelectionChangedCallback? onSelectionChanged; + /// Default tap handler used when [selectable] is set to true final VoidCallback? onTapText; @@ -942,6 +946,9 @@ class MarkdownBuilder implements md.NodeVisitor { text!, textScaler: styleSheet.textScaler, textAlign: textAlign ?? TextAlign.start, + onSelectionChanged: + (TextSelection selection, SelectionChangedCause? cause) => + onSelectionChanged!(text.text, selection, cause), onTap: onTapText, key: k, ); diff --git a/packages/flutter_markdown/lib/src/widget.dart b/packages/flutter_markdown/lib/src/widget.dart index 3feee6b70f57..8edc949242ef 100644 --- a/packages/flutter_markdown/lib/src/widget.dart +++ b/packages/flutter_markdown/lib/src/widget.dart @@ -12,6 +12,18 @@ import 'package:markdown/markdown.dart' as md; import '../flutter_markdown.dart'; import '_functions_io.dart' if (dart.library.html) '_functions_web.dart'; +/// Signature for callbacks used by [MarkdownWidget] when +/// [MarkdownWidget.selectable] is set to true and the user changes selection. +/// +/// The callback will return the entire block of text available for selection, +/// along with the current [selection] and the [cause] of the selection change. +/// This is a wrapper of [SelectionChangedCallback] with additional context +/// [text] for the caller to process. +/// +/// Used by [MarkdownWidget.onSelectionChanged] +typedef MarkdownOnSelectionChangedCallback = void Function( + String? text, TextSelection selection, SelectionChangedCause? cause); + /// Signature for callbacks used by [MarkdownWidget] when the user taps a link. /// The callback will return the link text, destination, and title from the /// Markdown link tag in the document. @@ -173,6 +185,7 @@ abstract class MarkdownWidget extends StatefulWidget { this.styleSheet, this.styleSheetTheme = MarkdownStyleSheetBaseTheme.material, this.syntaxHighlighter, + this.onSelectionChanged, this.onTapLink, this.onTapText, this.imageDirectory, @@ -216,6 +229,9 @@ abstract class MarkdownWidget extends StatefulWidget { /// Called when the user taps a link. final MarkdownTapLinkCallback? onTapLink; + /// Called when the user changes selection when [selectable] is set to true. + final MarkdownOnSelectionChangedCallback? onSelectionChanged; + /// Default tap handler used when [selectable] is set to true final VoidCallback? onTapText; @@ -353,6 +369,7 @@ class _MarkdownWidgetState extends State paddingBuilders: widget.paddingBuilders, fitContent: widget.fitContent, listItemCrossAxisAlignment: widget.listItemCrossAxisAlignment, + onSelectionChanged: widget.onSelectionChanged, onTapText: widget.onTapText, softLineBreak: widget.softLineBreak, ); @@ -415,6 +432,7 @@ class MarkdownBody extends MarkdownWidget { super.styleSheet, super.styleSheetTheme = null, super.syntaxHighlighter, + super.onSelectionChanged, super.onTapLink, super.onTapText, super.imageDirectory, @@ -469,6 +487,7 @@ class Markdown extends MarkdownWidget { super.styleSheet, super.styleSheetTheme = null, super.syntaxHighlighter, + super.onSelectionChanged, super.onTapLink, super.onTapText, super.imageDirectory, diff --git a/packages/flutter_markdown/pubspec.yaml b/packages/flutter_markdown/pubspec.yaml index 9811a80d213f..20639b245fac 100644 --- a/packages/flutter_markdown/pubspec.yaml +++ b/packages/flutter_markdown/pubspec.yaml @@ -4,7 +4,7 @@ description: A Markdown renderer for Flutter. Create rich text output, formatted with simple Markdown tags. repository: https://github.com/flutter/packages/tree/main/packages/flutter_markdown issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_markdown%22 -version: 0.6.21 +version: 0.6.21+1 environment: sdk: ^3.3.0 diff --git a/packages/flutter_markdown/test/text_test.dart b/packages/flutter_markdown/test/text_test.dart index cb4610c3f5cc..27f16cc90044 100644 --- a/packages/flutter_markdown/test/text_test.dart +++ b/packages/flutter_markdown/test/text_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -282,6 +283,62 @@ void defineTests() { expect(textTapResults == 'Text has been tapped.', true); }, ); + + testWidgets( + 'header with line of text and onSelectionChanged callback', + (WidgetTester tester) async { + const String data = '# abc def ghi\njkl opq'; + String? selectableText; + String? selectedText; + void onSelectionChanged(String? text, TextSelection selection, + SelectionChangedCause? cause) { + selectableText = text; + selectedText = text != null ? selection.textInside(text) : null; + } + + await tester.pumpWidget( + MaterialApp( + home: Material( + child: MarkdownBody( + data: data, + selectable: true, + onSelectionChanged: onSelectionChanged, + ), + ), + ), + ); + + // Find the positions before character 'd' and 'f'. + final Offset dPos = positionInRenderedText(tester, 'abc def ghi', 4); + final Offset fPos = positionInRenderedText(tester, 'abc def ghi', 6); + // Select from 'd' until 'f'. + final TestGesture firstGesture = + await tester.startGesture(dPos, kind: PointerDeviceKind.mouse); + addTearDown(firstGesture.removePointer); + await tester.pump(); + await firstGesture.moveTo(fPos); + await firstGesture.up(); + await tester.pump(); + + expect(selectableText, 'abc def ghi'); + expect(selectedText, 'de'); + + // Find the positions before character 'j' and 'o'. + final Offset jPos = positionInRenderedText(tester, 'jkl opq', 0); + final Offset oPos = positionInRenderedText(tester, 'jkl opq', 4); + // Select from 'j' until 'o'. + final TestGesture secondGesture = + await tester.startGesture(jPos, kind: PointerDeviceKind.mouse); + addTearDown(secondGesture.removePointer); + await tester.pump(); + await secondGesture.moveTo(oPos); + await secondGesture.up(); + await tester.pump(); + + expect(selectableText, 'jkl opq'); + expect(selectedText, 'jkl '); + }, + ); }); group('Strikethrough', () { diff --git a/packages/flutter_markdown/test/utils.dart b/packages/flutter_markdown/test/utils.dart index 2d1a4ff27de5..fa635f27c706 100644 --- a/packages/flutter_markdown/test/utils.dart +++ b/packages/flutter_markdown/test/utils.dart @@ -5,8 +5,10 @@ import 'dart:convert'; import 'dart:io' as io; +import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -25,6 +27,50 @@ Iterable selfAndDescendantWidgetsOf(Finder start, WidgetTester tester) { ]; } +// Returns the RenderEditable displaying the given text. +RenderEditable findRenderEditableWithText(WidgetTester tester, String text) { + final Iterable roots = + tester.renderObjectList(find.byType(EditableText)); + expect(roots, isNotEmpty); + + late RenderEditable renderEditable; + void recursiveFinder(RenderObject child) { + if (child is RenderEditable && child.plainText == text) { + renderEditable = child; + return; + } + child.visitChildren(recursiveFinder); + } + + for (final RenderObject root in roots) { + root.visitChildren(recursiveFinder); + } + + expect(renderEditable, isNotNull); + return renderEditable; +} + +// Returns the [textOffset] position in rendered [text]. +Offset positionInRenderedText( + WidgetTester tester, String text, int textOffset) { + final RenderEditable renderEditable = + findRenderEditableWithText(tester, text); + final Iterable textOffsetPoints = + renderEditable.getEndpointsForSelection( + TextSelection.collapsed(offset: textOffset), + ); + // Map the points to global positions. + final List endpoints = + textOffsetPoints.map((TextSelectionPoint point) { + return TextSelectionPoint( + renderEditable.localToGlobal(point.point), + point.direction, + ); + }).toList(); + expect(endpoints.length, 1); + return endpoints[0].point + const Offset(kIsWeb ? 1.0 : 0.0, -2.0); +} + void expectWidgetTypes(Iterable widgets, List expected) { final List actual = widgets.map((Widget w) => w.runtimeType).toList(); expect(actual, expected); From 02128ee64b3e32b3a67b180f302da18aa0611564 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 18 Mar 2024 12:28:18 -0400 Subject: [PATCH 088/126] Roll Flutter from 7c860ddf9705 to f217fc173918 (9 revisions) (#6350) https://github.com/flutter/flutter/compare/7c860ddf9705...f217fc173918 2024-03-18 engine-flutter-autoroll@skia.org Roll Packages from 756dcc11180b to a757073ac4ea (3 revisions) (flutter/flutter#145325) 2024-03-18 engine-flutter-autoroll@skia.org Roll Flutter Engine from f11015e14d24 to 86de0f75606f (1 revision) (flutter/flutter#145322) 2024-03-18 leroux_bruno@yahoo.fr InputDecorator M3 tests migration - Step6 - constraints (flutter/flutter#145213) 2024-03-18 tessertaha@gmail.com Update `inherited_theme_test.dart`, `ink_paint_test.dart`, `ink_splash_test.dart`, `opacity_test.dart` for Material 3 (flutter/flutter#144013) 2024-03-18 engine-flutter-autoroll@skia.org Roll Flutter Engine from eac9db75f902 to f11015e14d24 (2 revisions) (flutter/flutter#145321) 2024-03-18 engine-flutter-autoroll@skia.org Roll Flutter Engine from aebc0021cd18 to eac9db75f902 (1 revision) (flutter/flutter#145308) 2024-03-18 engine-flutter-autoroll@skia.org Roll Flutter Engine from 89c775963926 to aebc0021cd18 (1 revision) (flutter/flutter#145305) 2024-03-18 engine-flutter-autoroll@skia.org Roll Flutter Engine from 5c31faa15ab7 to 89c775963926 (1 revision) (flutter/flutter#145303) 2024-03-17 engine-flutter-autoroll@skia.org Roll Flutter Engine from 4c420dd2dfd9 to 5c31faa15ab7 (2 revisions) (flutter/flutter#145294) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC camillesimon@google.com,rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index de6612a5dadb..eb2a979c26cf 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -7c860ddf970575d4ca6dd0ccc4bd823613a16359 +f217fc1739182ac5c29c4bf6ebbce5361a1fbdd6 From a2f4ce0a50570a623e54788901314b5f8352c0cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 17:31:25 +0000 Subject: [PATCH 089/126] [sign_in]: Bump com.google.android.gms:play-services-auth from 20.7.0 to 21.0.0 in /packages/google_sign_in/google_sign_in_android/android (#6159) Bumps com.google.android.gms:play-services-auth from 20.7.0 to 21.0.0. [![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=com.google.android.gms:play-services-auth&package-manager=gradle&previous-version=20.7.0&new-version=21.0.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- packages/google_sign_in/google_sign_in_android/CHANGELOG.md | 3 ++- .../google_sign_in/google_sign_in_android/android/build.gradle | 2 +- packages/google_sign_in/google_sign_in_android/pubspec.yaml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md index 87622e88d6e6..ddfb3ea65262 100644 --- a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md @@ -1,7 +1,8 @@ -## NEXT +## 6.1.22 * Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. * Updates compileSdk version to 34. +* Updates play-services-auth version to 21.0.0. ## 6.1.21 diff --git a/packages/google_sign_in/google_sign_in_android/android/build.gradle b/packages/google_sign_in/google_sign_in_android/android/build.gradle index de3ca75ca1c7..b98cfec12c62 100644 --- a/packages/google_sign_in/google_sign_in_android/android/build.gradle +++ b/packages/google_sign_in/google_sign_in_android/android/build.gradle @@ -59,7 +59,7 @@ android { } dependencies { - implementation 'com.google.android.gms:play-services-auth:20.7.0' + implementation 'com.google.android.gms:play-services-auth:21.0.0' implementation 'com.google.guava:guava:32.0.1-android' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:5.0.0' diff --git a/packages/google_sign_in/google_sign_in_android/pubspec.yaml b/packages/google_sign_in/google_sign_in_android/pubspec.yaml index 7f4e93169184..fbc7135c1bb2 100644 --- a/packages/google_sign_in/google_sign_in_android/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_android/pubspec.yaml @@ -2,7 +2,7 @@ name: google_sign_in_android description: Android implementation of the google_sign_in plugin. repository: https://github.com/flutter/packages/tree/main/packages/google_sign_in/google_sign_in_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 6.1.21 +version: 6.1.22 environment: sdk: ^3.1.0 From 52ed70260b352b7e45f2931e42ddf42073d96b21 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 19 Mar 2024 11:42:25 -0400 Subject: [PATCH 090/126] Roll Flutter from f217fc173918 to d31a85ba5c5e (23 revisions) (#6356) https://github.com/flutter/flutter/compare/f217fc173918...d31a85ba5c5e 2024-03-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from 4cec701f4635 to d8fbcfbd799c (1 revision) (flutter/flutter#145390) 2024-03-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from c9fbe6bab899 to 4cec701f4635 (1 revision) (flutter/flutter#145386) 2024-03-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from de6b8f49b849 to c9fbe6bab899 (2 revisions) (flutter/flutter#145383) 2024-03-19 leroux_bruno@yahoo.fr Activate shortcuts based on NumLock state (flutter/flutter#145146) 2024-03-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from 59519ceee30a to de6b8f49b849 (1 revision) (flutter/flutter#145381) 2024-03-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from ac8f1f233d6f to 59519ceee30a (2 revisions) (flutter/flutter#145379) 2024-03-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from 4a86b5b17c39 to ac8f1f233d6f (3 revisions) (flutter/flutter#145375) 2024-03-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from c0d1b0d5d43f to 4a86b5b17c39 (1 revision) (flutter/flutter#145373) 2024-03-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from 3d909f14118e to c0d1b0d5d43f (1 revision) (flutter/flutter#145371) 2024-03-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from 0ee413ee276a to 3d909f14118e (5 revisions) (flutter/flutter#145370) 2024-03-18 engine-flutter-autoroll@skia.org Roll Flutter Engine from 89df726bf13a to 0ee413ee276a (3 revisions) (flutter/flutter#145365) 2024-03-18 engine-flutter-autoroll@skia.org Roll Flutter Engine from 3fde3678a357 to 89df726bf13a (1 revision) (flutter/flutter#145359) 2024-03-18 49699333+dependabot[bot]@users.noreply.github.com Bump github/codeql-action from 3.24.7 to 3.24.8 (flutter/flutter#145358) 2024-03-18 danny@tuppeny.com Roll pub packages + update DAP tests (flutter/flutter#145349) 2024-03-18 prasadsunny1@gmail.com Fix for issue 140372 (flutter/flutter#144947) 2024-03-18 31859944+LongCatIsLooong@users.noreply.github.com Introduce methods for computing the baseline location of a RenderBox without affecting the current layout (flutter/flutter#144655) 2024-03-18 engine-flutter-autoroll@skia.org Roll Flutter Engine from 016206de75bc to 3fde3678a357 (2 revisions) (flutter/flutter#145350) 2024-03-18 engine-flutter-autoroll@skia.org Roll Flutter Engine from 90c4d64d410f to 016206de75bc (2 revisions) (flutter/flutter#145345) 2024-03-18 32242716+ricardoamador@users.noreply.github.com Switch hot_mode_dev_cycle_linux__benchmark to run in postsubmit (flutter/flutter#145343) 2024-03-18 engine-flutter-autoroll@skia.org Roll Flutter Engine from 9162c8309e24 to 90c4d64d410f (1 revision) (flutter/flutter#145342) 2024-03-18 15619084+vashworth@users.noreply.github.com Add --no-dds to Mac_arm64_ios version of hot_mode_dev_cycle_ios__benchmark (flutter/flutter#145335) 2024-03-18 engine-flutter-autoroll@skia.org Roll Flutter Engine from 89fb886a162c to 9162c8309e24 (2 revisions) (flutter/flutter#145336) 2024-03-18 engine-flutter-autoroll@skia.org Roll Flutter Engine from 86de0f75606f to 89fb886a162c (1 revision) (flutter/flutter#145327) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC camillesimon@google.com,rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index eb2a979c26cf..174e1417a8da 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f217fc1739182ac5c29c4bf6ebbce5361a1fbdd6 +d31a85ba5c5e67199a9a3c95fd9fef51c89a9306 From 3be3ec1f4177b2ed7183c9c4c04eaae33a158818 Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Tue, 19 Mar 2024 12:37:51 -0400 Subject: [PATCH 091/126] [camerax] Implements `setFocusMode` (#6176) Implements `setFocusMode` (also adds support for disabling auto-cancel to `FocusMeteringAction` host API implementation to accomplish this) + some minor documentation improvements based on discoveries I made while working on this :) Fixes https://github.com/flutter/flutter/issues/120467. ~To be landed after: https://github.com/flutter/packages/pull/6110~ Done :) --- .../camera_android_camerax/CHANGELOG.md | 5 + .../camera/camera_android_camerax/README.md | 8 +- .../FocusMeteringActionHostApiImpl.java | 15 +- .../camerax/GeneratedCameraXLibrary.java | 9 +- .../camerax/FocusMeteringActionTest.java | 48 +- .../example/lib/main.dart | 10 +- .../lib/src/android_camera_camerax.dart | 222 ++++- .../lib/src/camera_control.dart | 4 +- .../lib/src/camerax_library.g.dart | 14 +- .../lib/src/camerax_proxy.dart | 29 +- .../lib/src/focus_metering_action.dart | 16 +- .../pigeons/camerax_library.dart | 3 +- .../camera_android_camerax/pubspec.yaml | 2 +- .../test/android_camera_camerax_test.dart | 815 +++++++++++++++++- .../android_camera_camerax_test.mocks.dart | 181 ++-- .../test/focus_metering_action_test.dart | 9 +- .../focus_metering_action_test.mocks.dart | 2 + .../test/test_camerax_library.g.dart | 7 +- 18 files changed, 1223 insertions(+), 176 deletions(-) diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index 647a57559951..2fd0eb9ed3d5 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.6.0 + +* Implements `setFocusMode`, which makes this plugin reach feature parity with camera_android. +* Fixes `setExposureCompensationIndex` return value to use index returned by CameraX. + ## 0.5.0+36 * Implements `setExposureMode`. diff --git a/packages/camera/camera_android_camerax/README.md b/packages/camera/camera_android_camerax/README.md index fdc50955de43..d60a71a46c84 100644 --- a/packages/camera/camera_android_camerax/README.md +++ b/packages/camera/camera_android_camerax/README.md @@ -3,7 +3,7 @@ An Android implementation of [`camera`][1] that uses the [CameraX library][2]. *Note*: This package is under development, so please note the -[missing features and limitations](#missing-features-and-limitations), but +[missing features and limitations](#limitations), but otherwise feel free to try out the current implementation and provide any feedback by filing issues under [`flutter/flutter`][5] with `[camerax]` in the title, which will be actively triaged. @@ -22,7 +22,7 @@ dependencies: camera_android_camerax: ^0.5.0 ``` -## Missing features and limitations +## Limitations ### 240p resolution configuration for video recording @@ -30,10 +30,6 @@ dependencies: and thus, the plugin will fall back to 480p if configured with a `ResolutionPreset`. -### Focus mode configuration \[[Issue #120467][120467]\] - -`setFocusMode` is unimplemented. - ### Setting maximum duration and stream options for video capture Calling `startVideoCapturing` with `VideoCaptureOptions` configured with diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/FocusMeteringActionHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/FocusMeteringActionHostApiImpl.java index 5eeedd15211c..dcda333c2e90 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/FocusMeteringActionHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/FocusMeteringActionHostApiImpl.java @@ -5,6 +5,7 @@ package io.flutter.plugins.camerax; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.camera.core.FocusMeteringAction; import androidx.camera.core.MeteringPoint; @@ -29,7 +30,9 @@ public class FocusMeteringActionHostApiImpl implements FocusMeteringActionHostAp public static class FocusMeteringActionProxy { /** Creates an instance of {@link FocusMeteringAction}. */ public @NonNull FocusMeteringAction create( - @NonNull List meteringPoints, @NonNull List meteringPointModes) { + @NonNull List meteringPoints, + @NonNull List meteringPointModes, + @Nullable Boolean disableAutoCancel) { if (meteringPoints.size() >= 1 && meteringPoints.size() != meteringPointModes.size()) { throw new IllegalArgumentException( "One metering point must be specified and the number of specified metering points must match the number of specified metering point modes."); @@ -59,6 +62,10 @@ public static class FocusMeteringActionProxy { } } + if (disableAutoCancel != null && disableAutoCancel == true) { + focusMeteringActionBuilder.disableAutoCancel(); + } + return focusMeteringActionBuilder.build(); } @@ -100,7 +107,9 @@ public FocusMeteringActionHostApiImpl(@NonNull InstanceManager instanceManager) @Override public void create( - @NonNull Long identifier, @NonNull List meteringPointInfos) { + @NonNull Long identifier, + @NonNull List meteringPointInfos, + @Nullable Boolean disableAutoCancel) { final List meteringPoints = new ArrayList(); final List meteringPointModes = new ArrayList(); for (MeteringPointInfo meteringPointInfo : meteringPointInfos) { @@ -110,6 +119,6 @@ public void create( } instanceManager.addDartCreatedInstance( - proxy.create(meteringPoints, meteringPointModes), identifier); + proxy.create(meteringPoints, meteringPointModes, disableAutoCancel), identifier); } } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java index ac92427314f9..75b4a8c276bc 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java @@ -3796,7 +3796,10 @@ protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface FocusMeteringActionHostApi { - void create(@NonNull Long identifier, @NonNull List meteringPointInfos); + void create( + @NonNull Long identifier, + @NonNull List meteringPointInfos, + @Nullable Boolean disableAutoCancel); /** The codec used by FocusMeteringActionHostApi. */ static @NonNull MessageCodec getCodec() { @@ -3822,10 +3825,12 @@ static void setup( Number identifierArg = (Number) args.get(0); List meteringPointInfosArg = (List) args.get(1); + Boolean disableAutoCancelArg = (Boolean) args.get(2); try { api.create( (identifierArg == null) ? null : identifierArg.longValue(), - meteringPointInfosArg); + meteringPointInfosArg, + disableAutoCancelArg); wrapped.add(0, null); } catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/FocusMeteringActionTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/FocusMeteringActionTest.java index 64db80541619..6d96d3036cd7 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/FocusMeteringActionTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/FocusMeteringActionTest.java @@ -6,6 +6,7 @@ import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -43,7 +44,7 @@ public void tearDown() { } @Test - public void hostApiCreatecreatesExpectedFocusMeteringActionWithInitialPointThatHasMode() { + public void hostApiCreate_createsExpectedFocusMeteringActionWithInitialPointThatHasMode() { FocusMeteringActionHostApiImpl.FocusMeteringActionProxy proxySpy = spy(new FocusMeteringActionHostApiImpl.FocusMeteringActionProxy()); FocusMeteringActionHostApiImpl hostApi = @@ -89,7 +90,7 @@ public void hostApiCreatecreatesExpectedFocusMeteringActionWithInitialPointThatH List mockMeteringPointInfos = Arrays.asList(fakeMeteringPointInfo1, fakeMeteringPointInfo2, fakeMeteringPointInfo3); - hostApi.create(focusMeteringActionIdentifier, mockMeteringPointInfos); + hostApi.create(focusMeteringActionIdentifier, mockMeteringPointInfos, null); verify(mockFocusMeteringActionBuilder).addPoint(mockMeteringPoint2, mockMeteringPoint2Mode); verify(mockFocusMeteringActionBuilder).addPoint(mockMeteringPoint3); @@ -98,7 +99,8 @@ public void hostApiCreatecreatesExpectedFocusMeteringActionWithInitialPointThatH } @Test - public void hostApiCreatecreatesExpectedFocusMeteringActionWithInitialPointThatDoesNotHaveMode() { + public void + hostApiCreate_createsExpectedFocusMeteringActionWithInitialPointThatDoesNotHaveMode() { FocusMeteringActionHostApiImpl.FocusMeteringActionProxy proxySpy = spy(new FocusMeteringActionHostApiImpl.FocusMeteringActionProxy()); FocusMeteringActionHostApiImpl hostApi = @@ -142,11 +144,49 @@ public void hostApiCreatecreatesExpectedFocusMeteringActionWithInitialPointThatD List mockMeteringPointInfos = Arrays.asList(fakeMeteringPointInfo1, fakeMeteringPointInfo2, fakeMeteringPointInfo3); - hostApi.create(focusMeteringActionIdentifier, mockMeteringPointInfos); + hostApi.create(focusMeteringActionIdentifier, mockMeteringPointInfos, null); verify(mockFocusMeteringActionBuilder).addPoint(mockMeteringPoint2, mockMeteringPoint2Mode); verify(mockFocusMeteringActionBuilder).addPoint(mockMeteringPoint3); assertEquals( testInstanceManager.getInstance(focusMeteringActionIdentifier), focusMeteringAction); } + + @Test + public void hostApiCreate_disablesAutoCancelAsExpected() { + FocusMeteringActionHostApiImpl.FocusMeteringActionProxy proxySpy = + spy(new FocusMeteringActionHostApiImpl.FocusMeteringActionProxy()); + FocusMeteringActionHostApiImpl hostApi = + new FocusMeteringActionHostApiImpl(testInstanceManager, proxySpy); + + FocusMeteringAction.Builder mockFocusMeteringActionBuilder = + mock(FocusMeteringAction.Builder.class); + final MeteringPoint mockMeteringPoint = mock(MeteringPoint.class); + final Long mockMeteringPointId = 47L; + + MeteringPointInfo fakeMeteringPointInfo = + new MeteringPointInfo.Builder() + .setMeteringPointId(mockMeteringPointId) + .setMeteringMode(null) + .build(); + + testInstanceManager.addDartCreatedInstance(mockMeteringPoint, mockMeteringPointId); + + when(proxySpy.getFocusMeteringActionBuilder(mockMeteringPoint)) + .thenReturn(mockFocusMeteringActionBuilder); + when(mockFocusMeteringActionBuilder.build()).thenReturn(focusMeteringAction); + + List mockMeteringPointInfos = Arrays.asList(fakeMeteringPointInfo); + + // Test not disabling auto cancel. + hostApi.create(73L, mockMeteringPointInfos, /* disableAutoCancel */ null); + verify(mockFocusMeteringActionBuilder, never()).disableAutoCancel(); + + hostApi.create(74L, mockMeteringPointInfos, /* disableAutoCancel */ false); + verify(mockFocusMeteringActionBuilder, never()).disableAutoCancel(); + + // Test disabling auto cancel. + hostApi.create(75L, mockMeteringPointInfos, /* disableAutoCancel */ true); + verify(mockFocusMeteringActionBuilder).disableAutoCancel(); + } } diff --git a/packages/camera/camera_android_camerax/example/lib/main.dart b/packages/camera/camera_android_camerax/example/lib/main.dart index ff1f620e03fa..22221fd1de96 100644 --- a/packages/camera/camera_android_camerax/example/lib/main.dart +++ b/packages/camera/camera_android_camerax/example/lib/main.dart @@ -477,8 +477,9 @@ class _CameraExampleHomeState extends State children: [ TextButton( style: styleAuto, - onPressed: - () {}, // TODO(camsim99): Add functionality back here. + onPressed: controller != null + ? () => onSetFocusModeButtonPressed(FocusMode.auto) + : null, onLongPress: () { if (controller != null) { CameraPlatform.instance @@ -490,8 +491,9 @@ class _CameraExampleHomeState extends State ), TextButton( style: styleLocked, - onPressed: - () {}, // TODO(camsim99): Add functionality back here. + onPressed: controller != null + ? () => onSetFocusModeButtonPressed(FocusMode.locked) + : null, child: const Text('LOCKED'), ), ], diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart index 261525113d45..1612ab512d32 100644 --- a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart @@ -26,6 +26,7 @@ import 'device_orientation_manager.dart'; import 'exposure_state.dart'; import 'fallback_strategy.dart'; import 'focus_metering_action.dart'; +import 'focus_metering_result.dart'; import 'image_analysis.dart'; import 'image_capture.dart'; import 'image_proxy.dart'; @@ -189,11 +190,33 @@ class AndroidCameraCameraX extends CameraPlatform { /// for an example on how setting target rotations for [UseCase]s works. bool shouldSetDefaultRotation = false; + /// Error code indicating that an exposure offset value failed to be set. + static const String setExposureOffsetFailedErrorCode = + 'setExposureOffsetFailed'; + /// The currently set [FocusMeteringAction] used to enable auto-focus and /// auto-exposure. @visibleForTesting FocusMeteringAction? currentFocusMeteringAction; + /// Current focus mode set via [setFocusMode]. + /// + /// CameraX defaults to auto focus mode. + FocusMode _currentFocusMode = FocusMode.auto; + + /// Current exposure mode set via [setExposureMode]. + /// + /// CameraX defaults to auto exposure mode. + ExposureMode _currentExposureMode = ExposureMode.auto; + + /// Whether or not a default focus point of the entire sensor area was focused + /// and locked. + /// + /// This should only be true if [setExposureMode] was called to set + /// [FocusMode.locked] and no previous focus point was set via + /// [setFocusPoint]. + bool _defaultFocusPointLocked = false; + /// Error code indicating that exposure compensation is not supported by /// CameraX for the device. static const String exposureCompensationNotSupported = @@ -451,11 +474,21 @@ class AndroidCameraCameraX extends CameraPlatform { /// Supplying `null` for the [point] argument will result in resetting to the /// original exposure point value. /// + /// Supplied non-null point must be mapped to the entire un-altered preview + /// surface for the exposure point to be applied accurately. + /// /// [cameraId] is not used. @override Future setExposurePoint(int cameraId, Point? point) async { - await _startFocusAndMeteringFor( - point: point, meteringMode: FocusMeteringAction.flagAe); + // We lock the new focus and metering action if focus mode has been locked + // to ensure that the current focus point remains locked. Any exposure mode + // setting will not be impacted by this lock (setting an exposure mode + // is implemented with Camera2 interop that will override settings to + // achieve the expected exposure mode as needed). + await _startFocusAndMeteringForPoint( + point: point, + meteringMode: FocusMeteringAction.flagAe, + disableAutoCancel: _currentFocusMode == FocusMode.locked); } /// Gets the minimum supported exposure offset for the selected camera in EV units. @@ -478,6 +511,86 @@ class AndroidCameraCameraX extends CameraPlatform { exposureState.exposureCompensationStep; } + /// Sets the focus mode for taking pictures. + /// + /// Setting [FocusMode.locked] will lock the current focus point if one exists + /// or the center of entire sensor area if not, and will stay locked until + /// either: + /// * Another focus point is set via [setFocusPoint] (which will then become + /// the locked focus point), or + /// * Locked focus mode is unset by setting [FocusMode.auto]. + @override + Future setFocusMode(int cameraId, FocusMode mode) async { + if (_currentFocusMode == mode) { + // Desired focus mode is already set. + return; + } + + MeteringPoint? autoFocusPoint; + bool? disableAutoCancel; + switch (mode) { + case FocusMode.auto: + // Determine auto-focus point to restore, if any. We do not restore + // default auto-focus point if set previously to lock focus. + final MeteringPoint? unLockedFocusPoint = _defaultFocusPointLocked + ? null + : currentFocusMeteringAction!.meteringPointInfos + .where(((MeteringPoint, int?) meteringPointInfo) => + meteringPointInfo.$2 == FocusMeteringAction.flagAf) + .toList() + .first + .$1; + _defaultFocusPointLocked = false; + autoFocusPoint = unLockedFocusPoint; + disableAutoCancel = false; + case FocusMode.locked: + MeteringPoint? lockedFocusPoint; + + // Determine if there is an auto-focus point set currently to lock. + if (currentFocusMeteringAction != null) { + final List<(MeteringPoint, int?)> possibleCurrentAfPoints = + currentFocusMeteringAction!.meteringPointInfos + .where(((MeteringPoint, int?) meteringPointInfo) => + meteringPointInfo.$2 == FocusMeteringAction.flagAf) + .toList(); + lockedFocusPoint = possibleCurrentAfPoints.isEmpty + ? null + : possibleCurrentAfPoints.first.$1; + } + + // If there isn't, lock center of entire sensor area by default. + if (lockedFocusPoint == null) { + lockedFocusPoint = + proxy.createMeteringPoint(0.5, 0.5, 1, cameraInfo!); + _defaultFocusPointLocked = true; + } + + autoFocusPoint = lockedFocusPoint; + disableAutoCancel = true; + } + // Start appropriate focus and metering action. + final bool focusAndMeteringWasSuccessful = await _startFocusAndMeteringFor( + meteringPoint: autoFocusPoint, + meteringMode: FocusMeteringAction.flagAf, + disableAutoCancel: disableAutoCancel); + + if (!focusAndMeteringWasSuccessful) { + // Do not update current focus mode. + return; + } + + // Update current focus mode. + _currentFocusMode = mode; + + // If focus mode was just locked and exposure mode is not, set auto exposure + // mode to ensure that disabling auto-cancel does not interfere with + // automatic exposure metering. + if (_currentExposureMode == ExposureMode.auto && + _currentFocusMode == FocusMode.locked) { + await setExposureMode(cameraId, _currentExposureMode); + } + } + /// Gets the supported step size for exposure offset for the selected camera in EV units. /// /// Returns -1 if exposure compensation is not supported for the device. @@ -524,15 +637,20 @@ class AndroidCameraCameraX extends CameraPlatform { (offset / exposureOffsetStepSize).round(); try { - await cameraControl + final int? newIndex = await cameraControl .setExposureCompensationIndex(roundedExposureCompensationIndex); + if (newIndex == null) { + throw CameraException(setExposureOffsetFailedErrorCode, + 'Setting exposure compensation index was canceled due to the camera being closed or a new request being submitted.'); + } + + return newIndex.toDouble(); } on PlatformException catch (e) { throw CameraException( - 'setExposureOffsetFailed', + setExposureOffsetFailedErrorCode, e.message ?? 'Setting the camera exposure compensation index failed.'); } - return roundedExposureCompensationIndex * exposureOffsetStepSize; } /// Sets the focus point for automatically determining the focus values. @@ -540,11 +658,21 @@ class AndroidCameraCameraX extends CameraPlatform { /// Supplying `null` for the [point] argument will result in resetting to the /// original focus point value. /// + /// Supplied non-null point must be mapped to the entire un-altered preview + /// surface for the focus point to be applied accurately. + /// /// [cameraId] is not used. @override Future setFocusPoint(int cameraId, Point? point) async { - await _startFocusAndMeteringFor( - point: point, meteringMode: FocusMeteringAction.flagAf); + // We lock the new focus and metering action if focus mode has been locked + // to ensure that the current focus point remains locked. Any exposure mode + // setting will not be impacted by this lock (setting an exposure mode + // is implemented with Camera2 interop that will override settings to + // achieve the expected exposure mode as needed). + await _startFocusAndMeteringForPoint( + point: point, + meteringMode: FocusMeteringAction.flagAf, + disableAutoCancel: _currentFocusMode == FocusMode.locked); } /// Sets the exposure mode for taking pictures. @@ -566,6 +694,7 @@ class AndroidCameraCameraX extends CameraPlatform { )>[(CaptureRequestKeySupportedType.controlAeLock, lockExposureMode)]); await camera2Control.addCaptureRequestOptions(captureRequestOptions); + _currentExposureMode = mode; } /// Gets the maximum supported zoom level for the selected camera. @@ -1088,33 +1217,51 @@ class AndroidCameraCameraX extends CameraPlatform { // Methods for configuring auto-focus and auto-exposure: - /// Starts a focus and metering action. + Future _startFocusAndMeteringForPoint( + {required Point? point, + required int meteringMode, + bool disableAutoCancel = false}) async { + return _startFocusAndMeteringFor( + meteringPoint: point == null + ? null + : proxy.createMeteringPoint( + point.x, point.y, /* size */ null, cameraInfo!), + meteringMode: meteringMode, + disableAutoCancel: disableAutoCancel); + } + + /// Starts a focus and metering action and returns whether or not it was + /// successful. /// - /// This method will modify and start the current action's metering points - /// overriden with the [point] provided for the specified [meteringMode] type - /// only, with all other points of other modes left untouched. Thus, the - /// focus and metering action started will contain only the one most recently - /// set point for each metering mode: AF, AE, AWB. + /// This method will modify and start the current action's [MeteringPoint]s + /// overriden with the [meteringPoint] provided for the specified + /// [meteringMode] type only, with all other metering points of other modes + /// left untouched. If no current action exists, only the specified + /// [meteringPoint] will be set. Thus, the focus and metering action started + /// will only contain at most the one most recently set metering point for + /// each metering mode: AF, AE, AWB. /// - /// Thus, if [point] is non-null, this action includes: + /// Thus, if [meteringPoint] is non-null, this action includes: /// * metering points and their modes previously added to /// [currentFocusMeteringAction] that do not share a metering mode with - /// [point] and - /// * [point] with the specified [meteringMode]. - /// If [point] is null, this action includes only metering points and - /// their modes previously added to [currentFocusMeteringAction] that do not - /// share a metering mode with [point]. If there are no such metering - /// points, then the previously enabled focus and metering actions will be - /// canceled. - Future _startFocusAndMeteringFor( - {required Point? point, required int meteringMode}) async { - if (point == null) { + /// [meteringPoint] (if [currentFocusMeteringAction] is non-null) and + /// * [meteringPoint] with the specified [meteringMode]. + /// If [meteringPoint] is null and [currentFocusMeteringAction] is non-null, + /// this action includes only metering points and their modes previously added + /// to [currentFocusMeteringAction] that do not share a metering mode with + /// [meteringPoint]. If [meteringPoint] and [currentFocusMeteringAction] are + /// null, then focus and metering will be canceled. + Future _startFocusAndMeteringFor( + {required MeteringPoint? meteringPoint, + required int meteringMode, + bool disableAutoCancel = false}) async { + if (meteringPoint == null) { // Try to clear any metering point from previous action with the specified // meteringMode. if (currentFocusMeteringAction == null) { // Attempting to clear a metering point from a previous action, but no // such action exists. - return; + return false; } // Remove metering point with specified meteringMode from current focus @@ -1135,13 +1282,16 @@ class AndroidCameraCameraX extends CameraPlatform { // started focus and metering actions. await cameraControl.cancelFocusAndMetering(); currentFocusMeteringAction = null; - return; + return true; } - currentFocusMeteringAction = - proxy.createFocusMeteringAction(newMeteringPointInfos); - } else if (point.x < 0 || point.x > 1 || point.y < 0 || point.y > 1) { + currentFocusMeteringAction = proxy.createFocusMeteringAction( + newMeteringPointInfos, disableAutoCancel); + } else if (meteringPoint.x < 0 || + meteringPoint.x > 1 || + meteringPoint.y < 0 || + meteringPoint.y > 1) { throw CameraException('pointInvalid', - 'The coordinates of a metering point for an auto-focus or auto-exposure action must be within (0,0) and (1,1), but point $point was provided for metering mode $meteringMode.'); + 'The coordinates of a metering point for an auto-focus or auto-exposure action must be within (0,0) and (1,1), but a point with coordinates (${meteringPoint.x}, ${meteringPoint.y}) was provided for metering mode $meteringMode.'); } else { // Add new metering point with specified meteringMode, which may involve // replacing a metering point with the same specified meteringMode from @@ -1159,13 +1309,13 @@ class AndroidCameraCameraX extends CameraPlatform { meteringPointInfo.$2 != meteringMode) .toList(); } - final MeteringPoint newMeteringPoint = - proxy.createMeteringPoint(point.x, point.y, cameraInfo!); - newMeteringPointInfos.add((newMeteringPoint, meteringMode)); - currentFocusMeteringAction = - proxy.createFocusMeteringAction(newMeteringPointInfos); + newMeteringPointInfos.add((meteringPoint, meteringMode)); + currentFocusMeteringAction = proxy.createFocusMeteringAction( + newMeteringPointInfos, disableAutoCancel); } - await cameraControl.startFocusAndMetering(currentFocusMeteringAction!); + final FocusMeteringResult? result = + await cameraControl.startFocusAndMetering(currentFocusMeteringAction!); + return await result?.isFocusSuccessful() ?? false; } } diff --git a/packages/camera/camera_android_camerax/lib/src/camera_control.dart b/packages/camera/camera_android_camerax/lib/src/camera_control.dart index a233011975f0..0a307f3afc4a 100644 --- a/packages/camera/camera_android_camerax/lib/src/camera_control.dart +++ b/packages/camera/camera_android_camerax/lib/src/camera_control.dart @@ -153,7 +153,9 @@ class _CameraControlHostApiImpl extends CameraControlHostApi { } on PlatformException catch (e) { SystemServices.cameraErrorStreamController .add(e.message ?? 'Starting focus and metering failed.'); - return Future.value(); + // Surfacing error to differentiate an operation cancellation from an + // illegal argument exception at a plugin layer. + rethrow; } } diff --git a/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart b/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart index 14239dd01b69..78c167bc4a11 100644 --- a/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart +++ b/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart @@ -3035,14 +3035,18 @@ class FocusMeteringActionHostApi { static const MessageCodec codec = _FocusMeteringActionHostApiCodec(); - Future create(int arg_identifier, - List arg_meteringPointInfos) async { + Future create( + int arg_identifier, + List arg_meteringPointInfos, + bool? arg_disableAutoCancel) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.FocusMeteringActionHostApi.create', codec, binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_identifier, arg_meteringPointInfos]) - as List?; + final List? replyList = await channel.send([ + arg_identifier, + arg_meteringPointInfos, + arg_disableAutoCancel + ]) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', diff --git a/packages/camera/camera_android_camerax/lib/src/camerax_proxy.dart b/packages/camera/camera_android_camerax/lib/src/camerax_proxy.dart index 2ccb353aa66c..fb100ddb20a2 100644 --- a/packages/camera/camera_android_camerax/lib/src/camerax_proxy.dart +++ b/packages/camera/camera_android_camerax/lib/src/camerax_proxy.dart @@ -56,9 +56,9 @@ class CameraXProxy { this.setPreviewSurfaceProvider = _setPreviewSurfaceProvider, this.getDefaultDisplayRotation = _getDefaultDisplayRotation, this.getCamera2CameraControl = _getCamera2CameraControl, - this.createCaptureRequestOptions = _createCaptureRequestOptions, - this.createMeteringPoint = _createMeteringPoint, - this.createFocusMeteringAction = _createFocusMeteringAction, + this.createCaptureRequestOptions = _createAttachedCaptureRequestOptions, + this.createMeteringPoint = _createAttachedMeteringPoint, + this.createFocusMeteringAction = _createAttachedFocusMeteringAction, }); /// Returns a [ProcessCameraProvider] instance. @@ -158,13 +158,14 @@ class CameraXProxy { /// Returns a [MeteringPoint] with the specified coordinates based on /// [cameraInfo]. - MeteringPoint Function(double x, double y, CameraInfo cameraInfo) + MeteringPoint Function( + double x, double y, double? size, CameraInfo cameraInfo) createMeteringPoint; /// Returns a [FocusMeteringAction] based on the specified metering points /// and their modes. - FocusMeteringAction Function(List<(MeteringPoint, int?)> meteringPointInfos) - createFocusMeteringAction; + FocusMeteringAction Function(List<(MeteringPoint, int?)> meteringPointInfos, + bool? disableAutoCancel) createFocusMeteringAction; static Future _getProcessCameraProvider() { return ProcessCameraProvider.getInstance(); @@ -274,18 +275,20 @@ class CameraXProxy { return Camera2CameraControl(cameraControl: cameraControl); } - static CaptureRequestOptions _createCaptureRequestOptions( + static CaptureRequestOptions _createAttachedCaptureRequestOptions( List<(CaptureRequestKeySupportedType, Object?)> options) { return CaptureRequestOptions(requestedOptions: options); } - static MeteringPoint _createMeteringPoint( - double x, double y, CameraInfo cameraInfo) { - return MeteringPoint(x: x, y: y, cameraInfo: cameraInfo); + static MeteringPoint _createAttachedMeteringPoint( + double x, double y, double? size, CameraInfo cameraInfo) { + return MeteringPoint(x: x, y: y, size: size, cameraInfo: cameraInfo); } - static FocusMeteringAction _createFocusMeteringAction( - List<(MeteringPoint, int?)> meteringPointInfos) { - return FocusMeteringAction(meteringPointInfos: meteringPointInfos); + static FocusMeteringAction _createAttachedFocusMeteringAction( + List<(MeteringPoint, int?)> meteringPointInfos, bool? disableAutoCancel) { + return FocusMeteringAction( + meteringPointInfos: meteringPointInfos, + disableAutoCancel: disableAutoCancel); } } diff --git a/packages/camera/camera_android_camerax/lib/src/focus_metering_action.dart b/packages/camera/camera_android_camerax/lib/src/focus_metering_action.dart index fe09ebc0443e..81fa6f5abf14 100644 --- a/packages/camera/camera_android_camerax/lib/src/focus_metering_action.dart +++ b/packages/camera/camera_android_camerax/lib/src/focus_metering_action.dart @@ -20,13 +20,14 @@ class FocusMeteringAction extends JavaObject { BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, required this.meteringPointInfos, + this.disableAutoCancel, }) : super.detached( binaryMessenger: binaryMessenger, instanceManager: instanceManager, ) { _api = _FocusMeteringActionHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager); - _api.createFromInstance(this, meteringPointInfos); + _api.createFromInstance(this, meteringPointInfos, disableAutoCancel); } /// Creates a [FocusMeteringAction] that is not automatically attached to a @@ -35,6 +36,7 @@ class FocusMeteringAction extends JavaObject { BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, required this.meteringPointInfos, + this.disableAutoCancel, }) : super.detached( binaryMessenger: binaryMessenger, instanceManager: instanceManager, @@ -50,6 +52,12 @@ class FocusMeteringAction extends JavaObject { final List<(MeteringPoint meteringPoint, int? meteringMode)> meteringPointInfos; + /// Disables the auto-cancel. + /// + /// By default (and if set to false), auto-cancel is enabled with 5 seconds + /// duration. + final bool? disableAutoCancel; + /// Flag for metering mode that indicates the auto focus region is enabled. /// /// An autofocus scan is also triggered when [flagAf] is assigned. @@ -97,8 +105,8 @@ class _FocusMeteringActionHostApiImpl extends FocusMeteringActionHostApi { /// [MeteringPoint]s and their modes in order of descending priority. void createFromInstance( FocusMeteringAction instance, - List<(MeteringPoint meteringPoint, int? meteringMode)> - meteringPointInfos) { + List<(MeteringPoint meteringPoint, int? meteringMode)> meteringPointInfos, + bool? disableAutoCancel) { final int identifier = instanceManager.addDartCreatedInstance(instance, onCopy: (FocusMeteringAction original) { return FocusMeteringAction.detached( @@ -118,6 +126,6 @@ class _FocusMeteringActionHostApiImpl extends FocusMeteringActionHostApi { meteringMode: meteringPointInfo.$2)); } - create(identifier, meteringPointInfosWithIds); + create(identifier, meteringPointInfosWithIds, disableAutoCancel); } } diff --git a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart index fc78cae75d24..103c260c25c1 100644 --- a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart +++ b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart @@ -501,7 +501,8 @@ abstract class CameraControlFlutterApi { @HostApi(dartHostTestHandler: 'TestFocusMeteringActionHostApi') abstract class FocusMeteringActionHostApi { - void create(int identifier, List meteringPointInfos); + void create(int identifier, List meteringPointInfos, + bool? disableAutoCancel); } @HostApi(dartHostTestHandler: 'TestFocusMeteringResultHostApi') diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index 660997d5945f..a1a44f06f366 100644 --- a/packages/camera/camera_android_camerax/pubspec.yaml +++ b/packages/camera/camera_android_camerax/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android_camerax description: Android implementation of the camera plugin using the CameraX library. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android_camerax issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.5.0+36 +version: 0.6.0 environment: sdk: ^3.1.0 diff --git a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart index 05ee82abb4ab..d9ff081383e9 100644 --- a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart +++ b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart @@ -22,6 +22,7 @@ import 'package:camera_android_camerax/src/device_orientation_manager.dart'; import 'package:camera_android_camerax/src/exposure_state.dart'; import 'package:camera_android_camerax/src/fallback_strategy.dart'; import 'package:camera_android_camerax/src/focus_metering_action.dart'; +import 'package:camera_android_camerax/src/focus_metering_result.dart'; import 'package:camera_android_camerax/src/image_analysis.dart'; import 'package:camera_android_camerax/src/image_capture.dart'; import 'package:camera_android_camerax/src/image_proxy.dart'; @@ -58,10 +59,12 @@ import 'test_camerax_library.g.dart'; MockSpec(), MockSpec(), MockSpec(), + MockSpec(), MockSpec(), MockSpec(), MockSpec(), MockSpec(), + MockSpec(), MockSpec(), MockSpec(), MockSpec(), @@ -80,7 +83,6 @@ import 'test_camerax_library.g.dart'; MockSpec(), MockSpec(), MockSpec(), - MockSpec(), ]) @GenerateMocks([], customMocks: >[ MockSpec>(as: #MockLiveCameraState), @@ -122,14 +124,42 @@ void main() { /// CameraXProxy for testing exposure and focus related controls. /// - /// Modifies the creation of MeteringPoints and FocusMeteringActions to return - /// objects detached from a native object. + /// Modifies the creation of [MeteringPoint]s and [FocusMeteringAction]s to + /// return objects detached from a native object. CameraXProxy getProxyForExposureAndFocus() => CameraXProxy( - createMeteringPoint: (double x, double y, CameraInfo cameraInfo) => - MeteringPoint.detached(x: x, y: y, cameraInfo: cameraInfo), - createFocusMeteringAction: (List<(MeteringPoint, int?)> - meteringPointInfos) => - FocusMeteringAction.detached(meteringPointInfos: meteringPointInfos)); + createMeteringPoint: + (double x, double y, double? size, CameraInfo cameraInfo) => + MeteringPoint.detached( + x: x, y: y, size: size, cameraInfo: cameraInfo), + createFocusMeteringAction: + (List<(MeteringPoint, int?)> meteringPointInfos, + bool? disableAutoCancel) => + FocusMeteringAction.detached( + meteringPointInfos: meteringPointInfos, + disableAutoCancel: disableAutoCancel), + ); + + /// CameraXProxy for testing setting focus and exposure points. + /// + /// Modifies the retrieval of a [Camera2CameraControl] instance to depend on + /// interaction with expected [cameraControl] instance and modifies creation + /// of [CaptureRequestOptions] to return objects detached from a native object. + CameraXProxy getProxyForSettingFocusandExposurePoints( + CameraControl cameraControlForComparison, + Camera2CameraControl camera2cameraControl) { + final CameraXProxy proxy = getProxyForExposureAndFocus(); + + proxy.getCamera2CameraControl = (CameraControl cameraControl) => + cameraControl == cameraControlForComparison + ? camera2cameraControl + : Camera2CameraControl.detached(cameraControl: cameraControl); + + proxy.createCaptureRequestOptions = + (List<(CaptureRequestKeySupportedType, Object?)> options) => + CaptureRequestOptions.detached(requestedOptions: options); + + return proxy; + } test('Should fetch CameraDescription instances for available cameras', () async { @@ -2139,6 +2169,7 @@ void main() { // Set directly for test versus calling createCamera. camera.cameraControl = mockCameraControl; + camera.cameraInfo = MockCameraInfo(); camera.proxy = getProxyForExposureAndFocus(); @@ -2253,6 +2284,51 @@ void main() { equals(FocusMeteringAction.flagAe)); }); + test( + 'setExposurePoint disables auto-cancel for focus and metering as expected', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 2; + final MockCameraControl mockCameraControl = MockCameraControl(); + final FocusMeteringResult mockFocusMeteringResult = + MockFocusMeteringResult(); + const Point exposurePoint = Point(0.1, 0.2); + + // Set directly for test versus calling createCamera. + camera.cameraControl = mockCameraControl; + camera.cameraInfo = MockCameraInfo(); + + camera.proxy = getProxyForSettingFocusandExposurePoints( + mockCameraControl, MockCamera2CameraControl()); + + // Make setting focus and metering action successful for test. + when(mockFocusMeteringResult.isFocusSuccessful()) + .thenAnswer((_) async => Future.value(true)); + when(mockCameraControl.startFocusAndMetering(any)).thenAnswer((_) async => + Future.value(mockFocusMeteringResult)); + + // Test not disabling auto cancel. + await camera.setFocusMode(cameraId, FocusMode.auto); + clearInteractions(mockCameraControl); + await camera.setExposurePoint(cameraId, exposurePoint); + VerificationResult verificationResult = + verify(mockCameraControl.startFocusAndMetering(captureAny)); + FocusMeteringAction capturedAction = + verificationResult.captured.single as FocusMeteringAction; + expect(capturedAction.disableAutoCancel, isFalse); + + clearInteractions(mockCameraControl); + + // Test disabling auto cancel. + await camera.setFocusMode(cameraId, FocusMode.locked); + clearInteractions(mockCameraControl); + await camera.setExposurePoint(cameraId, exposurePoint); + verificationResult = + verify(mockCameraControl.startFocusAndMetering(captureAny)); + capturedAction = verificationResult.captured.single as FocusMeteringAction; + expect(capturedAction.disableAutoCancel, isTrue); + }); + test( 'setExposureOffset throws exception if exposure compensation not supported', () async { @@ -2276,7 +2352,7 @@ void main() { }); test( - 'setExposureOffset throws exception if exposure compensation could not be set', + 'setExposureOffset throws exception if exposure compensation could not be set for unknown reason', () async { final AndroidCameraCameraX camera = AndroidCameraCameraX(); const int cameraId = 11; @@ -2304,6 +2380,35 @@ void main() { throwsA(isA())); }); + test( + 'setExposureOffset throws exception if exposure compensation could not be set due to camera being closed or newer value being set', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 21; + const double offset = 5; + final MockCameraInfo mockCameraInfo = MockCameraInfo(); + final CameraControl mockCameraControl = MockCameraControl(); + final ExposureState exposureState = ExposureState.detached( + exposureCompensationRange: + ExposureCompensationRange(minCompensation: 3, maxCompensation: 4), + exposureCompensationStep: 0.1); + final int expectedExposureCompensationIndex = + (offset / exposureState.exposureCompensationStep).round(); + + // Set directly for test versus calling createCamera. + camera.cameraInfo = mockCameraInfo; + camera.cameraControl = mockCameraControl; + + when(mockCameraInfo.getExposureState()) + .thenAnswer((_) async => exposureState); + when(mockCameraControl + .setExposureCompensationIndex(expectedExposureCompensationIndex)) + .thenAnswer((_) async => Future.value()); + + expect(() => camera.setExposureOffset(cameraId, offset), + throwsA(isA())); + }); + test( 'setExposureOffset behaves as expected to successful attempt to set exposure compensation index', () async { @@ -2316,6 +2421,8 @@ void main() { exposureCompensationRange: ExposureCompensationRange(minCompensation: 3, maxCompensation: 4), exposureCompensationStep: 0.2); + final int expectedExposureCompensationIndex = + (offset / exposureState.exposureCompensationStep).round(); // Set directly for test versus calling createCamera. camera.cameraInfo = mockCameraInfo; @@ -2323,6 +2430,12 @@ void main() { when(mockCameraInfo.getExposureState()) .thenAnswer((_) async => exposureState); + when(mockCameraControl + .setExposureCompensationIndex(expectedExposureCompensationIndex)) + .thenAnswer((_) async => Future.value( + (expectedExposureCompensationIndex * + exposureState.exposureCompensationStep) + .round())); // Exposure index * exposure offset step size = exposure offset, i.e. // 15 * 0.2 = 3. @@ -2401,6 +2514,7 @@ void main() { // Set directly for test versus calling createCamera. camera.cameraControl = mockCameraControl; + camera.cameraInfo = MockCameraInfo(); camera.proxy = getProxyForExposureAndFocus(); @@ -2513,4 +2627,687 @@ void main() { expect(capturedMeteringPointInfos.first.$2, equals(FocusMeteringAction.flagAf)); }); + + test('setFocusPoint disables auto-cancel for focus and metering as expected', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 2; + final MockCameraControl mockCameraControl = MockCameraControl(); + final MockFocusMeteringResult mockFocusMeteringResult = + MockFocusMeteringResult(); + const Point exposurePoint = Point(0.1, 0.2); + + // Set directly for test versus calling createCamera. + camera.cameraControl = mockCameraControl; + camera.cameraInfo = MockCameraInfo(); + + camera.proxy = getProxyForSettingFocusandExposurePoints( + mockCameraControl, MockCamera2CameraControl()); + + // Make setting focus and metering action successful for test. + when(mockFocusMeteringResult.isFocusSuccessful()) + .thenAnswer((_) async => Future.value(true)); + when(mockCameraControl.startFocusAndMetering(any)).thenAnswer((_) async => + Future.value(mockFocusMeteringResult)); + + // Test not disabling auto cancel. + await camera.setFocusMode(cameraId, FocusMode.auto); + clearInteractions(mockCameraControl); + + await camera.setFocusPoint(cameraId, exposurePoint); + VerificationResult verificationResult = + verify(mockCameraControl.startFocusAndMetering(captureAny)); + FocusMeteringAction capturedAction = + verificationResult.captured.single as FocusMeteringAction; + expect(capturedAction.disableAutoCancel, isFalse); + + clearInteractions(mockCameraControl); + + // Test disabling auto cancel. + await camera.setFocusMode(cameraId, FocusMode.locked); + clearInteractions(mockCameraControl); + + await camera.setFocusPoint(cameraId, exposurePoint); + verificationResult = + verify(mockCameraControl.startFocusAndMetering(captureAny)); + capturedAction = verificationResult.captured.single as FocusMeteringAction; + expect(capturedAction.disableAutoCancel, isTrue); + }); + + test( + 'setFocusMode does nothing if setting auto-focus mode and is already using auto-focus mode', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 4; + final MockCameraControl mockCameraControl = MockCameraControl(); + final MockFocusMeteringResult mockFocusMeteringResult = + MockFocusMeteringResult(); + + // Set directly for test versus calling createCamera. + camera.cameraControl = mockCameraControl; + camera.cameraInfo = MockCameraInfo(); + + camera.proxy = getProxyForSettingFocusandExposurePoints( + mockCameraControl, MockCamera2CameraControl()); + + // Make setting focus and metering action successful for test. + when(mockFocusMeteringResult.isFocusSuccessful()) + .thenAnswer((_) async => Future.value(true)); + when(mockCameraControl.startFocusAndMetering(any)).thenAnswer((_) async => + Future.value(mockFocusMeteringResult)); + + // Set locked focus mode and then try to re-set it. + await camera.setFocusMode(cameraId, FocusMode.locked); + clearInteractions(mockCameraControl); + + await camera.setFocusMode(cameraId, FocusMode.locked); + verifyNoMoreInteractions(mockCameraControl); + }); + + test( + 'setFocusMode does nothing if setting locked focus mode and is already using locked focus mode', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 4; + final MockCameraControl mockCameraControl = MockCameraControl(); + + // Camera uses auto-focus by default, so try setting auto mode again. + await camera.setFocusMode(cameraId, FocusMode.auto); + + verifyNoMoreInteractions(mockCameraControl); + }); + + test( + 'setFocusMode removes default auto-focus point if previously set and setting auto-focus mode', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 5; + final MockCameraControl mockCameraControl = MockCameraControl(); + final MockFocusMeteringResult mockFocusMeteringResult = + MockFocusMeteringResult(); + final MockCamera2CameraControl mockCamera2CameraControl = + MockCamera2CameraControl(); + const double exposurePointX = 0.2; + const double exposurePointY = 0.7; + + // Set directly for test versus calling createCamera. + camera.cameraInfo = MockCameraInfo(); + camera.cameraControl = mockCameraControl; + + when(mockCamera2CameraControl.addCaptureRequestOptions(any)) + .thenAnswer((_) async => Future.value()); + + camera.proxy = getProxyForSettingFocusandExposurePoints( + mockCameraControl, mockCamera2CameraControl); + + // Make setting focus and metering action successful for test. + when(mockFocusMeteringResult.isFocusSuccessful()) + .thenAnswer((_) async => Future.value(true)); + when(mockCameraControl.startFocusAndMetering(any)).thenAnswer((_) async => + Future.value(mockFocusMeteringResult)); + + // Set exposure points. + await camera.setExposurePoint( + cameraId, const Point(exposurePointX, exposurePointY)); + + // Lock focus default focus point. + await camera.setFocusMode(cameraId, FocusMode.locked); + + clearInteractions(mockCameraControl); + + // Test removal of default focus point. + await camera.setFocusMode(cameraId, FocusMode.auto); + + final VerificationResult verificationResult = + verify(mockCameraControl.startFocusAndMetering(captureAny)); + final FocusMeteringAction capturedAction = + verificationResult.captured.single as FocusMeteringAction; + expect(capturedAction.disableAutoCancel, isFalse); + + // We expect only the previously set exposure point to be re-set. + final List<(MeteringPoint, int?)> capturedMeteringPointInfos = + capturedAction.meteringPointInfos; + expect(capturedMeteringPointInfos.length, equals(1)); + expect(capturedMeteringPointInfos.first.$1.x, equals(exposurePointX)); + expect(capturedMeteringPointInfos.first.$1.y, equals(exposurePointY)); + expect(capturedMeteringPointInfos.first.$1.size, isNull); + expect(capturedMeteringPointInfos.first.$2, + equals(FocusMeteringAction.flagAe)); + }); + + test( + 'setFocusMode cancels focus and metering if only focus point previously set is a focus point', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 5; + final MockCameraControl mockCameraControl = MockCameraControl(); + final FocusMeteringResult mockFocusMeteringResult = + MockFocusMeteringResult(); + final MockCamera2CameraControl mockCamera2CameraControl = + MockCamera2CameraControl(); + + // Set directly for test versus calling createCamera. + camera.cameraInfo = MockCameraInfo(); + camera.cameraControl = mockCameraControl; + + when(mockCamera2CameraControl.addCaptureRequestOptions(any)) + .thenAnswer((_) async => Future.value()); + + camera.proxy = getProxyForSettingFocusandExposurePoints( + mockCameraControl, mockCamera2CameraControl); + + // Make setting focus and metering action successful for test. + when(mockFocusMeteringResult.isFocusSuccessful()) + .thenAnswer((_) async => Future.value(true)); + when(mockCameraControl.startFocusAndMetering(any)).thenAnswer((_) async => + Future.value(mockFocusMeteringResult)); + + // Lock focus default focus point. + await camera.setFocusMode(cameraId, FocusMode.locked); + + // Test removal of default focus point. + await camera.setFocusMode(cameraId, FocusMode.auto); + + verify(mockCameraControl.cancelFocusAndMetering()); + }); + + test( + 'setFocusMode re-focuses on previously set auto-focus point with auto-canceled enabled if setting auto-focus mode', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 6; + final MockCameraControl mockCameraControl = MockCameraControl(); + final FocusMeteringResult mockFocusMeteringResult = + MockFocusMeteringResult(); + final MockCamera2CameraControl mockCamera2CameraControl = + MockCamera2CameraControl(); + const double focusPointX = 0.1; + const double focusPointY = 0.2; + + // Set directly for test versus calling createCamera. + camera.cameraInfo = MockCameraInfo(); + camera.cameraControl = mockCameraControl; + + when(mockCamera2CameraControl.addCaptureRequestOptions(any)) + .thenAnswer((_) async => Future.value()); + + camera.proxy = getProxyForSettingFocusandExposurePoints( + mockCameraControl, mockCamera2CameraControl); + + // Make setting focus and metering action successful for test. + when(mockFocusMeteringResult.isFocusSuccessful()) + .thenAnswer((_) async => Future.value(true)); + when(mockCameraControl.startFocusAndMetering(any)).thenAnswer((_) async => + Future.value(mockFocusMeteringResult)); + + // Lock a focus point. + await camera.setFocusPoint( + cameraId, const Point(focusPointX, focusPointY)); + await camera.setFocusMode(cameraId, FocusMode.locked); + + clearInteractions(mockCameraControl); + + // Test re-focusing on previously set auto-focus point with auto-cancel enabled. + await camera.setFocusMode(cameraId, FocusMode.auto); + + final VerificationResult verificationResult = + verify(mockCameraControl.startFocusAndMetering(captureAny)); + final FocusMeteringAction capturedAction = + verificationResult.captured.single as FocusMeteringAction; + expect(capturedAction.disableAutoCancel, isFalse); + final List<(MeteringPoint, int?)> capturedMeteringPointInfos = + capturedAction.meteringPointInfos; + expect(capturedMeteringPointInfos.length, equals(1)); + expect(capturedMeteringPointInfos.first.$1.x, equals(focusPointX)); + expect(capturedMeteringPointInfos.first.$1.y, equals(focusPointY)); + expect(capturedMeteringPointInfos.first.$1.size, isNull); + expect(capturedMeteringPointInfos.first.$2, + equals(FocusMeteringAction.flagAf)); + }); + + test( + 'setFocusMode starts expected focus and metering action with previously set auto-focus point if setting locked focus mode and current focus and metering action has auto-focus point', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 7; + final MockCameraControl mockCameraControl = MockCameraControl(); + final MockCamera2CameraControl mockCamera2CameraControl = + MockCamera2CameraControl(); + const double focusPointX = 0.88; + const double focusPointY = 0.33; + + // Set directly for test versus calling createCamera. + camera.cameraInfo = MockCameraInfo(); + camera.cameraControl = mockCameraControl; + + when(mockCamera2CameraControl.addCaptureRequestOptions(any)) + .thenAnswer((_) async => Future.value()); + + camera.proxy = getProxyForSettingFocusandExposurePoints( + mockCameraControl, mockCamera2CameraControl); + + // Set a focus point. + await camera.setFocusPoint( + cameraId, const Point(focusPointX, focusPointY)); + clearInteractions(mockCameraControl); + + // Lock focus point. + await camera.setFocusMode(cameraId, FocusMode.locked); + + final VerificationResult verificationResult = + verify(mockCameraControl.startFocusAndMetering(captureAny)); + final FocusMeteringAction capturedAction = + verificationResult.captured.single as FocusMeteringAction; + expect(capturedAction.disableAutoCancel, isTrue); + + // We expect the set focus point to be locked. + final List<(MeteringPoint, int?)> capturedMeteringPointInfos = + capturedAction.meteringPointInfos; + expect(capturedMeteringPointInfos.length, equals(1)); + expect(capturedMeteringPointInfos.first.$1.x, equals(focusPointX)); + expect(capturedMeteringPointInfos.first.$1.y, equals(focusPointY)); + expect(capturedMeteringPointInfos.first.$1.size, isNull); + expect(capturedMeteringPointInfos.first.$2, + equals(FocusMeteringAction.flagAf)); + }); + + test( + 'setFocusMode starts expected focus and metering action with previously set auto-focus point if setting locked focus mode and current focus and metering action has auto-focus point amongst others', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 8; + final MockCameraControl mockCameraControl = MockCameraControl(); + final MockCamera2CameraControl mockCamera2CameraControl = + MockCamera2CameraControl(); + const double focusPointX = 0.38; + const double focusPointY = 0.38; + const double exposurePointX = 0.54; + const double exposurePointY = 0.45; + + // Set directly for test versus calling createCamera. + camera.cameraInfo = MockCameraInfo(); + camera.cameraControl = mockCameraControl; + + when(mockCamera2CameraControl.addCaptureRequestOptions(any)) + .thenAnswer((_) async => Future.value()); + + camera.proxy = getProxyForSettingFocusandExposurePoints( + mockCameraControl, mockCamera2CameraControl); + + // Set focus and exposure points. + await camera.setFocusPoint( + cameraId, const Point(focusPointX, focusPointY)); + await camera.setExposurePoint( + cameraId, const Point(exposurePointX, exposurePointY)); + clearInteractions(mockCameraControl); + + // Lock focus point. + await camera.setFocusMode(cameraId, FocusMode.locked); + + final VerificationResult verificationResult = + verify(mockCameraControl.startFocusAndMetering(captureAny)); + final FocusMeteringAction capturedAction = + verificationResult.captured.single as FocusMeteringAction; + expect(capturedAction.disableAutoCancel, isTrue); + + // We expect two MeteringPoints, the set focus point and the set exposure + // point. + final List<(MeteringPoint, int?)> capturedMeteringPointInfos = + capturedAction.meteringPointInfos; + expect(capturedMeteringPointInfos.length, equals(2)); + + final List<(MeteringPoint, int?)> focusPoints = capturedMeteringPointInfos + .where(((MeteringPoint, int?) meteringPointInfo) => + meteringPointInfo.$2 == FocusMeteringAction.flagAf) + .toList(); + expect(focusPoints.length, equals(1)); + expect(focusPoints.first.$1.x, equals(focusPointX)); + expect(focusPoints.first.$1.y, equals(focusPointY)); + expect(focusPoints.first.$1.size, isNull); + + final List<(MeteringPoint, int?)> exposurePoints = + capturedMeteringPointInfos + .where(((MeteringPoint, int?) meteringPointInfo) => + meteringPointInfo.$2 == FocusMeteringAction.flagAe) + .toList(); + expect(exposurePoints.length, equals(1)); + expect(exposurePoints.first.$1.x, equals(exposurePointX)); + expect(exposurePoints.first.$1.y, equals(exposurePointY)); + expect(exposurePoints.first.$1.size, isNull); + }); + + test( + 'setFocusMode starts expected focus and metering action if setting locked focus mode and current focus and metering action does not contain an auto-focus point', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 9; + final MockCameraControl mockCameraControl = MockCameraControl(); + final MockCamera2CameraControl mockCamera2CameraControl = + MockCamera2CameraControl(); + const double exposurePointX = 0.8; + const double exposurePointY = 0.3; + const double defaultFocusPointX = 0.5; + const double defaultFocusPointY = 0.5; + const double defaultFocusPointSize = 1; + + // Set directly for test versus calling createCamera. + camera.cameraInfo = MockCameraInfo(); + camera.cameraControl = mockCameraControl; + + when(mockCamera2CameraControl.addCaptureRequestOptions(any)) + .thenAnswer((_) async => Future.value()); + + camera.proxy = getProxyForSettingFocusandExposurePoints( + mockCameraControl, mockCamera2CameraControl); + + // Set an exposure point (creates a current focus and metering action + // without a focus point). + await camera.setExposurePoint( + cameraId, const Point(exposurePointX, exposurePointY)); + clearInteractions(mockCameraControl); + + // Lock focus point. + await camera.setFocusMode(cameraId, FocusMode.locked); + + final VerificationResult verificationResult = + verify(mockCameraControl.startFocusAndMetering(captureAny)); + final FocusMeteringAction capturedAction = + verificationResult.captured.single as FocusMeteringAction; + expect(capturedAction.disableAutoCancel, isTrue); + + // We expect two MeteringPoints, the default focus point and the set + //exposure point. + final List<(MeteringPoint, int?)> capturedMeteringPointInfos = + capturedAction.meteringPointInfos; + expect(capturedMeteringPointInfos.length, equals(2)); + + final List<(MeteringPoint, int?)> focusPoints = capturedMeteringPointInfos + .where(((MeteringPoint, int?) meteringPointInfo) => + meteringPointInfo.$2 == FocusMeteringAction.flagAf) + .toList(); + expect(focusPoints.length, equals(1)); + expect(focusPoints.first.$1.x, equals(defaultFocusPointX)); + expect(focusPoints.first.$1.y, equals(defaultFocusPointY)); + expect(focusPoints.first.$1.size, equals(defaultFocusPointSize)); + + final List<(MeteringPoint, int?)> exposurePoints = + capturedMeteringPointInfos + .where(((MeteringPoint, int?) meteringPointInfo) => + meteringPointInfo.$2 == FocusMeteringAction.flagAe) + .toList(); + expect(exposurePoints.length, equals(1)); + expect(exposurePoints.first.$1.x, equals(exposurePointX)); + expect(exposurePoints.first.$1.y, equals(exposurePointY)); + expect(exposurePoints.first.$1.size, isNull); + }); + + test( + 'setFocusMode starts expected focus and metering action if there is no current focus and metering action', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 10; + final MockCameraControl mockCameraControl = MockCameraControl(); + final MockCamera2CameraControl mockCamera2CameraControl = + MockCamera2CameraControl(); + const double defaultFocusPointX = 0.5; + const double defaultFocusPointY = 0.5; + const double defaultFocusPointSize = 1; + + // Set directly for test versus calling createCamera. + camera.cameraInfo = MockCameraInfo(); + camera.cameraControl = mockCameraControl; + + when(mockCamera2CameraControl.addCaptureRequestOptions(any)) + .thenAnswer((_) async => Future.value()); + + camera.proxy = getProxyForSettingFocusandExposurePoints( + mockCameraControl, mockCamera2CameraControl); + + // Lock focus point. + await camera.setFocusMode(cameraId, FocusMode.locked); + + final VerificationResult verificationResult = + verify(mockCameraControl.startFocusAndMetering(captureAny)); + final FocusMeteringAction capturedAction = + verificationResult.captured.single as FocusMeteringAction; + expect(capturedAction.disableAutoCancel, isTrue); + + // We expect only the default focus point to be set. + final List<(MeteringPoint, int?)> capturedMeteringPointInfos = + capturedAction.meteringPointInfos; + expect(capturedMeteringPointInfos.length, equals(1)); + expect(capturedMeteringPointInfos.first.$1.x, equals(defaultFocusPointX)); + expect(capturedMeteringPointInfos.first.$1.y, equals(defaultFocusPointY)); + expect(capturedMeteringPointInfos.first.$1.size, + equals(defaultFocusPointSize)); + expect(capturedMeteringPointInfos.first.$2, + equals(FocusMeteringAction.flagAf)); + }); + + test( + 'setFocusMode re-sets exposure mode if setting locked focus mode while using auto exposure mode', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 11; + final MockCameraControl mockCameraControl = MockCameraControl(); + final FocusMeteringResult mockFocusMeteringResult = + MockFocusMeteringResult(); + final MockCamera2CameraControl mockCamera2CameraControl = + MockCamera2CameraControl(); + + // Set directly for test versus calling createCamera. + camera.cameraInfo = MockCameraInfo(); + camera.cameraControl = mockCameraControl; + + when(mockCamera2CameraControl.addCaptureRequestOptions(any)) + .thenAnswer((_) async => Future.value()); + + camera.proxy = getProxyForSettingFocusandExposurePoints( + mockCameraControl, mockCamera2CameraControl); + + // Make setting focus and metering action successful for test. + when(mockFocusMeteringResult.isFocusSuccessful()) + .thenAnswer((_) async => Future.value(true)); + when(mockCameraControl.startFocusAndMetering(any)).thenAnswer((_) async => + Future.value(mockFocusMeteringResult)); + + // Set auto exposure mode. + await camera.setExposureMode(cameraId, ExposureMode.auto); + clearInteractions(mockCamera2CameraControl); + + // Lock focus point. + await camera.setFocusMode(cameraId, FocusMode.locked); + + final VerificationResult verificationResult = + verify(mockCamera2CameraControl.addCaptureRequestOptions(captureAny)); + final CaptureRequestOptions capturedCaptureRequestOptions = + verificationResult.captured.single as CaptureRequestOptions; + final List<(CaptureRequestKeySupportedType, Object?)> requestedOptions = + capturedCaptureRequestOptions.requestedOptions; + expect(requestedOptions.length, equals(1)); + expect(requestedOptions.first.$1, + equals(CaptureRequestKeySupportedType.controlAeLock)); + expect(requestedOptions.first.$2, equals(false)); + }); + + test( + 'setFocusPoint disables auto-cancel if auto focus mode fails to be set after locked focus mode is set', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 22; + final MockCameraControl mockCameraControl = MockCameraControl(); + final MockFocusMeteringResult mockFocusMeteringResult = + MockFocusMeteringResult(); + const Point focusPoint = Point(0.21, 0.21); + + // Set directly for test versus calling createCamera. + camera.cameraControl = mockCameraControl; + camera.cameraInfo = MockCameraInfo(); + + camera.proxy = getProxyForSettingFocusandExposurePoints( + mockCameraControl, MockCamera2CameraControl()); + + // Make setting focus and metering action successful to set locked focus + // mode. + when(mockFocusMeteringResult.isFocusSuccessful()) + .thenAnswer((_) async => Future.value(true)); + when(mockCameraControl.startFocusAndMetering(any)).thenAnswer((_) async => + Future.value(mockFocusMeteringResult)); + + // Set exposure point to later mock failed call to set an exposure point ( + // otherwise, focus and metering will be canceled altogether, which is + //considered a successful call). + await camera.setExposurePoint(cameraId, const Point(0.3, 0.4)); + + // Set locked focus mode so we can set auto mode (cannot set auto mode + // directly since it is the default). + await camera.setFocusMode(cameraId, FocusMode.locked); + clearInteractions(mockCameraControl); + + // Make setting focus and metering action fail to test that auto-cancel is + // still disabled. + reset(mockFocusMeteringResult); + when(mockFocusMeteringResult.isFocusSuccessful()) + .thenAnswer((_) async => Future.value(false)); + + // Test disabling auto cancel. + await camera.setFocusMode(cameraId, FocusMode.auto); + clearInteractions(mockCameraControl); + + await camera.setFocusPoint(cameraId, focusPoint); + final VerificationResult verificationResult = + verify(mockCameraControl.startFocusAndMetering(captureAny)); + final FocusMeteringAction capturedAction = + verificationResult.captured.single as FocusMeteringAction; + expect(capturedAction.disableAutoCancel, isTrue); + }); + + test( + 'setExposurePoint disables auto-cancel if auto focus mode fails to be set after locked focus mode is set', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 342; + final MockCameraControl mockCameraControl = MockCameraControl(); + final MockFocusMeteringResult mockFocusMeteringResult = + MockFocusMeteringResult(); + const Point exposurePoint = Point(0.23, 0.32); + + // Set directly for test versus calling createCamera. + camera.cameraControl = mockCameraControl; + camera.cameraInfo = MockCameraInfo(); + + camera.proxy = getProxyForSettingFocusandExposurePoints( + mockCameraControl, MockCamera2CameraControl()); + + // Make setting focus and metering action successful to set locked focus + // mode. + when(mockFocusMeteringResult.isFocusSuccessful()) + .thenAnswer((_) async => Future.value(true)); + when(mockCameraControl.startFocusAndMetering(any)).thenAnswer((_) async => + Future.value(mockFocusMeteringResult)); + + // Set exposure point to later mock failed call to set an exposure point ( + // otherwise, focus and metering will be canceled altogether, which is + //considered a successful call). + await camera.setExposurePoint(cameraId, const Point(0.4, 0.3)); + + // Set locked focus mode so we can set auto mode (cannot set auto mode + // directly since it is the default). + await camera.setFocusMode(cameraId, FocusMode.locked); + clearInteractions(mockCameraControl); + + // Make setting focus and metering action fail to test that auto-cancel is + // still disabled. + when(mockFocusMeteringResult.isFocusSuccessful()) + .thenAnswer((_) async => Future.value(false)); + + // Test disabling auto cancel. + await camera.setFocusMode(cameraId, FocusMode.auto); + clearInteractions(mockCameraControl); + + await camera.setExposurePoint(cameraId, exposurePoint); + final VerificationResult verificationResult = + verify(mockCameraControl.startFocusAndMetering(captureAny)); + final FocusMeteringAction capturedAction = + verificationResult.captured.single as FocusMeteringAction; + expect(capturedAction.disableAutoCancel, isTrue); + }); + + test( + 'setFocusPoint enables auto-cancel if locked focus mode fails to be set after auto focus mode is set', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 232; + final MockCameraControl mockCameraControl = MockCameraControl(); + final MockFocusMeteringResult mockFocusMeteringResult = + MockFocusMeteringResult(); + const Point focusPoint = Point(0.221, 0.211); + + // Set directly for test versus calling createCamera. + camera.cameraControl = mockCameraControl; + camera.cameraInfo = MockCameraInfo(); + + camera.proxy = getProxyForSettingFocusandExposurePoints( + mockCameraControl, MockCamera2CameraControl()); + + // Make setting focus and metering action fail to test auto-cancel is not + // disabled. + when(mockFocusMeteringResult.isFocusSuccessful()) + .thenAnswer((_) async => Future.value(false)); + when(mockCameraControl.startFocusAndMetering(any)).thenAnswer((_) async => + Future.value(mockFocusMeteringResult)); + + // Set exposure point to later mock failed call to set an exposure point. + await camera.setExposurePoint(cameraId, const Point(0.43, 0.34)); + + // Test failing to set locked focus mode. + await camera.setFocusMode(cameraId, FocusMode.locked); + clearInteractions(mockCameraControl); + + await camera.setFocusPoint(cameraId, focusPoint); + final VerificationResult verificationResult = + verify(mockCameraControl.startFocusAndMetering(captureAny)); + final FocusMeteringAction capturedAction = + verificationResult.captured.single as FocusMeteringAction; + expect(capturedAction.disableAutoCancel, isFalse); + }); + + test( + 'setExposurePoint enables auto-cancel if locked focus mode fails to be set after auto focus mode is set', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const int cameraId = 323; + final MockCameraControl mockCameraControl = MockCameraControl(); + final MockFocusMeteringResult mockFocusMeteringResult = + MockFocusMeteringResult(); + const Point exposurePoint = Point(0.223, 0.332); + + // Set directly for test versus calling createCamera. + camera.cameraControl = mockCameraControl; + camera.cameraInfo = MockCameraInfo(); + + camera.proxy = getProxyForSettingFocusandExposurePoints( + mockCameraControl, MockCamera2CameraControl()); + + // Make setting focus and metering action fail to test auto-cancel is not + // disabled. + when(mockFocusMeteringResult.isFocusSuccessful()) + .thenAnswer((_) async => Future.value(false)); + when(mockCameraControl.startFocusAndMetering(any)).thenAnswer((_) async => + Future.value(mockFocusMeteringResult)); + + // Set exposure point to later mock failed call to set an exposure point. + await camera.setExposurePoint(cameraId, const Point(0.5, 0.2)); + + // Test failing to set locked focus mode. + await camera.setFocusMode(cameraId, FocusMode.locked); + clearInteractions(mockCameraControl); + + await camera.setExposurePoint(cameraId, exposurePoint); + final VerificationResult verificationResult = + verify(mockCameraControl.startFocusAndMetering(captureAny)); + final FocusMeteringAction capturedAction = + verificationResult.captured.single as FocusMeteringAction; + expect(capturedAction.disableAutoCancel, isFalse); + }); } diff --git a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.mocks.dart b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.mocks.dart index be2541b8f92b..80d76d8339ca 100644 --- a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.mocks.dart +++ b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.mocks.dart @@ -4,39 +4,39 @@ // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i16; -import 'dart:typed_data' as _i29; +import 'dart:typed_data' as _i31; import 'package:camera_android_camerax/src/analyzer.dart' as _i15; import 'package:camera_android_camerax/src/camera.dart' as _i9; -import 'package:camera_android_camerax/src/camera2_camera_control.dart' as _i38; +import 'package:camera_android_camerax/src/camera2_camera_control.dart' as _i22; import 'package:camera_android_camerax/src/camera_control.dart' as _i3; import 'package:camera_android_camerax/src/camera_info.dart' as _i2; -import 'package:camera_android_camerax/src/camera_selector.dart' as _i22; +import 'package:camera_android_camerax/src/camera_selector.dart' as _i24; import 'package:camera_android_camerax/src/camera_state.dart' as _i18; import 'package:camera_android_camerax/src/camerax_library.g.dart' as _i7; import 'package:camera_android_camerax/src/capture_request_options.dart' - as _i39; + as _i23; import 'package:camera_android_camerax/src/exposure_state.dart' as _i5; -import 'package:camera_android_camerax/src/fallback_strategy.dart' as _i23; +import 'package:camera_android_camerax/src/fallback_strategy.dart' as _i25; import 'package:camera_android_camerax/src/focus_metering_action.dart' as _i21; import 'package:camera_android_camerax/src/focus_metering_result.dart' as _i20; -import 'package:camera_android_camerax/src/image_analysis.dart' as _i24; -import 'package:camera_android_camerax/src/image_capture.dart' as _i25; +import 'package:camera_android_camerax/src/image_analysis.dart' as _i26; +import 'package:camera_android_camerax/src/image_capture.dart' as _i27; import 'package:camera_android_camerax/src/image_proxy.dart' as _i17; import 'package:camera_android_camerax/src/live_data.dart' as _i4; -import 'package:camera_android_camerax/src/observer.dart' as _i28; +import 'package:camera_android_camerax/src/observer.dart' as _i30; import 'package:camera_android_camerax/src/pending_recording.dart' as _i10; -import 'package:camera_android_camerax/src/plane_proxy.dart' as _i27; -import 'package:camera_android_camerax/src/preview.dart' as _i30; +import 'package:camera_android_camerax/src/plane_proxy.dart' as _i29; +import 'package:camera_android_camerax/src/preview.dart' as _i32; import 'package:camera_android_camerax/src/process_camera_provider.dart' - as _i31; -import 'package:camera_android_camerax/src/quality_selector.dart' as _i33; + as _i33; +import 'package:camera_android_camerax/src/quality_selector.dart' as _i35; import 'package:camera_android_camerax/src/recorder.dart' as _i11; import 'package:camera_android_camerax/src/recording.dart' as _i8; -import 'package:camera_android_camerax/src/resolution_selector.dart' as _i34; -import 'package:camera_android_camerax/src/resolution_strategy.dart' as _i35; -import 'package:camera_android_camerax/src/use_case.dart' as _i32; -import 'package:camera_android_camerax/src/video_capture.dart' as _i36; +import 'package:camera_android_camerax/src/resolution_selector.dart' as _i36; +import 'package:camera_android_camerax/src/resolution_strategy.dart' as _i37; +import 'package:camera_android_camerax/src/use_case.dart' as _i34; +import 'package:camera_android_camerax/src/video_capture.dart' as _i38; import 'package:camera_android_camerax/src/zoom_state.dart' as _i19; import 'package:camera_platform_interface/camera_platform_interface.dart' as _i6; @@ -44,9 +44,9 @@ import 'package:flutter/foundation.dart' as _i14; import 'package:flutter/services.dart' as _i13; import 'package:flutter/widgets.dart' as _i12; import 'package:mockito/mockito.dart' as _i1; -import 'package:mockito/src/dummies.dart' as _i26; +import 'package:mockito/src/dummies.dart' as _i28; -import 'test_camerax_library.g.dart' as _i37; +import 'test_camerax_library.g.dart' as _i39; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -443,6 +443,38 @@ class MockCameraControl extends _i1.Mock implements _i3.CameraControl { ) as _i16.Future); } +/// A class which mocks [Camera2CameraControl]. +/// +/// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable +class MockCamera2CameraControl extends _i1.Mock + implements _i22.Camera2CameraControl { + @override + _i3.CameraControl get cameraControl => (super.noSuchMethod( + Invocation.getter(#cameraControl), + returnValue: _FakeCameraControl_1( + this, + Invocation.getter(#cameraControl), + ), + returnValueForMissingStub: _FakeCameraControl_1( + this, + Invocation.getter(#cameraControl), + ), + ) as _i3.CameraControl); + + @override + _i16.Future addCaptureRequestOptions( + _i23.CaptureRequestOptions? captureRequestOptions) => + (super.noSuchMethod( + Invocation.method( + #addCaptureRequestOptions, + [captureRequestOptions], + ), + returnValue: _i16.Future.value(), + returnValueForMissingStub: _i16.Future.value(), + ) as _i16.Future); +} + /// A class which mocks [CameraImageData]. /// /// See the documentation for Mockito's code generation for more information. @@ -487,7 +519,7 @@ class MockCameraImageData extends _i1.Mock implements _i6.CameraImageData { /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockCameraSelector extends _i1.Mock implements _i22.CameraSelector { +class MockCameraSelector extends _i1.Mock implements _i24.CameraSelector { @override _i16.Future> filter(List<_i2.CameraInfo>? cameraInfos) => (super.noSuchMethod( @@ -533,7 +565,7 @@ class MockExposureState extends _i1.Mock implements _i5.ExposureState { /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockFallbackStrategy extends _i1.Mock implements _i23.FallbackStrategy { +class MockFallbackStrategy extends _i1.Mock implements _i25.FallbackStrategy { @override _i7.VideoQuality get quality => (super.noSuchMethod( Invocation.getter(#quality), @@ -550,11 +582,28 @@ class MockFallbackStrategy extends _i1.Mock implements _i23.FallbackStrategy { ) as _i7.VideoResolutionFallbackRule); } +/// A class which mocks [FocusMeteringResult]. +/// +/// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable +class MockFocusMeteringResult extends _i1.Mock + implements _i20.FocusMeteringResult { + @override + _i16.Future isFocusSuccessful() => (super.noSuchMethod( + Invocation.method( + #isFocusSuccessful, + [], + ), + returnValue: _i16.Future.value(false), + returnValueForMissingStub: _i16.Future.value(false), + ) as _i16.Future); +} + /// A class which mocks [ImageAnalysis]. /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockImageAnalysis extends _i1.Mock implements _i24.ImageAnalysis { +class MockImageAnalysis extends _i1.Mock implements _i26.ImageAnalysis { @override _i16.Future setTargetRotation(int? rotation) => (super.noSuchMethod( Invocation.method( @@ -590,7 +639,7 @@ class MockImageAnalysis extends _i1.Mock implements _i24.ImageAnalysis { /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockImageCapture extends _i1.Mock implements _i25.ImageCapture { +class MockImageCapture extends _i1.Mock implements _i27.ImageCapture { @override _i16.Future setTargetRotation(int? rotation) => (super.noSuchMethod( Invocation.method( @@ -617,7 +666,7 @@ class MockImageCapture extends _i1.Mock implements _i25.ImageCapture { #takePicture, [], ), - returnValue: _i16.Future.value(_i26.dummyValue( + returnValue: _i16.Future.value(_i28.dummyValue( this, Invocation.method( #takePicture, @@ -625,7 +674,7 @@ class MockImageCapture extends _i1.Mock implements _i25.ImageCapture { ), )), returnValueForMissingStub: - _i16.Future.value(_i26.dummyValue( + _i16.Future.value(_i28.dummyValue( this, Invocation.method( #takePicture, @@ -662,16 +711,16 @@ class MockImageProxy extends _i1.Mock implements _i17.ImageProxy { ) as int); @override - _i16.Future> getPlanes() => (super.noSuchMethod( + _i16.Future> getPlanes() => (super.noSuchMethod( Invocation.method( #getPlanes, [], ), returnValue: - _i16.Future>.value(<_i27.PlaneProxy>[]), + _i16.Future>.value(<_i29.PlaneProxy>[]), returnValueForMissingStub: - _i16.Future>.value(<_i27.PlaneProxy>[]), - ) as _i16.Future>); + _i16.Future>.value(<_i29.PlaneProxy>[]), + ) as _i16.Future>); @override _i16.Future close() => (super.noSuchMethod( @@ -688,7 +737,7 @@ class MockImageProxy extends _i1.Mock implements _i17.ImageProxy { /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockObserver extends _i1.Mock implements _i28.Observer<_i18.CameraState> { +class MockObserver extends _i1.Mock implements _i30.Observer<_i18.CameraState> { @override void Function(Object) get onChanged => (super.noSuchMethod( Invocation.getter(#onChanged), @@ -739,13 +788,13 @@ class MockPendingRecording extends _i1.Mock implements _i10.PendingRecording { /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockPlaneProxy extends _i1.Mock implements _i27.PlaneProxy { +class MockPlaneProxy extends _i1.Mock implements _i29.PlaneProxy { @override - _i29.Uint8List get buffer => (super.noSuchMethod( + _i31.Uint8List get buffer => (super.noSuchMethod( Invocation.getter(#buffer), - returnValue: _i29.Uint8List(0), - returnValueForMissingStub: _i29.Uint8List(0), - ) as _i29.Uint8List); + returnValue: _i31.Uint8List(0), + returnValueForMissingStub: _i31.Uint8List(0), + ) as _i31.Uint8List); @override int get pixelStride => (super.noSuchMethod( @@ -766,7 +815,7 @@ class MockPlaneProxy extends _i1.Mock implements _i27.PlaneProxy { /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockPreview extends _i1.Mock implements _i30.Preview { +class MockPreview extends _i1.Mock implements _i32.Preview { @override _i16.Future setTargetRotation(int? rotation) => (super.noSuchMethod( Invocation.method( @@ -826,7 +875,7 @@ class MockPreview extends _i1.Mock implements _i30.Preview { /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable class MockProcessCameraProvider extends _i1.Mock - implements _i31.ProcessCameraProvider { + implements _i33.ProcessCameraProvider { @override _i16.Future> getAvailableCameraInfos() => (super.noSuchMethod( @@ -842,8 +891,8 @@ class MockProcessCameraProvider extends _i1.Mock @override _i16.Future<_i9.Camera> bindToLifecycle( - _i22.CameraSelector? cameraSelector, - List<_i32.UseCase>? useCases, + _i24.CameraSelector? cameraSelector, + List<_i34.UseCase>? useCases, ) => (super.noSuchMethod( Invocation.method( @@ -876,7 +925,7 @@ class MockProcessCameraProvider extends _i1.Mock ) as _i16.Future<_i9.Camera>); @override - _i16.Future isBound(_i32.UseCase? useCase) => (super.noSuchMethod( + _i16.Future isBound(_i34.UseCase? useCase) => (super.noSuchMethod( Invocation.method( #isBound, [useCase], @@ -886,7 +935,7 @@ class MockProcessCameraProvider extends _i1.Mock ) as _i16.Future); @override - void unbind(List<_i32.UseCase>? useCases) => super.noSuchMethod( + void unbind(List<_i34.UseCase>? useCases) => super.noSuchMethod( Invocation.method( #unbind, [useCases], @@ -908,7 +957,7 @@ class MockProcessCameraProvider extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockQualitySelector extends _i1.Mock implements _i33.QualitySelector { +class MockQualitySelector extends _i1.Mock implements _i35.QualitySelector { @override List<_i7.VideoQualityData> get qualityList => (super.noSuchMethod( Invocation.getter(#qualityList), @@ -953,14 +1002,14 @@ class MockRecorder extends _i1.Mock implements _i11.Recorder { /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable class MockResolutionSelector extends _i1.Mock - implements _i34.ResolutionSelector {} + implements _i36.ResolutionSelector {} /// A class which mocks [ResolutionStrategy]. /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable class MockResolutionStrategy extends _i1.Mock - implements _i35.ResolutionStrategy {} + implements _i37.ResolutionStrategy {} /// A class which mocks [Recording]. /// @@ -1012,7 +1061,7 @@ class MockRecording extends _i1.Mock implements _i8.Recording { /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockVideoCapture extends _i1.Mock implements _i36.VideoCapture { +class MockVideoCapture extends _i1.Mock implements _i38.VideoCapture { @override _i16.Future setTargetRotation(int? rotation) => (super.noSuchMethod( Invocation.method( @@ -1235,7 +1284,7 @@ class MockBuildContext extends _i1.Mock implements _i12.BuildContext { /// /// See the documentation for Mockito's code generation for more information. class MockTestInstanceManagerHostApi extends _i1.Mock - implements _i37.TestInstanceManagerHostApi { + implements _i39.TestInstanceManagerHostApi { @override void clear() => super.noSuchMethod( Invocation.method( @@ -1250,7 +1299,7 @@ class MockTestInstanceManagerHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestSystemServicesHostApi extends _i1.Mock - implements _i37.TestSystemServicesHostApi { + implements _i39.TestSystemServicesHostApi { @override _i16.Future<_i7.CameraPermissionsErrorData?> requestCameraPermissions( bool? enableAudio) => @@ -1277,7 +1326,7 @@ class MockTestSystemServicesHostApi extends _i1.Mock suffix, ], ), - returnValue: _i26.dummyValue( + returnValue: _i28.dummyValue( this, Invocation.method( #getTempFilePath, @@ -1287,7 +1336,7 @@ class MockTestSystemServicesHostApi extends _i1.Mock ], ), ), - returnValueForMissingStub: _i26.dummyValue( + returnValueForMissingStub: _i28.dummyValue( this, Invocation.method( #getTempFilePath, @@ -1320,38 +1369,6 @@ class MockZoomState extends _i1.Mock implements _i19.ZoomState { ) as double); } -/// A class which mocks [Camera2CameraControl]. -/// -/// See the documentation for Mockito's code generation for more information. -// ignore: must_be_immutable -class MockCamera2CameraControl extends _i1.Mock - implements _i38.Camera2CameraControl { - @override - _i3.CameraControl get cameraControl => (super.noSuchMethod( - Invocation.getter(#cameraControl), - returnValue: _FakeCameraControl_1( - this, - Invocation.getter(#cameraControl), - ), - returnValueForMissingStub: _FakeCameraControl_1( - this, - Invocation.getter(#cameraControl), - ), - ) as _i3.CameraControl); - - @override - _i16.Future addCaptureRequestOptions( - _i39.CaptureRequestOptions? captureRequestOptions) => - (super.noSuchMethod( - Invocation.method( - #addCaptureRequestOptions, - [captureRequestOptions], - ), - returnValue: _i16.Future.value(), - returnValueForMissingStub: _i16.Future.value(), - ) as _i16.Future); -} - /// A class which mocks [LiveData]. /// /// See the documentation for Mockito's code generation for more information. @@ -1363,7 +1380,7 @@ class MockLiveCameraState extends _i1.Mock } @override - _i16.Future observe(_i28.Observer<_i18.CameraState>? observer) => + _i16.Future observe(_i30.Observer<_i18.CameraState>? observer) => (super.noSuchMethod( Invocation.method( #observe, @@ -1395,7 +1412,7 @@ class MockLiveZoomState extends _i1.Mock } @override - _i16.Future observe(_i28.Observer<_i19.ZoomState>? observer) => + _i16.Future observe(_i30.Observer<_i19.ZoomState>? observer) => (super.noSuchMethod( Invocation.method( #observe, diff --git a/packages/camera/camera_android_camerax/test/focus_metering_action_test.dart b/packages/camera/camera_android_camerax/test/focus_metering_action_test.dart index a74338e2735c..8a7dcdc6c117 100644 --- a/packages/camera/camera_android_camerax/test/focus_metering_action_test.dart +++ b/packages/camera/camera_android_camerax/test/focus_metering_action_test.dart @@ -43,8 +43,8 @@ void main() { instanceManager: instanceManager, ); - verifyNever( - mockApi.create(argThat(isA()), argThat(isA>()))); + verifyNever(mockApi.create(argThat(isA()), argThat(isA>()), + argThat(isA()))); }); test('create calls create on the Java side', () { final MockTestFocusMeteringActionHostApi mockApi = @@ -67,6 +67,7 @@ void main() { (mockMeteringPoint1, mockMeteringPoint1Mode), (mockMeteringPoint2, mockMeteringPoint2Mode) ]; + const bool disableAutoCancel = true; instanceManager .addHostCreatedInstance(mockMeteringPoint1, mockMeteringPoint1Id, @@ -81,12 +82,14 @@ void main() { final FocusMeteringAction instance = FocusMeteringAction( meteringPointInfos: meteringPointInfos, + disableAutoCancel: disableAutoCancel, instanceManager: instanceManager, ); final VerificationResult verificationResult = verify(mockApi.create( argThat(equals(instanceManager.getIdentifier(instance))), - captureAny)); + captureAny, + argThat(equals(disableAutoCancel)))); final List captureMeteringPointInfos = verificationResult.captured.single as List; expect(captureMeteringPointInfos.length, equals(2)); diff --git a/packages/camera/camera_android_camerax/test/focus_metering_action_test.mocks.dart b/packages/camera/camera_android_camerax/test/focus_metering_action_test.mocks.dart index d1ceef9daadd..3b0e4c824076 100644 --- a/packages/camera/camera_android_camerax/test/focus_metering_action_test.mocks.dart +++ b/packages/camera/camera_android_camerax/test/focus_metering_action_test.mocks.dart @@ -77,6 +77,7 @@ class MockTestFocusMeteringActionHostApi extends _i1.Mock void create( int? identifier, List<_i5.MeteringPointInfo?>? meteringPointInfos, + bool? disableAutoCancel, ) => super.noSuchMethod( Invocation.method( @@ -84,6 +85,7 @@ class MockTestFocusMeteringActionHostApi extends _i1.Mock [ identifier, meteringPointInfos, + disableAutoCancel, ], ), returnValueForMissingStub: null, diff --git a/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart b/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart index c847327cce59..b69825928dd9 100644 --- a/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart +++ b/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart @@ -2065,7 +2065,8 @@ abstract class TestFocusMeteringActionHostApi { static const MessageCodec codec = _TestFocusMeteringActionHostApiCodec(); - void create(int identifier, List meteringPointInfos); + void create(int identifier, List meteringPointInfos, + bool? disableAutoCancel); static void setup(TestFocusMeteringActionHostApi? api, {BinaryMessenger? binaryMessenger}) { @@ -2090,7 +2091,9 @@ abstract class TestFocusMeteringActionHostApi { (args[1] as List?)?.cast(); assert(arg_meteringPointInfos != null, 'Argument for dev.flutter.pigeon.FocusMeteringActionHostApi.create was null, expected non-null List.'); - api.create(arg_identifier!, arg_meteringPointInfos!); + final bool? arg_disableAutoCancel = (args[2] as bool?); + api.create( + arg_identifier!, arg_meteringPointInfos!, arg_disableAutoCancel); return []; }); } From da162699817b421efc254b4501de8e9f8a2de2ce Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 19 Mar 2024 13:58:13 -0400 Subject: [PATCH 092/126] [pigeon] Adds Dart implementation of ProxyApi (#6043) Part of https://github.com/flutter/flutter/issues/134777 --- packages/pigeon/CHANGELOG.md | 4 + packages/pigeon/lib/ast.dart | 127 + packages/pigeon/lib/dart/templates.dart | 403 ++ packages/pigeon/lib/dart_generator.dart | 1004 +++- packages/pigeon/lib/generator_tools.dart | 138 +- packages/pigeon/lib/objc_generator.dart | 5 +- packages/pigeon/lib/pigeon.dart | 4 +- packages/pigeon/lib/pigeon_lib.dart | 8 +- packages/pigeon/pigeons/proxy_api_tests.dart | 469 ++ .../src/generated/proxy_api_tests.gen.dart | 5023 +++++++++++++++++ .../test/instance_manager_test.dart | 168 + packages/pigeon/pubspec.yaml | 4 +- packages/pigeon/test/dart/proxy_api_test.dart | 1014 ++++ .../pigeon/test/generator_tools_test.dart | 13 +- packages/pigeon/tool/shared/generation.dart | 1 + script/configs/allowed_unpinned_deps.yaml | 2 + 16 files changed, 8259 insertions(+), 128 deletions(-) create mode 100644 packages/pigeon/lib/dart/templates.dart create mode 100644 packages/pigeon/pigeons/proxy_api_tests.dart create mode 100644 packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/proxy_api_tests.gen.dart create mode 100644 packages/pigeon/platform_tests/shared_test_plugin_code/test/instance_manager_test.dart create mode 100644 packages/pigeon/test/dart/proxy_api_test.dart diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md index 1c775d236405..ea6cd9df5b55 100644 --- a/packages/pigeon/CHANGELOG.md +++ b/packages/pigeon/CHANGELOG.md @@ -1,3 +1,7 @@ +## 17.2.0 + +* [dart] Adds implementation for `@ProxyApi`. + ## 17.1.3 * [objc] Fixes double prefixes added to enum names. diff --git a/packages/pigeon/lib/ast.dart b/packages/pigeon/lib/ast.dart index 0c48faa45456..bc4ec8c44777 100644 --- a/packages/pigeon/lib/ast.dart +++ b/packages/pigeon/lib/ast.dart @@ -177,6 +177,133 @@ class AstProxyApi extends Api { (ApiField field) => !field.isAttached, ); + /// A list of AstProxyApis where each `extends` the API that follows it. + /// + /// Returns an empty list if this api does not extend a ProxyApi. + /// + /// This method assumes the super classes of each ProxyApi doesn't create a + /// loop. Throws a [ArgumentError] if a loop is found. + /// + /// This method also assumes that all super classes are ProxyApis. Otherwise, + /// throws an [ArgumentError]. + Iterable allSuperClasses() { + final List superClassChain = []; + + if (superClass != null && !superClass!.isProxyApi) { + throw ArgumentError( + 'Could not find a ProxyApi for super class: ${superClass!.baseName}', + ); + } + + AstProxyApi? currentProxyApi = superClass?.associatedProxyApi; + while (currentProxyApi != null) { + if (superClassChain.contains(currentProxyApi)) { + throw ArgumentError( + 'Loop found when processing super classes for a ProxyApi: ' + '$name, ${superClassChain.map((AstProxyApi api) => api.name)}', + ); + } + + superClassChain.add(currentProxyApi); + + if (currentProxyApi.superClass != null && + !currentProxyApi.superClass!.isProxyApi) { + throw ArgumentError( + 'Could not find a ProxyApi for super class: ' + '${currentProxyApi.superClass!.baseName}', + ); + } + + currentProxyApi = currentProxyApi.superClass?.associatedProxyApi; + } + + return superClassChain; + } + + /// All ProxyApis this API `implements` and all the interfaces those APIs + /// `implements`. + Iterable apisOfInterfaces() => _recursiveFindAllInterfaceApis(); + + /// All methods inherited from interfaces and the interfaces of interfaces. + Iterable flutterMethodsFromInterfaces() sync* { + for (final AstProxyApi proxyApi in apisOfInterfaces()) { + yield* proxyApi.methods; + } + } + + /// A list of Flutter methods inherited from the ProxyApi that this ProxyApi + /// `extends`. + /// + /// This also recursively checks the ProxyApi that the super class `extends` + /// and so on. + /// + /// This also includes methods that super classes inherited from interfaces + /// with `implements`. + Iterable flutterMethodsFromSuperClasses() sync* { + for (final AstProxyApi proxyApi in allSuperClasses().toList().reversed) { + yield* proxyApi.flutterMethods; + } + if (superClass != null) { + final Set interfaceApisFromSuperClasses = + superClass!.associatedProxyApi!._recursiveFindAllInterfaceApis(); + for (final AstProxyApi proxyApi in interfaceApisFromSuperClasses) { + yield* proxyApi.methods; + } + } + } + + /// Whether the api has a method that callbacks to Dart to add a new instance + /// to the InstanceManager. + /// + /// This is possible as long as no callback methods are required to + /// instantiate the class. + bool hasCallbackConstructor() { + return flutterMethods + .followedBy(flutterMethodsFromSuperClasses()) + .followedBy(flutterMethodsFromInterfaces()) + .every((Method method) => !method.isRequired); + } + + // Recursively search for all the interfaces apis from a list of names of + // interfaces. + // + // This method assumes that all interfaces are ProxyApis and an api doesn't + // contains itself as an interface. Otherwise, throws an [ArgumentError]. + Set _recursiveFindAllInterfaceApis([ + Set seenApis = const {}, + ]) { + final Set allInterfaces = {}; + + allInterfaces.addAll( + interfaces.map( + (TypeDeclaration type) { + if (!type.isProxyApi) { + throw ArgumentError( + 'Could not find a valid ProxyApi for an interface: $type', + ); + } else if (seenApis.contains(type.associatedProxyApi)) { + throw ArgumentError( + 'A ProxyApi cannot be a super class of itself: ${type.baseName}', + ); + } + return type.associatedProxyApi!; + }, + ), + ); + + // Adds the current api since it would be invalid for it to be an interface + // of itself. + final Set newSeenApis = {...seenApis, this}; + + for (final AstProxyApi interfaceApi in {...allInterfaces}) { + allInterfaces.addAll( + interfaceApi._recursiveFindAllInterfaceApis(newSeenApis), + ); + } + + return allInterfaces; + } + @override String toString() { return '(ProxyApi name:$name methods:$methods field:$fields ' diff --git a/packages/pigeon/lib/dart/templates.dart b/packages/pigeon/lib/dart/templates.dart new file mode 100644 index 000000000000..ce986b888dbf --- /dev/null +++ b/packages/pigeon/lib/dart/templates.dart @@ -0,0 +1,403 @@ +// 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 '../generator_tools.dart'; + +/// Creates the `InstanceManager` with the passed string values. +String instanceManagerTemplate({ + required Iterable allProxyApiNames, +}) { + final Iterable apiHandlerSetUps = allProxyApiNames.map( + (String name) { + return '$name.${classMemberNamePrefix}setUpMessageHandlers(${classMemberNamePrefix}instanceManager: instanceManager);'; + }, + ); + + return ''' +/// Maintains instances used to communicate with the native objects they +/// represent. +/// +/// Added instances are stored as weak references and their copies are stored +/// as strong references to maintain access to their variables and callback +/// methods. Both are stored with the same identifier. +/// +/// When a weak referenced instance becomes inaccessible, +/// [onWeakReferenceRemoved] is called with its associated identifier. +/// +/// If an instance is retrieved and has the possibility to be used, +/// (e.g. calling [getInstanceWithWeakReference]) a copy of the strong reference +/// is added as a weak reference with the same identifier. This prevents a +/// scenario where the weak referenced instance was released and then later +/// returned by the host platform. +class $instanceManagerClassName { + /// Constructs a [$instanceManagerClassName]. + $instanceManagerClassName({required void Function(int) onWeakReferenceRemoved}) { + this.onWeakReferenceRemoved = (int identifier) { + _weakInstances.remove(identifier); + onWeakReferenceRemoved(identifier); + }; + _finalizer = Finalizer(this.onWeakReferenceRemoved); + } + + // Identifiers are locked to a specific range to avoid collisions with objects + // created simultaneously by the host platform. + // Host uses identifiers >= 2^16 and Dart is expected to use values n where, + // 0 <= n < 2^16. + static const int _maxDartCreatedIdentifier = 65536; + + /// The default [$instanceManagerClassName] used by ProxyApis. + /// + /// On creation, this manager makes a call to clear the native + /// InstanceManager. This is to prevent identifier conflicts after a host + /// restart. + static final $instanceManagerClassName instance = _initInstance(); + + // Expando is used because it doesn't prevent its keys from becoming + // inaccessible. This allows the manager to efficiently retrieve an identifier + // of an instance without holding a strong reference to that instance. + // + // It also doesn't use `==` to search for identifiers, which would lead to an + // infinite loop when comparing an object to its copy. (i.e. which was caused + // by calling instanceManager.getIdentifier() inside of `==` while this was a + // HashMap). + final Expando _identifiers = Expando(); + final Map> _weakInstances = + >{}; + final Map _strongInstances = {}; + late final Finalizer _finalizer; + int _nextIdentifier = 0; + + /// Called when a weak referenced instance is removed by [removeWeakReference] + /// or becomes inaccessible. + late final void Function(int) onWeakReferenceRemoved; + + static $instanceManagerClassName _initInstance() { + WidgetsFlutterBinding.ensureInitialized(); + final _${instanceManagerClassName}Api api = _${instanceManagerClassName}Api(); + // Clears the native `$instanceManagerClassName` on the initial use of the Dart one. + api.clear(); + final $instanceManagerClassName instanceManager = $instanceManagerClassName( + onWeakReferenceRemoved: (int identifier) { + api.removeStrongReference(identifier); + }, + ); + _${instanceManagerClassName}Api.setUpMessageHandlers(instanceManager: instanceManager); + ${apiHandlerSetUps.join('\n\t\t')} + return instanceManager; + } + + /// Adds a new instance that was instantiated by Dart. + /// + /// In other words, Dart wants to add a new instance that will represent + /// an object that will be instantiated on the host platform. + /// + /// Throws assertion error if the instance has already been added. + /// + /// Returns the randomly generated id of the [instance] added. + int addDartCreatedInstance($proxyApiBaseClassName instance) { + final int identifier = _nextUniqueIdentifier(); + _addInstanceWithIdentifier(instance, identifier); + return identifier; + } + + /// Removes the instance, if present, and call [onWeakReferenceRemoved] with + /// its identifier. + /// + /// Returns the identifier associated with the removed instance. Otherwise, + /// `null` if the instance was not found in this manager. + /// + /// This does not remove the strong referenced instance associated with + /// [instance]. This can be done with [remove]. + int? removeWeakReference($proxyApiBaseClassName instance) { + final int? identifier = getIdentifier(instance); + if (identifier == null) { + return null; + } + + _identifiers[instance] = null; + _finalizer.detach(instance); + onWeakReferenceRemoved(identifier); + + return identifier; + } + + /// Removes [identifier] and its associated strongly referenced instance, if + /// present, from the manager. + /// + /// Returns the strong referenced instance associated with [identifier] before + /// it was removed. Returns `null` if [identifier] was not associated with + /// any strong reference. + /// + /// This does not remove the weak referenced instance associated with + /// [identifier]. This can be done with [removeWeakReference]. + T? remove(int identifier) { + return _strongInstances.remove(identifier) as T?; + } + + /// Retrieves the instance associated with identifier. + /// + /// The value returned is chosen from the following order: + /// + /// 1. A weakly referenced instance associated with identifier. + /// 2. If the only instance associated with identifier is a strongly + /// referenced instance, a copy of the instance is added as a weak reference + /// with the same identifier. Returning the newly created copy. + /// 3. If no instance is associated with identifier, returns null. + /// + /// This method also expects the host `InstanceManager` to have a strong + /// reference to the instance the identifier is associated with. + T? getInstanceWithWeakReference(int identifier) { + final $proxyApiBaseClassName? weakInstance = _weakInstances[identifier]?.target; + + if (weakInstance == null) { + final $proxyApiBaseClassName? strongInstance = _strongInstances[identifier]; + if (strongInstance != null) { + final $proxyApiBaseClassName copy = strongInstance.${classMemberNamePrefix}copy(); + _identifiers[copy] = identifier; + _weakInstances[identifier] = WeakReference<$proxyApiBaseClassName>(copy); + _finalizer.attach(copy, identifier, detach: copy); + return copy as T; + } + return strongInstance as T?; + } + + return weakInstance as T; + } + + /// Retrieves the identifier associated with instance. + int? getIdentifier($proxyApiBaseClassName instance) { + return _identifiers[instance]; + } + + /// Adds a new instance that was instantiated by the host platform. + /// + /// In other words, the host platform wants to add a new instance that + /// represents an object on the host platform. Stored with [identifier]. + /// + /// Throws assertion error if the instance or its identifier has already been + /// added. + /// + /// Returns unique identifier of the [instance] added. + void addHostCreatedInstance($proxyApiBaseClassName instance, int identifier) { + _addInstanceWithIdentifier(instance, identifier); + } + + void _addInstanceWithIdentifier($proxyApiBaseClassName instance, int identifier) { + assert(!containsIdentifier(identifier)); + assert(getIdentifier(instance) == null); + assert(identifier >= 0); + + _identifiers[instance] = identifier; + _weakInstances[identifier] = WeakReference<$proxyApiBaseClassName>(instance); + _finalizer.attach(instance, identifier, detach: instance); + + final $proxyApiBaseClassName copy = instance.${classMemberNamePrefix}copy(); + _identifiers[copy] = identifier; + _strongInstances[identifier] = copy; + } + + /// Whether this manager contains the given [identifier]. + bool containsIdentifier(int identifier) { + return _weakInstances.containsKey(identifier) || + _strongInstances.containsKey(identifier); + } + + int _nextUniqueIdentifier() { + late int identifier; + do { + identifier = _nextIdentifier; + _nextIdentifier = (_nextIdentifier + 1) % _maxDartCreatedIdentifier; + } while (containsIdentifier(identifier)); + return identifier; + } +} +'''; +} + +/// Creates the `InstanceManagerApi` with the passed string values. +String instanceManagerApiTemplate({ + required String dartPackageName, + required String pigeonChannelCodecVarName, +}) { + const String apiName = '${instanceManagerClassName}Api'; + + final String removeStrongReferenceName = makeChannelNameWithStrings( + apiName: apiName, + methodName: 'removeStrongReference', + dartPackageName: dartPackageName, + ); + + final String clearName = makeChannelNameWithStrings( + apiName: apiName, + methodName: 'clear', + dartPackageName: dartPackageName, + ); + + return ''' +/// Generated API for managing the Dart and native `$instanceManagerClassName`s. +class _$apiName { + /// Constructor for [_$apiName]. + _$apiName({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec $pigeonChannelCodecVarName = + StandardMessageCodec(); + + static void setUpMessageHandlers({ + BinaryMessenger? binaryMessenger, + $instanceManagerClassName? instanceManager, + }) { + const String channelName = + r'$removeStrongReferenceName'; + final BasicMessageChannel channel = BasicMessageChannel( + channelName, + $pigeonChannelCodecVarName, + binaryMessenger: binaryMessenger, + ); + channel.setMessageHandler((Object? message) async { + assert( + message != null, + 'Argument for \$channelName was null.', + ); + final int? identifier = message as int?; + assert( + identifier != null, + r'Argument for \$channelName, expected non-null int.', + ); + (instanceManager ?? $instanceManagerClassName.instance).remove(identifier!); + return; + }); + } + + Future removeStrongReference(int identifier) async { + const String channelName = + r'$removeStrongReferenceName'; + final BasicMessageChannel channel = BasicMessageChannel( + channelName, + $pigeonChannelCodecVarName, + binaryMessenger: _binaryMessenger, + ); + final List? replyList = + await channel.send(identifier) as List?; + if (replyList == null) { + throw _createConnectionError(channelName); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return; + } + } + + /// Clear the native `$instanceManagerClassName`. + /// + /// This is typically called after a hot restart. + Future clear() async { + const String channelName = + r'$clearName'; + final BasicMessageChannel channel = BasicMessageChannel( + channelName, + $pigeonChannelCodecVarName, + binaryMessenger: _binaryMessenger, + ); + final List? replyList = await channel.send(null) as List?; + if (replyList == null) { + throw _createConnectionError(channelName); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return; + } + } +}'''; +} + +/// The base class for all ProxyApis. +/// +/// All Dart classes generated as a ProxyApi extends this one. +const String proxyApiBaseClass = ''' +/// An immutable object that serves as the base class for all ProxyApis and +/// can provide functional copies of itself. +/// +/// All implementers are expected to be [immutable] as defined by the annotation +/// and override [${classMemberNamePrefix}copy] returning an instance of itself. +@immutable +abstract class $proxyApiBaseClassName { + /// Construct a [$proxyApiBaseClassName]. + $proxyApiBaseClassName({ + this.$_proxyApiBaseClassMessengerVarName, + $instanceManagerClassName? $_proxyApiBaseClassInstanceManagerVarName, + }) : $_proxyApiBaseClassInstanceManagerVarName = + $_proxyApiBaseClassInstanceManagerVarName ?? $instanceManagerClassName.instance; + + /// Sends and receives binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used, which routes to + /// the host platform. + @protected + final BinaryMessenger? $_proxyApiBaseClassMessengerVarName; + + /// Maintains instances stored to communicate with native language objects. + @protected + final $instanceManagerClassName $_proxyApiBaseClassInstanceManagerVarName; + + /// Instantiates and returns a functionally identical object to oneself. + /// + /// Outside of tests, this method should only ever be called by + /// [$instanceManagerClassName]. + /// + /// Subclasses should always override their parent's implementation of this + /// method. + @protected + $proxyApiBaseClassName ${classMemberNamePrefix}copy(); +} +'''; + +/// The base codec for ProxyApis. +/// +/// All generated Dart proxy apis should use this codec or extend it. This codec +/// adds support to convert instances to their corresponding identifier from an +/// `InstanceManager` and vice versa. +const String proxyApiBaseCodec = ''' +class $_proxyApiCodecName extends StandardMessageCodec { + const $_proxyApiCodecName(this.instanceManager); + final $instanceManagerClassName instanceManager; + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is $proxyApiBaseClassName) { + buffer.putUint8(128); + writeValue(buffer, instanceManager.getIdentifier(value)); + } else { + super.writeValue(buffer, value); + } + } + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return instanceManager + .getInstanceWithWeakReference(readValue(buffer)! as int); + default: + return super.readValueOfType(type, buffer); + } + } +} +'''; + +/// Name of the base class of all ProxyApis. +const String proxyApiBaseClassName = '${classNamePrefix}ProxyApiBaseClass'; +const String _proxyApiBaseClassMessengerVarName = + '${classMemberNamePrefix}binaryMessenger'; +const String _proxyApiBaseClassInstanceManagerVarName = + '${classMemberNamePrefix}instanceManager'; +const String _proxyApiCodecName = '_${classNamePrefix}ProxyApiBaseCodec'; diff --git a/packages/pigeon/lib/dart_generator.dart b/packages/pigeon/lib/dart_generator.dart index 8cdea012554b..40ce7a6e5df4 100644 --- a/packages/pigeon/lib/dart_generator.dart +++ b/packages/pigeon/lib/dart_generator.dart @@ -2,9 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:code_builder/code_builder.dart' as cb; +import 'package:dart_style/dart_style.dart'; import 'package:path/path.dart' as path; import 'ast.dart'; +import 'dart/templates.dart'; import 'functional.dart'; import 'generator.dart'; import 'generator_tools.dart'; @@ -18,6 +21,10 @@ const String _docCommentPrefix = '///'; /// user defined parameters. const String _varNamePrefix = '__pigeon_'; +/// Name of the `InstanceManager` variable for a ProxyApi class; +const String _instanceManagerVarName = + '${classMemberNamePrefix}instanceManager'; + /// Name of field used for host API codec. const String _pigeonChannelCodec = 'pigeonChannelCodec'; @@ -111,9 +118,16 @@ class DartGenerator extends StructuredGenerator { "import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;", ); indent.newln(); + + final bool hasProxyApi = root.apis.any((Api api) => api is AstProxyApi); indent.writeln( - "import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;"); + "import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer${hasProxyApi ? ', immutable, protected' : ''};"); indent.writeln("import 'package:flutter/services.dart';"); + if (hasProxyApi) { + indent.writeln( + "import 'package:flutter/widgets.dart' show WidgetsFlutterBinding;", + ); + } } @override @@ -434,6 +448,158 @@ final BinaryMessenger? ${_varNamePrefix}binaryMessenger; }); } + @override + void writeInstanceManager( + DartOptions generatorOptions, + Root root, + Indent indent, { + required String dartPackageName, + }) { + indent.format(proxyApiBaseClass); + + indent.format( + instanceManagerTemplate( + allProxyApiNames: root.apis + .whereType() + .map((AstProxyApi api) => api.name), + ), + ); + } + + @override + void writeInstanceManagerApi( + DartOptions generatorOptions, + Root root, + Indent indent, { + required String dartPackageName, + }) { + indent.format( + instanceManagerApiTemplate( + dartPackageName: dartPackageName, + pigeonChannelCodecVarName: _pigeonChannelCodec, + ), + ); + } + + @override + void writeProxyApiBaseCodec( + DartOptions generatorOptions, + Root root, + Indent indent, + ) { + indent.format(proxyApiBaseCodec); + } + + @override + void writeProxyApi( + DartOptions generatorOptions, + Root root, + Indent indent, + AstProxyApi api, { + required String dartPackageName, + }) { + const String codecName = '_${classNamePrefix}ProxyApiBaseCodec'; + + // Each API has a private codec instance used by every host method, + // constructor, or non-static field. + final String codecInstanceName = '${_varNamePrefix}codec${api.name}'; + + // AST class used by code_builder to generate the code. + final cb.Class proxyApi = cb.Class( + (cb.ClassBuilder builder) => builder + ..name = api.name + ..extend = api.superClass != null + ? cb.refer(api.superClass!.baseName) + : cb.refer(proxyApiBaseClassName) + ..implements.addAll( + api.interfaces.map( + (TypeDeclaration type) => cb.refer(type.baseName), + ), + ) + ..docs.addAll( + asDocumentationComments(api.documentationComments, _docCommentSpec), + ) + ..constructors.addAll(_proxyApiConstructors( + api.constructors, + apiName: api.name, + dartPackageName: dartPackageName, + codecName: codecName, + codecInstanceName: codecInstanceName, + superClassApi: api.superClass?.associatedProxyApi, + unattachedFields: api.unattachedFields, + flutterMethodsFromSuperClasses: api.flutterMethodsFromSuperClasses(), + flutterMethodsFromInterfaces: api.flutterMethodsFromInterfaces(), + declaredFlutterMethods: api.flutterMethods, + )) + ..constructors.add( + _proxyApiDetachedConstructor( + apiName: api.name, + superClassApi: api.superClass?.associatedProxyApi, + unattachedFields: api.unattachedFields, + flutterMethodsFromSuperClasses: + api.flutterMethodsFromSuperClasses(), + flutterMethodsFromInterfaces: api.flutterMethodsFromInterfaces(), + declaredFlutterMethods: api.flutterMethods, + ), + ) + ..fields.addAll([ + if (api.constructors.isNotEmpty || + api.attachedFields.any((ApiField field) => !field.isStatic) || + api.hostMethods.isNotEmpty) + _proxyApiCodecInstanceField( + codecInstanceName: codecInstanceName, + codecName: codecName, + ), + ]) + ..fields.addAll(_proxyApiUnattachedFields(api.unattachedFields)) + ..fields.addAll(_proxyApiFlutterMethodFields( + api.flutterMethods, + apiName: api.name, + )) + ..fields.addAll(_proxyApiInterfaceApiFields(api.apisOfInterfaces())) + ..fields.addAll(_proxyApiAttachedFields(api.attachedFields)) + ..methods.add( + _proxyApiSetUpMessageHandlerMethod( + flutterMethods: api.flutterMethods, + apiName: api.name, + dartPackageName: dartPackageName, + codecName: codecName, + unattachedFields: api.unattachedFields, + hasCallbackConstructor: api.hasCallbackConstructor(), + ), + ) + ..methods.addAll( + _proxyApiAttachedFieldMethods( + api.attachedFields, + apiName: api.name, + dartPackageName: dartPackageName, + codecInstanceName: codecInstanceName, + codecName: codecName, + ), + ) + ..methods.addAll(_proxyApiHostMethods( + api.hostMethods, + apiName: api.name, + dartPackageName: dartPackageName, + codecInstanceName: codecInstanceName, + codecName: codecName, + )) + ..methods.add( + _proxyApiCopyMethod( + apiName: api.name, + unattachedFields: api.unattachedFields, + declaredAndInheritedFlutterMethods: api + .flutterMethodsFromSuperClasses() + .followedBy(api.flutterMethodsFromInterfaces()) + .followedBy(api.flutterMethods), + ), + ), + ); + + final cb.DartEmitter emitter = cb.DartEmitter(useNullSafetySyntax: true); + indent.format(DartFormatter().format('${proxyApi.accept(emitter)}')); + } + /// Generates Dart source code for test support libraries based on the given AST /// represented by [root], outputting the code to [sink]. [sourceOutPath] is the /// path of the generated dart code to be tested. [testOutPath] is where the @@ -521,15 +687,22 @@ final BinaryMessenger? ${_varNamePrefix}binaryMessenger; Indent indent, { required String dartPackageName, }) { - final bool hasHostApi = - root.apis.any((Api api) => api.methods.isNotEmpty && api is AstHostApi); - final bool hasFlutterApi = root.apis - .any((Api api) => api.methods.isNotEmpty && api is AstFlutterApi); - - if (hasHostApi) { + final bool hasHostMethod = root.apis + .whereType() + .any((AstHostApi api) => api.methods.isNotEmpty) || + root.apis.whereType().any((AstProxyApi api) => + api.constructors.isNotEmpty || + api.attachedFields.isNotEmpty || + api.hostMethods.isNotEmpty); + final bool hasFlutterMethod = root.apis + .whereType() + .any((AstFlutterApi api) => api.methods.isNotEmpty) || + root.apis.any((Api api) => api is AstProxyApi); + + if (hasHostMethod) { _writeCreateConnectionError(indent); } - if (hasFlutterApi || generatorOptions.testOutPath != null) { + if (hasFlutterMethod || generatorOptions.testOutPath != null) { _writeWrapResponse(generatorOptions, root, indent); } } @@ -678,7 +851,8 @@ if (${_varNamePrefix}replyList == null) { required bool isMockHandler, required bool isAsynchronous, String nullHandlerExpression = 'api == null', - String Function(String methodName, Iterable safeArgumentNames) + String Function(String methodName, Iterable parameters, + Iterable safeArgumentNames) onCreateApiCall = _createFlutterApiMethodCall, }) { indent.write(''); @@ -745,7 +919,7 @@ if (${_varNamePrefix}replyList == null) { final String name = _getSafeArgumentName(index, field); return '${field.isNamed ? '${field.name}: ' : ''}$name${field.type.isNullable ? '' : '!'}'; }); - call = onCreateApiCall(name, argNames); + call = onCreateApiCall(name, parameters, argNames); } indent.writeScoped('try {', '} ', () { if (returnType.isVoid) { @@ -787,10 +961,820 @@ if (${_varNamePrefix}replyList == null) { static String _createFlutterApiMethodCall( String methodName, + Iterable parameters, Iterable safeArgumentNames, ) { return 'api.$methodName(${safeArgumentNames.join(', ')})'; } + + /// Converts Constructors from the pigeon AST to a `code_builder` Constructor + /// for a ProxyApi. + Iterable _proxyApiConstructors( + Iterable constructors, { + required String apiName, + required String dartPackageName, + required String codecName, + required String codecInstanceName, + required AstProxyApi? superClassApi, + required Iterable unattachedFields, + required Iterable flutterMethodsFromSuperClasses, + required Iterable flutterMethodsFromInterfaces, + required Iterable declaredFlutterMethods, + }) sync* { + final cb.Parameter binaryMessengerParameter = cb.Parameter( + (cb.ParameterBuilder builder) => builder + ..name = '${classMemberNamePrefix}binaryMessenger' + ..named = true + ..toSuper = true, + ); + final cb.Parameter instanceManagerParameter = cb.Parameter( + (cb.ParameterBuilder builder) => builder + ..name = _instanceManagerVarName + ..named = true + ..toSuper = true, + ); + for (final Constructor constructor in constructors) { + yield cb.Constructor( + (cb.ConstructorBuilder builder) { + final String channelName = makeChannelNameWithStrings( + apiName: apiName, + methodName: constructor.name.isNotEmpty + ? constructor.name + : '${classMemberNamePrefix}defaultConstructor', + dartPackageName: dartPackageName, + ); + builder + ..name = constructor.name.isNotEmpty ? constructor.name : null + ..docs.addAll(asDocumentationComments( + constructor.documentationComments, + _docCommentSpec, + )) + ..optionalParameters.addAll( + [ + binaryMessengerParameter, + instanceManagerParameter, + for (final ApiField field in unattachedFields) + cb.Parameter( + (cb.ParameterBuilder builder) => builder + ..name = field.name + ..named = true + ..toThis = true + ..required = !field.type.isNullable, + ), + for (final Method method in flutterMethodsFromSuperClasses) + cb.Parameter( + (cb.ParameterBuilder builder) => builder + ..name = method.name + ..named = true + ..toSuper = true + ..required = method.isRequired, + ), + for (final Method method in flutterMethodsFromInterfaces + .followedBy(declaredFlutterMethods)) + cb.Parameter( + (cb.ParameterBuilder builder) => builder + ..name = method.name + ..named = true + ..toThis = true + ..required = method.isRequired, + ), + ...indexMap( + constructor.parameters, + (int index, NamedType parameter) => cb.Parameter( + (cb.ParameterBuilder builder) => builder + ..name = _getParameterName(index, parameter) + ..type = _refer(parameter.type) + ..named = true + ..required = !parameter.type.isNullable, + ), + ) + ], + ) + ..initializers.addAll( + [ + if (superClassApi != null) + const cb.Code('super.${classMemberNamePrefix}detached()') + ], + ) + ..body = cb.Block( + (cb.BlockBuilder builder) { + final StringBuffer messageCallSink = StringBuffer(); + _writeHostMethodMessageCall( + Indent(messageCallSink), + channelName: channelName, + parameters: [ + Parameter( + name: '${_varNamePrefix}instanceIdentifier', + type: const TypeDeclaration( + baseName: 'int', + isNullable: false, + ), + ), + ...unattachedFields.map( + (ApiField field) => Parameter( + name: field.name, + type: field.type, + ), + ), + ...constructor.parameters, + ], + returnType: const TypeDeclaration.voidDeclaration(), + ); + + builder.statements.addAll([ + const cb.Code( + 'final int ${_varNamePrefix}instanceIdentifier = $_instanceManagerVarName.addDartCreatedInstance(this);', + ), + cb.Code('final $codecName $_pigeonChannelCodec =\n' + ' $codecInstanceName;'), + cb.Code( + 'final BinaryMessenger? ${_varNamePrefix}binaryMessenger = ${binaryMessengerParameter.name};', + ), + const cb.Code('() async {'), + cb.Code(messageCallSink.toString()), + const cb.Code('}();'), + ]); + }, + ); + }, + ); + } + } + + /// The detached constructor present for every ProxyApi. + /// + /// This constructor doesn't include a host method call to create a new native + /// class instance. It is mainly used when the native side wants to create a + /// Dart instance or when the `InstanceManager` wants to create a copy for + /// automatic garbage collection. + cb.Constructor _proxyApiDetachedConstructor({ + required String apiName, + required AstProxyApi? superClassApi, + required Iterable unattachedFields, + required Iterable flutterMethodsFromSuperClasses, + required Iterable flutterMethodsFromInterfaces, + required Iterable declaredFlutterMethods, + }) { + final cb.Parameter binaryMessengerParameter = cb.Parameter( + (cb.ParameterBuilder builder) => builder + ..name = '${classMemberNamePrefix}binaryMessenger' + ..named = true + ..toSuper = true, + ); + final cb.Parameter instanceManagerParameter = cb.Parameter( + (cb.ParameterBuilder builder) => builder + ..name = _instanceManagerVarName + ..named = true + ..toSuper = true, + ); + return cb.Constructor( + (cb.ConstructorBuilder builder) => builder + ..name = '${classMemberNamePrefix}detached' + ..docs.addAll([ + '/// Constructs [$apiName] without creating the associated native object.', + '///', + '/// This should only be used by subclasses created by this library or to', + '/// create copies for an [$instanceManagerClassName].', + ]) + ..annotations.add(cb.refer('protected')) + ..optionalParameters.addAll([ + binaryMessengerParameter, + instanceManagerParameter, + for (final ApiField field in unattachedFields) + cb.Parameter( + (cb.ParameterBuilder builder) => builder + ..name = field.name + ..named = true + ..toThis = true + ..required = !field.type.isNullable, + ), + for (final Method method in flutterMethodsFromSuperClasses) + cb.Parameter( + (cb.ParameterBuilder builder) => builder + ..name = method.name + ..named = true + ..toSuper = true + ..required = method.isRequired, + ), + for (final Method method in flutterMethodsFromInterfaces + .followedBy(declaredFlutterMethods)) + cb.Parameter( + (cb.ParameterBuilder builder) => builder + ..name = method.name + ..named = true + ..toThis = true + ..required = method.isRequired, + ), + ]) + ..initializers.addAll([ + if (superClassApi != null) + const cb.Code('super.${classMemberNamePrefix}detached()'), + ]), + ); + } + + /// A private Field of the base codec. + cb.Field _proxyApiCodecInstanceField({ + required String codecInstanceName, + required String codecName, + }) { + return cb.Field( + (cb.FieldBuilder builder) => builder + ..name = codecInstanceName + ..type = cb.refer(codecName) + ..late = true + ..modifier = cb.FieldModifier.final$ + ..assignment = cb.Code('$codecName($_instanceManagerVarName)'), + ); + } + + /// Converts unattached fields from the pigeon AST to `code_builder` + /// Fields. + Iterable _proxyApiUnattachedFields( + Iterable fields, + ) sync* { + for (final ApiField field in fields) { + yield cb.Field( + (cb.FieldBuilder builder) => builder + ..name = field.name + ..type = cb.refer(_addGenericTypesNullable(field.type)) + ..modifier = cb.FieldModifier.final$ + ..docs.addAll(asDocumentationComments( + field.documentationComments, + _docCommentSpec, + )), + ); + } + } + + /// Converts Flutter methods from the pigeon AST to `code_builder` Fields. + /// + /// Flutter methods of a ProxyApi are set as an anonymous function of a class + /// instance, so this converts methods to a `Function` type field instance. + Iterable _proxyApiFlutterMethodFields( + Iterable methods, { + required String apiName, + }) sync* { + for (final Method method in methods) { + yield cb.Field( + (cb.FieldBuilder builder) => builder + ..name = method.name + ..modifier = cb.FieldModifier.final$ + ..docs.addAll(asDocumentationComments( + [ + ...method.documentationComments, + ...[ + if (method.documentationComments.isEmpty) 'Callback method.', + '', + 'For the associated Native object to be automatically garbage collected,', + "it is required that the implementation of this `Function` doesn't have a", + 'strong reference to the encapsulating class instance. When this `Function`', + 'references a non-local variable, it is strongly recommended to access it', + 'with a `WeakReference`:', + '', + '```dart', + 'final WeakReference weakMyVariable = WeakReference(myVariable);', + 'final $apiName instance = $apiName(', + ' ${method.name}: ($apiName ${classMemberNamePrefix}instance, ...) {', + ' print(weakMyVariable?.target);', + ' },', + ');', + '```', + '', + 'Alternatively, [$instanceManagerClassName.removeWeakReference] can be used to', + 'release the associated Native object manually.', + ], + ], + _docCommentSpec, + )) + ..type = cb.FunctionType( + (cb.FunctionTypeBuilder builder) => builder + ..returnType = _refer( + method.returnType, + asFuture: method.isAsynchronous, + ) + ..isNullable = !method.isRequired + ..requiredParameters.addAll([ + cb.refer('$apiName ${classMemberNamePrefix}instance'), + ...indexMap( + method.parameters, + (int index, NamedType parameter) { + return cb.refer( + '${_addGenericTypesNullable(parameter.type)} ${_getParameterName(index, parameter)}', + ); + }, + ), + ]), + ), + ); + } + } + + /// Converts the Flutter methods from the pigeon AST to `code_builder` Fields. + /// + /// Flutter methods of a ProxyApi are set as an anonymous function of a class + /// instance, so this converts methods to a `Function` type field instance. + /// + /// This is similar to [_proxyApiFlutterMethodFields] except all the methods are + /// inherited from apis that are being implemented (following the `implements` + /// keyword). + Iterable _proxyApiInterfaceApiFields( + Iterable apisOfInterfaces, + ) sync* { + for (final AstProxyApi proxyApi in apisOfInterfaces) { + for (final Method method in proxyApi.methods) { + yield cb.Field( + (cb.FieldBuilder builder) => builder + ..name = method.name + ..modifier = cb.FieldModifier.final$ + ..annotations.add(cb.refer('override')) + ..docs.addAll(asDocumentationComments( + method.documentationComments, + _docCommentSpec, + )) + ..type = cb.FunctionType( + (cb.FunctionTypeBuilder builder) => builder + ..returnType = _refer( + method.returnType, + asFuture: method.isAsynchronous, + ) + ..isNullable = !method.isRequired + ..requiredParameters.addAll([ + cb.refer( + '${proxyApi.name} ${classMemberNamePrefix}instance', + ), + ...indexMap( + method.parameters, + (int index, NamedType parameter) { + return cb.refer( + '${_addGenericTypesNullable(parameter.type)} ${_getParameterName(index, parameter)}', + ); + }, + ), + ]), + ), + ); + } + } + } + + /// Converts attached Fields from the pigeon AST to `code_builder` Field. + /// + /// Attached fields are set lazily by calling a private method that returns + /// it. + /// + /// Example Output: + /// + /// ```dart + /// final MyOtherProxyApiClass value = _pigeon_value(); + /// ``` + Iterable _proxyApiAttachedFields(Iterable fields) sync* { + for (final ApiField field in fields) { + yield cb.Field( + (cb.FieldBuilder builder) => builder + ..name = field.name + ..type = cb.refer(_addGenericTypesNullable(field.type)) + ..modifier = cb.FieldModifier.final$ + ..static = field.isStatic + ..late = !field.isStatic + ..docs.addAll(asDocumentationComments( + field.documentationComments, + _docCommentSpec, + )) + ..assignment = cb.Code('$_varNamePrefix${field.name}()'), + ); + } + } + + /// Creates the static `setUpMessageHandlers` method for a ProxyApi. + /// + /// This method handles setting the message handler for every un-inherited + /// Flutter method. + /// + /// This also adds a handler to receive a call from the platform to + /// instantiate a new Dart instance if [hasCallbackConstructor] is set to + /// true. + cb.Method _proxyApiSetUpMessageHandlerMethod({ + required Iterable flutterMethods, + required String apiName, + required String dartPackageName, + required String codecName, + required Iterable unattachedFields, + required bool hasCallbackConstructor, + }) { + final bool hasAnyMessageHandlers = + hasCallbackConstructor || flutterMethods.isNotEmpty; + return cb.Method.returnsVoid( + (cb.MethodBuilder builder) => builder + ..name = '${classMemberNamePrefix}setUpMessageHandlers' + ..returns = cb.refer('void') + ..static = true + ..optionalParameters.addAll([ + cb.Parameter( + (cb.ParameterBuilder builder) => builder + ..name = '${classMemberNamePrefix}clearHandlers' + ..type = cb.refer('bool') + ..named = true + ..defaultTo = const cb.Code('false'), + ), + cb.Parameter( + (cb.ParameterBuilder builder) => builder + ..name = '${classMemberNamePrefix}binaryMessenger' + ..named = true + ..type = cb.refer('BinaryMessenger?'), + ), + cb.Parameter( + (cb.ParameterBuilder builder) => builder + ..name = _instanceManagerVarName + ..named = true + ..type = cb.refer('$instanceManagerClassName?'), + ), + if (hasCallbackConstructor) + cb.Parameter( + (cb.ParameterBuilder builder) => builder + ..name = '${classMemberNamePrefix}newInstance' + ..named = true + ..type = cb.FunctionType( + (cb.FunctionTypeBuilder builder) => builder + ..returnType = cb.refer(apiName) + ..isNullable = true + ..requiredParameters.addAll( + indexMap( + unattachedFields, + (int index, ApiField field) { + return cb.refer( + '${_addGenericTypesNullable(field.type)} ${_getParameterName(index, field)}', + ); + }, + ), + ), + ), + ), + for (final Method method in flutterMethods) + cb.Parameter( + (cb.ParameterBuilder builder) => builder + ..name = method.name + ..type = cb.FunctionType( + (cb.FunctionTypeBuilder builder) => builder + ..returnType = _refer( + method.returnType, + asFuture: method.isAsynchronous, + ) + ..isNullable = true + ..requiredParameters.addAll([ + cb.refer('$apiName ${classMemberNamePrefix}instance'), + ...indexMap( + method.parameters, + (int index, NamedType parameter) { + return cb.refer( + '${_addGenericTypesNullable(parameter.type)} ${_getParameterName(index, parameter)}', + ); + }, + ), + ]), + ), + ), + ]) + ..body = cb.Block.of([ + if (hasAnyMessageHandlers) ...[ + cb.Code( + 'final $codecName $_pigeonChannelCodec = $codecName($_instanceManagerVarName ?? $instanceManagerClassName.instance);', + ), + const cb.Code( + 'final BinaryMessenger? binaryMessenger = ${classMemberNamePrefix}binaryMessenger;', + ) + ], + if (hasCallbackConstructor) + ...cb.Block((cb.BlockBuilder builder) { + final StringBuffer messageHandlerSink = StringBuffer(); + const String methodName = '${classMemberNamePrefix}newInstance'; + _writeFlutterMethodMessageHandler( + Indent(messageHandlerSink), + name: methodName, + parameters: [ + Parameter( + name: '${classMemberNamePrefix}instanceIdentifier', + type: const TypeDeclaration( + baseName: 'int', + isNullable: false, + ), + ), + ...unattachedFields.map( + (ApiField field) { + return Parameter(name: field.name, type: field.type); + }, + ), + ], + returnType: const TypeDeclaration.voidDeclaration(), + channelName: makeChannelNameWithStrings( + apiName: apiName, + methodName: methodName, + dartPackageName: dartPackageName, + ), + isMockHandler: false, + isAsynchronous: false, + nullHandlerExpression: '${classMemberNamePrefix}clearHandlers', + onCreateApiCall: ( + String methodName, + Iterable parameters, + Iterable safeArgumentNames, + ) { + final String argsAsNamedParams = map2( + parameters, + safeArgumentNames, + (Parameter parameter, String safeArgName) { + return '${parameter.name}: $safeArgName,\n'; + }, + ).skip(1).join(); + return '($_instanceManagerVarName ?? $instanceManagerClassName.instance)\n' + ' .addHostCreatedInstance(\n' + ' $methodName?.call(${safeArgumentNames.skip(1).join(',')}) ??\n' + ' $apiName.${classMemberNamePrefix}detached(' + ' ${classMemberNamePrefix}binaryMessenger: ${classMemberNamePrefix}binaryMessenger,\n' + ' $_instanceManagerVarName: $_instanceManagerVarName,\n' + ' $argsAsNamedParams\n' + ' ),\n' + ' ${safeArgumentNames.first},\n' + ')'; + }, + ); + builder.statements.add(cb.Code(messageHandlerSink.toString())); + }).statements, + for (final Method method in flutterMethods) + ...cb.Block((cb.BlockBuilder builder) { + final StringBuffer messageHandlerSink = StringBuffer(); + _writeFlutterMethodMessageHandler( + Indent(messageHandlerSink), + name: method.name, + parameters: [ + Parameter( + name: '${classMemberNamePrefix}instance', + type: TypeDeclaration( + baseName: apiName, + isNullable: false, + ), + ), + ...method.parameters, + ], + returnType: TypeDeclaration( + baseName: method.returnType.baseName, + isNullable: + !method.isRequired || method.returnType.isNullable, + typeArguments: method.returnType.typeArguments, + associatedEnum: method.returnType.associatedEnum, + associatedClass: method.returnType.associatedClass, + associatedProxyApi: method.returnType.associatedProxyApi, + ), + channelName: makeChannelNameWithStrings( + apiName: apiName, + methodName: method.name, + dartPackageName: dartPackageName, + ), + isMockHandler: false, + isAsynchronous: method.isAsynchronous, + nullHandlerExpression: '${classMemberNamePrefix}clearHandlers', + onCreateApiCall: ( + String methodName, + Iterable parameters, + Iterable safeArgumentNames, + ) { + final String nullability = method.isRequired ? '' : '?'; + return '($methodName ?? ${safeArgumentNames.first}.$methodName)$nullability.call(${safeArgumentNames.join(',')})'; + }, + ); + builder.statements.add(cb.Code(messageHandlerSink.toString())); + }).statements, + ]), + ); + } + + /// Converts attached fields from the pigeon AST to `code_builder` Methods. + /// + /// These private methods are used to lazily instantiate attached fields. The + /// instance is created and returned synchronously while the native instance + /// is created asynchronously. This is similar to how constructors work. + Iterable _proxyApiAttachedFieldMethods( + Iterable fields, { + required String apiName, + required String dartPackageName, + required String codecInstanceName, + required String codecName, + }) sync* { + for (final ApiField field in fields) { + yield cb.Method( + (cb.MethodBuilder builder) { + final String type = _addGenericTypesNullable(field.type); + const String instanceName = '${_varNamePrefix}instance'; + const String identifierInstanceName = + '${_varNamePrefix}instanceIdentifier'; + builder + ..name = '$_varNamePrefix${field.name}' + ..static = field.isStatic + ..returns = cb.refer(type) + ..body = cb.Block( + (cb.BlockBuilder builder) { + final StringBuffer messageCallSink = StringBuffer(); + _writeHostMethodMessageCall( + Indent(messageCallSink), + channelName: makeChannelNameWithStrings( + apiName: apiName, + methodName: field.name, + dartPackageName: dartPackageName, + ), + parameters: [ + if (!field.isStatic) + Parameter( + name: 'this', + type: TypeDeclaration( + baseName: apiName, + isNullable: false, + ), + ), + Parameter( + name: identifierInstanceName, + type: const TypeDeclaration( + baseName: 'int', + isNullable: false, + ), + ), + ], + returnType: const TypeDeclaration.voidDeclaration(), + ); + builder.statements.addAll([ + if (!field.isStatic) ...[ + cb.Code( + 'final $type $instanceName = $type.${classMemberNamePrefix}detached(\n' + ' pigeon_binaryMessenger: pigeon_binaryMessenger,\n' + ' pigeon_instanceManager: pigeon_instanceManager,\n' + ');', + ), + cb.Code('final $codecName $_pigeonChannelCodec =\n' + ' $codecInstanceName;'), + const cb.Code( + 'final BinaryMessenger? ${_varNamePrefix}binaryMessenger = ${classMemberNamePrefix}binaryMessenger;', + ), + const cb.Code( + 'final int $identifierInstanceName = $_instanceManagerVarName.addDartCreatedInstance($instanceName);', + ), + ] else ...[ + cb.Code( + 'final $type $instanceName = $type.${classMemberNamePrefix}detached();', + ), + cb.Code( + 'final $codecName $_pigeonChannelCodec = $codecName($instanceManagerClassName.instance);', + ), + const cb.Code( + 'final BinaryMessenger ${_varNamePrefix}binaryMessenger = ServicesBinding.instance.defaultBinaryMessenger;', + ), + const cb.Code( + 'final int $identifierInstanceName = $instanceManagerClassName.instance.addDartCreatedInstance($instanceName);', + ), + ], + const cb.Code('() async {'), + cb.Code(messageCallSink.toString()), + const cb.Code('}();'), + const cb.Code('return $instanceName;'), + ]); + }, + ); + }, + ); + } + } + + /// Converts host methods from pigeon AST to `code_builder` Methods. + /// + /// This creates methods like a HostApi except that it includes the calling + /// instance if the method is not static. + Iterable _proxyApiHostMethods( + Iterable methods, { + required String apiName, + required String dartPackageName, + required String codecInstanceName, + required String codecName, + }) sync* { + for (final Method method in methods) { + assert(method.location == ApiLocation.host); + yield cb.Method( + (cb.MethodBuilder builder) => builder + ..name = method.name + ..static = method.isStatic + ..modifier = cb.MethodModifier.async + ..docs.addAll(asDocumentationComments( + method.documentationComments, + _docCommentSpec, + )) + ..returns = _refer(method.returnType, asFuture: true) + ..requiredParameters.addAll( + indexMap( + method.parameters, + (int index, NamedType parameter) => cb.Parameter( + (cb.ParameterBuilder builder) => builder + ..name = _getParameterName(index, parameter) + ..type = cb.refer( + _addGenericTypesNullable(parameter.type), + ), + ), + ), + ) + ..optionalParameters.addAll([ + if (method.isStatic) ...[ + cb.Parameter( + (cb.ParameterBuilder builder) => builder + ..name = '${classMemberNamePrefix}binaryMessenger' + ..type = cb.refer('BinaryMessenger?') + ..named = true, + ), + cb.Parameter( + (cb.ParameterBuilder builder) => builder + ..name = _instanceManagerVarName + ..type = cb.refer('$instanceManagerClassName?'), + ), + ], + ]) + ..body = cb.Block( + (cb.BlockBuilder builder) { + final StringBuffer messageCallSink = StringBuffer(); + _writeHostMethodMessageCall( + Indent(messageCallSink), + channelName: makeChannelNameWithStrings( + apiName: apiName, + methodName: method.name, + dartPackageName: dartPackageName, + ), + parameters: [ + if (!method.isStatic) + Parameter( + name: 'this', + type: TypeDeclaration( + baseName: apiName, + isNullable: false, + ), + ), + ...method.parameters, + ], + returnType: method.returnType, + ); + builder.statements.addAll([ + if (!method.isStatic) + cb.Code('final $codecName $_pigeonChannelCodec =\n' + ' $codecInstanceName;') + else + cb.Code( + 'final $codecName $_pigeonChannelCodec = $codecName($_instanceManagerVarName ?? $instanceManagerClassName.instance);', + ), + const cb.Code( + 'final BinaryMessenger? ${_varNamePrefix}binaryMessenger = ${classMemberNamePrefix}binaryMessenger;', + ), + cb.Code(messageCallSink.toString()), + ]); + }, + ), + ); + } + } + + /// Creates the copy method for a ProxyApi. + /// + /// This method returns a copy of the instance with all the Flutter methods + /// and unattached fields passed to the new instance. This method is inherited + /// from the base ProxyApi class. + cb.Method _proxyApiCopyMethod({ + required String apiName, + required Iterable unattachedFields, + required Iterable declaredAndInheritedFlutterMethods, + }) { + return cb.Method( + (cb.MethodBuilder builder) => builder + ..name = '${classMemberNamePrefix}copy' + ..returns = cb.refer(apiName) + ..annotations.add(cb.refer('override')) + ..body = cb.Block.of([ + cb + .refer('$apiName.${classMemberNamePrefix}detached') + .call( + [], + { + '${classMemberNamePrefix}binaryMessenger': + cb.refer('${classMemberNamePrefix}binaryMessenger'), + _instanceManagerVarName: cb.refer(_instanceManagerVarName), + for (final ApiField field in unattachedFields) + field.name: cb.refer(field.name), + for (final Method method + in declaredAndInheritedFlutterMethods) + method.name: cb.refer(method.name), + }, + ) + .returned + .statement, + ]), + ); + } +} + +cb.Reference _refer(TypeDeclaration type, {bool asFuture = false}) { + final String symbol = _addGenericTypesNullable(type); + return cb.refer(asFuture ? 'Future<$symbol>' : symbol); } String _escapeForDartSingleQuotedString(String raw) { diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart index 47a9b6c76169..66dd99750f34 100644 --- a/packages/pigeon/lib/generator_tools.dart +++ b/packages/pigeon/lib/generator_tools.dart @@ -13,7 +13,7 @@ import 'ast.dart'; /// The current version of pigeon. /// /// This must match the version in pubspec.yaml. -const String pigeonVersion = '17.1.3'; +const String pigeonVersion = '17.2.0'; /// Read all the content from [stdin] to a String. String readStdin() { @@ -164,9 +164,22 @@ class Indent { } } -/// Create the generated channel name for a [func] on a [api]. -String makeChannelName(Api api, Method func, String dartPackageName) { - return 'dev.flutter.pigeon.$dartPackageName.${api.name}.${func.name}'; +/// Create the generated channel name for a [method] on an [api]. +String makeChannelName(Api api, Method method, String dartPackageName) { + return makeChannelNameWithStrings( + apiName: api.name, + methodName: method.name, + dartPackageName: dartPackageName, + ); +} + +/// Create the generated channel name for a method on an api. +String makeChannelNameWithStrings({ + required String apiName, + required String methodName, + required String dartPackageName, +}) { + return 'dev.flutter.pigeon.$dartPackageName.$apiName.$methodName'; } // TODO(tarrinneal): Determine whether HostDataType is needed. @@ -286,7 +299,7 @@ const String seeAlsoWarning = 'See also: https://pub.dev/packages/pigeon'; /// /// This lowers the chances of variable name collisions with user defined /// parameters. -const String classNamePrefix = 'Pigeon_'; +const String classNamePrefix = 'Pigeon'; /// Name for the generated InstanceManager for ProxyApis. /// @@ -541,6 +554,23 @@ void addDocumentationComments( DocumentCommentSpecification commentSpec, { List generatorComments = const [], }) { + asDocumentationComments( + comments, + commentSpec, + generatorComments: generatorComments, + ).forEach(indent.writeln); +} + +/// Formats documentation comments and adds them to current Indent. +/// +/// The [comments] list is meant for comments written in the input dart file. +/// The [generatorComments] list is meant for comments added by the generators. +/// Include white space for all tokens when called, no assumptions are made. +Iterable asDocumentationComments( + Iterable comments, + DocumentCommentSpecification commentSpec, { + List generatorComments = const [], +}) sync* { final List allComments = [ ...comments, if (comments.isNotEmpty && generatorComments.isNotEmpty) '', @@ -549,24 +579,20 @@ void addDocumentationComments( String currentLineOpenToken = commentSpec.openCommentToken; if (allComments.length > 1) { if (commentSpec.closeCommentToken != '') { - indent.writeln(commentSpec.openCommentToken); + yield commentSpec.openCommentToken; currentLineOpenToken = commentSpec.blockContinuationToken; } for (String line in allComments) { if (line.isNotEmpty && line[0] != ' ') { line = ' $line'; } - indent.writeln( - '$currentLineOpenToken$line', - ); + yield '$currentLineOpenToken$line'; } if (commentSpec.closeCommentToken != '') { - indent.writeln(commentSpec.closeCommentToken); + yield commentSpec.closeCommentToken; } } else if (allComments.length == 1) { - indent.writeln( - '$currentLineOpenToken${allComments.first}${commentSpec.closeCommentToken}', - ); + yield '$currentLineOpenToken${allComments.first}${commentSpec.closeCommentToken}'; } } @@ -619,92 +645,6 @@ String? deducePackageName(String mainDartFile) { } } -/// Recursively search for all the interfaces apis from a list of names of -/// interfaces. -/// -/// This method assumes that all interfaces are ProxyApis and an api doesn't -/// contains itself as an interface. Otherwise, throws an [ArgumentError]. -Set recursiveFindAllInterfaceApis( - AstProxyApi api, { - Set seenApis = const {}, -}) { - final Set allInterfaces = {}; - - allInterfaces.addAll( - api.interfaces.map( - (TypeDeclaration type) { - if (!type.isProxyApi) { - throw ArgumentError( - 'Could not find a valid ProxyApi for an interface: $type', - ); - } else if (seenApis.contains(type.associatedProxyApi)) { - throw ArgumentError( - 'A ProxyApi cannot be a super class of itself: ${type.baseName}', - ); - } - return type.associatedProxyApi!; - }, - ), - ); - - // Adds the current api since it would be invalid for it to be an interface - // of itself. - final Set newSeenApis = {...seenApis, api}; - - for (final AstProxyApi interfaceApi in {...allInterfaces}) { - allInterfaces.addAll(recursiveFindAllInterfaceApis( - interfaceApi, - seenApis: newSeenApis, - )); - } - - return allInterfaces; -} - -/// Creates a list of ProxyApis where each `extends` the ProxyApi that follows -/// it. -/// -/// Returns an empty list if [proxyApi] does not extend a ProxyApi. -/// -/// This method assumes the super classes of each ProxyApi doesn't create a -/// loop. Throws a [ArgumentError] if a loop is found. -/// -/// This method also assumes that all super classes are ProxyApis. Otherwise, -/// throws an [ArgumentError]. -List recursiveGetSuperClassApisChain(AstProxyApi api) { - final List superClassChain = []; - - if (api.superClass != null && !api.superClass!.isProxyApi) { - throw ArgumentError( - 'Could not find a ProxyApi for super class: ${api.superClass!.baseName}', - ); - } - - AstProxyApi? currentProxyApi = api.superClass?.associatedProxyApi; - while (currentProxyApi != null) { - if (superClassChain.contains(currentProxyApi)) { - throw ArgumentError( - 'Loop found when processing super classes for a ProxyApi: ' - '${api.name}, ${superClassChain.map((AstProxyApi api) => api.name)}', - ); - } - - superClassChain.add(currentProxyApi); - - if (currentProxyApi.superClass != null && - !currentProxyApi.superClass!.isProxyApi) { - throw ArgumentError( - 'Could not find a ProxyApi for super class: ' - '${currentProxyApi.superClass!.baseName}', - ); - } - - currentProxyApi = currentProxyApi.superClass?.associatedProxyApi; - } - - return superClassChain; -} - /// Enum to specify api type when generating code. enum ApiType { /// Flutter api. diff --git a/packages/pigeon/lib/objc_generator.dart b/packages/pigeon/lib/objc_generator.dart index d30f02d14719..67c93a74f3a9 100644 --- a/packages/pigeon/lib/objc_generator.dart +++ b/packages/pigeon/lib/objc_generator.dart @@ -715,7 +715,10 @@ class ObjcSourceGenerator extends StructuredGenerator { _writeCreateConnectionError(indent); indent.newln(); } - _writeGetNullableObjectAtIndex(indent); + + if (hasHostApi || hasFlutterApi) { + _writeGetNullableObjectAtIndex(indent); + } } void _writeWrapError(Indent indent) { diff --git a/packages/pigeon/lib/pigeon.dart b/packages/pigeon/lib/pigeon.dart index a2c6026e3117..7f2fb7505fa8 100644 --- a/packages/pigeon/lib/pigeon.dart +++ b/packages/pigeon/lib/pigeon.dart @@ -9,7 +9,5 @@ export 'dart_generator.dart' show DartOptions; export 'java_generator.dart' show JavaOptions; export 'kotlin_generator.dart' show KotlinOptions; export 'objc_generator.dart' show ObjcOptions; -// TODO(bparrishMines): Remove hide once implementation of the api is finished -// for Dart and one host language. -export 'pigeon_lib.dart' hide ProxyApi; +export 'pigeon_lib.dart'; export 'swift_generator.dart' show SwiftOptions; diff --git a/packages/pigeon/lib/pigeon_lib.dart b/packages/pigeon/lib/pigeon_lib.dart index 8d6fc961c2ec..8f418d042598 100644 --- a/packages/pigeon/lib/pigeon_lib.dart +++ b/packages/pigeon/lib/pigeon_lib.dart @@ -1030,10 +1030,10 @@ List _validateProxyApi( } // Validate this api isn't used as an interface and contains anything except - // Flutter methods. - final bool isValidInterfaceProxyApi = api.hostMethods.isEmpty && - api.constructors.isEmpty && - api.fields.isEmpty; + // Flutter methods, a static host method, attached methods. + final bool isValidInterfaceProxyApi = api.constructors.isEmpty && + api.fields.where((ApiField field) => !field.isStatic).isEmpty && + api.hostMethods.where((Method method) => !method.isStatic).isEmpty; if (!isValidInterfaceProxyApi) { final Iterable interfaceNames = proxyApi.interfaces.map( (TypeDeclaration type) => type.baseName, diff --git a/packages/pigeon/pigeons/proxy_api_tests.dart b/packages/pigeon/pigeons/proxy_api_tests.dart new file mode 100644 index 000000000000..699e5dfaed86 --- /dev/null +++ b/packages/pigeon/pigeons/proxy_api_tests.dart @@ -0,0 +1,469 @@ +// 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:pigeon/pigeon.dart'; + +enum ProxyApiTestEnum { + one, + two, + three, +} + +/// The core ProxyApi test class that each supported host language must +/// implement in platform_tests integration tests. +@ProxyApi() +abstract class ProxyApiTestClass extends ProxyApiSuperClass + implements ProxyApiInterface { + ProxyApiTestClass( + // ignore: avoid_unused_constructor_parameters + bool boolParam, + // ignore: avoid_unused_constructor_parameters + int intParam, + // ignore: avoid_unused_constructor_parameters + double doubleParam, + // ignore: avoid_unused_constructor_parameters + String stringParam, + // ignore: avoid_unused_constructor_parameters + Uint8List aUint8ListParam, + // ignore: avoid_unused_constructor_parameters + List listParam, + // ignore: avoid_unused_constructor_parameters + Map mapParam, + // ignore: avoid_unused_constructor_parameters + ProxyApiTestEnum enumParam, + // ignore: avoid_unused_constructor_parameters + ProxyApiSuperClass proxyApiParam, + // ignore: avoid_unused_constructor_parameters + bool? nullableBoolParam, + // ignore: avoid_unused_constructor_parameters + int? nullableIntParam, + // ignore: avoid_unused_constructor_parameters + double? nullableDoubleParam, + // ignore: avoid_unused_constructor_parameters + String? nullableStringParam, + // ignore: avoid_unused_constructor_parameters + Uint8List? nullableUint8ListParam, + // ignore: avoid_unused_constructor_parameters + List? nullableListParam, + // ignore: avoid_unused_constructor_parameters + Map? nullableMapParam, + // ignore: avoid_unused_constructor_parameters + ProxyApiTestEnum? nullableEnumParam, + // ignore: avoid_unused_constructor_parameters + ProxyApiSuperClass? nullableProxyApiParam, + ); + + late bool aBool; + late int anInt; + late double aDouble; + late String aString; + late Uint8List aUint8List; + late List aList; + late Map aMap; + late ProxyApiTestEnum anEnum; + late ProxyApiSuperClass aProxyApi; + + late bool? aNullableBool; + late int? aNullableInt; + late double? aNullableDouble; + late String? aNullableString; + late Uint8List? aNullableUint8List; + late List? aNullableList; + late Map? aNullableMap; + late ProxyApiTestEnum? aNullableEnum; + late ProxyApiSuperClass? aNullableProxyApi; + + @attached + late ProxyApiSuperClass attachedField; + + @static + late ProxyApiSuperClass staticAttachedField; + + /// A no-op function taking no arguments and returning no value, to sanity + /// test basic calling. + late void Function()? flutterNoop; + + /// Responds with an error from an async function returning a value. + late Object? Function()? flutterThrowError; + + /// Responds with an error from an async void function. + late void Function()? flutterThrowErrorFromVoid; + + // ========== Non-nullable argument/return type tests ========== + + /// Returns the passed boolean, to test serialization and deserialization. + late bool Function(bool aBool)? flutterEchoBool; + + /// Returns the passed int, to test serialization and deserialization. + late int Function(int anInt)? flutterEchoInt; + + /// Returns the passed double, to test serialization and deserialization. + late double Function(double aDouble)? flutterEchoDouble; + + /// Returns the passed string, to test serialization and deserialization. + late String Function(String aString)? flutterEchoString; + + /// Returns the passed byte list, to test serialization and deserialization. + late Uint8List Function(Uint8List aList)? flutterEchoUint8List; + + /// Returns the passed list, to test serialization and deserialization. + late List Function(List aList)? flutterEchoList; + + /// Returns the passed list with ProxyApis, to test serialization and + /// deserialization. + late List Function(List aList)? + flutterEchoProxyApiList; + + /// Returns the passed map, to test serialization and deserialization. + late Map Function(Map aMap)? + flutterEchoMap; + + /// Returns the passed map with ProxyApis, to test serialization and + /// deserialization. + late Map Function( + Map aMap)? flutterEchoProxyApiMap; + + /// Returns the passed enum to test serialization and deserialization. + late ProxyApiTestEnum Function(ProxyApiTestEnum anEnum)? flutterEchoEnum; + + /// Returns the passed ProxyApi to test serialization and deserialization. + late ProxyApiSuperClass Function(ProxyApiSuperClass aProxyApi)? + flutterEchoProxyApi; + + // ========== Nullable argument/return type tests ========== + + /// Returns the passed boolean, to test serialization and deserialization. + late bool? Function(bool? aBool)? flutterEchoNullableBool; + + /// Returns the passed int, to test serialization and deserialization. + late int? Function(int? anInt)? flutterEchoNullableInt; + + /// Returns the passed double, to test serialization and deserialization. + late double? Function(double? aDouble)? flutterEchoNullableDouble; + + /// Returns the passed string, to test serialization and deserialization. + late String? Function(String? aString)? flutterEchoNullableString; + + /// Returns the passed byte list, to test serialization and deserialization. + late Uint8List? Function(Uint8List? aList)? flutterEchoNullableUint8List; + + /// Returns the passed list, to test serialization and deserialization. + late List? Function(List? aList)? flutterEchoNullableList; + + /// Returns the passed map, to test serialization and deserialization. + late Map? Function(Map? aMap)? + flutterEchoNullableMap; + + /// Returns the passed enum to test serialization and deserialization. + late ProxyApiTestEnum? Function(ProxyApiTestEnum? anEnum)? + flutterEchoNullableEnum; + + /// Returns the passed ProxyApi to test serialization and deserialization. + late ProxyApiSuperClass? Function(ProxyApiSuperClass? aProxyApi)? + flutterEchoNullableProxyApi; + + // ========== Async tests ========== + // These are minimal since async FlutterApi only changes Dart generation. + // Currently they aren't integration tested, but having them here ensures + // analysis coverage. + + /// A no-op function taking no arguments and returning no value, to sanity + /// test basic asynchronous calling. + @async + late void Function()? flutterNoopAsync; + + /// Returns the passed in generic Object asynchronously. + @async + late String Function(String aString)? flutterEchoAsyncString; + + // ========== Synchronous host method tests ========== + + /// A no-op function taking no arguments and returning no value, to sanity + /// test basic calling. + void noop(); + + /// Returns an error, to test error handling. + Object? throwError(); + + /// Returns an error from a void function, to test error handling. + void throwErrorFromVoid(); + + /// Returns a Flutter error, to test error handling. + Object? throwFlutterError(); + + /// Returns passed in int. + int echoInt(int anInt); + + /// Returns passed in double. + double echoDouble(double aDouble); + + /// Returns the passed in boolean. + bool echoBool(bool aBool); + + /// Returns the passed in string. + String echoString(String aString); + + /// Returns the passed in Uint8List. + Uint8List echoUint8List(Uint8List aUint8List); + + /// Returns the passed in generic Object. + Object echoObject(Object anObject); + + /// Returns the passed list, to test serialization and deserialization. + List echoList(List aList); + + /// Returns the passed list with ProxyApis, to test serialization and + /// deserialization. + List echoProxyApiList( + List aList, + ); + + /// Returns the passed map, to test serialization and deserialization. + Map echoMap(Map aMap); + + /// Returns the passed map with ProxyApis, to test serialization and + /// deserialization. + Map echoProxyApiMap( + Map aMap, + ); + + /// Returns the passed enum to test serialization and deserialization. + ProxyApiTestEnum echoEnum(ProxyApiTestEnum anEnum); + + /// Returns the passed ProxyApi to test serialization and deserialization. + ProxyApiSuperClass echoProxyApi(ProxyApiSuperClass aProxyApi); + + // ========== Synchronous host nullable method tests ========== + + /// Returns passed in int. + int? echoNullableInt(int? aNullableInt); + + /// Returns passed in double. + double? echoNullableDouble(double? aNullableDouble); + + /// Returns the passed in boolean. + bool? echoNullableBool(bool? aNullableBool); + + /// Returns the passed in string. + String? echoNullableString(String? aNullableString); + + /// Returns the passed in Uint8List. + Uint8List? echoNullableUint8List(Uint8List? aNullableUint8List); + + /// Returns the passed in generic Object. + Object? echoNullableObject(Object? aNullableObject); + + /// Returns the passed list, to test serialization and deserialization. + List? echoNullableList(List? aNullableList); + + /// Returns the passed map, to test serialization and deserialization. + Map? echoNullableMap(Map? aNullableMap); + + ProxyApiTestEnum? echoNullableEnum(ProxyApiTestEnum? aNullableEnum); + + /// Returns the passed ProxyApi to test serialization and deserialization. + ProxyApiSuperClass? echoNullableProxyApi( + ProxyApiSuperClass? aNullableProxyApi, + ); + + // ========== Asynchronous method tests ========== + + /// A no-op function taking no arguments and returning no value, to sanity + /// test basic asynchronous calling. + @async + void noopAsync(); + + /// Returns passed in int asynchronously. + @async + int echoAsyncInt(int anInt); + + /// Returns passed in double asynchronously. + @async + double echoAsyncDouble(double aDouble); + + /// Returns the passed in boolean asynchronously. + @async + bool echoAsyncBool(bool aBool); + + /// Returns the passed string asynchronously. + @async + String echoAsyncString(String aString); + + /// Returns the passed in Uint8List asynchronously. + @async + Uint8List echoAsyncUint8List(Uint8List aUint8List); + + /// Returns the passed in generic Object asynchronously. + @async + Object echoAsyncObject(Object anObject); + + /// Returns the passed list, to test asynchronous serialization and deserialization. + @async + List echoAsyncList(List aList); + + /// Returns the passed map, to test asynchronous serialization and deserialization. + @async + Map echoAsyncMap(Map aMap); + + /// Returns the passed enum, to test asynchronous serialization and deserialization. + @async + ProxyApiTestEnum echoAsyncEnum(ProxyApiTestEnum anEnum); + + /// Responds with an error from an async function returning a value. + @async + Object? throwAsyncError(); + + /// Responds with an error from an async void function. + @async + void throwAsyncErrorFromVoid(); + + /// Responds with a Flutter error from an async function returning a value. + @async + Object? throwAsyncFlutterError(); + + /// Returns passed in int asynchronously. + @async + int? echoAsyncNullableInt(int? anInt); + + /// Returns passed in double asynchronously. + @async + double? echoAsyncNullableDouble(double? aDouble); + + /// Returns the passed in boolean asynchronously. + @async + bool? echoAsyncNullableBool(bool? aBool); + + /// Returns the passed string asynchronously. + @async + String? echoAsyncNullableString(String? aString); + + /// Returns the passed in Uint8List asynchronously. + @async + Uint8List? echoAsyncNullableUint8List(Uint8List? aUint8List); + + /// Returns the passed in generic Object asynchronously. + @async + Object? echoAsyncNullableObject(Object? anObject); + + /// Returns the passed list, to test asynchronous serialization and deserialization. + @async + List? echoAsyncNullableList(List? aList); + + /// Returns the passed map, to test asynchronous serialization and deserialization. + @async + Map? echoAsyncNullableMap(Map? aMap); + + /// Returns the passed enum, to test asynchronous serialization and deserialization. + @async + ProxyApiTestEnum? echoAsyncNullableEnum(ProxyApiTestEnum? anEnum); + + // ========== Static method test ========== + + @static + void staticNoop(); + + @static + String echoStaticString(String aString); + + @static + @async + void staticAsyncNoop(); + + // ========== Flutter methods test wrappers ========== + + @async + void callFlutterNoop(); + + @async + Object? callFlutterThrowError(); + + @async + void callFlutterThrowErrorFromVoid(); + + @async + bool callFlutterEchoBool(bool aBool); + + @async + int callFlutterEchoInt(int anInt); + + @async + double callFlutterEchoDouble(double aDouble); + + @async + String callFlutterEchoString(String aString); + + @async + Uint8List callFlutterEchoUint8List(Uint8List aUint8List); + + @async + List callFlutterEchoList(List aList); + + @async + List callFlutterEchoProxyApiList( + List aList); + + @async + Map callFlutterEchoMap(Map aMap); + + @async + Map callFlutterEchoProxyApiMap( + Map aMap); + + @async + ProxyApiTestEnum callFlutterEchoEnum(ProxyApiTestEnum anEnum); + + @async + ProxyApiSuperClass callFlutterEchoProxyApi(ProxyApiSuperClass aProxyApi); + + @async + bool? callFlutterEchoNullableBool(bool? aBool); + + @async + int? callFlutterEchoNullableInt(int? anInt); + + @async + double? callFlutterEchoNullableDouble(double? aDouble); + + @async + String? callFlutterEchoNullableString(String? aString); + + @async + Uint8List? callFlutterEchoNullableUint8List(Uint8List? aUint8List); + + @async + List? callFlutterEchoNullableList(List? aList); + + @async + Map? callFlutterEchoNullableMap( + Map? aMap, + ); + + @async + ProxyApiTestEnum? callFlutterEchoNullableEnum(ProxyApiTestEnum? anEnum); + + @async + ProxyApiSuperClass? callFlutterEchoNullableProxyApi( + ProxyApiSuperClass? aProxyApi, + ); + + @async + void callFlutterNoopAsync(); + + @async + String callFlutterEchoAsyncString(String aString); +} + +/// ProxyApi to serve as a super class to the core ProxyApi class. +@ProxyApi() +abstract class ProxyApiSuperClass { + ProxyApiSuperClass(); + + void aSuperMethod(); +} + +/// ProxyApi to serve as an interface to the core ProxyApi class. +@ProxyApi() +abstract class ProxyApiInterface { + late void Function()? anInterfaceMethod; +} diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/proxy_api_tests.gen.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/proxy_api_tests.gen.dart new file mode 100644 index 000000000000..762f65d5aad0 --- /dev/null +++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/proxy_api_tests.gen.dart @@ -0,0 +1,5023 @@ +// 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, 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, no_leading_underscores_for_local_identifiers + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' + show ReadBuffer, WriteBuffer, immutable, protected; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart' show WidgetsFlutterBinding; + +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} + +List wrapResponse( + {Object? result, PlatformException? error, bool empty = false}) { + if (empty) { + return []; + } + if (error == null) { + return [result]; + } + return [error.code, error.message, error.details]; +} + +/// An immutable object that serves as the base class for all ProxyApis and +/// can provide functional copies of itself. +/// +/// All implementers are expected to be [immutable] as defined by the annotation +/// and override [pigeon_copy] returning an instance of itself. +@immutable +abstract class PigeonProxyApiBaseClass { + /// Construct a [PigeonProxyApiBaseClass]. + PigeonProxyApiBaseClass({ + this.pigeon_binaryMessenger, + PigeonInstanceManager? pigeon_instanceManager, + }) : pigeon_instanceManager = + pigeon_instanceManager ?? PigeonInstanceManager.instance; + + /// Sends and receives binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used, which routes to + /// the host platform. + @protected + final BinaryMessenger? pigeon_binaryMessenger; + + /// Maintains instances stored to communicate with native language objects. + @protected + final PigeonInstanceManager pigeon_instanceManager; + + /// Instantiates and returns a functionally identical object to oneself. + /// + /// Outside of tests, this method should only ever be called by + /// [PigeonInstanceManager]. + /// + /// Subclasses should always override their parent's implementation of this + /// method. + @protected + PigeonProxyApiBaseClass pigeon_copy(); +} + +/// Maintains instances used to communicate with the native objects they +/// represent. +/// +/// Added instances are stored as weak references and their copies are stored +/// as strong references to maintain access to their variables and callback +/// methods. Both are stored with the same identifier. +/// +/// When a weak referenced instance becomes inaccessible, +/// [onWeakReferenceRemoved] is called with its associated identifier. +/// +/// If an instance is retrieved and has the possibility to be used, +/// (e.g. calling [getInstanceWithWeakReference]) a copy of the strong reference +/// is added as a weak reference with the same identifier. This prevents a +/// scenario where the weak referenced instance was released and then later +/// returned by the host platform. +class PigeonInstanceManager { + /// Constructs a [PigeonInstanceManager]. + PigeonInstanceManager({required void Function(int) onWeakReferenceRemoved}) { + this.onWeakReferenceRemoved = (int identifier) { + _weakInstances.remove(identifier); + onWeakReferenceRemoved(identifier); + }; + _finalizer = Finalizer(this.onWeakReferenceRemoved); + } + + // Identifiers are locked to a specific range to avoid collisions with objects + // created simultaneously by the host platform. + // Host uses identifiers >= 2^16 and Dart is expected to use values n where, + // 0 <= n < 2^16. + static const int _maxDartCreatedIdentifier = 65536; + + /// The default [PigeonInstanceManager] used by ProxyApis. + /// + /// On creation, this manager makes a call to clear the native + /// InstanceManager. This is to prevent identifier conflicts after a host + /// restart. + static final PigeonInstanceManager instance = _initInstance(); + + // Expando is used because it doesn't prevent its keys from becoming + // inaccessible. This allows the manager to efficiently retrieve an identifier + // of an instance without holding a strong reference to that instance. + // + // It also doesn't use `==` to search for identifiers, which would lead to an + // infinite loop when comparing an object to its copy. (i.e. which was caused + // by calling instanceManager.getIdentifier() inside of `==` while this was a + // HashMap). + final Expando _identifiers = Expando(); + final Map> _weakInstances = + >{}; + final Map _strongInstances = + {}; + late final Finalizer _finalizer; + int _nextIdentifier = 0; + + /// Called when a weak referenced instance is removed by [removeWeakReference] + /// or becomes inaccessible. + late final void Function(int) onWeakReferenceRemoved; + + static PigeonInstanceManager _initInstance() { + WidgetsFlutterBinding.ensureInitialized(); + final _PigeonInstanceManagerApi api = _PigeonInstanceManagerApi(); + // Clears the native `PigeonInstanceManager` on the initial use of the Dart one. + api.clear(); + final PigeonInstanceManager instanceManager = PigeonInstanceManager( + onWeakReferenceRemoved: (int identifier) { + api.removeStrongReference(identifier); + }, + ); + _PigeonInstanceManagerApi.setUpMessageHandlers( + instanceManager: instanceManager); + ProxyApiTestClass.pigeon_setUpMessageHandlers( + pigeon_instanceManager: instanceManager); + ProxyApiSuperClass.pigeon_setUpMessageHandlers( + pigeon_instanceManager: instanceManager); + ProxyApiInterface.pigeon_setUpMessageHandlers( + pigeon_instanceManager: instanceManager); + return instanceManager; + } + + /// Adds a new instance that was instantiated by Dart. + /// + /// In other words, Dart wants to add a new instance that will represent + /// an object that will be instantiated on the host platform. + /// + /// Throws assertion error if the instance has already been added. + /// + /// Returns the randomly generated id of the [instance] added. + int addDartCreatedInstance(PigeonProxyApiBaseClass instance) { + final int identifier = _nextUniqueIdentifier(); + _addInstanceWithIdentifier(instance, identifier); + return identifier; + } + + /// Removes the instance, if present, and call [onWeakReferenceRemoved] with + /// its identifier. + /// + /// Returns the identifier associated with the removed instance. Otherwise, + /// `null` if the instance was not found in this manager. + /// + /// This does not remove the strong referenced instance associated with + /// [instance]. This can be done with [remove]. + int? removeWeakReference(PigeonProxyApiBaseClass instance) { + final int? identifier = getIdentifier(instance); + if (identifier == null) { + return null; + } + + _identifiers[instance] = null; + _finalizer.detach(instance); + onWeakReferenceRemoved(identifier); + + return identifier; + } + + /// Removes [identifier] and its associated strongly referenced instance, if + /// present, from the manager. + /// + /// Returns the strong referenced instance associated with [identifier] before + /// it was removed. Returns `null` if [identifier] was not associated with + /// any strong reference. + /// + /// This does not remove the weak referenced instance associated with + /// [identifier]. This can be done with [removeWeakReference]. + T? remove(int identifier) { + return _strongInstances.remove(identifier) as T?; + } + + /// Retrieves the instance associated with identifier. + /// + /// The value returned is chosen from the following order: + /// + /// 1. A weakly referenced instance associated with identifier. + /// 2. If the only instance associated with identifier is a strongly + /// referenced instance, a copy of the instance is added as a weak reference + /// with the same identifier. Returning the newly created copy. + /// 3. If no instance is associated with identifier, returns null. + /// + /// This method also expects the host `InstanceManager` to have a strong + /// reference to the instance the identifier is associated with. + T? getInstanceWithWeakReference( + int identifier) { + final PigeonProxyApiBaseClass? weakInstance = + _weakInstances[identifier]?.target; + + if (weakInstance == null) { + final PigeonProxyApiBaseClass? strongInstance = + _strongInstances[identifier]; + if (strongInstance != null) { + final PigeonProxyApiBaseClass copy = strongInstance.pigeon_copy(); + _identifiers[copy] = identifier; + _weakInstances[identifier] = + WeakReference(copy); + _finalizer.attach(copy, identifier, detach: copy); + return copy as T; + } + return strongInstance as T?; + } + + return weakInstance as T; + } + + /// Retrieves the identifier associated with instance. + int? getIdentifier(PigeonProxyApiBaseClass instance) { + return _identifiers[instance]; + } + + /// Adds a new instance that was instantiated by the host platform. + /// + /// In other words, the host platform wants to add a new instance that + /// represents an object on the host platform. Stored with [identifier]. + /// + /// Throws assertion error if the instance or its identifier has already been + /// added. + /// + /// Returns unique identifier of the [instance] added. + void addHostCreatedInstance( + PigeonProxyApiBaseClass instance, int identifier) { + _addInstanceWithIdentifier(instance, identifier); + } + + void _addInstanceWithIdentifier( + PigeonProxyApiBaseClass instance, int identifier) { + assert(!containsIdentifier(identifier)); + assert(getIdentifier(instance) == null); + assert(identifier >= 0); + + _identifiers[instance] = identifier; + _weakInstances[identifier] = + WeakReference(instance); + _finalizer.attach(instance, identifier, detach: instance); + + final PigeonProxyApiBaseClass copy = instance.pigeon_copy(); + _identifiers[copy] = identifier; + _strongInstances[identifier] = copy; + } + + /// Whether this manager contains the given [identifier]. + bool containsIdentifier(int identifier) { + return _weakInstances.containsKey(identifier) || + _strongInstances.containsKey(identifier); + } + + int _nextUniqueIdentifier() { + late int identifier; + do { + identifier = _nextIdentifier; + _nextIdentifier = (_nextIdentifier + 1) % _maxDartCreatedIdentifier; + } while (containsIdentifier(identifier)); + return identifier; + } +} + +/// Generated API for managing the Dart and native `PigeonInstanceManager`s. +class _PigeonInstanceManagerApi { + /// Constructor for [_PigeonInstanceManagerApi]. + _PigeonInstanceManagerApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec pigeonChannelCodec = + StandardMessageCodec(); + + static void setUpMessageHandlers({ + BinaryMessenger? binaryMessenger, + PigeonInstanceManager? instanceManager, + }) { + const String channelName = + r'dev.flutter.pigeon.pigeon_integration_tests.PigeonInstanceManagerApi.removeStrongReference'; + final BasicMessageChannel channel = BasicMessageChannel( + channelName, + pigeonChannelCodec, + binaryMessenger: binaryMessenger, + ); + channel.setMessageHandler((Object? message) async { + assert( + message != null, + 'Argument for $channelName was null.', + ); + final int? identifier = message as int?; + assert( + identifier != null, + r'Argument for $channelName, expected non-null int.', + ); + (instanceManager ?? PigeonInstanceManager.instance).remove(identifier!); + return; + }); + } + + Future removeStrongReference(int identifier) async { + const String channelName = + r'dev.flutter.pigeon.pigeon_integration_tests.PigeonInstanceManagerApi.removeStrongReference'; + final BasicMessageChannel channel = BasicMessageChannel( + channelName, + pigeonChannelCodec, + binaryMessenger: _binaryMessenger, + ); + final List? replyList = + await channel.send(identifier) as List?; + if (replyList == null) { + throw _createConnectionError(channelName); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return; + } + } + + /// Clear the native `PigeonInstanceManager`. + /// + /// This is typically called after a hot restart. + Future clear() async { + const String channelName = + r'dev.flutter.pigeon.pigeon_integration_tests.PigeonInstanceManagerApi.clear'; + final BasicMessageChannel channel = BasicMessageChannel( + channelName, + pigeonChannelCodec, + binaryMessenger: _binaryMessenger, + ); + final List? replyList = await channel.send(null) as List?; + if (replyList == null) { + throw _createConnectionError(channelName); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return; + } + } +} + +class _PigeonProxyApiBaseCodec extends StandardMessageCodec { + const _PigeonProxyApiBaseCodec(this.instanceManager); + final PigeonInstanceManager instanceManager; + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is PigeonProxyApiBaseClass) { + buffer.putUint8(128); + writeValue(buffer, instanceManager.getIdentifier(value)); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return instanceManager + .getInstanceWithWeakReference(readValue(buffer)! as int); + default: + return super.readValueOfType(type, buffer); + } + } +} + +enum ProxyApiTestEnum { + one, + two, + three, +} + +/// The core ProxyApi test class that each supported host language must +/// implement in platform_tests integration tests. +class ProxyApiTestClass extends ProxyApiSuperClass + implements ProxyApiInterface { + ProxyApiTestClass({ + super.pigeon_binaryMessenger, + super.pigeon_instanceManager, + required this.aBool, + required this.anInt, + required this.aDouble, + required this.aString, + required this.aUint8List, + required this.aList, + required this.aMap, + required this.anEnum, + required this.aProxyApi, + this.aNullableBool, + this.aNullableInt, + this.aNullableDouble, + this.aNullableString, + this.aNullableUint8List, + this.aNullableList, + this.aNullableMap, + this.aNullableEnum, + this.aNullableProxyApi, + this.anInterfaceMethod, + this.flutterNoop, + this.flutterThrowError, + this.flutterThrowErrorFromVoid, + this.flutterEchoBool, + this.flutterEchoInt, + this.flutterEchoDouble, + this.flutterEchoString, + this.flutterEchoUint8List, + this.flutterEchoList, + this.flutterEchoProxyApiList, + this.flutterEchoMap, + this.flutterEchoProxyApiMap, + this.flutterEchoEnum, + this.flutterEchoProxyApi, + this.flutterEchoNullableBool, + this.flutterEchoNullableInt, + this.flutterEchoNullableDouble, + this.flutterEchoNullableString, + this.flutterEchoNullableUint8List, + this.flutterEchoNullableList, + this.flutterEchoNullableMap, + this.flutterEchoNullableEnum, + this.flutterEchoNullableProxyApi, + this.flutterNoopAsync, + this.flutterEchoAsyncString, + required bool boolParam, + required int intParam, + required double doubleParam, + required String stringParam, + required Uint8List aUint8ListParam, + required List listParam, + required Map mapParam, + required ProxyApiTestEnum enumParam, + required ProxyApiSuperClass proxyApiParam, + bool? nullableBoolParam, + int? nullableIntParam, + double? nullableDoubleParam, + String? nullableStringParam, + Uint8List? nullableUint8ListParam, + List? nullableListParam, + Map? nullableMapParam, + ProxyApiTestEnum? nullableEnumParam, + ProxyApiSuperClass? nullableProxyApiParam, + }) : super.pigeon_detached() { + final int __pigeon_instanceIdentifier = + pigeon_instanceManager.addDartCreatedInstance(this); + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + () async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.pigeon_defaultConstructor'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([ + __pigeon_instanceIdentifier, + aBool, + anInt, + aDouble, + aString, + aUint8List, + aList, + aMap, + anEnum.index, + aProxyApi, + aNullableBool, + aNullableInt, + aNullableDouble, + aNullableString, + aNullableUint8List, + aNullableList, + aNullableMap, + aNullableEnum?.index, + aNullableProxyApi, + boolParam, + intParam, + doubleParam, + stringParam, + aUint8ListParam, + listParam, + mapParam, + enumParam.index, + proxyApiParam, + nullableBoolParam, + nullableIntParam, + nullableDoubleParam, + nullableStringParam, + nullableUint8ListParam, + nullableListParam, + nullableMapParam, + nullableEnumParam?.index, + nullableProxyApiParam + ]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + }(); + } + + /// Constructs [ProxyApiTestClass] without creating the associated native object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies for an [PigeonInstanceManager]. + @protected + ProxyApiTestClass.pigeon_detached({ + super.pigeon_binaryMessenger, + super.pigeon_instanceManager, + required this.aBool, + required this.anInt, + required this.aDouble, + required this.aString, + required this.aUint8List, + required this.aList, + required this.aMap, + required this.anEnum, + required this.aProxyApi, + this.aNullableBool, + this.aNullableInt, + this.aNullableDouble, + this.aNullableString, + this.aNullableUint8List, + this.aNullableList, + this.aNullableMap, + this.aNullableEnum, + this.aNullableProxyApi, + this.anInterfaceMethod, + this.flutterNoop, + this.flutterThrowError, + this.flutterThrowErrorFromVoid, + this.flutterEchoBool, + this.flutterEchoInt, + this.flutterEchoDouble, + this.flutterEchoString, + this.flutterEchoUint8List, + this.flutterEchoList, + this.flutterEchoProxyApiList, + this.flutterEchoMap, + this.flutterEchoProxyApiMap, + this.flutterEchoEnum, + this.flutterEchoProxyApi, + this.flutterEchoNullableBool, + this.flutterEchoNullableInt, + this.flutterEchoNullableDouble, + this.flutterEchoNullableString, + this.flutterEchoNullableUint8List, + this.flutterEchoNullableList, + this.flutterEchoNullableMap, + this.flutterEchoNullableEnum, + this.flutterEchoNullableProxyApi, + this.flutterNoopAsync, + this.flutterEchoAsyncString, + }) : super.pigeon_detached(); + + late final _PigeonProxyApiBaseCodec __pigeon_codecProxyApiTestClass = + _PigeonProxyApiBaseCodec(pigeon_instanceManager); + + final bool aBool; + + final int anInt; + + final double aDouble; + + final String aString; + + final Uint8List aUint8List; + + final List aList; + + final Map aMap; + + final ProxyApiTestEnum anEnum; + + final ProxyApiSuperClass aProxyApi; + + final bool? aNullableBool; + + final int? aNullableInt; + + final double? aNullableDouble; + + final String? aNullableString; + + final Uint8List? aNullableUint8List; + + final List? aNullableList; + + final Map? aNullableMap; + + final ProxyApiTestEnum? aNullableEnum; + + final ProxyApiSuperClass? aNullableProxyApi; + + /// A no-op function taking no arguments and returning no value, to sanity + /// test basic calling. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterNoop: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final void Function(ProxyApiTestClass pigeon_instance)? flutterNoop; + + /// Responds with an error from an async function returning a value. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterThrowError: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final Object? Function(ProxyApiTestClass pigeon_instance)? flutterThrowError; + + /// Responds with an error from an async void function. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterThrowErrorFromVoid: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final void Function(ProxyApiTestClass pigeon_instance)? + flutterThrowErrorFromVoid; + + /// Returns the passed boolean, to test serialization and deserialization. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterEchoBool: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final bool Function( + ProxyApiTestClass pigeon_instance, + bool aBool, + )? flutterEchoBool; + + /// Returns the passed int, to test serialization and deserialization. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterEchoInt: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final int Function( + ProxyApiTestClass pigeon_instance, + int anInt, + )? flutterEchoInt; + + /// Returns the passed double, to test serialization and deserialization. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterEchoDouble: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final double Function( + ProxyApiTestClass pigeon_instance, + double aDouble, + )? flutterEchoDouble; + + /// Returns the passed string, to test serialization and deserialization. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterEchoString: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final String Function( + ProxyApiTestClass pigeon_instance, + String aString, + )? flutterEchoString; + + /// Returns the passed byte list, to test serialization and deserialization. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterEchoUint8List: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final Uint8List Function( + ProxyApiTestClass pigeon_instance, + Uint8List aList, + )? flutterEchoUint8List; + + /// Returns the passed list, to test serialization and deserialization. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterEchoList: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final List Function( + ProxyApiTestClass pigeon_instance, + List aList, + )? flutterEchoList; + + /// Returns the passed list with ProxyApis, to test serialization and + /// deserialization. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterEchoProxyApiList: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final List Function( + ProxyApiTestClass pigeon_instance, + List aList, + )? flutterEchoProxyApiList; + + /// Returns the passed map, to test serialization and deserialization. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterEchoMap: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final Map Function( + ProxyApiTestClass pigeon_instance, + Map aMap, + )? flutterEchoMap; + + /// Returns the passed map with ProxyApis, to test serialization and + /// deserialization. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterEchoProxyApiMap: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final Map Function( + ProxyApiTestClass pigeon_instance, + Map aMap, + )? flutterEchoProxyApiMap; + + /// Returns the passed enum to test serialization and deserialization. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterEchoEnum: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final ProxyApiTestEnum Function( + ProxyApiTestClass pigeon_instance, + ProxyApiTestEnum anEnum, + )? flutterEchoEnum; + + /// Returns the passed ProxyApi to test serialization and deserialization. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterEchoProxyApi: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final ProxyApiSuperClass Function( + ProxyApiTestClass pigeon_instance, + ProxyApiSuperClass aProxyApi, + )? flutterEchoProxyApi; + + /// Returns the passed boolean, to test serialization and deserialization. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterEchoNullableBool: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final bool? Function( + ProxyApiTestClass pigeon_instance, + bool? aBool, + )? flutterEchoNullableBool; + + /// Returns the passed int, to test serialization and deserialization. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterEchoNullableInt: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final int? Function( + ProxyApiTestClass pigeon_instance, + int? anInt, + )? flutterEchoNullableInt; + + /// Returns the passed double, to test serialization and deserialization. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterEchoNullableDouble: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final double? Function( + ProxyApiTestClass pigeon_instance, + double? aDouble, + )? flutterEchoNullableDouble; + + /// Returns the passed string, to test serialization and deserialization. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterEchoNullableString: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final String? Function( + ProxyApiTestClass pigeon_instance, + String? aString, + )? flutterEchoNullableString; + + /// Returns the passed byte list, to test serialization and deserialization. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterEchoNullableUint8List: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final Uint8List? Function( + ProxyApiTestClass pigeon_instance, + Uint8List? aList, + )? flutterEchoNullableUint8List; + + /// Returns the passed list, to test serialization and deserialization. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterEchoNullableList: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final List? Function( + ProxyApiTestClass pigeon_instance, + List? aList, + )? flutterEchoNullableList; + + /// Returns the passed map, to test serialization and deserialization. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterEchoNullableMap: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final Map? Function( + ProxyApiTestClass pigeon_instance, + Map? aMap, + )? flutterEchoNullableMap; + + /// Returns the passed enum to test serialization and deserialization. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterEchoNullableEnum: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final ProxyApiTestEnum? Function( + ProxyApiTestClass pigeon_instance, + ProxyApiTestEnum? anEnum, + )? flutterEchoNullableEnum; + + /// Returns the passed ProxyApi to test serialization and deserialization. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterEchoNullableProxyApi: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final ProxyApiSuperClass? Function( + ProxyApiTestClass pigeon_instance, + ProxyApiSuperClass? aProxyApi, + )? flutterEchoNullableProxyApi; + + /// A no-op function taking no arguments and returning no value, to sanity + /// test basic asynchronous calling. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterNoopAsync: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final Future Function(ProxyApiTestClass pigeon_instance)? + flutterNoopAsync; + + /// Returns the passed in generic Object asynchronously. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiTestClass instance = ProxyApiTestClass( + /// flutterEchoAsyncString: (ProxyApiTestClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final Future Function( + ProxyApiTestClass pigeon_instance, + String aString, + )? flutterEchoAsyncString; + + @override + final void Function(ProxyApiInterface pigeon_instance)? anInterfaceMethod; + + late final ProxyApiSuperClass attachedField = __pigeon_attachedField(); + + static final ProxyApiSuperClass staticAttachedField = + __pigeon_staticAttachedField(); + + static void pigeon_setUpMessageHandlers({ + bool pigeon_clearHandlers = false, + BinaryMessenger? pigeon_binaryMessenger, + PigeonInstanceManager? pigeon_instanceManager, + ProxyApiTestClass Function( + bool aBool, + int anInt, + double aDouble, + String aString, + Uint8List aUint8List, + List aList, + Map aMap, + ProxyApiTestEnum anEnum, + ProxyApiSuperClass aProxyApi, + bool? aNullableBool, + int? aNullableInt, + double? aNullableDouble, + String? aNullableString, + Uint8List? aNullableUint8List, + List? aNullableList, + Map? aNullableMap, + ProxyApiTestEnum? aNullableEnum, + ProxyApiSuperClass? aNullableProxyApi, + )? pigeon_newInstance, + void Function(ProxyApiTestClass pigeon_instance)? flutterNoop, + Object? Function(ProxyApiTestClass pigeon_instance)? flutterThrowError, + void Function(ProxyApiTestClass pigeon_instance)? flutterThrowErrorFromVoid, + bool Function( + ProxyApiTestClass pigeon_instance, + bool aBool, + )? flutterEchoBool, + int Function( + ProxyApiTestClass pigeon_instance, + int anInt, + )? flutterEchoInt, + double Function( + ProxyApiTestClass pigeon_instance, + double aDouble, + )? flutterEchoDouble, + String Function( + ProxyApiTestClass pigeon_instance, + String aString, + )? flutterEchoString, + Uint8List Function( + ProxyApiTestClass pigeon_instance, + Uint8List aList, + )? flutterEchoUint8List, + List Function( + ProxyApiTestClass pigeon_instance, + List aList, + )? flutterEchoList, + List Function( + ProxyApiTestClass pigeon_instance, + List aList, + )? flutterEchoProxyApiList, + Map Function( + ProxyApiTestClass pigeon_instance, + Map aMap, + )? flutterEchoMap, + Map Function( + ProxyApiTestClass pigeon_instance, + Map aMap, + )? flutterEchoProxyApiMap, + ProxyApiTestEnum Function( + ProxyApiTestClass pigeon_instance, + ProxyApiTestEnum anEnum, + )? flutterEchoEnum, + ProxyApiSuperClass Function( + ProxyApiTestClass pigeon_instance, + ProxyApiSuperClass aProxyApi, + )? flutterEchoProxyApi, + bool? Function( + ProxyApiTestClass pigeon_instance, + bool? aBool, + )? flutterEchoNullableBool, + int? Function( + ProxyApiTestClass pigeon_instance, + int? anInt, + )? flutterEchoNullableInt, + double? Function( + ProxyApiTestClass pigeon_instance, + double? aDouble, + )? flutterEchoNullableDouble, + String? Function( + ProxyApiTestClass pigeon_instance, + String? aString, + )? flutterEchoNullableString, + Uint8List? Function( + ProxyApiTestClass pigeon_instance, + Uint8List? aList, + )? flutterEchoNullableUint8List, + List? Function( + ProxyApiTestClass pigeon_instance, + List? aList, + )? flutterEchoNullableList, + Map? Function( + ProxyApiTestClass pigeon_instance, + Map? aMap, + )? flutterEchoNullableMap, + ProxyApiTestEnum? Function( + ProxyApiTestClass pigeon_instance, + ProxyApiTestEnum? anEnum, + )? flutterEchoNullableEnum, + ProxyApiSuperClass? Function( + ProxyApiTestClass pigeon_instance, + ProxyApiSuperClass? aProxyApi, + )? flutterEchoNullableProxyApi, + Future Function(ProxyApiTestClass pigeon_instance)? flutterNoopAsync, + Future Function( + ProxyApiTestClass pigeon_instance, + String aString, + )? flutterEchoAsyncString, + }) { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + _PigeonProxyApiBaseCodec( + pigeon_instanceManager ?? PigeonInstanceManager.instance); + final BinaryMessenger? binaryMessenger = pigeon_binaryMessenger; + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.pigeon_newInstance', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.pigeon_newInstance was null.'); + final List args = (message as List?)!; + final int? arg_pigeon_instanceIdentifier = (args[0] as int?); + assert(arg_pigeon_instanceIdentifier != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.pigeon_newInstance was null, expected non-null int.'); + final bool? arg_aBool = (args[1] as bool?); + assert(arg_aBool != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.pigeon_newInstance was null, expected non-null bool.'); + final int? arg_anInt = (args[2] as int?); + assert(arg_anInt != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.pigeon_newInstance was null, expected non-null int.'); + final double? arg_aDouble = (args[3] as double?); + assert(arg_aDouble != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.pigeon_newInstance was null, expected non-null double.'); + final String? arg_aString = (args[4] as String?); + assert(arg_aString != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.pigeon_newInstance was null, expected non-null String.'); + final Uint8List? arg_aUint8List = (args[5] as Uint8List?); + assert(arg_aUint8List != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.pigeon_newInstance was null, expected non-null Uint8List.'); + final List? arg_aList = + (args[6] as List?)?.cast(); + assert(arg_aList != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.pigeon_newInstance was null, expected non-null List.'); + final Map? arg_aMap = + (args[7] as Map?)?.cast(); + assert(arg_aMap != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.pigeon_newInstance was null, expected non-null Map.'); + final ProxyApiTestEnum? arg_anEnum = + args[8] == null ? null : ProxyApiTestEnum.values[args[8]! as int]; + assert(arg_anEnum != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.pigeon_newInstance was null, expected non-null ProxyApiTestEnum.'); + final ProxyApiSuperClass? arg_aProxyApi = + (args[9] as ProxyApiSuperClass?); + assert(arg_aProxyApi != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.pigeon_newInstance was null, expected non-null ProxyApiSuperClass.'); + final bool? arg_aNullableBool = (args[10] as bool?); + final int? arg_aNullableInt = (args[11] as int?); + final double? arg_aNullableDouble = (args[12] as double?); + final String? arg_aNullableString = (args[13] as String?); + final Uint8List? arg_aNullableUint8List = (args[14] as Uint8List?); + final List? arg_aNullableList = + (args[15] as List?)?.cast(); + final Map? arg_aNullableMap = + (args[16] as Map?)?.cast(); + final ProxyApiTestEnum? arg_aNullableEnum = args[17] == null + ? null + : ProxyApiTestEnum.values[args[17]! as int]; + final ProxyApiSuperClass? arg_aNullableProxyApi = + (args[18] as ProxyApiSuperClass?); + try { + (pigeon_instanceManager ?? PigeonInstanceManager.instance) + .addHostCreatedInstance( + pigeon_newInstance?.call( + arg_aBool!, + arg_anInt!, + arg_aDouble!, + arg_aString!, + arg_aUint8List!, + arg_aList!, + arg_aMap!, + arg_anEnum!, + arg_aProxyApi!, + arg_aNullableBool, + arg_aNullableInt, + arg_aNullableDouble, + arg_aNullableString, + arg_aNullableUint8List, + arg_aNullableList, + arg_aNullableMap, + arg_aNullableEnum, + arg_aNullableProxyApi) ?? + ProxyApiTestClass.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + aBool: arg_aBool!, + anInt: arg_anInt!, + aDouble: arg_aDouble!, + aString: arg_aString!, + aUint8List: arg_aUint8List!, + aList: arg_aList!, + aMap: arg_aMap!, + anEnum: arg_anEnum!, + aProxyApi: arg_aProxyApi!, + aNullableBool: arg_aNullableBool, + aNullableInt: arg_aNullableInt, + aNullableDouble: arg_aNullableDouble, + aNullableString: arg_aNullableString, + aNullableUint8List: arg_aNullableUint8List, + aNullableList: arg_aNullableList, + aNullableMap: arg_aNullableMap, + aNullableEnum: arg_aNullableEnum, + aNullableProxyApi: arg_aNullableProxyApi, + ), + arg_pigeon_instanceIdentifier!, + ); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterNoop', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterNoop was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterNoop was null, expected non-null ProxyApiTestClass.'); + try { + (flutterNoop ?? arg_pigeon_instance!.flutterNoop) + ?.call(arg_pigeon_instance!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterThrowError', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterThrowError was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterThrowError was null, expected non-null ProxyApiTestClass.'); + try { + final Object? output = + (flutterThrowError ?? arg_pigeon_instance!.flutterThrowError) + ?.call(arg_pigeon_instance!); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterThrowErrorFromVoid', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterThrowErrorFromVoid was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterThrowErrorFromVoid was null, expected non-null ProxyApiTestClass.'); + try { + (flutterThrowErrorFromVoid ?? + arg_pigeon_instance!.flutterThrowErrorFromVoid) + ?.call(arg_pigeon_instance!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoBool', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoBool was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoBool was null, expected non-null ProxyApiTestClass.'); + final bool? arg_aBool = (args[1] as bool?); + assert(arg_aBool != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoBool was null, expected non-null bool.'); + try { + final bool? output = + (flutterEchoBool ?? arg_pigeon_instance!.flutterEchoBool) + ?.call(arg_pigeon_instance!, arg_aBool!); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoInt', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoInt was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoInt was null, expected non-null ProxyApiTestClass.'); + final int? arg_anInt = (args[1] as int?); + assert(arg_anInt != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoInt was null, expected non-null int.'); + try { + final int? output = + (flutterEchoInt ?? arg_pigeon_instance!.flutterEchoInt) + ?.call(arg_pigeon_instance!, arg_anInt!); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoDouble', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoDouble was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoDouble was null, expected non-null ProxyApiTestClass.'); + final double? arg_aDouble = (args[1] as double?); + assert(arg_aDouble != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoDouble was null, expected non-null double.'); + try { + final double? output = + (flutterEchoDouble ?? arg_pigeon_instance!.flutterEchoDouble) + ?.call(arg_pigeon_instance!, arg_aDouble!); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoString', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoString was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoString was null, expected non-null ProxyApiTestClass.'); + final String? arg_aString = (args[1] as String?); + assert(arg_aString != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoString was null, expected non-null String.'); + try { + final String? output = + (flutterEchoString ?? arg_pigeon_instance!.flutterEchoString) + ?.call(arg_pigeon_instance!, arg_aString!); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoUint8List', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoUint8List was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoUint8List was null, expected non-null ProxyApiTestClass.'); + final Uint8List? arg_aList = (args[1] as Uint8List?); + assert(arg_aList != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoUint8List was null, expected non-null Uint8List.'); + try { + final Uint8List? output = (flutterEchoUint8List ?? + arg_pigeon_instance!.flutterEchoUint8List) + ?.call(arg_pigeon_instance!, arg_aList!); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoList', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoList was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoList was null, expected non-null ProxyApiTestClass.'); + final List? arg_aList = + (args[1] as List?)?.cast(); + assert(arg_aList != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoList was null, expected non-null List.'); + try { + final List? output = + (flutterEchoList ?? arg_pigeon_instance!.flutterEchoList) + ?.call(arg_pigeon_instance!, arg_aList!); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoProxyApiList', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoProxyApiList was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoProxyApiList was null, expected non-null ProxyApiTestClass.'); + final List? arg_aList = + (args[1] as List?)?.cast(); + assert(arg_aList != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoProxyApiList was null, expected non-null List.'); + try { + final List? output = (flutterEchoProxyApiList ?? + arg_pigeon_instance!.flutterEchoProxyApiList) + ?.call(arg_pigeon_instance!, arg_aList!); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoMap', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoMap was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoMap was null, expected non-null ProxyApiTestClass.'); + final Map? arg_aMap = + (args[1] as Map?)?.cast(); + assert(arg_aMap != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoMap was null, expected non-null Map.'); + try { + final Map? output = + (flutterEchoMap ?? arg_pigeon_instance!.flutterEchoMap) + ?.call(arg_pigeon_instance!, arg_aMap!); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoProxyApiMap', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoProxyApiMap was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoProxyApiMap was null, expected non-null ProxyApiTestClass.'); + final Map? arg_aMap = + (args[1] as Map?) + ?.cast(); + assert(arg_aMap != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoProxyApiMap was null, expected non-null Map.'); + try { + final Map? output = + (flutterEchoProxyApiMap ?? + arg_pigeon_instance!.flutterEchoProxyApiMap) + ?.call(arg_pigeon_instance!, arg_aMap!); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoEnum', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoEnum was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoEnum was null, expected non-null ProxyApiTestClass.'); + final ProxyApiTestEnum? arg_anEnum = + args[1] == null ? null : ProxyApiTestEnum.values[args[1]! as int]; + assert(arg_anEnum != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoEnum was null, expected non-null ProxyApiTestEnum.'); + try { + final ProxyApiTestEnum? output = + (flutterEchoEnum ?? arg_pigeon_instance!.flutterEchoEnum) + ?.call(arg_pigeon_instance!, arg_anEnum!); + return wrapResponse(result: output?.index); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoProxyApi', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoProxyApi was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoProxyApi was null, expected non-null ProxyApiTestClass.'); + final ProxyApiSuperClass? arg_aProxyApi = + (args[1] as ProxyApiSuperClass?); + assert(arg_aProxyApi != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoProxyApi was null, expected non-null ProxyApiSuperClass.'); + try { + final ProxyApiSuperClass? output = (flutterEchoProxyApi ?? + arg_pigeon_instance!.flutterEchoProxyApi) + ?.call(arg_pigeon_instance!, arg_aProxyApi!); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableBool', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableBool was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableBool was null, expected non-null ProxyApiTestClass.'); + final bool? arg_aBool = (args[1] as bool?); + try { + final bool? output = (flutterEchoNullableBool ?? + arg_pigeon_instance!.flutterEchoNullableBool) + ?.call(arg_pigeon_instance!, arg_aBool); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableInt', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableInt was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableInt was null, expected non-null ProxyApiTestClass.'); + final int? arg_anInt = (args[1] as int?); + try { + final int? output = (flutterEchoNullableInt ?? + arg_pigeon_instance!.flutterEchoNullableInt) + ?.call(arg_pigeon_instance!, arg_anInt); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableDouble', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableDouble was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableDouble was null, expected non-null ProxyApiTestClass.'); + final double? arg_aDouble = (args[1] as double?); + try { + final double? output = (flutterEchoNullableDouble ?? + arg_pigeon_instance!.flutterEchoNullableDouble) + ?.call(arg_pigeon_instance!, arg_aDouble); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableString', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableString was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableString was null, expected non-null ProxyApiTestClass.'); + final String? arg_aString = (args[1] as String?); + try { + final String? output = (flutterEchoNullableString ?? + arg_pigeon_instance!.flutterEchoNullableString) + ?.call(arg_pigeon_instance!, arg_aString); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableUint8List', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableUint8List was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableUint8List was null, expected non-null ProxyApiTestClass.'); + final Uint8List? arg_aList = (args[1] as Uint8List?); + try { + final Uint8List? output = (flutterEchoNullableUint8List ?? + arg_pigeon_instance!.flutterEchoNullableUint8List) + ?.call(arg_pigeon_instance!, arg_aList); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableList', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableList was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableList was null, expected non-null ProxyApiTestClass.'); + final List? arg_aList = + (args[1] as List?)?.cast(); + try { + final List? output = (flutterEchoNullableList ?? + arg_pigeon_instance!.flutterEchoNullableList) + ?.call(arg_pigeon_instance!, arg_aList); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableMap', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableMap was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableMap was null, expected non-null ProxyApiTestClass.'); + final Map? arg_aMap = + (args[1] as Map?)?.cast(); + try { + final Map? output = (flutterEchoNullableMap ?? + arg_pigeon_instance!.flutterEchoNullableMap) + ?.call(arg_pigeon_instance!, arg_aMap); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableEnum', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableEnum was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableEnum was null, expected non-null ProxyApiTestClass.'); + final ProxyApiTestEnum? arg_anEnum = + args[1] == null ? null : ProxyApiTestEnum.values[args[1]! as int]; + try { + final ProxyApiTestEnum? output = (flutterEchoNullableEnum ?? + arg_pigeon_instance!.flutterEchoNullableEnum) + ?.call(arg_pigeon_instance!, arg_anEnum); + return wrapResponse(result: output?.index); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableProxyApi', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableProxyApi was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoNullableProxyApi was null, expected non-null ProxyApiTestClass.'); + final ProxyApiSuperClass? arg_aProxyApi = + (args[1] as ProxyApiSuperClass?); + try { + final ProxyApiSuperClass? output = (flutterEchoNullableProxyApi ?? + arg_pigeon_instance!.flutterEchoNullableProxyApi) + ?.call(arg_pigeon_instance!, arg_aProxyApi); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterNoopAsync', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterNoopAsync was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterNoopAsync was null, expected non-null ProxyApiTestClass.'); + try { + await (flutterNoopAsync ?? arg_pigeon_instance!.flutterNoopAsync) + ?.call(arg_pigeon_instance!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoAsyncString', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoAsyncString was null.'); + final List args = (message as List?)!; + final ProxyApiTestClass? arg_pigeon_instance = + (args[0] as ProxyApiTestClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoAsyncString was null, expected non-null ProxyApiTestClass.'); + final String? arg_aString = (args[1] as String?); + assert(arg_aString != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.flutterEchoAsyncString was null, expected non-null String.'); + try { + final String? output = await (flutterEchoAsyncString ?? + arg_pigeon_instance!.flutterEchoAsyncString) + ?.call(arg_pigeon_instance!, arg_aString!); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + } + + ProxyApiSuperClass __pigeon_attachedField() { + final ProxyApiSuperClass __pigeon_instance = + ProxyApiSuperClass.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + ); + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + final int __pigeon_instanceIdentifier = + pigeon_instanceManager.addDartCreatedInstance(__pigeon_instance); + () async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.attachedField'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, __pigeon_instanceIdentifier]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + }(); + return __pigeon_instance; + } + + static ProxyApiSuperClass __pigeon_staticAttachedField() { + final ProxyApiSuperClass __pigeon_instance = + ProxyApiSuperClass.pigeon_detached(); + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + _PigeonProxyApiBaseCodec(PigeonInstanceManager.instance); + final BinaryMessenger __pigeon_binaryMessenger = + ServicesBinding.instance.defaultBinaryMessenger; + final int __pigeon_instanceIdentifier = PigeonInstanceManager.instance + .addDartCreatedInstance(__pigeon_instance); + () async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.staticAttachedField'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([__pigeon_instanceIdentifier]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + }(); + return __pigeon_instance; + } + + /// A no-op function taking no arguments and returning no value, to sanity + /// test basic calling. + Future noop() async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.noop'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Returns an error, to test error handling. + Future throwError() async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.throwError'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return __pigeon_replyList[0]; + } + } + + /// Returns an error from a void function, to test error handling. + Future throwErrorFromVoid() async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.throwErrorFromVoid'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Returns a Flutter error, to test error handling. + Future throwFlutterError() async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.throwFlutterError'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return __pigeon_replyList[0]; + } + } + + /// Returns passed in int. + Future echoInt(int anInt) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoInt'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, anInt]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as int?)!; + } + } + + /// Returns passed in double. + Future echoDouble(double aDouble) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoDouble'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aDouble]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as double?)!; + } + } + + /// Returns the passed in boolean. + Future echoBool(bool aBool) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoBool'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aBool]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as bool?)!; + } + } + + /// Returns the passed in string. + Future echoString(String aString) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoString'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aString]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as String?)!; + } + } + + /// Returns the passed in Uint8List. + Future echoUint8List(Uint8List aUint8List) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoUint8List'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, aUint8List]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as Uint8List?)!; + } + } + + /// Returns the passed in generic Object. + Future echoObject(Object anObject) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoObject'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, anObject]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return __pigeon_replyList[0]!; + } + } + + /// Returns the passed list, to test serialization and deserialization. + Future> echoList(List aList) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoList'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aList]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as List?)!.cast(); + } + } + + /// Returns the passed list with ProxyApis, to test serialization and + /// deserialization. + Future> echoProxyApiList( + List aList) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoProxyApiList'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aList]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as List?)! + .cast(); + } + } + + /// Returns the passed map, to test serialization and deserialization. + Future> echoMap(Map aMap) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoMap'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aMap]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as Map?)! + .cast(); + } + } + + /// Returns the passed map with ProxyApis, to test serialization and + /// deserialization. + Future> echoProxyApiMap( + Map aMap) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoProxyApiMap'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aMap]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as Map?)! + .cast(); + } + } + + /// Returns the passed enum to test serialization and deserialization. + Future echoEnum(ProxyApiTestEnum anEnum) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoEnum'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, anEnum.index]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return ProxyApiTestEnum.values[__pigeon_replyList[0]! as int]; + } + } + + /// Returns the passed ProxyApi to test serialization and deserialization. + Future echoProxyApi(ProxyApiSuperClass aProxyApi) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoProxyApi'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, aProxyApi]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as ProxyApiSuperClass?)!; + } + } + + /// Returns passed in int. + Future echoNullableInt(int? aNullableInt) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoNullableInt'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, aNullableInt]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as int?); + } + } + + /// Returns passed in double. + Future echoNullableDouble(double? aNullableDouble) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoNullableDouble'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, aNullableDouble]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as double?); + } + } + + /// Returns the passed in boolean. + Future echoNullableBool(bool? aNullableBool) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoNullableBool'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, aNullableBool]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as bool?); + } + } + + /// Returns the passed in string. + Future echoNullableString(String? aNullableString) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoNullableString'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, aNullableString]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as String?); + } + } + + /// Returns the passed in Uint8List. + Future echoNullableUint8List( + Uint8List? aNullableUint8List) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoNullableUint8List'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, aNullableUint8List]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as Uint8List?); + } + } + + /// Returns the passed in generic Object. + Future echoNullableObject(Object? aNullableObject) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoNullableObject'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, aNullableObject]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return __pigeon_replyList[0]; + } + } + + /// Returns the passed list, to test serialization and deserialization. + Future?> echoNullableList(List? aNullableList) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoNullableList'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, aNullableList]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as List?)?.cast(); + } + } + + /// Returns the passed map, to test serialization and deserialization. + Future?> echoNullableMap( + Map? aNullableMap) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoNullableMap'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, aNullableMap]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as Map?) + ?.cast(); + } + } + + Future echoNullableEnum( + ProxyApiTestEnum? aNullableEnum) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoNullableEnum'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, aNullableEnum?.index]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as int?) == null + ? null + : ProxyApiTestEnum.values[__pigeon_replyList[0]! as int]; + } + } + + /// Returns the passed ProxyApi to test serialization and deserialization. + Future echoNullableProxyApi( + ProxyApiSuperClass? aNullableProxyApi) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoNullableProxyApi'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, aNullableProxyApi]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as ProxyApiSuperClass?); + } + } + + /// A no-op function taking no arguments and returning no value, to sanity + /// test basic asynchronous calling. + Future noopAsync() async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.noopAsync'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Returns passed in int asynchronously. + Future echoAsyncInt(int anInt) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoAsyncInt'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, anInt]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as int?)!; + } + } + + /// Returns passed in double asynchronously. + Future echoAsyncDouble(double aDouble) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoAsyncDouble'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aDouble]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as double?)!; + } + } + + /// Returns the passed in boolean asynchronously. + Future echoAsyncBool(bool aBool) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoAsyncBool'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aBool]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as bool?)!; + } + } + + /// Returns the passed string asynchronously. + Future echoAsyncString(String aString) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoAsyncString'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aString]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as String?)!; + } + } + + /// Returns the passed in Uint8List asynchronously. + Future echoAsyncUint8List(Uint8List aUint8List) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoAsyncUint8List'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, aUint8List]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as Uint8List?)!; + } + } + + /// Returns the passed in generic Object asynchronously. + Future echoAsyncObject(Object anObject) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoAsyncObject'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, anObject]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return __pigeon_replyList[0]!; + } + } + + /// Returns the passed list, to test asynchronous serialization and deserialization. + Future> echoAsyncList(List aList) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoAsyncList'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aList]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as List?)!.cast(); + } + } + + /// Returns the passed map, to test asynchronous serialization and deserialization. + Future> echoAsyncMap(Map aMap) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoAsyncMap'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aMap]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as Map?)! + .cast(); + } + } + + /// Returns the passed enum, to test asynchronous serialization and deserialization. + Future echoAsyncEnum(ProxyApiTestEnum anEnum) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoAsyncEnum'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, anEnum.index]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return ProxyApiTestEnum.values[__pigeon_replyList[0]! as int]; + } + } + + /// Responds with an error from an async function returning a value. + Future throwAsyncError() async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.throwAsyncError'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return __pigeon_replyList[0]; + } + } + + /// Responds with an error from an async void function. + Future throwAsyncErrorFromVoid() async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.throwAsyncErrorFromVoid'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Responds with a Flutter error from an async function returning a value. + Future throwAsyncFlutterError() async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.throwAsyncFlutterError'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return __pigeon_replyList[0]; + } + } + + /// Returns passed in int asynchronously. + Future echoAsyncNullableInt(int? anInt) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoAsyncNullableInt'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, anInt]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as int?); + } + } + + /// Returns passed in double asynchronously. + Future echoAsyncNullableDouble(double? aDouble) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoAsyncNullableDouble'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aDouble]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as double?); + } + } + + /// Returns the passed in boolean asynchronously. + Future echoAsyncNullableBool(bool? aBool) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoAsyncNullableBool'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aBool]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as bool?); + } + } + + /// Returns the passed string asynchronously. + Future echoAsyncNullableString(String? aString) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoAsyncNullableString'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aString]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as String?); + } + } + + /// Returns the passed in Uint8List asynchronously. + Future echoAsyncNullableUint8List(Uint8List? aUint8List) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoAsyncNullableUint8List'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, aUint8List]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as Uint8List?); + } + } + + /// Returns the passed in generic Object asynchronously. + Future echoAsyncNullableObject(Object? anObject) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoAsyncNullableObject'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, anObject]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return __pigeon_replyList[0]; + } + } + + /// Returns the passed list, to test asynchronous serialization and deserialization. + Future?> echoAsyncNullableList(List? aList) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoAsyncNullableList'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aList]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as List?)?.cast(); + } + } + + /// Returns the passed map, to test asynchronous serialization and deserialization. + Future?> echoAsyncNullableMap( + Map? aMap) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoAsyncNullableMap'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aMap]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as Map?) + ?.cast(); + } + } + + /// Returns the passed enum, to test asynchronous serialization and deserialization. + Future echoAsyncNullableEnum( + ProxyApiTestEnum? anEnum) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoAsyncNullableEnum'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, anEnum?.index]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as int?) == null + ? null + : ProxyApiTestEnum.values[__pigeon_replyList[0]! as int]; + } + } + + static Future staticNoop({ + BinaryMessenger? pigeon_binaryMessenger, + PigeonInstanceManager? pigeon_instanceManager, + }) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + _PigeonProxyApiBaseCodec( + pigeon_instanceManager ?? PigeonInstanceManager.instance); + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.staticNoop'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + static Future echoStaticString( + String aString, { + BinaryMessenger? pigeon_binaryMessenger, + PigeonInstanceManager? pigeon_instanceManager, + }) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + _PigeonProxyApiBaseCodec( + pigeon_instanceManager ?? PigeonInstanceManager.instance); + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.echoStaticString'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([aString]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as String?)!; + } + } + + static Future staticAsyncNoop({ + BinaryMessenger? pigeon_binaryMessenger, + PigeonInstanceManager? pigeon_instanceManager, + }) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + _PigeonProxyApiBaseCodec( + pigeon_instanceManager ?? PigeonInstanceManager.instance); + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.staticAsyncNoop'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future callFlutterNoop() async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterNoop'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future callFlutterThrowError() async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterThrowError'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return __pigeon_replyList[0]; + } + } + + Future callFlutterThrowErrorFromVoid() async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterThrowErrorFromVoid'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future callFlutterEchoBool(bool aBool) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterEchoBool'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aBool]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as bool?)!; + } + } + + Future callFlutterEchoInt(int anInt) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterEchoInt'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, anInt]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as int?)!; + } + } + + Future callFlutterEchoDouble(double aDouble) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterEchoDouble'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aDouble]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as double?)!; + } + } + + Future callFlutterEchoString(String aString) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterEchoString'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aString]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as String?)!; + } + } + + Future callFlutterEchoUint8List(Uint8List aUint8List) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterEchoUint8List'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, aUint8List]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as Uint8List?)!; + } + } + + Future> callFlutterEchoList(List aList) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterEchoList'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aList]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as List?)!.cast(); + } + } + + Future> callFlutterEchoProxyApiList( + List aList) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterEchoProxyApiList'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aList]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as List?)! + .cast(); + } + } + + Future> callFlutterEchoMap( + Map aMap) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterEchoMap'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aMap]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as Map?)! + .cast(); + } + } + + Future> callFlutterEchoProxyApiMap( + Map aMap) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterEchoProxyApiMap'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aMap]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as Map?)! + .cast(); + } + } + + Future callFlutterEchoEnum(ProxyApiTestEnum anEnum) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterEchoEnum'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, anEnum.index]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return ProxyApiTestEnum.values[__pigeon_replyList[0]! as int]; + } + } + + Future callFlutterEchoProxyApi( + ProxyApiSuperClass aProxyApi) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterEchoProxyApi'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, aProxyApi]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as ProxyApiSuperClass?)!; + } + } + + Future callFlutterEchoNullableBool(bool? aBool) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterEchoNullableBool'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aBool]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as bool?); + } + } + + Future callFlutterEchoNullableInt(int? anInt) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterEchoNullableInt'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, anInt]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as int?); + } + } + + Future callFlutterEchoNullableDouble(double? aDouble) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterEchoNullableDouble'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aDouble]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as double?); + } + } + + Future callFlutterEchoNullableString(String? aString) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterEchoNullableString'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aString]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as String?); + } + } + + Future callFlutterEchoNullableUint8List( + Uint8List? aUint8List) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterEchoNullableUint8List'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, aUint8List]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as Uint8List?); + } + } + + Future?> callFlutterEchoNullableList( + List? aList) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterEchoNullableList'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aList]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as List?)?.cast(); + } + } + + Future?> callFlutterEchoNullableMap( + Map? aMap) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterEchoNullableMap'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aMap]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as Map?) + ?.cast(); + } + } + + Future callFlutterEchoNullableEnum( + ProxyApiTestEnum? anEnum) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterEchoNullableEnum'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, anEnum?.index]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as int?) == null + ? null + : ProxyApiTestEnum.values[__pigeon_replyList[0]! as int]; + } + } + + Future callFlutterEchoNullableProxyApi( + ProxyApiSuperClass? aProxyApi) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterEchoNullableProxyApi'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([this, aProxyApi]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as ProxyApiSuperClass?); + } + } + + Future callFlutterNoopAsync() async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterNoopAsync'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future callFlutterEchoAsyncString(String aString) async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiTestClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiTestClass.callFlutterEchoAsyncString'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this, aString]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as String?)!; + } + } + + @override + ProxyApiTestClass pigeon_copy() { + return ProxyApiTestClass.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + aBool: aBool, + anInt: anInt, + aDouble: aDouble, + aString: aString, + aUint8List: aUint8List, + aList: aList, + aMap: aMap, + anEnum: anEnum, + aProxyApi: aProxyApi, + aNullableBool: aNullableBool, + aNullableInt: aNullableInt, + aNullableDouble: aNullableDouble, + aNullableString: aNullableString, + aNullableUint8List: aNullableUint8List, + aNullableList: aNullableList, + aNullableMap: aNullableMap, + aNullableEnum: aNullableEnum, + aNullableProxyApi: aNullableProxyApi, + anInterfaceMethod: anInterfaceMethod, + flutterNoop: flutterNoop, + flutterThrowError: flutterThrowError, + flutterThrowErrorFromVoid: flutterThrowErrorFromVoid, + flutterEchoBool: flutterEchoBool, + flutterEchoInt: flutterEchoInt, + flutterEchoDouble: flutterEchoDouble, + flutterEchoString: flutterEchoString, + flutterEchoUint8List: flutterEchoUint8List, + flutterEchoList: flutterEchoList, + flutterEchoProxyApiList: flutterEchoProxyApiList, + flutterEchoMap: flutterEchoMap, + flutterEchoProxyApiMap: flutterEchoProxyApiMap, + flutterEchoEnum: flutterEchoEnum, + flutterEchoProxyApi: flutterEchoProxyApi, + flutterEchoNullableBool: flutterEchoNullableBool, + flutterEchoNullableInt: flutterEchoNullableInt, + flutterEchoNullableDouble: flutterEchoNullableDouble, + flutterEchoNullableString: flutterEchoNullableString, + flutterEchoNullableUint8List: flutterEchoNullableUint8List, + flutterEchoNullableList: flutterEchoNullableList, + flutterEchoNullableMap: flutterEchoNullableMap, + flutterEchoNullableEnum: flutterEchoNullableEnum, + flutterEchoNullableProxyApi: flutterEchoNullableProxyApi, + flutterNoopAsync: flutterNoopAsync, + flutterEchoAsyncString: flutterEchoAsyncString, + ); + } +} + +/// ProxyApi to serve as a super class to the core ProxyApi class. +class ProxyApiSuperClass extends PigeonProxyApiBaseClass { + ProxyApiSuperClass({ + super.pigeon_binaryMessenger, + super.pigeon_instanceManager, + }) { + final int __pigeon_instanceIdentifier = + pigeon_instanceManager.addDartCreatedInstance(this); + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiSuperClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + () async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiSuperClass.pigeon_defaultConstructor'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([__pigeon_instanceIdentifier]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + }(); + } + + /// Constructs [ProxyApiSuperClass] without creating the associated native object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies for an [PigeonInstanceManager]. + @protected + ProxyApiSuperClass.pigeon_detached({ + super.pigeon_binaryMessenger, + super.pigeon_instanceManager, + }); + + late final _PigeonProxyApiBaseCodec __pigeon_codecProxyApiSuperClass = + _PigeonProxyApiBaseCodec(pigeon_instanceManager); + + static void pigeon_setUpMessageHandlers({ + bool pigeon_clearHandlers = false, + BinaryMessenger? pigeon_binaryMessenger, + PigeonInstanceManager? pigeon_instanceManager, + ProxyApiSuperClass Function()? pigeon_newInstance, + }) { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + _PigeonProxyApiBaseCodec( + pigeon_instanceManager ?? PigeonInstanceManager.instance); + final BinaryMessenger? binaryMessenger = pigeon_binaryMessenger; + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiSuperClass.pigeon_newInstance', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiSuperClass.pigeon_newInstance was null.'); + final List args = (message as List?)!; + final int? arg_pigeon_instanceIdentifier = (args[0] as int?); + assert(arg_pigeon_instanceIdentifier != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiSuperClass.pigeon_newInstance was null, expected non-null int.'); + try { + (pigeon_instanceManager ?? PigeonInstanceManager.instance) + .addHostCreatedInstance( + pigeon_newInstance?.call() ?? + ProxyApiSuperClass.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + ), + arg_pigeon_instanceIdentifier!, + ); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + } + + Future aSuperMethod() async { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + __pigeon_codecProxyApiSuperClass; + final BinaryMessenger? __pigeon_binaryMessenger = pigeon_binaryMessenger; + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiSuperClass.aSuperMethod'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([this]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + @override + ProxyApiSuperClass pigeon_copy() { + return ProxyApiSuperClass.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + ); + } +} + +/// ProxyApi to serve as an interface to the core ProxyApi class. +class ProxyApiInterface extends PigeonProxyApiBaseClass { + /// Constructs [ProxyApiInterface] without creating the associated native object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies for an [PigeonInstanceManager]. + @protected + ProxyApiInterface.pigeon_detached({ + super.pigeon_binaryMessenger, + super.pigeon_instanceManager, + this.anInterfaceMethod, + }); + + /// Callback method. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ProxyApiInterface instance = ProxyApiInterface( + /// anInterfaceMethod: (ProxyApiInterface pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final void Function(ProxyApiInterface pigeon_instance)? anInterfaceMethod; + + static void pigeon_setUpMessageHandlers({ + bool pigeon_clearHandlers = false, + BinaryMessenger? pigeon_binaryMessenger, + PigeonInstanceManager? pigeon_instanceManager, + ProxyApiInterface Function()? pigeon_newInstance, + void Function(ProxyApiInterface pigeon_instance)? anInterfaceMethod, + }) { + final _PigeonProxyApiBaseCodec pigeonChannelCodec = + _PigeonProxyApiBaseCodec( + pigeon_instanceManager ?? PigeonInstanceManager.instance); + final BinaryMessenger? binaryMessenger = pigeon_binaryMessenger; + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiInterface.pigeon_newInstance', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiInterface.pigeon_newInstance was null.'); + final List args = (message as List?)!; + final int? arg_pigeon_instanceIdentifier = (args[0] as int?); + assert(arg_pigeon_instanceIdentifier != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiInterface.pigeon_newInstance was null, expected non-null int.'); + try { + (pigeon_instanceManager ?? PigeonInstanceManager.instance) + .addHostCreatedInstance( + pigeon_newInstance?.call() ?? + ProxyApiInterface.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + ), + arg_pigeon_instanceIdentifier!, + ); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.ProxyApiInterface.anInterfaceMethod', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiInterface.anInterfaceMethod was null.'); + final List args = (message as List?)!; + final ProxyApiInterface? arg_pigeon_instance = + (args[0] as ProxyApiInterface?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.ProxyApiInterface.anInterfaceMethod was null, expected non-null ProxyApiInterface.'); + try { + (anInterfaceMethod ?? arg_pigeon_instance!.anInterfaceMethod) + ?.call(arg_pigeon_instance!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + } + + @override + ProxyApiInterface pigeon_copy() { + return ProxyApiInterface.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + anInterfaceMethod: anInterfaceMethod, + ); + } +} diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/test/instance_manager_test.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/test/instance_manager_test.dart new file mode 100644 index 000000000000..aadce1b81d7b --- /dev/null +++ b/packages/pigeon/platform_tests/shared_test_plugin_code/test/instance_manager_test.dart @@ -0,0 +1,168 @@ +// 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. + +// This file specifically tests the test PigeonInstanceManager generated by core_tests. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:shared_test_plugin_code/src/generated/proxy_api_tests.gen.dart'; + +void main() { + group('InstanceManager', () { + test('addHostCreatedInstance', () { + final PigeonInstanceManager instanceManager = + PigeonInstanceManager(onWeakReferenceRemoved: (_) {}); + + final CopyableObject object = CopyableObject( + pigeon_instanceManager: instanceManager, + ); + + instanceManager.addHostCreatedInstance(object, 0); + + expect(instanceManager.getIdentifier(object), 0); + expect( + instanceManager.getInstanceWithWeakReference(0), + object, + ); + }); + + test('addHostCreatedInstance prevents already used objects and ids', () { + final PigeonInstanceManager instanceManager = + PigeonInstanceManager(onWeakReferenceRemoved: (_) {}); + + final CopyableObject object = CopyableObject( + pigeon_instanceManager: instanceManager, + ); + + instanceManager.addHostCreatedInstance(object, 0); + + expect( + () => instanceManager.addHostCreatedInstance(object, 0), + throwsAssertionError, + ); + + expect( + () => instanceManager.addHostCreatedInstance( + CopyableObject(pigeon_instanceManager: instanceManager), + 0, + ), + throwsAssertionError, + ); + }); + + test('addFlutterCreatedInstance', () { + final PigeonInstanceManager instanceManager = + PigeonInstanceManager(onWeakReferenceRemoved: (_) {}); + + final CopyableObject object = CopyableObject( + pigeon_instanceManager: instanceManager, + ); + + instanceManager.addDartCreatedInstance(object); + + final int? instanceId = instanceManager.getIdentifier(object); + expect(instanceId, isNotNull); + expect( + instanceManager.getInstanceWithWeakReference(instanceId!), + object, + ); + }); + + test('removeWeakReference', () { + int? weakInstanceId; + final PigeonInstanceManager instanceManager = + PigeonInstanceManager(onWeakReferenceRemoved: (int instanceId) { + weakInstanceId = instanceId; + }); + + final CopyableObject object = CopyableObject( + pigeon_instanceManager: instanceManager, + ); + + instanceManager.addHostCreatedInstance(object, 0); + + expect(instanceManager.removeWeakReference(object), 0); + expect( + instanceManager.getInstanceWithWeakReference(0), + isA(), + ); + expect(weakInstanceId, 0); + }); + + test('removeWeakReference removes only weak reference', () { + final PigeonInstanceManager instanceManager = + PigeonInstanceManager(onWeakReferenceRemoved: (_) {}); + + final CopyableObject object = CopyableObject( + pigeon_instanceManager: instanceManager, + ); + + instanceManager.addHostCreatedInstance(object, 0); + + expect(instanceManager.removeWeakReference(object), 0); + final CopyableObject copy = instanceManager.getInstanceWithWeakReference( + 0, + )!; + expect(identical(object, copy), isFalse); + }); + + test('removeStrongReference', () { + final PigeonInstanceManager instanceManager = + PigeonInstanceManager(onWeakReferenceRemoved: (_) {}); + + final CopyableObject object = CopyableObject( + pigeon_instanceManager: instanceManager, + ); + + instanceManager.addHostCreatedInstance(object, 0); + instanceManager.removeWeakReference(object); + expect(instanceManager.remove(0), isA()); + expect(instanceManager.containsIdentifier(0), isFalse); + }); + + test('removeStrongReference removes only strong reference', () { + final PigeonInstanceManager instanceManager = + PigeonInstanceManager(onWeakReferenceRemoved: (_) {}); + + final CopyableObject object = CopyableObject( + pigeon_instanceManager: instanceManager, + ); + + instanceManager.addHostCreatedInstance(object, 0); + expect(instanceManager.remove(0), isA()); + expect( + instanceManager.getInstanceWithWeakReference(0), + object, + ); + }); + + test('getInstance can add a new weak reference', () { + final PigeonInstanceManager instanceManager = + PigeonInstanceManager(onWeakReferenceRemoved: (_) {}); + + final CopyableObject object = CopyableObject( + pigeon_instanceManager: instanceManager, + ); + + instanceManager.addHostCreatedInstance(object, 0); + instanceManager.removeWeakReference(object); + + final CopyableObject newWeakCopy = + instanceManager.getInstanceWithWeakReference( + 0, + )!; + expect(identical(object, newWeakCopy), isFalse); + }); + }); +} + +class CopyableObject extends PigeonProxyApiBaseClass { + // ignore: non_constant_identifier_names + CopyableObject({super.pigeon_instanceManager}); + + @override + // ignore: non_constant_identifier_names + CopyableObject pigeon_copy() { + return CopyableObject(pigeon_instanceManager: pigeon_instanceManager); + } +} diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml index c97ed7d51e10..d5b579405d45 100644 --- a/packages/pigeon/pubspec.yaml +++ b/packages/pigeon/pubspec.yaml @@ -2,7 +2,7 @@ name: pigeon description: Code generator tool to make communication between Flutter and the host platform type-safe and easier. repository: https://github.com/flutter/packages/tree/main/packages/pigeon issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+pigeon%22 -version: 17.1.3 # This must match the version in lib/generator_tools.dart +version: 17.2.0 # This must match the version in lib/generator_tools.dart environment: sdk: ^3.1.0 @@ -10,7 +10,9 @@ environment: dependencies: analyzer: ">=5.13.0 <7.0.0" args: ^2.1.0 + code_builder: ^4.10.0 collection: ^1.15.0 + dart_style: ^2.3.4 meta: ^1.9.0 path: ^1.8.0 yaml: ^3.1.1 diff --git a/packages/pigeon/test/dart/proxy_api_test.dart b/packages/pigeon/test/dart/proxy_api_test.dart new file mode 100644 index 000000000000..fb24a2038615 --- /dev/null +++ b/packages/pigeon/test/dart/proxy_api_test.dart @@ -0,0 +1,1014 @@ +// 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:pigeon/ast.dart'; +import 'package:pigeon/dart_generator.dart'; +import 'package:test/test.dart'; + +const String DEFAULT_PACKAGE_NAME = 'test_package'; + +void main() { + group('ProxyApi', () { + test('one api', () { + final Root root = Root(apis: [ + AstProxyApi(name: 'Api', constructors: [ + Constructor(name: 'name', parameters: [ + Parameter( + type: const TypeDeclaration( + baseName: 'Input', + isNullable: false, + ), + name: 'input', + ), + ]), + ], fields: [ + ApiField( + name: 'someField', + type: const TypeDeclaration( + baseName: 'int', + isNullable: false, + ), + ) + ], methods: [ + Method( + name: 'doSomething', + location: ApiLocation.host, + parameters: [ + Parameter( + type: const TypeDeclaration( + baseName: 'Input', + isNullable: false, + ), + name: 'input', + ) + ], + returnType: const TypeDeclaration( + baseName: 'String', + isNullable: false, + ), + ), + Method( + name: 'doSomethingElse', + location: ApiLocation.flutter, + parameters: [ + Parameter( + type: const TypeDeclaration( + baseName: 'Input', + isNullable: false, + ), + name: 'input', + ) + ], + returnType: const TypeDeclaration( + baseName: 'String', + isNullable: false, + ), + isRequired: false, + ), + ]) + ], classes: [], enums: []); + final StringBuffer sink = StringBuffer(); + const DartGenerator generator = DartGenerator(); + generator.generate( + const DartOptions(), + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final String code = sink.toString(); + final String collapsedCode = _collapseNewlineAndIndentation(code); + + // Instance Manager + expect(code, contains(r'class PigeonInstanceManager')); + expect(code, contains(r'class _PigeonInstanceManagerApi')); + + // Base Api class + expect( + code, + contains(r'abstract class PigeonProxyApiBaseClass'), + ); + + // Codec and class + expect(code, contains('class _PigeonProxyApiBaseCodec')); + expect(code, contains(r'class Api extends PigeonProxyApiBaseClass')); + + // Constructors + expect( + collapsedCode, + contains( + r'Api.name({ super.pigeon_binaryMessenger, super.pigeon_instanceManager, required this.someField, this.doSomethingElse, required Input input, })', + ), + ); + expect( + code, + contains( + r'Api.pigeon_detached', + ), + ); + + // Field + expect(code, contains('final int someField;')); + + // Dart -> Host method + expect(code, contains('Future doSomething(Input input)')); + + // Host -> Dart method + expect(code, contains(r'static void pigeon_setUpMessageHandlers({')); + expect( + collapsedCode, + contains( + 'final String Function( Api pigeon_instance, Input input, )? doSomethingElse;', + ), + ); + + // Copy method + expect(code, contains(r'Api pigeon_copy(')); + }); + + group('inheritance', () { + test('extends', () { + final AstProxyApi api2 = AstProxyApi( + name: 'Api2', + constructors: [], + fields: [], + methods: [], + ); + final Root root = Root(apis: [ + AstProxyApi( + name: 'Api', + constructors: [], + fields: [], + methods: [], + superClass: TypeDeclaration( + baseName: 'Api2', + isNullable: false, + associatedProxyApi: api2, + ), + ), + api2, + ], classes: [], enums: []); + final StringBuffer sink = StringBuffer(); + const DartGenerator generator = DartGenerator(); + generator.generate( + const DartOptions(), + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final String code = sink.toString(); + final String collapsedCode = _collapseNewlineAndIndentation(code); + expect(code, contains(r'class Api extends Api2')); + expect( + collapsedCode, + contains( + r'Api.pigeon_detached({ super.pigeon_binaryMessenger, super.pigeon_instanceManager, }) : super.pigeon_detached();', + ), + ); + }); + + test('implements', () { + final AstProxyApi api2 = AstProxyApi( + name: 'Api2', + constructors: [], + fields: [], + methods: [], + ); + final Root root = Root(apis: [ + AstProxyApi( + name: 'Api', + constructors: [], + fields: [], + methods: [], + interfaces: { + TypeDeclaration( + baseName: 'Api2', + isNullable: false, + associatedProxyApi: api2, + ) + }, + ), + api2, + ], classes: [], enums: []); + final StringBuffer sink = StringBuffer(); + const DartGenerator generator = DartGenerator(); + generator.generate( + const DartOptions(), + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final String code = sink.toString(); + expect( + code, + contains( + r'class Api extends PigeonProxyApiBaseClass implements Api2', + ), + ); + }); + + test('implements 2 ProxyApis', () { + final AstProxyApi api2 = AstProxyApi( + name: 'Api2', + constructors: [], + fields: [], + methods: [], + ); + final AstProxyApi api3 = AstProxyApi( + name: 'Api3', + constructors: [], + fields: [], + methods: [], + ); + final Root root = Root(apis: [ + AstProxyApi( + name: 'Api', + constructors: [], + fields: [], + methods: [], + interfaces: { + TypeDeclaration( + baseName: 'Api2', + isNullable: false, + associatedProxyApi: api2, + ), + TypeDeclaration( + baseName: 'Api3', + isNullable: false, + associatedProxyApi: api2, + ), + }, + ), + api2, + api3, + ], classes: [], enums: []); + final StringBuffer sink = StringBuffer(); + const DartGenerator generator = DartGenerator(); + generator.generate( + const DartOptions(), + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final String code = sink.toString(); + expect( + code, + contains( + r'class Api extends PigeonProxyApiBaseClass implements Api2, Api3', + ), + ); + }); + + test('implements inherits flutter methods', () { + final AstProxyApi api2 = AstProxyApi( + name: 'Api2', + constructors: [], + fields: [], + methods: [ + Method( + name: 'aFlutterMethod', + returnType: const TypeDeclaration.voidDeclaration(), + parameters: [], + location: ApiLocation.flutter, + ), + Method( + name: 'aNullableFlutterMethod', + returnType: const TypeDeclaration.voidDeclaration(), + parameters: [], + location: ApiLocation.flutter, + isRequired: false, + ), + ], + ); + final Root root = Root(apis: [ + AstProxyApi( + name: 'Api', + constructors: [], + fields: [], + methods: [], + interfaces: { + TypeDeclaration( + baseName: 'Api2', + isNullable: false, + associatedProxyApi: api2, + ) + }, + ), + api2, + ], classes: [], enums: []); + final StringBuffer sink = StringBuffer(); + const DartGenerator generator = DartGenerator(); + generator.generate( + const DartOptions(), + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final String code = sink.toString(); + final String collapsedCode = _collapseNewlineAndIndentation(code); + expect( + code, + contains( + r'class Api extends PigeonProxyApiBaseClass implements Api2', + ), + ); + expect( + collapsedCode, + contains( + r'Api.pigeon_detached({ super.pigeon_binaryMessenger, ' + r'super.pigeon_instanceManager, ' + r'required this.aFlutterMethod, ' + r'this.aNullableFlutterMethod, })', + ), + ); + }); + }); + + group('Constructors', () { + test('empty name and no params constructor', () { + final Root root = Root( + apis: [ + AstProxyApi(name: 'Api', constructors: [ + Constructor( + name: '', + parameters: [], + ) + ], fields: [], methods: []), + ], + classes: [], + enums: [], + ); + final StringBuffer sink = StringBuffer(); + const DartGenerator generator = DartGenerator(); + generator.generate( + const DartOptions(), + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final String code = sink.toString(); + final String collapsedCode = _collapseNewlineAndIndentation(code); + expect(code, contains('class Api')); + expect( + collapsedCode, + contains( + r'Api({ super.pigeon_binaryMessenger, ' + r'super.pigeon_instanceManager, })', + ), + ); + expect( + collapsedCode, + contains( + r"const String __pigeon_channelName = 'dev.flutter.pigeon.test_package.Api.pigeon_defaultConstructor';", + ), + ); + expect( + collapsedCode, + contains( + r'__pigeon_channel .send([__pigeon_instanceIdentifier])', + ), + ); + }); + + test('multiple params constructor', () { + final Enum anEnum = Enum( + name: 'AnEnum', + members: [EnumMember(name: 'one')], + ); + final Root root = Root( + apis: [ + AstProxyApi(name: 'Api', constructors: [ + Constructor( + name: 'name', + parameters: [ + Parameter( + type: const TypeDeclaration( + isNullable: false, + baseName: 'int', + ), + name: 'validType', + ), + Parameter( + type: TypeDeclaration( + isNullable: false, + baseName: 'AnEnum', + associatedEnum: anEnum, + ), + name: 'enumType', + ), + Parameter( + type: const TypeDeclaration( + isNullable: false, + baseName: 'Api2', + ), + name: 'proxyApiType', + ), + Parameter( + type: const TypeDeclaration( + isNullable: true, + baseName: 'int', + ), + name: 'nullableValidType', + ), + Parameter( + type: TypeDeclaration( + isNullable: true, + baseName: 'AnEnum', + associatedEnum: anEnum, + ), + name: 'nullableEnumType', + ), + Parameter( + type: const TypeDeclaration( + isNullable: true, + baseName: 'Api2', + ), + name: 'nullableProxyApiType', + ), + ], + ) + ], fields: [], methods: []), + AstProxyApi( + name: 'Api2', + constructors: [], + fields: [], + methods: [], + ), + ], + classes: [], + enums: [anEnum], + ); + final StringBuffer sink = StringBuffer(); + const DartGenerator generator = DartGenerator(); + generator.generate( + const DartOptions(), + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final String code = sink.toString(); + final String collapsedCode = _collapseNewlineAndIndentation(code); + expect(code, contains('class Api')); + expect( + collapsedCode, + contains( + r'Api.name({ super.pigeon_binaryMessenger, ' + r'super.pigeon_instanceManager, ' + r'required int validType, ' + r'required AnEnum enumType, ' + r'required Api2 proxyApiType, ' + r'int? nullableValidType, ' + r'AnEnum? nullableEnumType, ' + r'Api2? nullableProxyApiType, })', + ), + ); + expect( + collapsedCode, + contains( + r'__pigeon_channel.send([ ' + r'__pigeon_instanceIdentifier, ' + r'validType, enumType.index, proxyApiType, ' + r'nullableValidType, nullableEnumType?.index, nullableProxyApiType ])', + ), + ); + }); + }); + + group('Fields', () { + test('constructor with fields', () { + final Enum anEnum = Enum( + name: 'AnEnum', + members: [EnumMember(name: 'one')], + ); + final Root root = Root( + apis: [ + AstProxyApi( + name: 'Api', + constructors: [ + Constructor( + name: 'name', + parameters: [], + ) + ], + fields: [ + ApiField( + type: const TypeDeclaration( + isNullable: false, + baseName: 'int', + ), + name: 'validType', + ), + ApiField( + type: TypeDeclaration( + isNullable: false, + baseName: 'AnEnum', + associatedEnum: anEnum, + ), + name: 'enumType', + ), + ApiField( + type: const TypeDeclaration( + isNullable: false, + baseName: 'Api2', + ), + name: 'proxyApiType', + ), + ApiField( + type: const TypeDeclaration( + isNullable: true, + baseName: 'int', + ), + name: 'nullableValidType', + ), + ApiField( + type: TypeDeclaration( + isNullable: true, + baseName: 'AnEnum', + associatedEnum: anEnum, + ), + name: 'nullableEnumType', + ), + ApiField( + type: const TypeDeclaration( + isNullable: true, + baseName: 'Api2', + ), + name: 'nullableProxyApiType', + ), + ], + methods: [], + ), + AstProxyApi( + name: 'Api2', + constructors: [], + fields: [], + methods: [], + ), + ], + classes: [], + enums: [anEnum], + ); + final StringBuffer sink = StringBuffer(); + const DartGenerator generator = DartGenerator(); + generator.generate( + const DartOptions(), + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final String code = sink.toString(); + final String collapsedCode = _collapseNewlineAndIndentation(code); + expect(code, contains('class Api')); + expect( + collapsedCode, + contains( + r'Api.name({ super.pigeon_binaryMessenger, ' + r'super.pigeon_instanceManager, ' + r'required this.validType, ' + r'required this.enumType, ' + r'required this.proxyApiType, ' + r'this.nullableValidType, ' + r'this.nullableEnumType, ' + r'this.nullableProxyApiType, })', + ), + ); + expect( + collapsedCode, + contains( + r'__pigeon_channel.send([ ' + r'__pigeon_instanceIdentifier, ' + r'validType, enumType.index, proxyApiType, ' + r'nullableValidType, nullableEnumType?.index, nullableProxyApiType ])', + ), + ); + expect( + code, + contains(r'final int validType;'), + ); + expect( + code, + contains(r'final AnEnum enumType;'), + ); + expect( + code, + contains(r'final Api2 proxyApiType;'), + ); + expect( + code, + contains(r'final int? nullableValidType;'), + ); + expect( + code, + contains(r'final AnEnum? nullableEnumType;'), + ); + expect( + code, + contains(r'final Api2? nullableProxyApiType;'), + ); + }); + + test('attached field', () { + final AstProxyApi api2 = AstProxyApi( + name: 'Api2', + constructors: [], + fields: [], + methods: [], + ); + final Root root = Root( + apis: [ + AstProxyApi( + name: 'Api', + constructors: [], + fields: [ + ApiField( + name: 'aField', + isAttached: true, + type: TypeDeclaration( + baseName: 'Api2', + isNullable: false, + associatedProxyApi: api2, + ), + ), + ], + methods: [], + ), + api2, + ], + classes: [], + enums: [], + ); + final StringBuffer sink = StringBuffer(); + const DartGenerator generator = DartGenerator(); + generator.generate( + const DartOptions(), + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final String code = sink.toString(); + expect(code, contains('class Api')); + expect(code, contains(r'late final Api2 aField = __pigeon_aField();')); + expect(code, contains(r'Api2 __pigeon_aField()')); + }); + + test('static attached field', () { + final AstProxyApi api2 = AstProxyApi( + name: 'Api2', + constructors: [], + fields: [], + methods: [], + ); + final Root root = Root( + apis: [ + AstProxyApi( + name: 'Api', + constructors: [], + fields: [ + ApiField( + name: 'aField', + isStatic: true, + isAttached: true, + type: TypeDeclaration( + baseName: 'Api2', + isNullable: false, + associatedProxyApi: api2, + ), + ), + ], + methods: [], + ), + api2, + ], + classes: [], + enums: [], + ); + final StringBuffer sink = StringBuffer(); + const DartGenerator generator = DartGenerator(); + generator.generate( + const DartOptions(), + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final String code = sink.toString(); + expect(code, contains('class Api')); + expect( + code, contains(r'static final Api2 aField = __pigeon_aField();')); + expect(code, contains(r'static Api2 __pigeon_aField()')); + }); + }); + + group('Host methods', () { + test('multiple params method', () { + final Enum anEnum = Enum( + name: 'AnEnum', + members: [EnumMember(name: 'one')], + ); + final Root root = Root( + apis: [ + AstProxyApi( + name: 'Api', + constructors: [], + fields: [], + methods: [ + Method( + name: 'doSomething', + location: ApiLocation.host, + parameters: [ + Parameter( + type: const TypeDeclaration( + isNullable: false, + baseName: 'int', + ), + name: 'validType', + ), + Parameter( + type: TypeDeclaration( + isNullable: false, + baseName: 'AnEnum', + associatedEnum: anEnum, + ), + name: 'enumType', + ), + Parameter( + type: const TypeDeclaration( + isNullable: false, + baseName: 'Api2', + ), + name: 'proxyApiType', + ), + Parameter( + type: const TypeDeclaration( + isNullable: true, + baseName: 'int', + ), + name: 'nullableValidType', + ), + Parameter( + type: TypeDeclaration( + isNullable: true, + baseName: 'AnEnum', + associatedEnum: anEnum, + ), + name: 'nullableEnumType', + ), + Parameter( + type: const TypeDeclaration( + isNullable: true, + baseName: 'Api2', + ), + name: 'nullableProxyApiType', + ), + ], + returnType: const TypeDeclaration.voidDeclaration(), + ), + ], + ), + AstProxyApi( + name: 'Api2', + constructors: [], + fields: [], + methods: [], + ), + ], + classes: [], + enums: [anEnum], + ); + final StringBuffer sink = StringBuffer(); + const DartGenerator generator = DartGenerator(); + generator.generate( + const DartOptions(), + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final String code = sink.toString(); + final String collapsedCode = _collapseNewlineAndIndentation(code); + expect(code, contains('class Api')); + expect( + collapsedCode, + contains( + r'Future doSomething( int validType, AnEnum enumType, ' + r'Api2 proxyApiType, int? nullableValidType, ' + r'AnEnum? nullableEnumType, Api2? nullableProxyApiType, )', + ), + ); + expect( + collapsedCode, + contains( + r'await __pigeon_channel.send([ this, validType, ' + r'enumType.index, proxyApiType, nullableValidType, ' + r'nullableEnumType?.index, nullableProxyApiType ])', + ), + ); + }); + + test('static method', () { + final Root root = Root( + apis: [ + AstProxyApi( + name: 'Api', + constructors: [], + fields: [], + methods: [ + Method( + name: 'doSomething', + location: ApiLocation.host, + isStatic: true, + parameters: [], + returnType: const TypeDeclaration.voidDeclaration(), + ), + ], + ), + ], + classes: [], + enums: [], + ); + final StringBuffer sink = StringBuffer(); + const DartGenerator generator = DartGenerator(); + generator.generate( + const DartOptions(), + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final String code = sink.toString(); + final String collapsedCode = _collapseNewlineAndIndentation(code); + expect(code, contains('class Api')); + expect( + collapsedCode, + contains( + r'static Future doSomething({ BinaryMessenger? pigeon_binaryMessenger, ' + r'PigeonInstanceManager? pigeon_instanceManager, })', + ), + ); + expect( + collapsedCode, + contains(r'await __pigeon_channel.send(null)'), + ); + }); + }); + + group('Flutter methods', () { + test('multiple params flutter method', () { + final Enum anEnum = Enum( + name: 'AnEnum', + members: [EnumMember(name: 'one')], + ); + final Root root = Root(apis: [ + AstProxyApi( + name: 'Api', + constructors: [], + fields: [], + methods: [ + Method( + name: 'doSomething', + location: ApiLocation.flutter, + isRequired: false, + parameters: [ + Parameter( + type: const TypeDeclaration( + isNullable: false, + baseName: 'int', + ), + name: 'validType', + ), + Parameter( + type: TypeDeclaration( + isNullable: false, + baseName: 'AnEnum', + associatedEnum: anEnum, + ), + name: 'enumType', + ), + Parameter( + type: const TypeDeclaration( + isNullable: false, + baseName: 'Api2', + ), + name: 'proxyApiType', + ), + Parameter( + type: const TypeDeclaration( + isNullable: true, + baseName: 'int', + ), + name: 'nullableValidType', + ), + Parameter( + type: TypeDeclaration( + isNullable: true, + baseName: 'AnEnum', + associatedEnum: anEnum, + ), + name: 'nullableEnumType', + ), + Parameter( + type: const TypeDeclaration( + isNullable: true, + baseName: 'Api2', + ), + name: 'nullableProxyApiType', + ), + ], + returnType: const TypeDeclaration.voidDeclaration(), + ), + ]) + ], classes: [], enums: [ + anEnum + ]); + final StringBuffer sink = StringBuffer(); + const DartGenerator generator = DartGenerator(); + generator.generate( + const DartOptions(), + root, + sink, + dartPackageName: DEFAULT_PACKAGE_NAME, + ); + final String code = sink.toString(); + final String collapsedCode = _collapseNewlineAndIndentation(code); + expect(code, contains('class Api')); + expect( + collapsedCode, + contains( + r'final void Function( Api pigeon_instance, int validType, ' + r'AnEnum enumType, Api2 proxyApiType, int? nullableValidType, ' + r'AnEnum? nullableEnumType, Api2? nullableProxyApiType, )? ' + r'doSomething;', + ), + ); + expect( + collapsedCode, + contains( + r'void Function( Api pigeon_instance, int validType, AnEnum enumType, ' + r'Api2 proxyApiType, int? nullableValidType, ' + r'AnEnum? nullableEnumType, Api2? nullableProxyApiType, )? ' + r'doSomething'), + ); + expect( + code, + contains(r'final Api? arg_pigeon_instance = (args[0] as Api?);'), + ); + expect( + code, + contains(r'final int? arg_validType = (args[1] as int?);'), + ); + expect( + collapsedCode, + contains( + r'final AnEnum? arg_enumType = args[2] == null ? ' + r'null : AnEnum.values[args[2]! as int];', + ), + ); + expect( + code, + contains(r'final Api2? arg_proxyApiType = (args[3] as Api2?);'), + ); + expect( + code, + contains(r'final int? arg_nullableValidType = (args[4] as int?);'), + ); + expect( + collapsedCode, + contains( + r'final AnEnum? arg_nullableEnumType = args[5] == null ? ' + r'null : AnEnum.values[args[5]! as int];', + ), + ); + expect( + collapsedCode, + contains( + r'(doSomething ?? arg_pigeon_instance!.doSomething)?.call( arg_pigeon_instance!, ' + r'arg_validType!, arg_enumType!, arg_proxyApiType!, ' + r'arg_nullableValidType, arg_nullableEnumType, ' + r'arg_nullableProxyApiType);', + ), + ); + }); + }); + }); +} + +/// Replaces a new line and the indentation with a single white space +/// +/// This +/// +/// ```dart +/// void method( +/// int param1, +/// int param2, +/// ) +/// ``` +/// +/// converts to +/// +/// ```dart +/// void method( int param1, int param2, ) +/// ``` +String _collapseNewlineAndIndentation(String string) { + final StringBuffer result = StringBuffer(); + for (final String line in string.split('\n')) { + result.write('${line.trimLeft()} '); + } + return result.toString().trim(); +} diff --git a/packages/pigeon/test/generator_tools_test.dart b/packages/pigeon/test/generator_tools_test.dart index 6b43b93eecc5..e68bb96239c1 100644 --- a/packages/pigeon/test/generator_tools_test.dart +++ b/packages/pigeon/test/generator_tools_test.dart @@ -404,10 +404,8 @@ void main() { ), ); - final List apiChain = recursiveGetSuperClassApisChain(api); - expect( - apiChain, + api.allSuperClasses().toList(), containsAllInOrder([ superClassApi, superClassOfSuperClassApi, @@ -478,10 +476,8 @@ void main() { }, ); - final Set allInterfaces = recursiveFindAllInterfaceApis(api); - expect( - allInterfaces, + api.apisOfInterfaces(), containsAll([ interfaceApi, interfaceApi2, @@ -523,9 +519,6 @@ void main() { TypeDeclaration(baseName: 'A', isNullable: false, associatedProxyApi: a), }; - expect( - () => recursiveFindAllInterfaceApis(a), - throwsArgumentError, - ); + expect(() => a.apisOfInterfaces(), throwsArgumentError); }); } diff --git a/packages/pigeon/tool/shared/generation.dart b/packages/pigeon/tool/shared/generation.dart index 51f0e95e851f..1cd3b6e5ea9b 100644 --- a/packages/pigeon/tool/shared/generation.dart +++ b/packages/pigeon/tool/shared/generation.dart @@ -68,6 +68,7 @@ Future generateTestPigeons({required String baseDir}) async { 'null_fields', 'nullable_returns', 'primitive', + 'proxy_api_tests', ]; final String outputBase = p.join(baseDir, 'platform_tests', 'test_plugin'); diff --git a/script/configs/allowed_unpinned_deps.yaml b/script/configs/allowed_unpinned_deps.yaml index fe4c138b0a63..84ff94a1101b 100644 --- a/script/configs/allowed_unpinned_deps.yaml +++ b/script/configs/allowed_unpinned_deps.yaml @@ -25,9 +25,11 @@ - build_config - build_runner - build_test +- code_builder - collection - convert - crypto +- dart_style - fake_async - ffi - gcloud From bb247f71b9e3345e6dcf2a8e1ce1f127677ac241 Mon Sep 17 00:00:00 2001 From: Dawid Wenderski Date: Tue, 19 Mar 2024 20:13:07 +0100 Subject: [PATCH 093/126] [flutter_markdown] Allow for custom block element (#5815) Fixes problem with adding custom block syntax. Issue: https://github.com/flutter/flutter/issues/135848 --- packages/flutter_markdown/CHANGELOG.md | 5 ++ .../flutter_markdown/lib/src/builder.dart | 8 ++- packages/flutter_markdown/lib/src/widget.dart | 5 ++ packages/flutter_markdown/pubspec.yaml | 2 +- .../test/custom_syntax_test.dart | 49 +++++++++++++++++++ 5 files changed, 67 insertions(+), 2 deletions(-) diff --git a/packages/flutter_markdown/CHANGELOG.md b/packages/flutter_markdown/CHANGELOG.md index 3ce5c52c1a7b..f3ed8654f361 100644 --- a/packages/flutter_markdown/CHANGELOG.md +++ b/packages/flutter_markdown/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.6.22 + +* Introduces a new `MarkdownElementBuilder.isBlockElement()` method to specify if custom element + is a block. + ## 0.6.21+1 * Adds `onSelectionChanged` to the constructors of `Markdown` and `MarkdownBody`. diff --git a/packages/flutter_markdown/lib/src/builder.dart b/packages/flutter_markdown/lib/src/builder.dart index 8ecdd7b9b3fb..739520588db7 100644 --- a/packages/flutter_markdown/lib/src/builder.dart +++ b/packages/flutter_markdown/lib/src/builder.dart @@ -10,7 +10,7 @@ import '_functions_io.dart' if (dart.library.js_interop) '_functions_web.dart'; import 'style_sheet.dart'; import 'widget.dart'; -const List _kBlockTags = [ +final List _kBlockTags = [ 'p', 'h1', 'h2', @@ -190,6 +190,12 @@ class MarkdownBuilder implements md.NodeVisitor { _linkHandlers.clear(); _isInBlockquote = false; + builders.forEach((String key, MarkdownElementBuilder value) { + if (value.isBlockElement()) { + _kBlockTags.add(key); + } + }); + _blocks.add(_BlockElement(null)); for (final md.Node node in nodes) { diff --git a/packages/flutter_markdown/lib/src/widget.dart b/packages/flutter_markdown/lib/src/widget.dart index 8edc949242ef..38ffbdcc32ef 100644 --- a/packages/flutter_markdown/lib/src/widget.dart +++ b/packages/flutter_markdown/lib/src/widget.dart @@ -70,6 +70,11 @@ abstract class SyntaxHighlighter { /// An interface for an element builder. abstract class MarkdownElementBuilder { + /// For block syntax has to return true. + /// + /// By default returns false. + bool isBlockElement() => false; + /// Called when an Element has been reached, before its children have been /// visited. void visitElementBefore(md.Element element) {} diff --git a/packages/flutter_markdown/pubspec.yaml b/packages/flutter_markdown/pubspec.yaml index 20639b245fac..c077bb64f93e 100644 --- a/packages/flutter_markdown/pubspec.yaml +++ b/packages/flutter_markdown/pubspec.yaml @@ -4,7 +4,7 @@ description: A Markdown renderer for Flutter. Create rich text output, formatted with simple Markdown tags. repository: https://github.com/flutter/packages/tree/main/packages/flutter_markdown issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_markdown%22 -version: 0.6.21+1 +version: 0.6.22 environment: sdk: ^3.3.0 diff --git a/packages/flutter_markdown/test/custom_syntax_test.dart b/packages/flutter_markdown/test/custom_syntax_test.dart index 2bee4ebbf0bd..28d55cdfd0b5 100644 --- a/packages/flutter_markdown/test/custom_syntax_test.dart +++ b/packages/flutter_markdown/test/custom_syntax_test.dart @@ -35,6 +35,30 @@ void defineTests() { }, ); + testWidgets( + 'Custom block element', + (WidgetTester tester) async { + const String blockContent = 'note block'; + await tester.pumpWidget( + boilerplate( + Markdown( + data: '[!NOTE] $blockContent', + extensionSet: md.ExtensionSet.none, + blockSyntaxes: [NoteSyntax()], + builders: { + 'note': NoteBuilder(), + }, + ), + ), + ); + final ColoredBox container = + tester.widgetList(find.byType(ColoredBox)).first as ColoredBox; + expect(container.color, Colors.red); + expect(container.child, isInstanceOf()); + expect((container.child! as Text).data, blockContent); + }, + ); + testWidgets( 'link for wikistyle', (WidgetTester tester) async { @@ -331,3 +355,28 @@ class ImgBuilder extends MarkdownElementBuilder { return Text('foo', style: preferredStyle); } } + +class NoteBuilder extends MarkdownElementBuilder { + @override + Widget? visitText(md.Text text, TextStyle? preferredStyle) { + return ColoredBox( + color: Colors.red, child: Text(text.text, style: preferredStyle)); + } + + @override + bool isBlockElement() { + return true; + } +} + +class NoteSyntax extends md.BlockSyntax { + @override + md.Node? parse(md.BlockParser parser) { + final md.Line line = parser.current; + parser.advance(); + return md.Element('note', [md.Text(line.content.substring(8))]); + } + + @override + RegExp get pattern => RegExp(r'^\[!NOTE] '); +} From 23e56af4a622ded77838958ffccc89cf4aaa23b2 Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Tue, 19 Mar 2024 18:15:18 -0400 Subject: [PATCH 094/126] [camerax] Update README to encourage users to opt in (#6352) Modify README to encourage users to opt into `camera_android_camerax` to start ramping up usage before changing it to the default plugin implementation on Android. --- .../camera_android_camerax/CHANGELOG.md | 4 +++ .../camera/camera_android_camerax/README.md | 25 ++++++++----------- .../camera_android_camerax/pubspec.yaml | 2 +- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index 2fd0eb9ed3d5..2226ba4352c7 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.0+1 + +* Updates `README.md` to encourage developers to opt into this implementation of the camera plugin. + ## 0.6.0 * Implements `setFocusMode`, which makes this plugin reach feature parity with camera_android. diff --git a/packages/camera/camera_android_camerax/README.md b/packages/camera/camera_android_camerax/README.md index d60a71a46c84..64a56f1a3b54 100644 --- a/packages/camera/camera_android_camerax/README.md +++ b/packages/camera/camera_android_camerax/README.md @@ -2,26 +2,23 @@ An Android implementation of [`camera`][1] that uses the [CameraX library][2]. -*Note*: This package is under development, so please note the -[missing features and limitations](#limitations), but -otherwise feel free to try out the current implementation and provide any -feedback by filing issues under [`flutter/flutter`][5] with `[camerax]` in -the title, which will be actively triaged. +*Note*: This implementation will become the default implementation of `camera` +on Android by May 2024, so **we strongly encourage you to opt into it** +by using [the instructions](#usage) below. If any of [the limitations](#limitations) +prevent you from using `camera_android_camerax` or if you run into any problems, +please report these issues under [`flutter/flutter`][5] with `[camerax]` in +the title. ## Usage -This package is [non-endorsed][3]; the endorsed Android implementation of `camera` -is [`camera_android`][4]. To use this implementation of the plugin instead of -`camera_android`, you will need to specify it in your `pubspec.yaml` file as a -dependency in addition to `camera`: +To use this plugin instead of [`camera_android`][4], run -```yaml -dependencies: - # ...along with your other dependencies - camera: ^0.10.4 - camera_android_camerax: ^0.5.0 +```sh +$ flutter pub add camera_android_camerax ``` +from your project's root directory. + ## Limitations ### 240p resolution configuration for video recording diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index a1a44f06f366..248da8bbd97d 100644 --- a/packages/camera/camera_android_camerax/pubspec.yaml +++ b/packages/camera/camera_android_camerax/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android_camerax description: Android implementation of the camera plugin using the CameraX library. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android_camerax issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.6.0 +version: 0.6.0+1 environment: sdk: ^3.1.0 From 6975ba719d39f47b02cc5d520a699d395b5a81a7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 20 Mar 2024 11:54:16 -0400 Subject: [PATCH 095/126] Roll Flutter from d31a85ba5c5e to b96c13d1e9ff (19 revisions) (#6359) https://github.com/flutter/flutter/compare/d31a85ba5c5e...b96c13d1e9ff 2024-03-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from f42fc937028c to 883adfe2ef61 (1 revision) (flutter/flutter#145459) 2024-03-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from 32ae508316a6 to f42fc937028c (1 revision) (flutter/flutter#145456) 2024-03-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from 1e89d04c6b52 to 32ae508316a6 (2 revisions) (flutter/flutter#145449) 2024-03-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from e96b2b0047f7 to 1e89d04c6b52 (1 revision) (flutter/flutter#145446) 2024-03-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from 24b84203f6cc to e96b2b0047f7 (1 revision) (flutter/flutter#145444) 2024-03-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from 3f67bc531eef to 24b84203f6cc (1 revision) (flutter/flutter#145441) 2024-03-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from 00bfdb580b1b to 3f67bc531eef (1 revision) (flutter/flutter#145438) 2024-03-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from dce639a4c453 to 00bfdb580b1b (1 revision) (flutter/flutter#145435) 2024-03-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from bd3a9241c2c5 to dce639a4c453 (3 revisions) (flutter/flutter#145429) 2024-03-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from bacb027aa824 to bd3a9241c2c5 (1 revision) (flutter/flutter#145427) 2024-03-19 chingjun@google.com Fix remote DDS in proxied devices. (flutter/flutter#145346) 2024-03-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from 7095e6a4a643 to bacb027aa824 (2 revisions) (flutter/flutter#145424) 2024-03-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from 59138d5f8e88 to 7095e6a4a643 (1 revision) (flutter/flutter#145409) 2024-03-19 jhy03261997@gmail.com Add a `minTileHeight` to ListTile widget so its height can be customized to less than the default height. (flutter/flutter#145244) 2024-03-19 58190796+MitchellGoodwin@users.noreply.github.com Widget state properties (flutter/flutter#142151) 2024-03-19 164015983+goodmost@users.noreply.github.com chore: fix some comments (flutter/flutter#145397) 2024-03-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from 7ea6b003ffeb to 59138d5f8e88 (1 revision) (flutter/flutter#145404) 2024-03-19 engine-flutter-autoroll@skia.org Roll Packages from a757073ac4ea to a2f4ce0a5057 (3 revisions) (flutter/flutter#145399) 2024-03-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from d8fbcfbd799c to 7ea6b003ffeb (2 revisions) (flutter/flutter#145398) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC camillesimon@google.com,rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 174e1417a8da..da89d6b1d41f 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -d31a85ba5c5e67199a9a3c95fd9fef51c89a9306 +b96c13d1e9ff3e8ebb9f3647b43c8d51c221e82e From 8df9848e9f22d601e47d6301908ef92112aeb923 Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Wed, 20 Mar 2024 13:50:01 -0400 Subject: [PATCH 096/126] [camera_android] Update README to encourage users to use CameraX implementation (#6353) Modify README to encourage users to opt into camera_android_camerax to start ramping up usage before changing it to the default plugin implementation on Android. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [relevant style guides] and ran the auto-formatter. (Unlike the flutter/flutter repo, the flutter/packages repo does use `dart format`.) - [x] I signed the [CLA]. - [x] The title of the PR starts with the name of the package surrounded by square brackets, e.g. `[shared_preferences]` - [ ] I [linked to at least one issue that this PR fixes] in the description above. - [x] I updated `pubspec.yaml` with an appropriate new version according to the [pub versioning philosophy], or this PR is [exempt from version changes]. - [x] I updated `CHANGELOG.md` to add a description of the change, [following repository CHANGELOG style]. - [ ] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] All existing and new tests are passing. [Contributor Guide]: https://github.com/flutter/packages/blob/main/CONTRIBUTING.md [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [relevant style guides]: https://github.com/flutter/packages/blob/main/CONTRIBUTING.md#style [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat [linked to at least one issue that this PR fixes]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [pub versioning philosophy]: https://dart.dev/tools/pub/versioning [exempt from version changes]: https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#version-and-changelog-updates [following repository CHANGELOG style]: https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changelog-style [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests --- packages/camera/camera_android/CHANGELOG.md | 3 ++- packages/camera/camera_android/README.md | 9 +++++++++ packages/camera/camera_android/pubspec.yaml | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index d7bbc2bdecd5..37790bd7f5b1 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,7 +1,8 @@ -## NEXT +## 0.10.8+17 * Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. * Updates compileSdk version to 34. +* Updates `README.md` to encourage developers to opt into `camera_android_camerax`. ## 0.10.8+16 diff --git a/packages/camera/camera_android/README.md b/packages/camera/camera_android/README.md index 509d5b880226..31f2d66ae4e4 100644 --- a/packages/camera/camera_android/README.md +++ b/packages/camera/camera_android/README.md @@ -2,6 +2,12 @@ The Android implementation of [`camera`][1]. +*Note*: [`camera_android_camerax`][3] will become the default implementation of +`camera` on Android by May 2024, so **we strongly encourage you to opt into it** +by using [these instructions][4]. If any [limitations][5] of `camera_android_camerax` +prevent you from using it or if you run into any problems, please report these +issues under [`flutter/flutter`][5] with `[camerax]` in the title. + ## Usage This package is [endorsed][2], which means you can simply use `camera` @@ -13,3 +19,6 @@ should add it to your `pubspec.yaml` as usual. [1]: https://pub.dev/packages/camera [2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin +[3]: https://pub.dev/packages/camera_android_camerax +[4]: https://pub.dev/packages/camera_android_camerax#usage +[5]: https://pub.dev/packages/camera_android_camerax#limitations diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 48a04d46ef30..fadf4b6bcc69 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -3,7 +3,7 @@ description: Android implementation of the camera plugin. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.10.8+16 +version: 0.10.8+17 environment: sdk: ^3.1.0 From b7fbe68d4ec2105eb7b8e3a68ce3dfbabc5a72a6 Mon Sep 17 00:00:00 2001 From: Aliasgar Vohra <38110731+aliasgar4558@users.noreply.github.com> Date: Thu, 21 Mar 2024 22:18:23 +0530 Subject: [PATCH 097/126] =?UTF-8?q?[adaptive=5Fscaffold]=20:=20?= =?UTF-8?q?=F0=9F=90=9B=20#141938=20-=20Drawer=20stays=20open=20even=20on?= =?UTF-8?q?=20destination=20tap.=20(#6289)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (#141938) *Changes included in PR are listed as follows* - As per material guidelines, Drawer shall be dismissed when user taps any destination/item. If drawer is open, and user taps on any item, before calling onDestinationSelected() - we are now dismissing drawer. - CHANGELOG.md file updated. - Updated to v0.1.9. *Issue : https://github.com/flutter/flutter/issues/141938* --- packages/flutter_adaptive_scaffold/AUTHORS | 3 +- .../flutter_adaptive_scaffold/CHANGELOG.md | 5 +- .../lib/src/adaptive_scaffold.dart | 20 +++- .../flutter_adaptive_scaffold/pubspec.yaml | 2 +- .../test/adaptive_scaffold_test.dart | 113 ++++++++++++++++++ 5 files changed, 138 insertions(+), 5 deletions(-) diff --git a/packages/flutter_adaptive_scaffold/AUTHORS b/packages/flutter_adaptive_scaffold/AUTHORS index 0bbdf210f3c2..f53e3e1620fb 100644 --- a/packages/flutter_adaptive_scaffold/AUTHORS +++ b/packages/flutter_adaptive_scaffold/AUTHORS @@ -4,4 +4,5 @@ # Name/Organization Google Inc. -Jason C.H \ No newline at end of file +Jason C.H +Aliasgar Vohra \ No newline at end of file diff --git a/packages/flutter_adaptive_scaffold/CHANGELOG.md b/packages/flutter_adaptive_scaffold/CHANGELOG.md index 623da45aaf86..8873f7e26452 100644 --- a/packages/flutter_adaptive_scaffold/CHANGELOG.md +++ b/packages/flutter_adaptive_scaffold/CHANGELOG.md @@ -1,10 +1,11 @@ -## NEXT +## 0.1.9 +* FIX : Drawer stays open even on destination tap - [flutter/flutter#141938](https://github.com/flutter/flutter/issues/141938) * Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. ## 0.1.8 -* Adds `transitionDuration` parameter for specifying how long the animation should be. +* Adds `transitionDuration` parameter for specifying how long the animation should be. ## 0.1.7+2 diff --git a/packages/flutter_adaptive_scaffold/lib/src/adaptive_scaffold.dart b/packages/flutter_adaptive_scaffold/lib/src/adaptive_scaffold.dart index d134f73cb79f..531a088b7943 100644 --- a/packages/flutter_adaptive_scaffold/lib/src/adaptive_scaffold.dart +++ b/packages/flutter_adaptive_scaffold/lib/src/adaptive_scaffold.dart @@ -502,12 +502,16 @@ class AdaptiveScaffold extends StatefulWidget { } class _AdaptiveScaffoldState extends State { + // Global scaffold key that will help to manage drawer state. + final GlobalKey _scaffoldKey = GlobalKey(); + @override Widget build(BuildContext context) { final NavigationRailThemeData navRailTheme = Theme.of(context).navigationRailTheme; return Scaffold( + key: _scaffoldKey, appBar: widget.drawerBreakpoint.isActive(context) && widget.useDrawer || (widget.appBarBreakpoint?.isActive(context) ?? false) ? widget.appBar ?? AppBar() @@ -523,7 +527,7 @@ class _AdaptiveScaffoldState extends State { .map((NavigationDestination destination) => AdaptiveScaffold.toRailDestination(destination)) .toList(), - onDestinationSelected: widget.onSelectedIndexChange, + onDestinationSelected: _onDrawerDestinationSelected, ), ) : null, @@ -670,6 +674,20 @@ class _AdaptiveScaffoldState extends State { ), ); } + + void _onDrawerDestinationSelected(int index) { + if (widget.useDrawer) { + // If [useDrawer] is true, then retrieve the current state. + final ScaffoldState? scaffoldCurrentContext = _scaffoldKey.currentState; + if (scaffoldCurrentContext != null) { + if (scaffoldCurrentContext.isDrawerOpen) { + // If drawer is open, call [closeDrawer] to dismiss drawer as per material guidelines. + scaffoldCurrentContext.closeDrawer(); + } + } + } + widget.onSelectedIndexChange?.call(index); + } } class _BrickLayout extends StatelessWidget { diff --git a/packages/flutter_adaptive_scaffold/pubspec.yaml b/packages/flutter_adaptive_scaffold/pubspec.yaml index 6ebf6d894f92..a494b4fc6111 100644 --- a/packages/flutter_adaptive_scaffold/pubspec.yaml +++ b/packages/flutter_adaptive_scaffold/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_adaptive_scaffold description: Widgets to easily build adaptive layouts, including navigation elements. -version: 0.1.8 +version: 0.1.9 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_adaptive_scaffold%22 repository: https://github.com/flutter/packages/tree/main/packages/flutter_adaptive_scaffold diff --git a/packages/flutter_adaptive_scaffold/test/adaptive_scaffold_test.dart b/packages/flutter_adaptive_scaffold/test/adaptive_scaffold_test.dart index 3761fbfe29a0..808b03ea1389 100644 --- a/packages/flutter_adaptive_scaffold/test/adaptive_scaffold_test.dart +++ b/packages/flutter_adaptive_scaffold/test/adaptive_scaffold_test.dart @@ -501,6 +501,119 @@ void main() { }, ); + testWidgets( + 'when drawer item tap, it shall close the already open drawer', + (WidgetTester tester) async { + //region Keys + const ValueKey firstDestinationIconKey = ValueKey( + 'first-normal-icon', + ); + const ValueKey firstDestinationSelectedIconKey = ValueKey( + 'first-selected-icon', + ); + const ValueKey lastDestinationIconKey = ValueKey( + 'last-normal-icon', + ); + const ValueKey lastDestinationSelectedIconKey = ValueKey( + 'last-selected-icon', + ); + //endregion + + //region Finder for destinations as per its icon. + final Finder lastDestinationWithIcon = find.byKey( + lastDestinationIconKey, + ); + final Finder lastDestinationWithSelectedIcon = find.byKey( + lastDestinationSelectedIconKey, + ); + //endregion + + const List destinations = [ + NavigationDestination( + icon: Icon( + Icons.inbox_outlined, + key: firstDestinationIconKey, + ), + selectedIcon: Icon( + Icons.inbox, + key: firstDestinationSelectedIconKey, + ), + label: 'Inbox', + ), + NavigationDestination( + icon: Icon( + Icons.video_call_outlined, + key: lastDestinationIconKey, + ), + selectedIcon: Icon( + Icons.video_call, + key: lastDestinationSelectedIconKey, + ), + label: 'Video', + ), + ]; + int selectedDestination = 0; + + await tester.pumpWidget( + MaterialApp( + home: MediaQuery( + data: const MediaQueryData(size: Size(450, 900)), + child: StatefulBuilder( + builder: ( + BuildContext context, + void Function(void Function()) setState, + ) { + return AdaptiveScaffold( + destinations: destinations, + selectedIndex: selectedDestination, + smallBreakpoint: TestBreakpoint400(), + drawerBreakpoint: TestBreakpoint400(), + onSelectedIndexChange: (int value) { + setState(() { + selectedDestination = value; + }); + }, + ); + }, + ), + ), + ), + ); + + expect(selectedDestination, 0); + Finder fDrawer = find.byType(Drawer); + Finder fNavigationRail = find.descendant( + of: fDrawer, + matching: find.byType(NavigationRail), + ); + expect(fDrawer, findsNothing); + expect(fNavigationRail, findsNothing); + + final ScaffoldState state = tester.state(find.byType(Scaffold)); + state.openDrawer(); + await tester.pumpAndSettle(Durations.short4); + expect(state.isDrawerOpen, isTrue); + + // Need to find again as Scaffold's state has been updated + fDrawer = find.byType(Drawer); + fNavigationRail = find.descendant( + of: fDrawer, + matching: find.byType(NavigationRail), + ); + expect(fDrawer, findsOneWidget); + expect(fNavigationRail, findsOneWidget); + + expect(lastDestinationWithIcon, findsOneWidget); + expect(lastDestinationWithSelectedIcon, findsNothing); + + await tester.ensureVisible(lastDestinationWithIcon); + await tester.tap(lastDestinationWithIcon); + await tester.pumpAndSettle(Durations.short4); + expect(selectedDestination, 1); + expect(state.isDrawerOpen, isFalse); + }, + ); + // This test checks whether AdaptiveScaffold.standardNavigationRail function // creates a NavigationRail widget as expected with groupAlignment provided, // and checks whether the NavigationRail's groupAlignment matches the expected value. From 7cc7e315e76e39b72cd95b2fc01fbe4327aa71a8 Mon Sep 17 00:00:00 2001 From: Aliasgar Vohra <38110731+aliasgar4558@users.noreply.github.com> Date: Thu, 21 Mar 2024 22:42:08 +0530 Subject: [PATCH 098/126] =?UTF-8?q?[adaptive=5Fscaffold]=20:=20?= =?UTF-8?q?=F0=9F=90=9B=20:=20#110902=20:=20Assertion=20added=20when=20try?= =?UTF-8?q?=20with=20less=20that=202=20destinations.=20(#6360)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit *Issue : https://github.com/flutter/flutter/issues/110902* Changes included in PR are listed as follows - As `NavigationRail` & `NavigationBar` etc has assertion which won't allow less than 2 destinations, Similar assertion added in "AdaptiveScaffold" to acknowledge users regarding the same with a user friendly message. - CHANGELOG.md file updated. - Updated to v0.1.10 --- .../flutter_adaptive_scaffold/CHANGELOG.md | 4 +++ .../lib/src/adaptive_scaffold.dart | 5 ++- .../flutter_adaptive_scaffold/pubspec.yaml | 2 +- .../test/adaptive_scaffold_test.dart | 31 +++++++++++++++++-- 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/packages/flutter_adaptive_scaffold/CHANGELOG.md b/packages/flutter_adaptive_scaffold/CHANGELOG.md index 8873f7e26452..31547d9a62a4 100644 --- a/packages/flutter_adaptive_scaffold/CHANGELOG.md +++ b/packages/flutter_adaptive_scaffold/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.10 + +* FIX : Assertion added when tried with less than 2 destinations - [flutter/flutter#110902](https://github.com/flutter/flutter/issues/110902) + ## 0.1.9 * FIX : Drawer stays open even on destination tap - [flutter/flutter#141938](https://github.com/flutter/flutter/issues/141938) diff --git a/packages/flutter_adaptive_scaffold/lib/src/adaptive_scaffold.dart b/packages/flutter_adaptive_scaffold/lib/src/adaptive_scaffold.dart index 531a088b7943..663817a7ec16 100644 --- a/packages/flutter_adaptive_scaffold/lib/src/adaptive_scaffold.dart +++ b/packages/flutter_adaptive_scaffold/lib/src/adaptive_scaffold.dart @@ -103,7 +103,10 @@ class AdaptiveScaffold extends StatefulWidget { this.navigationRailWidth = 72, this.extendedNavigationRailWidth = 192, this.appBarBreakpoint, - }); + }) : assert( + destinations.length >= 2, + 'At least two destinations are required', + ); /// The destinations to be used in navigation items. These are converted to /// [NavigationRailDestination]s and [BottomNavigationBarItem]s and inserted diff --git a/packages/flutter_adaptive_scaffold/pubspec.yaml b/packages/flutter_adaptive_scaffold/pubspec.yaml index a494b4fc6111..73b8537e7603 100644 --- a/packages/flutter_adaptive_scaffold/pubspec.yaml +++ b/packages/flutter_adaptive_scaffold/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_adaptive_scaffold description: Widgets to easily build adaptive layouts, including navigation elements. -version: 0.1.9 +version: 0.1.10 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_adaptive_scaffold%22 repository: https://github.com/flutter/packages/tree/main/packages/flutter_adaptive_scaffold diff --git a/packages/flutter_adaptive_scaffold/test/adaptive_scaffold_test.dart b/packages/flutter_adaptive_scaffold/test/adaptive_scaffold_test.dart index 808b03ea1389..8dcaf2d5a0a4 100644 --- a/packages/flutter_adaptive_scaffold/test/adaptive_scaffold_test.dart +++ b/packages/flutter_adaptive_scaffold/test/adaptive_scaffold_test.dart @@ -257,9 +257,9 @@ void main() { ]; await tester.pumpWidget( - const MaterialApp( + MaterialApp( home: MediaQuery( - data: MediaQueryData(size: Size(700, 900)), + data: const MediaQueryData(size: Size(700, 900)), child: AdaptiveScaffold( destinations: destinations, ), @@ -717,6 +717,33 @@ void main() { expect(appBar, findsOneWidget); }, ); + + testWidgets( + 'When only one destination passed, shall throw assertion error', + (WidgetTester tester) async { + const List destinations = [ + NavigationDestination( + icon: Icon(Icons.inbox_outlined), + selectedIcon: Icon(Icons.inbox), + label: 'Inbox', + ), + ]; + + expect( + () => tester.pumpWidget( + MaterialApp( + home: MediaQuery( + data: const MediaQueryData(size: Size(700, 900)), + child: AdaptiveScaffold( + destinations: destinations, + ), + ), + ), + ), + throwsA(isA()), + ); + }, + ); } /// An empty widget that implements [PreferredSizeWidget] to ensure that From 352ddac5c4ae5dbc78d92477fc8a7632a0c02615 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 21 Mar 2024 14:06:49 -0400 Subject: [PATCH 099/126] [flutter_adaptive_scaffold] Remove broken link from README (#6364) The document this linked to is no longer accessible (possibly overrun with spam?), and we don't have a way to recover it. Given that, remove the link. Fixes https://github.com/flutter/flutter/issues/145292 --- packages/flutter_adaptive_scaffold/CHANGELOG.md | 4 ++++ packages/flutter_adaptive_scaffold/README.md | 5 ----- packages/flutter_adaptive_scaffold/pubspec.yaml | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/flutter_adaptive_scaffold/CHANGELOG.md b/packages/flutter_adaptive_scaffold/CHANGELOG.md index 31547d9a62a4..065983360e9d 100644 --- a/packages/flutter_adaptive_scaffold/CHANGELOG.md +++ b/packages/flutter_adaptive_scaffold/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.10+1 + +* Removes a broken design document link from the README. + ## 0.1.10 * FIX : Assertion added when tried with less than 2 destinations - [flutter/flutter#110902](https://github.com/flutter/flutter/issues/110902) diff --git a/packages/flutter_adaptive_scaffold/README.md b/packages/flutter_adaptive_scaffold/README.md index 2f3e186c0e0f..3dda09753536 100644 --- a/packages/flutter_adaptive_scaffold/README.md +++ b/packages/flutter_adaptive_scaffold/README.md @@ -247,8 +247,3 @@ return AdaptiveLayout( Both of the examples shown here produce the same output: !["Example of a display made with AdaptiveScaffold"](example/demo_files/adaptiveScaffold.gif) - -## Additional information - -You can find more information on this package and its usage in the public -[design doc](https://docs.google.com/document/d/1qhrpTWYs5f67X8v32NCCNTRMIjSrVHuaMEFAul-Q_Ms/edit?usp=sharing). diff --git a/packages/flutter_adaptive_scaffold/pubspec.yaml b/packages/flutter_adaptive_scaffold/pubspec.yaml index 73b8537e7603..cd322d41d2a3 100644 --- a/packages/flutter_adaptive_scaffold/pubspec.yaml +++ b/packages/flutter_adaptive_scaffold/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_adaptive_scaffold description: Widgets to easily build adaptive layouts, including navigation elements. -version: 0.1.10 +version: 0.1.10+1 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_adaptive_scaffold%22 repository: https://github.com/flutter/packages/tree/main/packages/flutter_adaptive_scaffold From 04b8ecc58a69d869217f9882be3aface32338ef1 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 21 Mar 2024 14:28:19 -0400 Subject: [PATCH 100/126] Roll Flutter (stable) from ba3931984302 to 68bfaea22488 (2 revisions) (#6368) https://github.com/flutter/flutter/compare/ba3931984302...68bfaea22488 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-stable-packages Please CC camillesimon@google.com,rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter (stable): https://github.com/flutter/flutter/issues/new/choose To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_stable.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_stable.version b/.ci/flutter_stable.version index 7f39f1a61c63..57c34a63ace5 100644 --- a/.ci/flutter_stable.version +++ b/.ci/flutter_stable.version @@ -1 +1 @@ -ba393198430278b6595976de84fe170f553cc728 +68bfaea224880b488c617afe30ab12091ea8fa4e From 611aea1657fbfc0d2564a14b08e12dffc70189bb Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 21 Mar 2024 15:46:30 -0400 Subject: [PATCH 101/126] Roll Flutter from b96c13d1e9ff to 18340ea16cee (26 revisions) (#6370) https://github.com/flutter/flutter/compare/b96c13d1e9ff...18340ea16cee 2024-03-21 engine-flutter-autoroll@skia.org Roll Flutter Engine from 7cdb240c4a16 to bad4a30e1c75 (2 revisions) (flutter/flutter#145551) 2024-03-21 engine-flutter-autoroll@skia.org Roll Packages from 23e56af4a622 to b7fbe68d4ec2 (3 revisions) (flutter/flutter#145547) 2024-03-21 pascal@welsch.dev Add WidgetsApp.debugShowWidgetInspectorOverride again (deprecated) (flutter/flutter#145334) 2024-03-21 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Roll pub packages (#145509)" (flutter/flutter#145550) 2024-03-21 137456488+flutter-pub-roller-bot@users.noreply.github.com Roll pub packages (flutter/flutter#145509) 2024-03-21 engine-flutter-autoroll@skia.org Roll Flutter Engine from 14b67475cf80 to 7cdb240c4a16 (1 revision) (flutter/flutter#145533) 2024-03-21 engine-flutter-autoroll@skia.org Roll Flutter Engine from 6de3d9b6196a to 14b67475cf80 (1 revision) (flutter/flutter#145529) 2024-03-21 engine-flutter-autoroll@skia.org Roll Flutter Engine from c0d3ac3178fa to 6de3d9b6196a (1 revision) (flutter/flutter#145520) 2024-03-21 engine-flutter-autoroll@skia.org Roll Flutter Engine from 23dc0cacc4db to c0d3ac3178fa (1 revision) (flutter/flutter#145519) 2024-03-21 engine-flutter-autoroll@skia.org Roll Flutter Engine from a73e01364de0 to 23dc0cacc4db (2 revisions) (flutter/flutter#145517) 2024-03-21 engine-flutter-autoroll@skia.org Roll Flutter Engine from 45ed36c17bb7 to a73e01364de0 (1 revision) (flutter/flutter#145516) 2024-03-21 engine-flutter-autoroll@skia.org Roll Flutter Engine from 34b304a27f73 to 45ed36c17bb7 (1 revision) (flutter/flutter#145513) 2024-03-21 engine-flutter-autoroll@skia.org Roll Flutter Engine from 912c61f30512 to 34b304a27f73 (1 revision) (flutter/flutter#145511) 2024-03-21 engine-flutter-autoroll@skia.org Roll Flutter Engine from 98cfd9213332 to 912c61f30512 (1 revision) (flutter/flutter#145504) 2024-03-21 137456488+flutter-pub-roller-bot@users.noreply.github.com Roll pub packages (flutter/flutter#145476) 2024-03-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from fe6927c79dc3 to 98cfd9213332 (1 revision) (flutter/flutter#145498) 2024-03-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from d1fe8994dedf to fe6927c79dc3 (1 revision) (flutter/flutter#145493) 2024-03-20 bhesaniyavatsal@gmail.com Add helper widget parameter to InputDecoration (flutter/flutter#145157) 2024-03-20 34871572+gmackall@users.noreply.github.com Remove embedding v1 code in framework (flutter/flutter#144726) 2024-03-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from 1b52f57ca07c to d1fe8994dedf (8 revisions) (flutter/flutter#145491) 2024-03-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from f803f2adec54 to 1b52f57ca07c (1 revision) (flutter/flutter#145479) 2024-03-20 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Reland #128236 "Improve build output for all platforms" (#145376)" (flutter/flutter#145487) 2024-03-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from fc2b7a9076b4 to f803f2adec54 (1 revision) (flutter/flutter#145475) 2024-03-20 6655696+guidezpl@users.noreply.github.com Reland #128236 "Improve build output for all platforms" (flutter/flutter#145376) 2024-03-20 engine-flutter-autoroll@skia.org Roll Packages from a2f4ce0a5057 to 23e56af4a622 (5 revisions) (flutter/flutter#145470) 2024-03-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from 883adfe2ef61 to fc2b7a9076b4 (1 revision) (flutter/flutter#145469) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC camillesimon@google.com,rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index da89d6b1d41f..ed7d95a846f7 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b96c13d1e9ff3e8ebb9f3647b43c8d51c221e82e +18340ea16cee656f14daa2aa686f1e05be8f7daf From 28d126c54c63eb79980e52a0b955cf184f2eb86d Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 25 Mar 2024 10:03:13 -0400 Subject: [PATCH 102/126] Roll Flutter from 18340ea16cee to 14774b95c250 (20 revisions) (#6376) https://github.com/flutter/flutter/compare/18340ea16cee...14774b95c250 2024-03-22 engine-flutter-autoroll@skia.org Roll Flutter Engine from eba6e31498b8 to 09dadce76828 (1 revision) (flutter/flutter#145603) 2024-03-22 engine-flutter-autoroll@skia.org Roll Flutter Engine from f9a34ae0b14f to eba6e31498b8 (1 revision) (flutter/flutter#145598) 2024-03-22 nate.w5687@gmail.com Intensive `if` chain refactoring (flutter/flutter#145194) 2024-03-22 leroux_bruno@yahoo.fr Adds numpad navigation shortcuts for Linux (flutter/flutter#145464) 2024-03-22 engine-flutter-autoroll@skia.org Roll Flutter Engine from 5a12de1beab7 to f9a34ae0b14f (1 revision) (flutter/flutter#145581) 2024-03-22 engine-flutter-autoroll@skia.org Roll Flutter Engine from e2f324beac3b to 5a12de1beab7 (1 revision) (flutter/flutter#145578) 2024-03-22 31859944+LongCatIsLooong@users.noreply.github.com Replace `RenderBox.compute*` with `RenderBox.get*` and add `@visibleForOverriding` (flutter/flutter#145503) 2024-03-22 gspencergoog@users.noreply.github.com Add some cross references in the docs, move an example to a dartpad example (flutter/flutter#145571) 2024-03-22 bernaferrari2@gmail.com Fix `BorderSide.none` requiring explicit transparent color for `UnderlineInputBorder` (flutter/flutter#145329) 2024-03-22 engine-flutter-autoroll@skia.org Roll Flutter Engine from a46a7b273a5b to e2f324beac3b (1 revision) (flutter/flutter#145576) 2024-03-21 goderbauer@google.com Fix nullability of getFullHeightForCaret (flutter/flutter#145554) 2024-03-21 34871572+gmackall@users.noreply.github.com Add a `--no-gradle-generation` mode to the `generate_gradle_lockfiles.dart` script (flutter/flutter#145568) 2024-03-21 engine-flutter-autoroll@skia.org Roll Flutter Engine from 1b842ae58b3d to a46a7b273a5b (2 revisions) (flutter/flutter#145569) 2024-03-21 chingjun@google.com Fixed race condition in PollingDeviceDiscovery. (flutter/flutter#145506) 2024-03-21 engine-flutter-autoroll@skia.org Roll Flutter Engine from a2ed373fa70f to 1b842ae58b3d (1 revision) (flutter/flutter#145565) 2024-03-21 ian@hixie.ch Clarify AutomaticKeepAliveClientMixin semantics in build method (flutter/flutter#145297) 2024-03-21 goderbauer@google.com Eliminate more window singleton usages (flutter/flutter#145560) 2024-03-21 jacksongardner@google.com `flutter test --wasm` support (flutter/flutter#145347) 2024-03-21 engine-flutter-autoroll@skia.org Roll Flutter Engine from eb262e9c34db to a2ed373fa70f (2 revisions) (flutter/flutter#145556) 2024-03-21 engine-flutter-autoroll@skia.org Roll Flutter Engine from bad4a30e1c75 to eb262e9c34db (1 revision) (flutter/flutter#145555) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC camillesimon@google.com,rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index ed7d95a846f7..c43d284b50cf 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -18340ea16cee656f14daa2aa686f1e05be8f7daf +14774b95c250d59b1a1bbfa7b3da63d24b59e008 From ab1630b9b9bd1130b4d5d1ac18a588b690fd0fa3 Mon Sep 17 00:00:00 2001 From: Tarrin Neal Date: Mon, 25 Mar 2024 23:33:05 -0700 Subject: [PATCH 103/126] [pigeon] Adds @SwiftClass annotation (#6372) Adds annotation for @SwiftClass which will cause the swift generator to create a `class` instead of a `struct` in the generated Swift code. This will allow for recursive classes as well as allow for Objc interop, without forcing users to lose the potential benefits of structs if they prefer them instead. Also creates recursive data class integration test to check for coverage on all languages. fixes https://github.com/flutter/flutter/issues/145175 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [relevant style guides] and ran the auto-formatter. (Unlike the flutter/flutter repo, the flutter/packages repo does use `dart format`.) - [x] I signed the [CLA]. - [x] The title of the PR starts with the name of the package surrounded by square brackets, e.g. `[shared_preferences]` - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated `pubspec.yaml` with an appropriate new version according to the [pub versioning philosophy], or this PR is [exempt from version changes]. - [x] I updated `CHANGELOG.md` to add a description of the change, [following repository CHANGELOG style]. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [Contributor Guide]: https://github.com/flutter/packages/blob/main/CONTRIBUTING.md [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [relevant style guides]: https://github.com/flutter/packages/blob/main/CONTRIBUTING.md#style [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat [pub versioning philosophy]: https://dart.dev/tools/pub/versioning [exempt from version changes]: https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#version-and-changelog-updates [following repository CHANGELOG style]: https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changelog-style [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests --------- Co-authored-by: Stuart Morgan --- packages/pigeon/CHANGELOG.md | 5 + packages/pigeon/README.md | 5 + packages/pigeon/lib/ast.dart | 7 + packages/pigeon/lib/cpp_generator.dart | 144 ++- packages/pigeon/lib/generator_tools.dart | 2 +- packages/pigeon/lib/pigeon_lib.dart | 7 + packages/pigeon/lib/swift_generator.dart | 62 +- packages/pigeon/pigeons/core_tests.dart | 102 +- .../AlternateLanguageTestPlugin.java | 45 + .../CoreTests.java | 736 ++++++++++++- .../ios/Classes/AlternateLanguageTestPlugin.m | 68 +- .../ios/Classes/CoreTests.gen.h | 96 ++ .../ios/Classes/CoreTests.gen.m | 336 +++++- .../Classes/AlternateLanguageTestPlugin.m | 66 +- .../macos/Classes/CoreTests.gen.h | 95 ++ .../macos/Classes/CoreTests.gen.m | 333 +++++- .../lib/integration_tests.dart | 305 +++++- .../lib/src/generated/core_tests.gen.dart | 355 ++++++- .../com/example/test_plugin/CoreTests.gen.kt | 339 +++++- .../com/example/test_plugin/TestPlugin.kt | 52 +- .../example/test_plugin/AllDatatypesTest.kt | 1 + .../ios/Classes/CoreTests.gen.swift | 369 ++++++- .../test_plugin/ios/Classes/TestPlugin.swift | 53 + .../macos/Classes/CoreTests.gen.swift | 369 ++++++- .../macos/Classes/TestPlugin.swift | 76 +- .../windows/pigeon/core_tests.gen.cpp | 999 +++++++++++++++++- .../windows/pigeon/core_tests.gen.h | 197 +++- .../test_plugin/windows/test_plugin.cpp | 63 ++ .../test_plugin/windows/test_plugin.h | 32 + packages/pigeon/pubspec.yaml | 2 +- packages/pigeon/test/cpp_generator_test.dart | 28 +- 31 files changed, 5190 insertions(+), 159 deletions(-) diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md index ea6cd9df5b55..be39b24414c6 100644 --- a/packages/pigeon/CHANGELOG.md +++ b/packages/pigeon/CHANGELOG.md @@ -1,3 +1,8 @@ +## 17.3.0 + +* [swift] Adds `@SwiftClass` annotation to allow choice between `struct` and `class` for data classes. +* [cpp] Adds support for recursive data class definitions. + ## 17.2.0 * [dart] Adds implementation for `@ProxyApi`. diff --git a/packages/pigeon/README.md b/packages/pigeon/README.md index 213e650c6324..8e1edaa30df2 100644 --- a/packages/pigeon/README.md +++ b/packages/pigeon/README.md @@ -27,6 +27,11 @@ Custom classes, nested datatypes, and enums are also supported. Nullable enums in Objective-C generated code will be wrapped in a class to allow for nullability. +By default, custom classes in Swift are defined as structs. +Structs don't support some features - recursive data, or Objective-C interop. +Use the @SwiftClass annotation when defining the class to generate the data +as a Swift class instead. + ### Synchronous and Asynchronous methods While all calls across platform channel APIs (such as pigeon methods) are asynchronous, diff --git a/packages/pigeon/lib/ast.dart b/packages/pigeon/lib/ast.dart index bc4ec8c44777..08eef8cc6c7c 100644 --- a/packages/pigeon/lib/ast.dart +++ b/packages/pigeon/lib/ast.dart @@ -639,6 +639,7 @@ class Class extends Node { Class({ required this.name, required this.fields, + this.isSwiftClass = false, this.documentationComments = const [], }); @@ -648,6 +649,12 @@ class Class extends Node { /// All the fields contained in the class. List fields; + /// Determines whether the defined class should be represented as a struct or + /// a class in Swift generation. + /// + /// Defaults to false, which would represent a struct. + bool isSwiftClass; + /// List of documentation comments, separated by line. /// /// Lines should not include the comment marker itself, but should include any diff --git a/packages/pigeon/lib/cpp_generator.dart b/packages/pigeon/lib/cpp_generator.dart index c5e3a1c053af..5e993137b9f4 100644 --- a/packages/pigeon/lib/cpp_generator.dart +++ b/packages/pigeon/lib/cpp_generator.dart @@ -255,6 +255,35 @@ class CppHeaderGenerator extends StructuredGenerator { _writeClassConstructor(root, indent, classDefinition, orderedFields, 'Constructs an object setting all fields.'); + // If any fields are pointer type, then the class requires a custom + // copy constructor, so declare the rule-of-five group of functions. + if (orderedFields.any((NamedType field) => _isPointerField( + getFieldHostDatatype(field, _baseCppTypeForBuiltinDartType)))) { + final String className = classDefinition.name; + // Add the default destructor, since unique_ptr destroys itself. + _writeFunctionDeclaration(indent, '~$className', defaultImpl: true); + // Declare custom copy/assign to deep-copy the pointer. + _writeFunctionDeclaration(indent, className, + isConstructor: true, + isCopy: true, + parameters: ['const $className& other']); + _writeFunctionDeclaration(indent, 'operator=', + returnType: '$className&', + parameters: ['const $className& other']); + // Re-add the default move operations, since they work fine with + // unique_ptr. + _writeFunctionDeclaration(indent, className, + isConstructor: true, + isCopy: true, + parameters: ['$className&& other'], + defaultImpl: true); + _writeFunctionDeclaration(indent, 'operator=', + returnType: '$className&', + parameters: ['$className&& other'], + defaultImpl: true, + noexcept: true); + } + for (final NamedType field in orderedFields) { addDocumentationComments( indent, field.documentationComments, _docCommentSpec); @@ -313,7 +342,7 @@ class CppHeaderGenerator extends StructuredGenerator { final HostDatatype hostDatatype = getFieldHostDatatype(field, _baseCppTypeForBuiltinDartType); indent.writeln( - '${_valueType(hostDatatype)} ${_makeInstanceVariableName(field)};'); + '${_fieldType(hostDatatype)} ${_makeInstanceVariableName(field)};'); } }); }, nestCount: 0); @@ -693,6 +722,13 @@ class CppSourceGenerator extends StructuredGenerator { // All-field constructor. _writeClassConstructor(root, indent, classDefinition, orderedFields); + // Custom copy/assign to handle pointer fields, if necessary. + if (orderedFields.any((NamedType field) => _isPointerField( + getFieldHostDatatype(field, _baseCppTypeForBuiltinDartType)))) { + _writeCopyConstructor(root, indent, classDefinition, orderedFields); + _writeAssignmentOperator(root, indent, classDefinition, orderedFields); + } + // Getters and setters. for (final NamedType field in orderedFields) { _writeCppSourceClassField( @@ -1169,15 +1205,69 @@ return EncodableValue(EncodableList{ initializers: initializerStrings); } + void _writeCopyConstructor(Root root, Indent indent, Class classDefinition, + Iterable fields) { + final List initializerStrings = fields.map((NamedType param) { + final String fieldName = _makeInstanceVariableName(param); + final HostDatatype hostType = getFieldHostDatatype( + param, + _shortBaseCppTypeForBuiltinDartType, + ); + return '$fieldName(${_fieldValueExpression(hostType, 'other.$fieldName', sourceIsField: true)})'; + }).toList(); + _writeFunctionDefinition(indent, classDefinition.name, + scope: classDefinition.name, + parameters: ['const ${classDefinition.name}& other'], + initializers: initializerStrings); + } + + void _writeAssignmentOperator(Root root, Indent indent, Class classDefinition, + Iterable fields) { + _writeFunctionDefinition(indent, 'operator=', + scope: classDefinition.name, + returnType: '${classDefinition.name}&', + parameters: ['const ${classDefinition.name}& other'], body: () { + for (final NamedType field in fields) { + final HostDatatype hostDatatype = + getFieldHostDatatype(field, _shortBaseCppTypeForBuiltinDartType); + + final String ivarName = _makeInstanceVariableName(field); + final String otherIvar = 'other.$ivarName'; + final String valueExpression; + if (_isPointerField(hostDatatype)) { + final String constructor = + 'std::make_unique<${hostDatatype.datatype}>(*$otherIvar)'; + valueExpression = hostDatatype.isNullable + ? '$otherIvar ? $constructor : nullptr' + : constructor; + } else { + valueExpression = otherIvar; + } + indent.writeln('$ivarName = $valueExpression;'); + } + indent.writeln('return *this;'); + }); + } + void _writeCppSourceClassField(CppOptions generatorOptions, Root root, Indent indent, Class classDefinition, NamedType field) { final HostDatatype hostDatatype = getFieldHostDatatype(field, _shortBaseCppTypeForBuiltinDartType); final String instanceVariableName = _makeInstanceVariableName(field); final String setterName = _makeSetterName(field); - final String returnExpression = hostDatatype.isNullable - ? '$instanceVariableName ? &(*$instanceVariableName) : nullptr' - : instanceVariableName; + final String returnExpression; + if (_isPointerField(hostDatatype)) { + // Convert std::unique_ptr to either T* or const T&. + returnExpression = hostDatatype.isNullable + ? '$instanceVariableName.get()' + : '*$instanceVariableName'; + } else if (hostDatatype.isNullable) { + // Convert std::optional to T*. + returnExpression = + '$instanceVariableName ? &(*$instanceVariableName) : nullptr'; + } else { + returnExpression = instanceVariableName; + } // Writes a setter treating the type as [type], to allow generating multiple // setter variants. @@ -1220,10 +1310,20 @@ return EncodableValue(EncodableList{ /// Returns the value to use when setting a field of the given type from /// an argument of that type. /// - /// For non-nullable values this is just the variable itself, but for nullable - /// values this handles the conversion between an argument type (a pointer) - /// and the field type (a std::optional). - String _fieldValueExpression(HostDatatype type, String variable) { + /// For non-nullable and non-custom-class values this is just the variable + /// itself, but for other values this handles the conversion between an + /// argument type (a pointer or value/reference) and the field type + /// (a std::optional or std::unique_ptr). + String _fieldValueExpression(HostDatatype type, String variable, + {bool sourceIsField = false}) { + if (_isPointerField(type)) { + final String constructor = 'std::make_unique<${type.datatype}>'; + // If the source is a pointer field, it always needs dereferencing. + final String maybeDereference = sourceIsField ? '*' : ''; + return type.isNullable + ? '$variable ? $constructor(*$variable) : nullptr' + : '$constructor($maybeDereference$variable)'; + } return type.isNullable ? '$variable ? ${_valueType(type)}(*$variable) : std::nullopt' : variable; @@ -1309,7 +1409,8 @@ ${prefix}reply(EncodableValue(std::move(wrapped)));'''; if (!hostType.isBuiltin && root.classes.any((Class c) => c.name == dartType.baseName)) { if (preSerializeClasses) { - final String operator = hostType.isNullable ? '->' : '.'; + final String operator = + hostType.isNullable || _isPointerField(hostType) ? '->' : '.'; encodableValue = 'EncodableValue($variableName${operator}ToEncodableList())'; } else { @@ -1547,6 +1648,23 @@ String _valueType(HostDatatype type) { return type.isNullable ? 'std::optional<$baseType>' : baseType; } +/// Returns the C++ type to use when declaring a data class field for the +/// given type. +String _fieldType(HostDatatype type) { + return _isPointerField(type) + ? 'std::unique_ptr<${type.datatype}>' + : _valueType(type); +} + +/// Returns true if [type] should be stored as a pointer, rather than a +/// value type, in a data class. +bool _isPointerField(HostDatatype type) { + // Custom class types are stored as `unique_ptr`s since they can have + // arbitrary size, and can also be arbitrarily (including recursively) + // nested, so must be stored as pointers. + return !type.isBuiltin && !type.isEnum; +} + /// Returns the C++ type to use in an argument context without ownership /// transfer for the given base type. String _unownedArgumentType(HostDatatype type) { @@ -1723,17 +1841,21 @@ void _writeFunctionDeclaration( bool isStatic = false, bool isVirtual = false, bool isConstructor = false, + bool isCopy = false, bool isPureVirtual = false, bool isConst = false, bool isOverride = false, bool deleted = false, + bool defaultImpl = false, bool inlineNoop = false, + bool noexcept = false, void Function()? inlineBody, }) { assert(!(isVirtual && isOverride), 'virtual is redundant with override'); assert(isVirtual || !isPureVirtual, 'pure virtual methods must be virtual'); assert(returnType == null || !isConstructor, 'constructors cannot have return types'); + assert(!(deleted && defaultImpl), 'a function cannot be deleted and default'); _writeFunction( indent, inlineNoop || (inlineBody != null) @@ -1746,12 +1868,14 @@ void _writeFunctionDeclaration( if (inlineBody != null) 'inline', if (isStatic) 'static', if (isVirtual) 'virtual', - if (isConstructor && parameters.isNotEmpty) 'explicit' + if (isConstructor && parameters.isNotEmpty && !isCopy) 'explicit' ], trailingAnnotations: [ if (isConst) 'const', + if (noexcept) 'noexcept', if (isOverride) 'override', if (deleted) '= delete', + if (defaultImpl) '= default', if (isPureVirtual) '= 0', ], body: inlineBody, diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart index 66dd99750f34..4540b5d04d8e 100644 --- a/packages/pigeon/lib/generator_tools.dart +++ b/packages/pigeon/lib/generator_tools.dart @@ -13,7 +13,7 @@ import 'ast.dart'; /// The current version of pigeon. /// /// This must match the version in pubspec.yaml. -const String pigeonVersion = '17.2.0'; +const String pigeonVersion = '17.3.0'; /// Read all the content from [stdin] to a String. String readStdin() { diff --git a/packages/pigeon/lib/pigeon_lib.dart b/packages/pigeon/lib/pigeon_lib.dart index 8f418d042598..addce55d54af 100644 --- a/packages/pigeon/lib/pigeon_lib.dart +++ b/packages/pigeon/lib/pigeon_lib.dart @@ -177,6 +177,12 @@ class SwiftFunction { final String value; } +/// Metadata to annotate data classes to be defined as class in Swift output. +class SwiftClass { + /// Constructor. + const SwiftClass(); +} + /// Type of TaskQueue which determines how handlers are dispatched for /// HostApi's. enum TaskQueueType { @@ -1550,6 +1556,7 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor { _currentClass = Class( name: node.name.lexeme, fields: [], + isSwiftClass: _hasMetadata(node.metadata, 'SwiftClass'), documentationComments: _documentationCommentsParser(node.documentationComment?.tokens), ); diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart index 1004a4f38d5b..791cdd2e9365 100644 --- a/packages/pigeon/lib/swift_generator.dart +++ b/packages/pigeon/lib/swift_generator.dart @@ -125,11 +125,26 @@ class SwiftGenerator extends StructuredGenerator { indent, classDefinition.documentationComments, _docCommentSpec, generatorComments: generatedComments); - indent.write('struct ${classDefinition.name} '); + if (classDefinition.isSwiftClass) { + indent.write('class ${classDefinition.name} '); + } else { + indent.write('struct ${classDefinition.name} '); + } indent.addScoped('{', '}', () { - getFieldsInSerializationOrder(classDefinition).forEach((NamedType field) { - _writeClassField(indent, field); - }); + final Iterable fields = + getFieldsInSerializationOrder(classDefinition); + + if (classDefinition.isSwiftClass) { + _writeClassInit(indent, fields.toList()); + } + + for (final NamedType field in fields) { + addDocumentationComments( + indent, field.documentationComments, _docCommentSpec); + indent.write('var '); + _writeClassField(indent, field, addNil: !classDefinition.isSwiftClass); + indent.newln(); + } indent.newln(); writeClassDecode( @@ -149,6 +164,35 @@ class SwiftGenerator extends StructuredGenerator { }); } + void _writeClassInit(Indent indent, List fields) { + indent.writeScoped('init(', ')', () { + for (int i = 0; i < fields.length; i++) { + indent.write(''); + _writeClassField(indent, fields[i]); + if (i == fields.length - 1) { + indent.newln(); + } else { + indent.addln(','); + } + } + }, addTrailingNewline: false); + indent.addScoped(' {', '}', () { + for (final NamedType field in fields) { + _writeClassFieldInit(indent, field); + } + }); + } + + void _writeClassField(Indent indent, NamedType field, {bool addNil = true}) { + indent.add('${field.name}: ${_nullsafeSwiftTypeForDartType(field.type)}'); + final String defaultNil = field.type.isNullable && addNil ? ' = nil' : ''; + indent.add(defaultNil); + } + + void _writeClassFieldInit(Indent indent, NamedType field) { + indent.writeln('self.${field.name} = ${field.name}'); + } + @override void writeClassEncode( SwiftOptions generatorOptions, @@ -222,16 +266,6 @@ class SwiftGenerator extends StructuredGenerator { }); } - void _writeClassField(Indent indent, NamedType field) { - addDocumentationComments( - indent, field.documentationComments, _docCommentSpec); - - indent.write( - 'var ${field.name}: ${_nullsafeSwiftTypeForDartType(field.type)}'); - final String defaultNil = field.type.isNullable ? ' = nil' : ''; - indent.addln(defaultNil); - } - @override void writeApis( SwiftOptions generatorOptions, diff --git a/packages/pigeon/pigeons/core_tests.dart b/packages/pigeon/pigeons/core_tests.dart index 9d571688a093..3fbac83fff8b 100644 --- a/packages/pigeon/pigeons/core_tests.dart +++ b/packages/pigeon/pigeons/core_tests.dart @@ -48,6 +48,7 @@ class AllTypes { } /// A class containing all supported nullable types. +@SwiftClass() class AllNullableTypes { AllNullableTypes( this.aNullableBool, @@ -66,6 +67,51 @@ class AllNullableTypes { this.aNullableEnum, this.aNullableString, this.aNullableObject, + this.allNullableTypes, + ); + + bool? aNullableBool; + int? aNullableInt; + int? aNullableInt64; + double? aNullableDouble; + Uint8List? aNullableByteArray; + Int32List? aNullable4ByteArray; + Int64List? aNullable8ByteArray; + Float64List? aNullableFloatArray; + // ignore: always_specify_types, strict_raw_type + List? aNullableList; + // ignore: always_specify_types, strict_raw_type + Map? aNullableMap; + List?>? nullableNestedList; + Map? nullableMapWithAnnotations; + Map? nullableMapWithObject; + AnEnum? aNullableEnum; + String? aNullableString; + Object? aNullableObject; + AllNullableTypes? allNullableTypes; +} + +/// The primary purpose for this class is to ensure coverage of Swift structs +/// with nullable items, as the primary [AllNullableTypes] class is being used to +/// test Swift classes. +class AllNullableTypesWithoutRecursion { + AllNullableTypesWithoutRecursion( + this.aNullableBool, + this.aNullableInt, + this.aNullableInt64, + this.aNullableDouble, + this.aNullableByteArray, + this.aNullable4ByteArray, + this.aNullable8ByteArray, + this.aNullableFloatArray, + this.aNullableList, + this.aNullableMap, + this.nullableNestedList, + this.nullableMapWithAnnotations, + this.nullableMapWithObject, + this.aNullableEnum, + this.aNullableString, + this.aNullableObject, ); bool? aNullableBool; @@ -94,8 +140,10 @@ class AllNullableTypes { /// `AllNullableTypes` is non-nullable here as it is easier to instantiate /// than `AllTypes` when testing doesn't require both (ie. testing null classes). class AllClassesWrapper { - AllClassesWrapper(this.allNullableTypes, this.allTypes); + AllClassesWrapper(this.allNullableTypes, + this.allNullableTypesWithoutRecursion, this.allTypes); AllNullableTypes allNullableTypes; + AllNullableTypesWithoutRecursion? allNullableTypesWithoutRecursion; AllTypes? allTypes; } @@ -195,6 +243,12 @@ abstract class HostIntegrationCoreApi { @SwiftFunction('echo(_:)') AllNullableTypes? echoAllNullableTypes(AllNullableTypes? everything); + /// Returns the passed object, to test serialization and deserialization. + @ObjCSelector('echoAllNullableTypesWithoutRecursion:') + @SwiftFunction('echo(_:)') + AllNullableTypesWithoutRecursion? echoAllNullableTypesWithoutRecursion( + AllNullableTypesWithoutRecursion? everything); + /// Returns the inner `aString` value from the wrapped object, to test /// sending of nested objects. @ObjCSelector('extractNestedNullableStringFrom:') @@ -213,6 +267,13 @@ abstract class HostIntegrationCoreApi { AllNullableTypes sendMultipleNullableTypes( bool? aNullableBool, int? aNullableInt, String? aNullableString); + /// Returns passed in arguments of multiple types. + @ObjCSelector('sendMultipleNullableTypesWithoutRecursionABool:anInt:aString:') + @SwiftFunction( + 'sendMultipleNullableTypesWithoutRecursion(aBool:anInt:aString:)') + AllNullableTypesWithoutRecursion sendMultipleNullableTypesWithoutRecursion( + bool? aNullableBool, int? aNullableInt, String? aNullableString); + /// Returns passed in int. @ObjCSelector('echoNullableInt:') @SwiftFunction('echo(_:)') @@ -353,6 +414,14 @@ abstract class HostIntegrationCoreApi { AllNullableTypes? echoAsyncNullableAllNullableTypes( AllNullableTypes? everything); + /// Returns the passed object, to test serialization and deserialization. + @async + @ObjCSelector('echoAsyncNullableAllNullableTypesWithoutRecursion:') + @SwiftFunction('echoAsync(_:)') + AllNullableTypesWithoutRecursion? + echoAsyncNullableAllNullableTypesWithoutRecursion( + AllNullableTypesWithoutRecursion? everything); + /// Returns passed in int asynchronously. @async @ObjCSelector('echoAsyncNullableInt:') @@ -435,6 +504,22 @@ abstract class HostIntegrationCoreApi { AllNullableTypes callFlutterSendMultipleNullableTypes( bool? aNullableBool, int? aNullableInt, String? aNullableString); + @async + @ObjCSelector('callFlutterEchoAllNullableTypesWithoutRecursion:') + @SwiftFunction('callFlutterEcho(_:)') + AllNullableTypesWithoutRecursion? + callFlutterEchoAllNullableTypesWithoutRecursion( + AllNullableTypesWithoutRecursion? everything); + + @async + @ObjCSelector( + 'callFlutterSendMultipleNullableTypesWithoutRecursionABool:anInt:aString:') + @SwiftFunction( + 'callFlutterSendMultipleNullableTypesWithoutRecursion(aBool:anInt:aString:)') + AllNullableTypesWithoutRecursion + callFlutterSendMultipleNullableTypesWithoutRecursion( + bool? aNullableBool, int? aNullableInt, String? aNullableString); + @async @ObjCSelector('callFlutterEchoBool:') @SwiftFunction('callFlutterEcho(_:)') @@ -549,6 +634,21 @@ abstract class FlutterIntegrationCoreApi { AllNullableTypes sendMultipleNullableTypes( bool? aNullableBool, int? aNullableInt, String? aNullableString); + /// Returns the passed object, to test serialization and deserialization. + @ObjCSelector('echoAllNullableTypesWithoutRecursion:') + @SwiftFunction('echoNullable(_:)') + AllNullableTypesWithoutRecursion? echoAllNullableTypesWithoutRecursion( + AllNullableTypesWithoutRecursion? everything); + + /// Returns passed in arguments of multiple types. + /// + /// Tests multiple-arity FlutterApi handling. + @ObjCSelector('sendMultipleNullableTypesWithoutRecursionABool:anInt:aString:') + @SwiftFunction( + 'sendMultipleNullableTypesWithoutRecursion(aBool:anInt:aString:)') + AllNullableTypesWithoutRecursion sendMultipleNullableTypesWithoutRecursion( + bool? aNullableBool, int? aNullableInt, String? aNullableString); + // ========== Non-nullable argument/return type tests ========== /// Returns the passed boolean, to test serialization and deserialization. diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/AlternateLanguageTestPlugin.java b/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/AlternateLanguageTestPlugin.java index a99aa2ef913f..7a44f6a88834 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/AlternateLanguageTestPlugin.java +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/AlternateLanguageTestPlugin.java @@ -8,6 +8,7 @@ import androidx.annotation.Nullable; import com.example.alternate_language_test_plugin.CoreTests.AllClassesWrapper; import com.example.alternate_language_test_plugin.CoreTests.AllNullableTypes; +import com.example.alternate_language_test_plugin.CoreTests.AllNullableTypesWithoutRecursion; import com.example.alternate_language_test_plugin.CoreTests.AllTypes; import com.example.alternate_language_test_plugin.CoreTests.AnEnum; import com.example.alternate_language_test_plugin.CoreTests.FlutterIntegrationCoreApi; @@ -47,6 +48,12 @@ public void noop() {} return everything; } + @Override + public @Nullable AllNullableTypesWithoutRecursion echoAllNullableTypesWithoutRecursion( + @Nullable AllNullableTypesWithoutRecursion everything) { + return everything; + } + @Override public @Nullable Object throwError() { throw new RuntimeException("An error"); @@ -153,6 +160,20 @@ public void throwErrorFromVoid() { return someThings; } + @Override + public @NonNull AllNullableTypesWithoutRecursion sendMultipleNullableTypesWithoutRecursion( + @Nullable Boolean aNullableBool, + @Nullable Long aNullableInt, + @Nullable String aNullableString) { + AllNullableTypesWithoutRecursion someThings = + new AllNullableTypesWithoutRecursion.Builder() + .setANullableBool(aNullableBool) + .setANullableInt(aNullableInt) + .setANullableString(aNullableString) + .build(); + return someThings; + } + @Override public @Nullable Long echoNullableInt(@Nullable Long aNullableInt) { return aNullableInt; @@ -239,6 +260,13 @@ public void echoAsyncNullableAllNullableTypes( result.success(everything); } + @Override + public void echoAsyncNullableAllNullableTypesWithoutRecursion( + @Nullable AllNullableTypesWithoutRecursion everything, + @NonNull NullableResult result) { + result.success(everything); + } + @Override public void echoAsyncInt(@NonNull Long anInt, @NonNull Result result) { result.success(anInt); @@ -374,6 +402,23 @@ public void callFlutterSendMultipleNullableTypes( flutterApi.sendMultipleNullableTypes(aNullableBool, aNullableInt, aNullableString, result); } + @Override + public void callFlutterEchoAllNullableTypesWithoutRecursion( + @Nullable AllNullableTypesWithoutRecursion everything, + @NonNull NullableResult result) { + flutterApi.echoAllNullableTypesWithoutRecursion(everything, result); + } + + @Override + public void callFlutterSendMultipleNullableTypesWithoutRecursion( + @Nullable Boolean aNullableBool, + @Nullable Long aNullableInt, + @Nullable String aNullableString, + @NonNull Result result) { + flutterApi.sendMultipleNullableTypesWithoutRecursion( + aNullableBool, aNullableInt, aNullableString, result); + } + @Override public void callFlutterEchoBool(@NonNull Boolean aBool, @NonNull Result result) { flutterApi.echoBool(aBool, result); diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/CoreTests.java b/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/CoreTests.java index 0917a8cfa791..93dad3828781 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/CoreTests.java +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/CoreTests.java @@ -612,6 +612,420 @@ public void setANullableObject(@Nullable Object setterArg) { this.aNullableObject = setterArg; } + private @Nullable AllNullableTypes allNullableTypes; + + public @Nullable AllNullableTypes getAllNullableTypes() { + return allNullableTypes; + } + + public void setAllNullableTypes(@Nullable AllNullableTypes setterArg) { + this.allNullableTypes = setterArg; + } + + public static final class Builder { + + private @Nullable Boolean aNullableBool; + + @CanIgnoreReturnValue + public @NonNull Builder setANullableBool(@Nullable Boolean setterArg) { + this.aNullableBool = setterArg; + return this; + } + + private @Nullable Long aNullableInt; + + @CanIgnoreReturnValue + public @NonNull Builder setANullableInt(@Nullable Long setterArg) { + this.aNullableInt = setterArg; + return this; + } + + private @Nullable Long aNullableInt64; + + @CanIgnoreReturnValue + public @NonNull Builder setANullableInt64(@Nullable Long setterArg) { + this.aNullableInt64 = setterArg; + return this; + } + + private @Nullable Double aNullableDouble; + + @CanIgnoreReturnValue + public @NonNull Builder setANullableDouble(@Nullable Double setterArg) { + this.aNullableDouble = setterArg; + return this; + } + + private @Nullable byte[] aNullableByteArray; + + @CanIgnoreReturnValue + public @NonNull Builder setANullableByteArray(@Nullable byte[] setterArg) { + this.aNullableByteArray = setterArg; + return this; + } + + private @Nullable int[] aNullable4ByteArray; + + @CanIgnoreReturnValue + public @NonNull Builder setANullable4ByteArray(@Nullable int[] setterArg) { + this.aNullable4ByteArray = setterArg; + return this; + } + + private @Nullable long[] aNullable8ByteArray; + + @CanIgnoreReturnValue + public @NonNull Builder setANullable8ByteArray(@Nullable long[] setterArg) { + this.aNullable8ByteArray = setterArg; + return this; + } + + private @Nullable double[] aNullableFloatArray; + + @CanIgnoreReturnValue + public @NonNull Builder setANullableFloatArray(@Nullable double[] setterArg) { + this.aNullableFloatArray = setterArg; + return this; + } + + private @Nullable List aNullableList; + + @CanIgnoreReturnValue + public @NonNull Builder setANullableList(@Nullable List setterArg) { + this.aNullableList = setterArg; + return this; + } + + private @Nullable Map aNullableMap; + + @CanIgnoreReturnValue + public @NonNull Builder setANullableMap(@Nullable Map setterArg) { + this.aNullableMap = setterArg; + return this; + } + + private @Nullable List> nullableNestedList; + + @CanIgnoreReturnValue + public @NonNull Builder setNullableNestedList(@Nullable List> setterArg) { + this.nullableNestedList = setterArg; + return this; + } + + private @Nullable Map nullableMapWithAnnotations; + + @CanIgnoreReturnValue + public @NonNull Builder setNullableMapWithAnnotations( + @Nullable Map setterArg) { + this.nullableMapWithAnnotations = setterArg; + return this; + } + + private @Nullable Map nullableMapWithObject; + + @CanIgnoreReturnValue + public @NonNull Builder setNullableMapWithObject(@Nullable Map setterArg) { + this.nullableMapWithObject = setterArg; + return this; + } + + private @Nullable AnEnum aNullableEnum; + + @CanIgnoreReturnValue + public @NonNull Builder setANullableEnum(@Nullable AnEnum setterArg) { + this.aNullableEnum = setterArg; + return this; + } + + private @Nullable String aNullableString; + + @CanIgnoreReturnValue + public @NonNull Builder setANullableString(@Nullable String setterArg) { + this.aNullableString = setterArg; + return this; + } + + private @Nullable Object aNullableObject; + + @CanIgnoreReturnValue + public @NonNull Builder setANullableObject(@Nullable Object setterArg) { + this.aNullableObject = setterArg; + return this; + } + + private @Nullable AllNullableTypes allNullableTypes; + + @CanIgnoreReturnValue + public @NonNull Builder setAllNullableTypes(@Nullable AllNullableTypes setterArg) { + this.allNullableTypes = setterArg; + return this; + } + + public @NonNull AllNullableTypes build() { + AllNullableTypes pigeonReturn = new AllNullableTypes(); + pigeonReturn.setANullableBool(aNullableBool); + pigeonReturn.setANullableInt(aNullableInt); + pigeonReturn.setANullableInt64(aNullableInt64); + pigeonReturn.setANullableDouble(aNullableDouble); + pigeonReturn.setANullableByteArray(aNullableByteArray); + pigeonReturn.setANullable4ByteArray(aNullable4ByteArray); + pigeonReturn.setANullable8ByteArray(aNullable8ByteArray); + pigeonReturn.setANullableFloatArray(aNullableFloatArray); + pigeonReturn.setANullableList(aNullableList); + pigeonReturn.setANullableMap(aNullableMap); + pigeonReturn.setNullableNestedList(nullableNestedList); + pigeonReturn.setNullableMapWithAnnotations(nullableMapWithAnnotations); + pigeonReturn.setNullableMapWithObject(nullableMapWithObject); + pigeonReturn.setANullableEnum(aNullableEnum); + pigeonReturn.setANullableString(aNullableString); + pigeonReturn.setANullableObject(aNullableObject); + pigeonReturn.setAllNullableTypes(allNullableTypes); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(17); + toListResult.add(aNullableBool); + toListResult.add(aNullableInt); + toListResult.add(aNullableInt64); + toListResult.add(aNullableDouble); + toListResult.add(aNullableByteArray); + toListResult.add(aNullable4ByteArray); + toListResult.add(aNullable8ByteArray); + toListResult.add(aNullableFloatArray); + toListResult.add(aNullableList); + toListResult.add(aNullableMap); + toListResult.add(nullableNestedList); + toListResult.add(nullableMapWithAnnotations); + toListResult.add(nullableMapWithObject); + toListResult.add(aNullableEnum == null ? null : aNullableEnum.index); + toListResult.add(aNullableString); + toListResult.add(aNullableObject); + toListResult.add((allNullableTypes == null) ? null : allNullableTypes.toList()); + return toListResult; + } + + static @NonNull AllNullableTypes fromList(@NonNull ArrayList list) { + AllNullableTypes pigeonResult = new AllNullableTypes(); + Object aNullableBool = list.get(0); + pigeonResult.setANullableBool((Boolean) aNullableBool); + Object aNullableInt = list.get(1); + pigeonResult.setANullableInt( + (aNullableInt == null) + ? null + : ((aNullableInt instanceof Integer) ? (Integer) aNullableInt : (Long) aNullableInt)); + Object aNullableInt64 = list.get(2); + pigeonResult.setANullableInt64( + (aNullableInt64 == null) + ? null + : ((aNullableInt64 instanceof Integer) + ? (Integer) aNullableInt64 + : (Long) aNullableInt64)); + Object aNullableDouble = list.get(3); + pigeonResult.setANullableDouble((Double) aNullableDouble); + Object aNullableByteArray = list.get(4); + pigeonResult.setANullableByteArray((byte[]) aNullableByteArray); + Object aNullable4ByteArray = list.get(5); + pigeonResult.setANullable4ByteArray((int[]) aNullable4ByteArray); + Object aNullable8ByteArray = list.get(6); + pigeonResult.setANullable8ByteArray((long[]) aNullable8ByteArray); + Object aNullableFloatArray = list.get(7); + pigeonResult.setANullableFloatArray((double[]) aNullableFloatArray); + Object aNullableList = list.get(8); + pigeonResult.setANullableList((List) aNullableList); + Object aNullableMap = list.get(9); + pigeonResult.setANullableMap((Map) aNullableMap); + Object nullableNestedList = list.get(10); + pigeonResult.setNullableNestedList((List>) nullableNestedList); + Object nullableMapWithAnnotations = list.get(11); + pigeonResult.setNullableMapWithAnnotations((Map) nullableMapWithAnnotations); + Object nullableMapWithObject = list.get(12); + pigeonResult.setNullableMapWithObject((Map) nullableMapWithObject); + Object aNullableEnum = list.get(13); + pigeonResult.setANullableEnum( + aNullableEnum == null ? null : AnEnum.values()[(int) aNullableEnum]); + Object aNullableString = list.get(14); + pigeonResult.setANullableString((String) aNullableString); + Object aNullableObject = list.get(15); + pigeonResult.setANullableObject(aNullableObject); + Object allNullableTypes = list.get(16); + pigeonResult.setAllNullableTypes( + (allNullableTypes == null) + ? null + : AllNullableTypes.fromList((ArrayList) allNullableTypes)); + return pigeonResult; + } + } + + /** + * The primary purpose for this class is to ensure coverage of Swift structs with nullable items, + * as the primary [AllNullableTypes] class is being used to test Swift classes. + * + *

Generated class from Pigeon that represents data sent in messages. + */ + public static final class AllNullableTypesWithoutRecursion { + private @Nullable Boolean aNullableBool; + + public @Nullable Boolean getANullableBool() { + return aNullableBool; + } + + public void setANullableBool(@Nullable Boolean setterArg) { + this.aNullableBool = setterArg; + } + + private @Nullable Long aNullableInt; + + public @Nullable Long getANullableInt() { + return aNullableInt; + } + + public void setANullableInt(@Nullable Long setterArg) { + this.aNullableInt = setterArg; + } + + private @Nullable Long aNullableInt64; + + public @Nullable Long getANullableInt64() { + return aNullableInt64; + } + + public void setANullableInt64(@Nullable Long setterArg) { + this.aNullableInt64 = setterArg; + } + + private @Nullable Double aNullableDouble; + + public @Nullable Double getANullableDouble() { + return aNullableDouble; + } + + public void setANullableDouble(@Nullable Double setterArg) { + this.aNullableDouble = setterArg; + } + + private @Nullable byte[] aNullableByteArray; + + public @Nullable byte[] getANullableByteArray() { + return aNullableByteArray; + } + + public void setANullableByteArray(@Nullable byte[] setterArg) { + this.aNullableByteArray = setterArg; + } + + private @Nullable int[] aNullable4ByteArray; + + public @Nullable int[] getANullable4ByteArray() { + return aNullable4ByteArray; + } + + public void setANullable4ByteArray(@Nullable int[] setterArg) { + this.aNullable4ByteArray = setterArg; + } + + private @Nullable long[] aNullable8ByteArray; + + public @Nullable long[] getANullable8ByteArray() { + return aNullable8ByteArray; + } + + public void setANullable8ByteArray(@Nullable long[] setterArg) { + this.aNullable8ByteArray = setterArg; + } + + private @Nullable double[] aNullableFloatArray; + + public @Nullable double[] getANullableFloatArray() { + return aNullableFloatArray; + } + + public void setANullableFloatArray(@Nullable double[] setterArg) { + this.aNullableFloatArray = setterArg; + } + + private @Nullable List aNullableList; + + public @Nullable List getANullableList() { + return aNullableList; + } + + public void setANullableList(@Nullable List setterArg) { + this.aNullableList = setterArg; + } + + private @Nullable Map aNullableMap; + + public @Nullable Map getANullableMap() { + return aNullableMap; + } + + public void setANullableMap(@Nullable Map setterArg) { + this.aNullableMap = setterArg; + } + + private @Nullable List> nullableNestedList; + + public @Nullable List> getNullableNestedList() { + return nullableNestedList; + } + + public void setNullableNestedList(@Nullable List> setterArg) { + this.nullableNestedList = setterArg; + } + + private @Nullable Map nullableMapWithAnnotations; + + public @Nullable Map getNullableMapWithAnnotations() { + return nullableMapWithAnnotations; + } + + public void setNullableMapWithAnnotations(@Nullable Map setterArg) { + this.nullableMapWithAnnotations = setterArg; + } + + private @Nullable Map nullableMapWithObject; + + public @Nullable Map getNullableMapWithObject() { + return nullableMapWithObject; + } + + public void setNullableMapWithObject(@Nullable Map setterArg) { + this.nullableMapWithObject = setterArg; + } + + private @Nullable AnEnum aNullableEnum; + + public @Nullable AnEnum getANullableEnum() { + return aNullableEnum; + } + + public void setANullableEnum(@Nullable AnEnum setterArg) { + this.aNullableEnum = setterArg; + } + + private @Nullable String aNullableString; + + public @Nullable String getANullableString() { + return aNullableString; + } + + public void setANullableString(@Nullable String setterArg) { + this.aNullableString = setterArg; + } + + private @Nullable Object aNullableObject; + + public @Nullable Object getANullableObject() { + return aNullableObject; + } + + public void setANullableObject(@Nullable Object setterArg) { + this.aNullableObject = setterArg; + } + public static final class Builder { private @Nullable Boolean aNullableBool; @@ -743,8 +1157,8 @@ public static final class Builder { return this; } - public @NonNull AllNullableTypes build() { - AllNullableTypes pigeonReturn = new AllNullableTypes(); + public @NonNull AllNullableTypesWithoutRecursion build() { + AllNullableTypesWithoutRecursion pigeonReturn = new AllNullableTypesWithoutRecursion(); pigeonReturn.setANullableBool(aNullableBool); pigeonReturn.setANullableInt(aNullableInt); pigeonReturn.setANullableInt64(aNullableInt64); @@ -787,8 +1201,8 @@ ArrayList toList() { return toListResult; } - static @NonNull AllNullableTypes fromList(@NonNull ArrayList list) { - AllNullableTypes pigeonResult = new AllNullableTypes(); + static @NonNull AllNullableTypesWithoutRecursion fromList(@NonNull ArrayList list) { + AllNullableTypesWithoutRecursion pigeonResult = new AllNullableTypesWithoutRecursion(); Object aNullableBool = list.get(0); pigeonResult.setANullableBool((Boolean) aNullableBool); Object aNullableInt = list.get(1); @@ -857,6 +1271,17 @@ public void setAllNullableTypes(@NonNull AllNullableTypes setterArg) { this.allNullableTypes = setterArg; } + private @Nullable AllNullableTypesWithoutRecursion allNullableTypesWithoutRecursion; + + public @Nullable AllNullableTypesWithoutRecursion getAllNullableTypesWithoutRecursion() { + return allNullableTypesWithoutRecursion; + } + + public void setAllNullableTypesWithoutRecursion( + @Nullable AllNullableTypesWithoutRecursion setterArg) { + this.allNullableTypesWithoutRecursion = setterArg; + } + private @Nullable AllTypes allTypes; public @Nullable AllTypes getAllTypes() { @@ -880,6 +1305,15 @@ public static final class Builder { return this; } + private @Nullable AllNullableTypesWithoutRecursion allNullableTypesWithoutRecursion; + + @CanIgnoreReturnValue + public @NonNull Builder setAllNullableTypesWithoutRecursion( + @Nullable AllNullableTypesWithoutRecursion setterArg) { + this.allNullableTypesWithoutRecursion = setterArg; + return this; + } + private @Nullable AllTypes allTypes; @CanIgnoreReturnValue @@ -891,6 +1325,7 @@ public static final class Builder { public @NonNull AllClassesWrapper build() { AllClassesWrapper pigeonReturn = new AllClassesWrapper(); pigeonReturn.setAllNullableTypes(allNullableTypes); + pigeonReturn.setAllNullableTypesWithoutRecursion(allNullableTypesWithoutRecursion); pigeonReturn.setAllTypes(allTypes); return pigeonReturn; } @@ -898,8 +1333,12 @@ public static final class Builder { @NonNull ArrayList toList() { - ArrayList toListResult = new ArrayList(2); + ArrayList toListResult = new ArrayList(3); toListResult.add((allNullableTypes == null) ? null : allNullableTypes.toList()); + toListResult.add( + (allNullableTypesWithoutRecursion == null) + ? null + : allNullableTypesWithoutRecursion.toList()); toListResult.add((allTypes == null) ? null : allTypes.toList()); return toListResult; } @@ -911,7 +1350,13 @@ ArrayList toList() { (allNullableTypes == null) ? null : AllNullableTypes.fromList((ArrayList) allNullableTypes)); - Object allTypes = list.get(1); + Object allNullableTypesWithoutRecursion = list.get(1); + pigeonResult.setAllNullableTypesWithoutRecursion( + (allNullableTypesWithoutRecursion == null) + ? null + : AllNullableTypesWithoutRecursion.fromList( + (ArrayList) allNullableTypesWithoutRecursion)); + Object allTypes = list.get(2); pigeonResult.setAllTypes( (allTypes == null) ? null : AllTypes.fromList((ArrayList) allTypes)); return pigeonResult; @@ -1004,8 +1449,10 @@ protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { case (byte) 129: return AllNullableTypes.fromList((ArrayList) readValue(buffer)); case (byte) 130: - return AllTypes.fromList((ArrayList) readValue(buffer)); + return AllNullableTypesWithoutRecursion.fromList((ArrayList) readValue(buffer)); case (byte) 131: + return AllTypes.fromList((ArrayList) readValue(buffer)); + case (byte) 132: return TestMessage.fromList((ArrayList) readValue(buffer)); default: return super.readValueOfType(type, buffer); @@ -1020,11 +1467,14 @@ protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { } else if (value instanceof AllNullableTypes) { stream.write(129); writeValue(stream, ((AllNullableTypes) value).toList()); - } else if (value instanceof AllTypes) { + } else if (value instanceof AllNullableTypesWithoutRecursion) { stream.write(130); + writeValue(stream, ((AllNullableTypesWithoutRecursion) value).toList()); + } else if (value instanceof AllTypes) { + stream.write(131); writeValue(stream, ((AllTypes) value).toList()); } else if (value instanceof TestMessage) { - stream.write(131); + stream.write(132); writeValue(stream, ((TestMessage) value).toList()); } else { super.writeValue(stream, value); @@ -1096,6 +1546,10 @@ public interface HostIntegrationCoreApi { /** Returns the passed object, to test serialization and deserialization. */ @Nullable AllNullableTypes echoAllNullableTypes(@Nullable AllNullableTypes everything); + /** Returns the passed object, to test serialization and deserialization. */ + @Nullable + AllNullableTypesWithoutRecursion echoAllNullableTypesWithoutRecursion( + @Nullable AllNullableTypesWithoutRecursion everything); /** * Returns the inner `aString` value from the wrapped object, to test sending of nested objects. */ @@ -1112,6 +1566,12 @@ AllNullableTypes sendMultipleNullableTypes( @Nullable Boolean aNullableBool, @Nullable Long aNullableInt, @Nullable String aNullableString); + /** Returns passed in arguments of multiple types. */ + @NonNull + AllNullableTypesWithoutRecursion sendMultipleNullableTypesWithoutRecursion( + @Nullable Boolean aNullableBool, + @Nullable Long aNullableInt, + @Nullable String aNullableString); /** Returns passed in int. */ @Nullable Long echoNullableInt(@Nullable Long aNullableInt); @@ -1180,6 +1640,10 @@ void echoAsyncMap( /** Returns the passed object, to test serialization and deserialization. */ void echoAsyncNullableAllNullableTypes( @Nullable AllNullableTypes everything, @NonNull NullableResult result); + /** Returns the passed object, to test serialization and deserialization. */ + void echoAsyncNullableAllNullableTypesWithoutRecursion( + @Nullable AllNullableTypesWithoutRecursion everything, + @NonNull NullableResult result); /** Returns passed in int asynchronously. */ void echoAsyncNullableInt(@Nullable Long anInt, @NonNull NullableResult result); /** Returns passed in double asynchronously. */ @@ -1219,6 +1683,16 @@ void callFlutterSendMultipleNullableTypes( @Nullable String aNullableString, @NonNull Result result); + void callFlutterEchoAllNullableTypesWithoutRecursion( + @Nullable AllNullableTypesWithoutRecursion everything, + @NonNull NullableResult result); + + void callFlutterSendMultipleNullableTypesWithoutRecursion( + @Nullable Boolean aNullableBool, + @Nullable Long aNullableInt, + @Nullable String aNullableString, + @NonNull Result result); + void callFlutterEchoBool(@NonNull Boolean aBool, @NonNull Result result); void callFlutterEchoInt(@NonNull Long anInt, @NonNull Result result); @@ -1737,6 +2211,33 @@ static void setUp( channel.setMessageHandler(null); } } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAllNullableTypesWithoutRecursion", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + AllNullableTypesWithoutRecursion everythingArg = + (AllNullableTypesWithoutRecursion) args.get(0); + try { + AllNullableTypesWithoutRecursion output = + api.echoAllNullableTypesWithoutRecursion(everythingArg); + wrapped.add(0, output); + } catch (Throwable exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } { BasicMessageChannel channel = new BasicMessageChannel<>( @@ -1818,6 +2319,37 @@ static void setUp( channel.setMessageHandler(null); } } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.sendMultipleNullableTypesWithoutRecursion", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + Boolean aNullableBoolArg = (Boolean) args.get(0); + Number aNullableIntArg = (Number) args.get(1); + String aNullableStringArg = (String) args.get(2); + try { + AllNullableTypesWithoutRecursion output = + api.sendMultipleNullableTypesWithoutRecursion( + aNullableBoolArg, + (aNullableIntArg == null) ? null : aNullableIntArg.longValue(), + aNullableStringArg); + wrapped.add(0, output); + } catch (Throwable exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } { BasicMessageChannel channel = new BasicMessageChannel<>( @@ -2554,6 +3086,39 @@ public void error(Throwable error) { channel.setMessageHandler(null); } } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncNullableAllNullableTypesWithoutRecursion", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + AllNullableTypesWithoutRecursion everythingArg = + (AllNullableTypesWithoutRecursion) args.get(0); + NullableResult resultCallback = + new NullableResult() { + public void success(AllNullableTypesWithoutRecursion result) { + wrapped.add(0, result); + reply.reply(wrapped); + } + + public void error(Throwable error) { + ArrayList wrappedError = wrapError(error); + reply.reply(wrappedError); + } + }; + + api.echoAsyncNullableAllNullableTypesWithoutRecursion( + everythingArg, resultCallback); + }); + } else { + channel.setMessageHandler(null); + } + } { BasicMessageChannel channel = new BasicMessageChannel<>( @@ -3020,6 +3585,75 @@ public void error(Throwable error) { channel.setMessageHandler(null); } } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoAllNullableTypesWithoutRecursion", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + AllNullableTypesWithoutRecursion everythingArg = + (AllNullableTypesWithoutRecursion) args.get(0); + NullableResult resultCallback = + new NullableResult() { + public void success(AllNullableTypesWithoutRecursion result) { + wrapped.add(0, result); + reply.reply(wrapped); + } + + public void error(Throwable error) { + ArrayList wrappedError = wrapError(error); + reply.reply(wrappedError); + } + }; + + api.callFlutterEchoAllNullableTypesWithoutRecursion(everythingArg, resultCallback); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterSendMultipleNullableTypesWithoutRecursion", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + Boolean aNullableBoolArg = (Boolean) args.get(0); + Number aNullableIntArg = (Number) args.get(1); + String aNullableStringArg = (String) args.get(2); + Result resultCallback = + new Result() { + public void success(AllNullableTypesWithoutRecursion result) { + wrapped.add(0, result); + reply.reply(wrapped); + } + + public void error(Throwable error) { + ArrayList wrappedError = wrapError(error); + reply.reply(wrappedError); + } + }; + + api.callFlutterSendMultipleNullableTypesWithoutRecursion( + aNullableBoolArg, + (aNullableIntArg == null) ? null : aNullableIntArg.longValue(), + aNullableStringArg, + resultCallback); + }); + } else { + channel.setMessageHandler(null); + } + } { BasicMessageChannel channel = new BasicMessageChannel<>( @@ -3535,8 +4169,10 @@ protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { case (byte) 129: return AllNullableTypes.fromList((ArrayList) readValue(buffer)); case (byte) 130: - return AllTypes.fromList((ArrayList) readValue(buffer)); + return AllNullableTypesWithoutRecursion.fromList((ArrayList) readValue(buffer)); case (byte) 131: + return AllTypes.fromList((ArrayList) readValue(buffer)); + case (byte) 132: return TestMessage.fromList((ArrayList) readValue(buffer)); default: return super.readValueOfType(type, buffer); @@ -3551,11 +4187,14 @@ protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { } else if (value instanceof AllNullableTypes) { stream.write(129); writeValue(stream, ((AllNullableTypes) value).toList()); - } else if (value instanceof AllTypes) { + } else if (value instanceof AllNullableTypesWithoutRecursion) { stream.write(130); + writeValue(stream, ((AllNullableTypesWithoutRecursion) value).toList()); + } else if (value instanceof AllTypes) { + stream.write(131); writeValue(stream, ((AllTypes) value).toList()); } else if (value instanceof TestMessage) { - stream.write(131); + stream.write(132); writeValue(stream, ((TestMessage) value).toList()); } else { super.writeValue(stream, value); @@ -3764,6 +4403,79 @@ public void sendMultipleNullableTypes( } }); } + /** Returns the passed object, to test serialization and deserialization. */ + public void echoAllNullableTypesWithoutRecursion( + @Nullable AllNullableTypesWithoutRecursion everythingArg, + @NonNull NullableResult result) { + final String channelName = + "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAllNullableTypesWithoutRecursion"; + BasicMessageChannel channel = + new BasicMessageChannel<>(binaryMessenger, channelName, getCodec()); + channel.send( + new ArrayList(Collections.singletonList(everythingArg)), + channelReply -> { + if (channelReply instanceof List) { + List listReply = (List) channelReply; + if (listReply.size() > 1) { + result.error( + new FlutterError( + (String) listReply.get(0), + (String) listReply.get(1), + (String) listReply.get(2))); + } else { + @SuppressWarnings("ConstantConditions") + AllNullableTypesWithoutRecursion output = + (AllNullableTypesWithoutRecursion) listReply.get(0); + result.success(output); + } + } else { + result.error(createConnectionError(channelName)); + } + }); + } + /** + * Returns passed in arguments of multiple types. + * + *

Tests multiple-arity FlutterApi handling. + */ + public void sendMultipleNullableTypesWithoutRecursion( + @Nullable Boolean aNullableBoolArg, + @Nullable Long aNullableIntArg, + @Nullable String aNullableStringArg, + @NonNull Result result) { + final String channelName = + "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.sendMultipleNullableTypesWithoutRecursion"; + BasicMessageChannel channel = + new BasicMessageChannel<>(binaryMessenger, channelName, getCodec()); + channel.send( + new ArrayList( + Arrays.asList(aNullableBoolArg, aNullableIntArg, aNullableStringArg)), + channelReply -> { + if (channelReply instanceof List) { + List listReply = (List) channelReply; + if (listReply.size() > 1) { + result.error( + new FlutterError( + (String) listReply.get(0), + (String) listReply.get(1), + (String) listReply.get(2))); + } else if (listReply.get(0) == null) { + result.error( + new FlutterError( + "null-error", + "Flutter api returned null value for non-null return value.", + "")); + } else { + @SuppressWarnings("ConstantConditions") + AllNullableTypesWithoutRecursion output = + (AllNullableTypesWithoutRecursion) listReply.get(0); + result.success(output); + } + } else { + result.error(createConnectionError(channelName)); + } + }); + } /** Returns the passed boolean, to test serialization and deserialization. */ public void echoBool(@NonNull Boolean aBoolArg, @NonNull Result result) { final String channelName = diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.m b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.m index b10d8d99bcc0..c3501d5ce7ae 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.m +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.m @@ -34,6 +34,12 @@ - (nullable FLTAllNullableTypes *)echoAllNullableTypes:(nullable FLTAllNullableT return everything; } +- (nullable FLTAllNullableTypesWithoutRecursion *) + echoAllNullableTypesWithoutRecursion:(nullable FLTAllNullableTypesWithoutRecursion *)everything + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + return everything; +} + - (nullable id)throwErrorWithError:(FlutterError *_Nullable *_Nonnull)error { *error = [FlutterError errorWithCode:@"An error" message:nil details:nil]; return nil; @@ -119,7 +125,9 @@ - (nullable NSString *)extractNestedNullableStringFrom:(FLTAllClassesWrapper *)w error:(FlutterError *_Nullable *_Nonnull)error { FLTAllNullableTypes *innerObject = [[FLTAllNullableTypes alloc] init]; innerObject.aNullableString = nullableString; - return [FLTAllClassesWrapper makeWithAllNullableTypes:innerObject allTypes:nil]; + return [FLTAllClassesWrapper makeWithAllNullableTypes:innerObject + allNullableTypesWithoutRecursion:nil + allTypes:nil]; } - (nullable FLTAllNullableTypes *) @@ -134,6 +142,21 @@ - (nullable NSString *)extractNestedNullableStringFrom:(FLTAllClassesWrapper *)w return someTypes; } +- (nullable FLTAllNullableTypesWithoutRecursion *) + sendMultipleNullableTypesWithoutRecursionABool:(nullable NSNumber *)aNullableBool + anInt:(nullable NSNumber *)aNullableInt + aString:(nullable NSString *)aNullableString + error: + (FlutterError *_Nullable __autoreleasing *_Nonnull) + error { + FLTAllNullableTypesWithoutRecursion *someTypes = + [[FLTAllNullableTypesWithoutRecursion alloc] init]; + someTypes.aNullableBool = aNullableBool; + someTypes.aNullableInt = aNullableInt; + someTypes.aNullableString = aNullableString; + return someTypes; +} + - (nullable NSNumber *)echoNullableInt:(nullable NSNumber *)aNullableInt error:(FlutterError *_Nullable *_Nonnull)error { return aNullableInt; @@ -219,6 +242,16 @@ - (void)echoAsyncNullableAllNullableTypes:(nullable FLTAllNullableTypes *)everyt completion(everything, nil); } +- (void) + echoAsyncNullableAllNullableTypesWithoutRecursion: + (nullable FLTAllNullableTypesWithoutRecursion *)everything + completion: + (nonnull void (^)( + FLTAllNullableTypesWithoutRecursion *_Nullable, + FlutterError *_Nullable))completion { + completion(everything, nil); +} + - (void)echoAsyncInt:(NSInteger)anInt completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion { completion(@(anInt), nil); @@ -357,6 +390,25 @@ - (void)callFlutterSendMultipleNullableTypesABool:(nullable NSNumber *)aNullable }]; } +- (void)callFlutterSendMultipleNullableTypesWithoutRecursionABool:(nullable NSNumber *)aNullableBool + anInt:(nullable NSNumber *)aNullableInt + aString: + (nullable NSString *)aNullableString + completion: + (nonnull void (^)( + FLTAllNullableTypesWithoutRecursion + *_Nullable, + FlutterError *_Nullable))completion { + [self.flutterAPI + sendMultipleNullableTypesWithoutRecursionABool:aNullableBool + anInt:aNullableInt + aString:aNullableString + completion:^(FLTAllNullableTypesWithoutRecursion *value, + FlutterError *error) { + completion(value, error); + }]; +} + - (void)callFlutterEchoBool:(BOOL)aBool completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion { [self.flutterAPI echoBool:aBool @@ -432,6 +484,20 @@ - (void)callFlutterEchoAllNullableTypes:(nullable FLTAllNullableTypes *)everythi }]; } +- (void)callFlutterEchoAllNullableTypesWithoutRecursion: + (nullable FLTAllNullableTypesWithoutRecursion *)everything + completion: + (nonnull void (^)( + FLTAllNullableTypesWithoutRecursion *_Nullable, + FlutterError *_Nullable))completion { + [self.flutterAPI + echoAllNullableTypesWithoutRecursion:everything + completion:^(FLTAllNullableTypesWithoutRecursion *value, + FlutterError *error) { + completion(value, error); + }]; +} + - (void)callFlutterEchoNullableBool:(nullable NSNumber *)aBool completion: (void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion { diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.h b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.h index d3506f9d3e92..72c1e9d2ec22 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.h +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.h @@ -30,6 +30,7 @@ typedef NS_ENUM(NSUInteger, FLTAnEnum) { @class FLTAllTypes; @class FLTAllNullableTypes; +@class FLTAllNullableTypesWithoutRecursion; @class FLTAllClassesWrapper; @class FLTTestMessage; @@ -67,6 +68,48 @@ typedef NS_ENUM(NSUInteger, FLTAnEnum) { /// A class containing all supported nullable types. @interface FLTAllNullableTypes : NSObject ++ (instancetype)makeWithANullableBool:(nullable NSNumber *)aNullableBool + aNullableInt:(nullable NSNumber *)aNullableInt + aNullableInt64:(nullable NSNumber *)aNullableInt64 + aNullableDouble:(nullable NSNumber *)aNullableDouble + aNullableByteArray:(nullable FlutterStandardTypedData *)aNullableByteArray + aNullable4ByteArray:(nullable FlutterStandardTypedData *)aNullable4ByteArray + aNullable8ByteArray:(nullable FlutterStandardTypedData *)aNullable8ByteArray + aNullableFloatArray:(nullable FlutterStandardTypedData *)aNullableFloatArray + aNullableList:(nullable NSArray *)aNullableList + aNullableMap:(nullable NSDictionary *)aNullableMap + nullableNestedList:(nullable NSArray *> *)nullableNestedList + nullableMapWithAnnotations: + (nullable NSDictionary *)nullableMapWithAnnotations + nullableMapWithObject:(nullable NSDictionary *)nullableMapWithObject + aNullableEnum:(nullable FLTAnEnumBox *)aNullableEnum + aNullableString:(nullable NSString *)aNullableString + aNullableObject:(nullable id)aNullableObject + allNullableTypes:(nullable FLTAllNullableTypes *)allNullableTypes; +@property(nonatomic, strong, nullable) NSNumber *aNullableBool; +@property(nonatomic, strong, nullable) NSNumber *aNullableInt; +@property(nonatomic, strong, nullable) NSNumber *aNullableInt64; +@property(nonatomic, strong, nullable) NSNumber *aNullableDouble; +@property(nonatomic, strong, nullable) FlutterStandardTypedData *aNullableByteArray; +@property(nonatomic, strong, nullable) FlutterStandardTypedData *aNullable4ByteArray; +@property(nonatomic, strong, nullable) FlutterStandardTypedData *aNullable8ByteArray; +@property(nonatomic, strong, nullable) FlutterStandardTypedData *aNullableFloatArray; +@property(nonatomic, copy, nullable) NSArray *aNullableList; +@property(nonatomic, copy, nullable) NSDictionary *aNullableMap; +@property(nonatomic, copy, nullable) NSArray *> *nullableNestedList; +@property(nonatomic, copy, nullable) + NSDictionary *nullableMapWithAnnotations; +@property(nonatomic, copy, nullable) NSDictionary *nullableMapWithObject; +@property(nonatomic, strong, nullable) FLTAnEnumBox *aNullableEnum; +@property(nonatomic, copy, nullable) NSString *aNullableString; +@property(nonatomic, strong, nullable) id aNullableObject; +@property(nonatomic, strong, nullable) FLTAllNullableTypes *allNullableTypes; +@end + +/// The primary purpose for this class is to ensure coverage of Swift structs +/// with nullable items, as the primary [AllNullableTypes] class is being used to +/// test Swift classes. +@interface FLTAllNullableTypesWithoutRecursion : NSObject + (instancetype)makeWithANullableBool:(nullable NSNumber *)aNullableBool aNullableInt:(nullable NSNumber *)aNullableInt aNullableInt64:(nullable NSNumber *)aNullableInt64 @@ -112,8 +155,12 @@ typedef NS_ENUM(NSUInteger, FLTAnEnum) { /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithAllNullableTypes:(FLTAllNullableTypes *)allNullableTypes + allNullableTypesWithoutRecursion: + (nullable FLTAllNullableTypesWithoutRecursion *)allNullableTypesWithoutRecursion allTypes:(nullable FLTAllTypes *)allTypes; @property(nonatomic, strong) FLTAllNullableTypes *allNullableTypes; +@property(nonatomic, strong, nullable) + FLTAllNullableTypesWithoutRecursion *allNullableTypesWithoutRecursion; @property(nonatomic, strong, nullable) FLTAllTypes *allTypes; @end @@ -207,6 +254,10 @@ NSObject *FLTHostIntegrationCoreApiGetCodec(void); /// Returns the passed object, to test serialization and deserialization. - (nullable FLTAllNullableTypes *)echoAllNullableTypes:(nullable FLTAllNullableTypes *)everything error:(FlutterError *_Nullable *_Nonnull)error; +/// Returns the passed object, to test serialization and deserialization. +- (nullable FLTAllNullableTypesWithoutRecursion *) + echoAllNullableTypesWithoutRecursion:(nullable FLTAllNullableTypesWithoutRecursion *)everything + error:(FlutterError *_Nullable *_Nonnull)error; /// Returns the inner `aString` value from the wrapped object, to test /// sending of nested objects. - (nullable NSString *)extractNestedNullableStringFrom:(FLTAllClassesWrapper *)wrapper @@ -226,6 +277,14 @@ NSObject *FLTHostIntegrationCoreApiGetCodec(void); anInt:(nullable NSNumber *)aNullableInt aString:(nullable NSString *)aNullableString error:(FlutterError *_Nullable *_Nonnull)error; +/// Returns passed in arguments of multiple types. +/// +/// @return `nil` only when `error != nil`. +- (nullable FLTAllNullableTypesWithoutRecursion *) + sendMultipleNullableTypesWithoutRecursionABool:(nullable NSNumber *)aNullableBool + anInt:(nullable NSNumber *)aNullableInt + aString:(nullable NSString *)aNullableString + error:(FlutterError *_Nullable *_Nonnull)error; /// Returns passed in int. - (nullable NSNumber *)echoNullableInt:(nullable NSNumber *)aNullableInt error:(FlutterError *_Nullable *_Nonnull)error; @@ -306,6 +365,13 @@ NSObject *FLTHostIntegrationCoreApiGetCodec(void); - (void)echoAsyncNullableAllNullableTypes:(nullable FLTAllNullableTypes *)everything completion:(void (^)(FLTAllNullableTypes *_Nullable, FlutterError *_Nullable))completion; +/// Returns the passed object, to test serialization and deserialization. +- (void)echoAsyncNullableAllNullableTypesWithoutRecursion: + (nullable FLTAllNullableTypesWithoutRecursion *)everything + completion: + (void (^)(FLTAllNullableTypesWithoutRecursion + *_Nullable, + FlutterError *_Nullable))completion; /// Returns passed in int asynchronously. - (void)echoAsyncNullableInt:(nullable NSNumber *)anInt completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion; @@ -351,6 +417,21 @@ NSObject *FLTHostIntegrationCoreApiGetCodec(void); aString:(nullable NSString *)aNullableString completion:(void (^)(FLTAllNullableTypes *_Nullable, FlutterError *_Nullable))completion; +- (void)callFlutterEchoAllNullableTypesWithoutRecursion: + (nullable FLTAllNullableTypesWithoutRecursion *)everything + completion: + (void (^)( + FLTAllNullableTypesWithoutRecursion *_Nullable, + FlutterError *_Nullable))completion; +- (void) + callFlutterSendMultipleNullableTypesWithoutRecursionABool:(nullable NSNumber *)aNullableBool + anInt:(nullable NSNumber *)aNullableInt + aString:(nullable NSString *)aNullableString + completion: + (void (^)(FLTAllNullableTypesWithoutRecursion + *_Nullable, + FlutterError *_Nullable)) + completion; - (void)callFlutterEchoBool:(BOOL)aBool completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion; - (void)callFlutterEchoInt:(NSInteger)anInt @@ -427,6 +508,21 @@ NSObject *FLTFlutterIntegrationCoreApiGetCodec(void); aString:(nullable NSString *)aNullableString completion:(void (^)(FLTAllNullableTypes *_Nullable, FlutterError *_Nullable))completion; +/// Returns the passed object, to test serialization and deserialization. +- (void) + echoAllNullableTypesWithoutRecursion:(nullable FLTAllNullableTypesWithoutRecursion *)everything + completion:(void (^)(FLTAllNullableTypesWithoutRecursion *_Nullable, + FlutterError *_Nullable))completion; +/// Returns passed in arguments of multiple types. +/// +/// Tests multiple-arity FlutterApi handling. +- (void)sendMultipleNullableTypesWithoutRecursionABool:(nullable NSNumber *)aNullableBool + anInt:(nullable NSNumber *)aNullableInt + aString:(nullable NSString *)aNullableString + completion: + (void (^)( + FLTAllNullableTypesWithoutRecursion *_Nullable, + FlutterError *_Nullable))completion; /// Returns the passed boolean, to test serialization and deserialization. - (void)echoBool:(BOOL)aBool completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion; diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.m b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.m index d69afc01e134..2861a8880485 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.m +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.m @@ -62,6 +62,12 @@ + (nullable FLTAllNullableTypes *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end +@interface FLTAllNullableTypesWithoutRecursion () ++ (FLTAllNullableTypesWithoutRecursion *)fromList:(NSArray *)list; ++ (nullable FLTAllNullableTypesWithoutRecursion *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + @interface FLTAllClassesWrapper () + (FLTAllClassesWrapper *)fromList:(NSArray *)list; + (nullable FLTAllClassesWrapper *)nullableFromList:(NSArray *)list; @@ -160,7 +166,8 @@ + (instancetype)makeWithANullableBool:(nullable NSNumber *)aNullableBool nullableMapWithObject:(nullable NSDictionary *)nullableMapWithObject aNullableEnum:(nullable FLTAnEnumBox *)aNullableEnum aNullableString:(nullable NSString *)aNullableString - aNullableObject:(nullable id)aNullableObject { + aNullableObject:(nullable id)aNullableObject + allNullableTypes:(nullable FLTAllNullableTypes *)allNullableTypes { FLTAllNullableTypes *pigeonResult = [[FLTAllNullableTypes alloc] init]; pigeonResult.aNullableBool = aNullableBool; pigeonResult.aNullableInt = aNullableInt; @@ -178,6 +185,7 @@ + (instancetype)makeWithANullableBool:(nullable NSNumber *)aNullableBool pigeonResult.aNullableEnum = aNullableEnum; pigeonResult.aNullableString = aNullableString; pigeonResult.aNullableObject = aNullableObject; + pigeonResult.allNullableTypes = allNullableTypes; return pigeonResult; } + (FLTAllNullableTypes *)fromList:(NSArray *)list { @@ -203,11 +211,104 @@ + (FLTAllNullableTypes *)fromList:(NSArray *)list { pigeonResult.aNullableEnum = aNullableEnum; pigeonResult.aNullableString = GetNullableObjectAtIndex(list, 14); pigeonResult.aNullableObject = GetNullableObjectAtIndex(list, 15); + pigeonResult.allNullableTypes = + [FLTAllNullableTypes nullableFromList:(GetNullableObjectAtIndex(list, 16))]; return pigeonResult; } + (nullable FLTAllNullableTypes *)nullableFromList:(NSArray *)list { return (list) ? [FLTAllNullableTypes fromList:list] : nil; } +- (NSArray *)toList { + return @[ + self.aNullableBool ?: [NSNull null], + self.aNullableInt ?: [NSNull null], + self.aNullableInt64 ?: [NSNull null], + self.aNullableDouble ?: [NSNull null], + self.aNullableByteArray ?: [NSNull null], + self.aNullable4ByteArray ?: [NSNull null], + self.aNullable8ByteArray ?: [NSNull null], + self.aNullableFloatArray ?: [NSNull null], + self.aNullableList ?: [NSNull null], + self.aNullableMap ?: [NSNull null], + self.nullableNestedList ?: [NSNull null], + self.nullableMapWithAnnotations ?: [NSNull null], + self.nullableMapWithObject ?: [NSNull null], + (self.aNullableEnum == nil ? [NSNull null] + : [NSNumber numberWithInteger:self.aNullableEnum.value]), + self.aNullableString ?: [NSNull null], + self.aNullableObject ?: [NSNull null], + (self.allNullableTypes ? [self.allNullableTypes toList] : [NSNull null]), + ]; +} +@end + +@implementation FLTAllNullableTypesWithoutRecursion ++ (instancetype)makeWithANullableBool:(nullable NSNumber *)aNullableBool + aNullableInt:(nullable NSNumber *)aNullableInt + aNullableInt64:(nullable NSNumber *)aNullableInt64 + aNullableDouble:(nullable NSNumber *)aNullableDouble + aNullableByteArray:(nullable FlutterStandardTypedData *)aNullableByteArray + aNullable4ByteArray:(nullable FlutterStandardTypedData *)aNullable4ByteArray + aNullable8ByteArray:(nullable FlutterStandardTypedData *)aNullable8ByteArray + aNullableFloatArray:(nullable FlutterStandardTypedData *)aNullableFloatArray + aNullableList:(nullable NSArray *)aNullableList + aNullableMap:(nullable NSDictionary *)aNullableMap + nullableNestedList:(nullable NSArray *> *)nullableNestedList + nullableMapWithAnnotations: + (nullable NSDictionary *)nullableMapWithAnnotations + nullableMapWithObject:(nullable NSDictionary *)nullableMapWithObject + aNullableEnum:(nullable FLTAnEnumBox *)aNullableEnum + aNullableString:(nullable NSString *)aNullableString + aNullableObject:(nullable id)aNullableObject { + FLTAllNullableTypesWithoutRecursion *pigeonResult = + [[FLTAllNullableTypesWithoutRecursion alloc] init]; + pigeonResult.aNullableBool = aNullableBool; + pigeonResult.aNullableInt = aNullableInt; + pigeonResult.aNullableInt64 = aNullableInt64; + pigeonResult.aNullableDouble = aNullableDouble; + pigeonResult.aNullableByteArray = aNullableByteArray; + pigeonResult.aNullable4ByteArray = aNullable4ByteArray; + pigeonResult.aNullable8ByteArray = aNullable8ByteArray; + pigeonResult.aNullableFloatArray = aNullableFloatArray; + pigeonResult.aNullableList = aNullableList; + pigeonResult.aNullableMap = aNullableMap; + pigeonResult.nullableNestedList = nullableNestedList; + pigeonResult.nullableMapWithAnnotations = nullableMapWithAnnotations; + pigeonResult.nullableMapWithObject = nullableMapWithObject; + pigeonResult.aNullableEnum = aNullableEnum; + pigeonResult.aNullableString = aNullableString; + pigeonResult.aNullableObject = aNullableObject; + return pigeonResult; +} ++ (FLTAllNullableTypesWithoutRecursion *)fromList:(NSArray *)list { + FLTAllNullableTypesWithoutRecursion *pigeonResult = + [[FLTAllNullableTypesWithoutRecursion alloc] init]; + pigeonResult.aNullableBool = GetNullableObjectAtIndex(list, 0); + pigeonResult.aNullableInt = GetNullableObjectAtIndex(list, 1); + pigeonResult.aNullableInt64 = GetNullableObjectAtIndex(list, 2); + pigeonResult.aNullableDouble = GetNullableObjectAtIndex(list, 3); + pigeonResult.aNullableByteArray = GetNullableObjectAtIndex(list, 4); + pigeonResult.aNullable4ByteArray = GetNullableObjectAtIndex(list, 5); + pigeonResult.aNullable8ByteArray = GetNullableObjectAtIndex(list, 6); + pigeonResult.aNullableFloatArray = GetNullableObjectAtIndex(list, 7); + pigeonResult.aNullableList = GetNullableObjectAtIndex(list, 8); + pigeonResult.aNullableMap = GetNullableObjectAtIndex(list, 9); + pigeonResult.nullableNestedList = GetNullableObjectAtIndex(list, 10); + pigeonResult.nullableMapWithAnnotations = GetNullableObjectAtIndex(list, 11); + pigeonResult.nullableMapWithObject = GetNullableObjectAtIndex(list, 12); + NSNumber *aNullableEnumAsNumber = GetNullableObjectAtIndex(list, 13); + FLTAnEnumBox *aNullableEnum = + aNullableEnumAsNumber == nil + ? nil + : [[FLTAnEnumBox alloc] initWithValue:[aNullableEnumAsNumber integerValue]]; + pigeonResult.aNullableEnum = aNullableEnum; + pigeonResult.aNullableString = GetNullableObjectAtIndex(list, 14); + pigeonResult.aNullableObject = GetNullableObjectAtIndex(list, 15); + return pigeonResult; +} ++ (nullable FLTAllNullableTypesWithoutRecursion *)nullableFromList:(NSArray *)list { + return (list) ? [FLTAllNullableTypesWithoutRecursion fromList:list] : nil; +} - (NSArray *)toList { return @[ self.aNullableBool ?: [NSNull null], @@ -233,9 +334,12 @@ - (NSArray *)toList { @implementation FLTAllClassesWrapper + (instancetype)makeWithAllNullableTypes:(FLTAllNullableTypes *)allNullableTypes + allNullableTypesWithoutRecursion: + (nullable FLTAllNullableTypesWithoutRecursion *)allNullableTypesWithoutRecursion allTypes:(nullable FLTAllTypes *)allTypes { FLTAllClassesWrapper *pigeonResult = [[FLTAllClassesWrapper alloc] init]; pigeonResult.allNullableTypes = allNullableTypes; + pigeonResult.allNullableTypesWithoutRecursion = allNullableTypesWithoutRecursion; pigeonResult.allTypes = allTypes; return pigeonResult; } @@ -243,7 +347,9 @@ + (FLTAllClassesWrapper *)fromList:(NSArray *)list { FLTAllClassesWrapper *pigeonResult = [[FLTAllClassesWrapper alloc] init]; pigeonResult.allNullableTypes = [FLTAllNullableTypes nullableFromList:(GetNullableObjectAtIndex(list, 0))]; - pigeonResult.allTypes = [FLTAllTypes nullableFromList:(GetNullableObjectAtIndex(list, 1))]; + pigeonResult.allNullableTypesWithoutRecursion = + [FLTAllNullableTypesWithoutRecursion nullableFromList:(GetNullableObjectAtIndex(list, 1))]; + pigeonResult.allTypes = [FLTAllTypes nullableFromList:(GetNullableObjectAtIndex(list, 2))]; return pigeonResult; } + (nullable FLTAllClassesWrapper *)nullableFromList:(NSArray *)list { @@ -252,6 +358,8 @@ + (nullable FLTAllClassesWrapper *)nullableFromList:(NSArray *)list { - (NSArray *)toList { return @[ (self.allNullableTypes ? [self.allNullableTypes toList] : [NSNull null]), + (self.allNullableTypesWithoutRecursion ? [self.allNullableTypesWithoutRecursion toList] + : [NSNull null]), (self.allTypes ? [self.allTypes toList] : [NSNull null]), ]; } @@ -288,8 +396,10 @@ - (nullable id)readValueOfType:(UInt8)type { case 129: return [FLTAllNullableTypes fromList:[self readValue]]; case 130: - return [FLTAllTypes fromList:[self readValue]]; + return [FLTAllNullableTypesWithoutRecursion fromList:[self readValue]]; case 131: + return [FLTAllTypes fromList:[self readValue]]; + case 132: return [FLTTestMessage fromList:[self readValue]]; default: return [super readValueOfType:type]; @@ -307,12 +417,15 @@ - (void)writeValue:(id)value { } else if ([value isKindOfClass:[FLTAllNullableTypes class]]) { [self writeByte:129]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FLTAllTypes class]]) { + } else if ([value isKindOfClass:[FLTAllNullableTypesWithoutRecursion class]]) { [self writeByte:130]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FLTTestMessage class]]) { + } else if ([value isKindOfClass:[FLTAllTypes class]]) { [self writeByte:131]; [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FLTTestMessage class]]) { + [self writeByte:132]; + [self writeValue:[value toList]]; } else { [super writeValue:value]; } @@ -767,6 +880,30 @@ void SetUpFLTHostIntegrationCoreApi(id binaryMessenger, [channel setMessageHandler:nil]; } } + /// Returns the passed object, to test serialization and deserialization. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." + @"echoAllNullableTypesWithoutRecursion" + binaryMessenger:binaryMessenger + codec:FLTHostIntegrationCoreApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(echoAllNullableTypesWithoutRecursion:error:)], + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " + @"@selector(echoAllNullableTypesWithoutRecursion:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FLTAllNullableTypesWithoutRecursion *arg_everything = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + FLTAllNullableTypesWithoutRecursion *output = + [api echoAllNullableTypesWithoutRecursion:arg_everything error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } /// Returns the inner `aString` value from the wrapped object, to test /// sending of nested objects. { @@ -845,6 +982,36 @@ void SetUpFLTHostIntegrationCoreApi(id binaryMessenger, [channel setMessageHandler:nil]; } } + /// Returns passed in arguments of multiple types. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." + @"sendMultipleNullableTypesWithoutRecursion" + binaryMessenger:binaryMessenger + codec:FLTHostIntegrationCoreApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (sendMultipleNullableTypesWithoutRecursionABool:anInt:aString:error:)], + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " + @"@selector(sendMultipleNullableTypesWithoutRecursionABool:anInt:aString:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_aNullableBool = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_aNullableInt = GetNullableObjectAtIndex(args, 1); + NSString *arg_aNullableString = GetNullableObjectAtIndex(args, 2); + FlutterError *error; + FLTAllNullableTypesWithoutRecursion *output = + [api sendMultipleNullableTypesWithoutRecursionABool:arg_aNullableBool + anInt:arg_aNullableInt + aString:arg_aNullableString + error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } /// Returns passed in int. { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] @@ -1458,6 +1625,33 @@ void SetUpFLTHostIntegrationCoreApi(id binaryMessenger, [channel setMessageHandler:nil]; } } + /// Returns the passed object, to test serialization and deserialization. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." + @"echoAsyncNullableAllNullableTypesWithoutRecursion" + binaryMessenger:binaryMessenger + codec:FLTHostIntegrationCoreApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (echoAsyncNullableAllNullableTypesWithoutRecursion:completion:)], + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " + @"@selector(echoAsyncNullableAllNullableTypesWithoutRecursion:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FLTAllNullableTypesWithoutRecursion *arg_everything = GetNullableObjectAtIndex(args, 0); + [api echoAsyncNullableAllNullableTypesWithoutRecursion:arg_everything + completion:^(FLTAllNullableTypesWithoutRecursion + *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } /// Returns passed in int asynchronously. { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] @@ -1821,6 +2015,66 @@ void SetUpFLTHostIntegrationCoreApi(id binaryMessenger, [channel setMessageHandler:nil]; } } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." + @"callFlutterEchoAllNullableTypesWithoutRecursion" + binaryMessenger:binaryMessenger + codec:FLTHostIntegrationCoreApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (callFlutterEchoAllNullableTypesWithoutRecursion:completion:)], + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " + @"@selector(callFlutterEchoAllNullableTypesWithoutRecursion:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FLTAllNullableTypesWithoutRecursion *arg_everything = GetNullableObjectAtIndex(args, 0); + [api callFlutterEchoAllNullableTypesWithoutRecursion:arg_everything + completion:^(FLTAllNullableTypesWithoutRecursion + *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." + @"callFlutterSendMultipleNullableTypesWithoutRecursion" + binaryMessenger:binaryMessenger + codec:FLTHostIntegrationCoreApiGetCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector + (callFlutterSendMultipleNullableTypesWithoutRecursionABool: + anInt:aString:completion:)], + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " + @"@selector(callFlutterSendMultipleNullableTypesWithoutRecursionABool:anInt:aString:" + @"completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_aNullableBool = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_aNullableInt = GetNullableObjectAtIndex(args, 1); + NSString *arg_aNullableString = GetNullableObjectAtIndex(args, 2); + [api callFlutterSendMultipleNullableTypesWithoutRecursionABool:arg_aNullableBool + anInt:arg_aNullableInt + aString:arg_aNullableString + completion:^( + FLTAllNullableTypesWithoutRecursion + *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @@ -2220,8 +2474,10 @@ - (nullable id)readValueOfType:(UInt8)type { case 129: return [FLTAllNullableTypes fromList:[self readValue]]; case 130: - return [FLTAllTypes fromList:[self readValue]]; + return [FLTAllNullableTypesWithoutRecursion fromList:[self readValue]]; case 131: + return [FLTAllTypes fromList:[self readValue]]; + case 132: return [FLTTestMessage fromList:[self readValue]]; default: return [super readValueOfType:type]; @@ -2239,12 +2495,15 @@ - (void)writeValue:(id)value { } else if ([value isKindOfClass:[FLTAllNullableTypes class]]) { [self writeByte:129]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FLTAllTypes class]]) { + } else if ([value isKindOfClass:[FLTAllNullableTypesWithoutRecursion class]]) { [self writeByte:130]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FLTTestMessage class]]) { + } else if ([value isKindOfClass:[FLTAllTypes class]]) { [self writeByte:131]; [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FLTTestMessage class]]) { + [self writeByte:132]; + [self writeValue:[value toList]]; } else { [super writeValue:value]; } @@ -2432,6 +2691,67 @@ - (void)sendMultipleNullableTypesABool:(nullable NSNumber *)arg_aNullableBool } }]; } +- (void)echoAllNullableTypesWithoutRecursion: + (nullable FLTAllNullableTypesWithoutRecursion *)arg_everything + completion: + (void (^)(FLTAllNullableTypesWithoutRecursion *_Nullable, + FlutterError *_Nullable))completion { + NSString *channelName = @"dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." + @"echoAllNullableTypesWithoutRecursion"; + FlutterBasicMessageChannel *channel = + [FlutterBasicMessageChannel messageChannelWithName:channelName + binaryMessenger:self.binaryMessenger + codec:FLTFlutterIntegrationCoreApiGetCodec()]; + [channel sendMessage:@[ arg_everything ?: [NSNull null] ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion(nil, [FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + FLTAllNullableTypesWithoutRecursion *output = + reply[0] == [NSNull null] ? nil : reply[0]; + completion(output, nil); + } + } else { + completion(nil, createConnectionError(channelName)); + } + }]; +} +- (void)sendMultipleNullableTypesWithoutRecursionABool:(nullable NSNumber *)arg_aNullableBool + anInt:(nullable NSNumber *)arg_aNullableInt + aString:(nullable NSString *)arg_aNullableString + completion: + (void (^)( + FLTAllNullableTypesWithoutRecursion *_Nullable, + FlutterError *_Nullable))completion { + NSString *channelName = @"dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." + @"sendMultipleNullableTypesWithoutRecursion"; + FlutterBasicMessageChannel *channel = + [FlutterBasicMessageChannel messageChannelWithName:channelName + binaryMessenger:self.binaryMessenger + codec:FLTFlutterIntegrationCoreApiGetCodec()]; + [channel sendMessage:@[ + arg_aNullableBool ?: [NSNull null], arg_aNullableInt ?: [NSNull null], + arg_aNullableString ?: [NSNull null] + ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion(nil, [FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + FLTAllNullableTypesWithoutRecursion *output = + reply[0] == [NSNull null] ? nil : reply[0]; + completion(output, nil); + } + } else { + completion(nil, createConnectionError(channelName)); + } + }]; +} - (void)echoBool:(BOOL)arg_aBool completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion { NSString *channelName = diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/AlternateLanguageTestPlugin.m b/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/AlternateLanguageTestPlugin.m index 671116aacaec..ea91a7eaa1b1 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/AlternateLanguageTestPlugin.m +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/AlternateLanguageTestPlugin.m @@ -34,6 +34,12 @@ - (nullable AllNullableTypes *)echoAllNullableTypes:(nullable AllNullableTypes * return everything; } +- (nullable AllNullableTypesWithoutRecursion *) + echoAllNullableTypesWithoutRecursion:(nullable AllNullableTypesWithoutRecursion *)everything + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + return everything; +} + - (nullable id)throwErrorWithError:(FlutterError *_Nullable *_Nonnull)error { *error = [FlutterError errorWithCode:@"An error" message:nil details:nil]; return nil; @@ -92,6 +98,7 @@ - (nullable AllClassesWrapper *)echoClassWrapper:(AllClassesWrapper *)wrapper - (AnEnumBox *_Nullable)echoEnum:(AnEnum)anEnum error:(FlutterError *_Nullable *_Nonnull)error { return [[AnEnumBox alloc] initWithValue:anEnum]; } + - (nullable NSString *)echoNamedDefaultString:(NSString *)aString error:(FlutterError *_Nullable *_Nonnull)error { return aString; @@ -117,7 +124,9 @@ - (nullable NSString *)extractNestedNullableStringFrom:(AllClassesWrapper *)wrap error:(FlutterError *_Nullable *_Nonnull)error { AllNullableTypes *innerObject = [[AllNullableTypes alloc] init]; innerObject.aNullableString = nullableString; - return [AllClassesWrapper makeWithAllNullableTypes:innerObject allTypes:nil]; + return [AllClassesWrapper makeWithAllNullableTypes:innerObject + allNullableTypesWithoutRecursion:nil + allTypes:nil]; } - (nullable AllNullableTypes *)sendMultipleNullableTypesABool:(nullable NSNumber *)aNullableBool @@ -132,6 +141,20 @@ - (nullable AllNullableTypes *)sendMultipleNullableTypesABool:(nullable NSNumber return someTypes; } +- (nullable AllNullableTypesWithoutRecursion *) + sendMultipleNullableTypesWithoutRecursionABool:(nullable NSNumber *)aNullableBool + anInt:(nullable NSNumber *)aNullableInt + aString:(nullable NSString *)aNullableString + error: + (FlutterError *_Nullable __autoreleasing *_Nonnull) + error { + AllNullableTypesWithoutRecursion *someTypes = [[AllNullableTypesWithoutRecursion alloc] init]; + someTypes.aNullableBool = aNullableBool; + someTypes.aNullableInt = aNullableInt; + someTypes.aNullableString = aNullableString; + return someTypes; +} + - (nullable NSNumber *)echoNullableInt:(nullable NSNumber *)aNullableInt error:(FlutterError *_Nullable *_Nonnull)error { return aNullableInt; @@ -217,6 +240,15 @@ - (void)echoAsyncNullableAllNullableTypes:(nullable AllNullableTypes *)everythin completion(everything, nil); } +- (void)echoAsyncNullableAllNullableTypesWithoutRecursion: + (nullable AllNullableTypesWithoutRecursion *)everything + completion: + (nonnull void (^)( + AllNullableTypesWithoutRecursion *_Nullable, + FlutterError *_Nullable))completion { + completion(everything, nil); +} + - (void)echoAsyncInt:(NSInteger)anInt completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion { completion(@(anInt), nil); @@ -352,6 +384,25 @@ - (void)callFlutterSendMultipleNullableTypesABool:(nullable NSNumber *)aNullable }]; } +- (void)callFlutterSendMultipleNullableTypesWithoutRecursionABool:(nullable NSNumber *)aNullableBool + anInt:(nullable NSNumber *)aNullableInt + aString: + (nullable NSString *)aNullableString + completion: + (nonnull void (^)( + AllNullableTypesWithoutRecursion + *_Nullable, + FlutterError *_Nullable))completion { + [self.flutterAPI + sendMultipleNullableTypesWithoutRecursionABool:aNullableBool + anInt:aNullableInt + aString:aNullableString + completion:^(AllNullableTypesWithoutRecursion *value, + FlutterError *error) { + completion(value, error); + }]; +} + - (void)callFlutterEchoBool:(BOOL)aBool completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion { [self.flutterAPI echoBool:aBool @@ -427,6 +478,19 @@ - (void)callFlutterEchoAllNullableTypes:(nullable AllNullableTypes *)everything }]; } +- (void)callFlutterEchoAllNullableTypesWithoutRecursion: + (nullable AllNullableTypesWithoutRecursion *)everything + completion: + (nonnull void (^)( + AllNullableTypesWithoutRecursion *_Nullable, + FlutterError *_Nullable))completion { + [self.flutterAPI echoAllNullableTypesWithoutRecursion:everything + completion:^(AllNullableTypesWithoutRecursion *value, + FlutterError *error) { + completion(value, error); + }]; +} + - (void)callFlutterEchoNullableBool:(nullable NSNumber *)aBool completion: (void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion { diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/CoreTests.gen.h b/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/CoreTests.gen.h index e7022baa9403..a7ec37232c18 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/CoreTests.gen.h +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/CoreTests.gen.h @@ -30,6 +30,7 @@ typedef NS_ENUM(NSUInteger, AnEnum) { @class AllTypes; @class AllNullableTypes; +@class AllNullableTypesWithoutRecursion; @class AllClassesWrapper; @class TestMessage; @@ -67,6 +68,48 @@ typedef NS_ENUM(NSUInteger, AnEnum) { /// A class containing all supported nullable types. @interface AllNullableTypes : NSObject ++ (instancetype)makeWithANullableBool:(nullable NSNumber *)aNullableBool + aNullableInt:(nullable NSNumber *)aNullableInt + aNullableInt64:(nullable NSNumber *)aNullableInt64 + aNullableDouble:(nullable NSNumber *)aNullableDouble + aNullableByteArray:(nullable FlutterStandardTypedData *)aNullableByteArray + aNullable4ByteArray:(nullable FlutterStandardTypedData *)aNullable4ByteArray + aNullable8ByteArray:(nullable FlutterStandardTypedData *)aNullable8ByteArray + aNullableFloatArray:(nullable FlutterStandardTypedData *)aNullableFloatArray + aNullableList:(nullable NSArray *)aNullableList + aNullableMap:(nullable NSDictionary *)aNullableMap + nullableNestedList:(nullable NSArray *> *)nullableNestedList + nullableMapWithAnnotations: + (nullable NSDictionary *)nullableMapWithAnnotations + nullableMapWithObject:(nullable NSDictionary *)nullableMapWithObject + aNullableEnum:(nullable AnEnumBox *)aNullableEnum + aNullableString:(nullable NSString *)aNullableString + aNullableObject:(nullable id)aNullableObject + allNullableTypes:(nullable AllNullableTypes *)allNullableTypes; +@property(nonatomic, strong, nullable) NSNumber *aNullableBool; +@property(nonatomic, strong, nullable) NSNumber *aNullableInt; +@property(nonatomic, strong, nullable) NSNumber *aNullableInt64; +@property(nonatomic, strong, nullable) NSNumber *aNullableDouble; +@property(nonatomic, strong, nullable) FlutterStandardTypedData *aNullableByteArray; +@property(nonatomic, strong, nullable) FlutterStandardTypedData *aNullable4ByteArray; +@property(nonatomic, strong, nullable) FlutterStandardTypedData *aNullable8ByteArray; +@property(nonatomic, strong, nullable) FlutterStandardTypedData *aNullableFloatArray; +@property(nonatomic, copy, nullable) NSArray *aNullableList; +@property(nonatomic, copy, nullable) NSDictionary *aNullableMap; +@property(nonatomic, copy, nullable) NSArray *> *nullableNestedList; +@property(nonatomic, copy, nullable) + NSDictionary *nullableMapWithAnnotations; +@property(nonatomic, copy, nullable) NSDictionary *nullableMapWithObject; +@property(nonatomic, strong, nullable) AnEnumBox *aNullableEnum; +@property(nonatomic, copy, nullable) NSString *aNullableString; +@property(nonatomic, strong, nullable) id aNullableObject; +@property(nonatomic, strong, nullable) AllNullableTypes *allNullableTypes; +@end + +/// The primary purpose for this class is to ensure coverage of Swift structs +/// with nullable items, as the primary [AllNullableTypes] class is being used to +/// test Swift classes. +@interface AllNullableTypesWithoutRecursion : NSObject + (instancetype)makeWithANullableBool:(nullable NSNumber *)aNullableBool aNullableInt:(nullable NSNumber *)aNullableInt aNullableInt64:(nullable NSNumber *)aNullableInt64 @@ -112,8 +155,12 @@ typedef NS_ENUM(NSUInteger, AnEnum) { /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithAllNullableTypes:(AllNullableTypes *)allNullableTypes + allNullableTypesWithoutRecursion: + (nullable AllNullableTypesWithoutRecursion *)allNullableTypesWithoutRecursion allTypes:(nullable AllTypes *)allTypes; @property(nonatomic, strong) AllNullableTypes *allNullableTypes; +@property(nonatomic, strong, nullable) + AllNullableTypesWithoutRecursion *allNullableTypesWithoutRecursion; @property(nonatomic, strong, nullable) AllTypes *allTypes; @end @@ -206,6 +253,10 @@ NSObject *HostIntegrationCoreApiGetCodec(void); /// Returns the passed object, to test serialization and deserialization. - (nullable AllNullableTypes *)echoAllNullableTypes:(nullable AllNullableTypes *)everything error:(FlutterError *_Nullable *_Nonnull)error; +/// Returns the passed object, to test serialization and deserialization. +- (nullable AllNullableTypesWithoutRecursion *) + echoAllNullableTypesWithoutRecursion:(nullable AllNullableTypesWithoutRecursion *)everything + error:(FlutterError *_Nullable *_Nonnull)error; /// Returns the inner `aString` value from the wrapped object, to test /// sending of nested objects. - (nullable NSString *)extractNestedNullableStringFrom:(AllClassesWrapper *)wrapper @@ -225,6 +276,14 @@ NSObject *HostIntegrationCoreApiGetCodec(void); aString:(nullable NSString *)aNullableString error:(FlutterError *_Nullable *_Nonnull) error; +/// Returns passed in arguments of multiple types. +/// +/// @return `nil` only when `error != nil`. +- (nullable AllNullableTypesWithoutRecursion *) + sendMultipleNullableTypesWithoutRecursionABool:(nullable NSNumber *)aNullableBool + anInt:(nullable NSNumber *)aNullableInt + aString:(nullable NSString *)aNullableString + error:(FlutterError *_Nullable *_Nonnull)error; /// Returns passed in int. - (nullable NSNumber *)echoNullableInt:(nullable NSNumber *)aNullableInt error:(FlutterError *_Nullable *_Nonnull)error; @@ -305,6 +364,13 @@ NSObject *HostIntegrationCoreApiGetCodec(void); - (void)echoAsyncNullableAllNullableTypes:(nullable AllNullableTypes *)everything completion:(void (^)(AllNullableTypes *_Nullable, FlutterError *_Nullable))completion; +/// Returns the passed object, to test serialization and deserialization. +- (void)echoAsyncNullableAllNullableTypesWithoutRecursion: + (nullable AllNullableTypesWithoutRecursion *)everything + completion: + (void (^)( + AllNullableTypesWithoutRecursion *_Nullable, + FlutterError *_Nullable))completion; /// Returns passed in int asynchronously. - (void)echoAsyncNullableInt:(nullable NSNumber *)anInt completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion; @@ -348,6 +414,21 @@ NSObject *HostIntegrationCoreApiGetCodec(void); aString:(nullable NSString *)aNullableString completion:(void (^)(AllNullableTypes *_Nullable, FlutterError *_Nullable))completion; +- (void) + callFlutterEchoAllNullableTypesWithoutRecursion: + (nullable AllNullableTypesWithoutRecursion *)everything + completion: + (void (^)(AllNullableTypesWithoutRecursion *_Nullable, + FlutterError *_Nullable))completion; +- (void) + callFlutterSendMultipleNullableTypesWithoutRecursionABool:(nullable NSNumber *)aNullableBool + anInt:(nullable NSNumber *)aNullableInt + aString:(nullable NSString *)aNullableString + completion: + (void (^)(AllNullableTypesWithoutRecursion + *_Nullable, + FlutterError *_Nullable)) + completion; - (void)callFlutterEchoBool:(BOOL)aBool completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion; - (void)callFlutterEchoInt:(NSInteger)anInt @@ -424,6 +505,20 @@ NSObject *FlutterIntegrationCoreApiGetCodec(void); aString:(nullable NSString *)aNullableString completion:(void (^)(AllNullableTypes *_Nullable, FlutterError *_Nullable))completion; +/// Returns the passed object, to test serialization and deserialization. +- (void)echoAllNullableTypesWithoutRecursion:(nullable AllNullableTypesWithoutRecursion *)everything + completion:(void (^)(AllNullableTypesWithoutRecursion *_Nullable, + FlutterError *_Nullable))completion; +/// Returns passed in arguments of multiple types. +/// +/// Tests multiple-arity FlutterApi handling. +- (void) + sendMultipleNullableTypesWithoutRecursionABool:(nullable NSNumber *)aNullableBool + anInt:(nullable NSNumber *)aNullableInt + aString:(nullable NSString *)aNullableString + completion: + (void (^)(AllNullableTypesWithoutRecursion *_Nullable, + FlutterError *_Nullable))completion; /// Returns the passed boolean, to test serialization and deserialization. - (void)echoBool:(BOOL)aBool completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion; diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/CoreTests.gen.m b/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/CoreTests.gen.m index 0cc5b059c04f..b90345836c5a 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/CoreTests.gen.m +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/CoreTests.gen.m @@ -62,6 +62,12 @@ + (nullable AllNullableTypes *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end +@interface AllNullableTypesWithoutRecursion () ++ (AllNullableTypesWithoutRecursion *)fromList:(NSArray *)list; ++ (nullable AllNullableTypesWithoutRecursion *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + @interface AllClassesWrapper () + (AllClassesWrapper *)fromList:(NSArray *)list; + (nullable AllClassesWrapper *)nullableFromList:(NSArray *)list; @@ -160,7 +166,8 @@ + (instancetype)makeWithANullableBool:(nullable NSNumber *)aNullableBool nullableMapWithObject:(nullable NSDictionary *)nullableMapWithObject aNullableEnum:(nullable AnEnumBox *)aNullableEnum aNullableString:(nullable NSString *)aNullableString - aNullableObject:(nullable id)aNullableObject { + aNullableObject:(nullable id)aNullableObject + allNullableTypes:(nullable AllNullableTypes *)allNullableTypes { AllNullableTypes *pigeonResult = [[AllNullableTypes alloc] init]; pigeonResult.aNullableBool = aNullableBool; pigeonResult.aNullableInt = aNullableInt; @@ -178,6 +185,7 @@ + (instancetype)makeWithANullableBool:(nullable NSNumber *)aNullableBool pigeonResult.aNullableEnum = aNullableEnum; pigeonResult.aNullableString = aNullableString; pigeonResult.aNullableObject = aNullableObject; + pigeonResult.allNullableTypes = allNullableTypes; return pigeonResult; } + (AllNullableTypes *)fromList:(NSArray *)list { @@ -203,11 +211,102 @@ + (AllNullableTypes *)fromList:(NSArray *)list { pigeonResult.aNullableEnum = aNullableEnum; pigeonResult.aNullableString = GetNullableObjectAtIndex(list, 14); pigeonResult.aNullableObject = GetNullableObjectAtIndex(list, 15); + pigeonResult.allNullableTypes = + [AllNullableTypes nullableFromList:(GetNullableObjectAtIndex(list, 16))]; return pigeonResult; } + (nullable AllNullableTypes *)nullableFromList:(NSArray *)list { return (list) ? [AllNullableTypes fromList:list] : nil; } +- (NSArray *)toList { + return @[ + self.aNullableBool ?: [NSNull null], + self.aNullableInt ?: [NSNull null], + self.aNullableInt64 ?: [NSNull null], + self.aNullableDouble ?: [NSNull null], + self.aNullableByteArray ?: [NSNull null], + self.aNullable4ByteArray ?: [NSNull null], + self.aNullable8ByteArray ?: [NSNull null], + self.aNullableFloatArray ?: [NSNull null], + self.aNullableList ?: [NSNull null], + self.aNullableMap ?: [NSNull null], + self.nullableNestedList ?: [NSNull null], + self.nullableMapWithAnnotations ?: [NSNull null], + self.nullableMapWithObject ?: [NSNull null], + (self.aNullableEnum == nil ? [NSNull null] + : [NSNumber numberWithInteger:self.aNullableEnum.value]), + self.aNullableString ?: [NSNull null], + self.aNullableObject ?: [NSNull null], + (self.allNullableTypes ? [self.allNullableTypes toList] : [NSNull null]), + ]; +} +@end + +@implementation AllNullableTypesWithoutRecursion ++ (instancetype)makeWithANullableBool:(nullable NSNumber *)aNullableBool + aNullableInt:(nullable NSNumber *)aNullableInt + aNullableInt64:(nullable NSNumber *)aNullableInt64 + aNullableDouble:(nullable NSNumber *)aNullableDouble + aNullableByteArray:(nullable FlutterStandardTypedData *)aNullableByteArray + aNullable4ByteArray:(nullable FlutterStandardTypedData *)aNullable4ByteArray + aNullable8ByteArray:(nullable FlutterStandardTypedData *)aNullable8ByteArray + aNullableFloatArray:(nullable FlutterStandardTypedData *)aNullableFloatArray + aNullableList:(nullable NSArray *)aNullableList + aNullableMap:(nullable NSDictionary *)aNullableMap + nullableNestedList:(nullable NSArray *> *)nullableNestedList + nullableMapWithAnnotations: + (nullable NSDictionary *)nullableMapWithAnnotations + nullableMapWithObject:(nullable NSDictionary *)nullableMapWithObject + aNullableEnum:(nullable AnEnumBox *)aNullableEnum + aNullableString:(nullable NSString *)aNullableString + aNullableObject:(nullable id)aNullableObject { + AllNullableTypesWithoutRecursion *pigeonResult = [[AllNullableTypesWithoutRecursion alloc] init]; + pigeonResult.aNullableBool = aNullableBool; + pigeonResult.aNullableInt = aNullableInt; + pigeonResult.aNullableInt64 = aNullableInt64; + pigeonResult.aNullableDouble = aNullableDouble; + pigeonResult.aNullableByteArray = aNullableByteArray; + pigeonResult.aNullable4ByteArray = aNullable4ByteArray; + pigeonResult.aNullable8ByteArray = aNullable8ByteArray; + pigeonResult.aNullableFloatArray = aNullableFloatArray; + pigeonResult.aNullableList = aNullableList; + pigeonResult.aNullableMap = aNullableMap; + pigeonResult.nullableNestedList = nullableNestedList; + pigeonResult.nullableMapWithAnnotations = nullableMapWithAnnotations; + pigeonResult.nullableMapWithObject = nullableMapWithObject; + pigeonResult.aNullableEnum = aNullableEnum; + pigeonResult.aNullableString = aNullableString; + pigeonResult.aNullableObject = aNullableObject; + return pigeonResult; +} ++ (AllNullableTypesWithoutRecursion *)fromList:(NSArray *)list { + AllNullableTypesWithoutRecursion *pigeonResult = [[AllNullableTypesWithoutRecursion alloc] init]; + pigeonResult.aNullableBool = GetNullableObjectAtIndex(list, 0); + pigeonResult.aNullableInt = GetNullableObjectAtIndex(list, 1); + pigeonResult.aNullableInt64 = GetNullableObjectAtIndex(list, 2); + pigeonResult.aNullableDouble = GetNullableObjectAtIndex(list, 3); + pigeonResult.aNullableByteArray = GetNullableObjectAtIndex(list, 4); + pigeonResult.aNullable4ByteArray = GetNullableObjectAtIndex(list, 5); + pigeonResult.aNullable8ByteArray = GetNullableObjectAtIndex(list, 6); + pigeonResult.aNullableFloatArray = GetNullableObjectAtIndex(list, 7); + pigeonResult.aNullableList = GetNullableObjectAtIndex(list, 8); + pigeonResult.aNullableMap = GetNullableObjectAtIndex(list, 9); + pigeonResult.nullableNestedList = GetNullableObjectAtIndex(list, 10); + pigeonResult.nullableMapWithAnnotations = GetNullableObjectAtIndex(list, 11); + pigeonResult.nullableMapWithObject = GetNullableObjectAtIndex(list, 12); + NSNumber *aNullableEnumAsNumber = GetNullableObjectAtIndex(list, 13); + AnEnumBox *aNullableEnum = + aNullableEnumAsNumber == nil + ? nil + : [[AnEnumBox alloc] initWithValue:[aNullableEnumAsNumber integerValue]]; + pigeonResult.aNullableEnum = aNullableEnum; + pigeonResult.aNullableString = GetNullableObjectAtIndex(list, 14); + pigeonResult.aNullableObject = GetNullableObjectAtIndex(list, 15); + return pigeonResult; +} ++ (nullable AllNullableTypesWithoutRecursion *)nullableFromList:(NSArray *)list { + return (list) ? [AllNullableTypesWithoutRecursion fromList:list] : nil; +} - (NSArray *)toList { return @[ self.aNullableBool ?: [NSNull null], @@ -233,9 +332,12 @@ - (NSArray *)toList { @implementation AllClassesWrapper + (instancetype)makeWithAllNullableTypes:(AllNullableTypes *)allNullableTypes + allNullableTypesWithoutRecursion: + (nullable AllNullableTypesWithoutRecursion *)allNullableTypesWithoutRecursion allTypes:(nullable AllTypes *)allTypes { AllClassesWrapper *pigeonResult = [[AllClassesWrapper alloc] init]; pigeonResult.allNullableTypes = allNullableTypes; + pigeonResult.allNullableTypesWithoutRecursion = allNullableTypesWithoutRecursion; pigeonResult.allTypes = allTypes; return pigeonResult; } @@ -243,7 +345,9 @@ + (AllClassesWrapper *)fromList:(NSArray *)list { AllClassesWrapper *pigeonResult = [[AllClassesWrapper alloc] init]; pigeonResult.allNullableTypes = [AllNullableTypes nullableFromList:(GetNullableObjectAtIndex(list, 0))]; - pigeonResult.allTypes = [AllTypes nullableFromList:(GetNullableObjectAtIndex(list, 1))]; + pigeonResult.allNullableTypesWithoutRecursion = + [AllNullableTypesWithoutRecursion nullableFromList:(GetNullableObjectAtIndex(list, 1))]; + pigeonResult.allTypes = [AllTypes nullableFromList:(GetNullableObjectAtIndex(list, 2))]; return pigeonResult; } + (nullable AllClassesWrapper *)nullableFromList:(NSArray *)list { @@ -252,6 +356,8 @@ + (nullable AllClassesWrapper *)nullableFromList:(NSArray *)list { - (NSArray *)toList { return @[ (self.allNullableTypes ? [self.allNullableTypes toList] : [NSNull null]), + (self.allNullableTypesWithoutRecursion ? [self.allNullableTypesWithoutRecursion toList] + : [NSNull null]), (self.allTypes ? [self.allTypes toList] : [NSNull null]), ]; } @@ -288,8 +394,10 @@ - (nullable id)readValueOfType:(UInt8)type { case 129: return [AllNullableTypes fromList:[self readValue]]; case 130: - return [AllTypes fromList:[self readValue]]; + return [AllNullableTypesWithoutRecursion fromList:[self readValue]]; case 131: + return [AllTypes fromList:[self readValue]]; + case 132: return [TestMessage fromList:[self readValue]]; default: return [super readValueOfType:type]; @@ -307,12 +415,15 @@ - (void)writeValue:(id)value { } else if ([value isKindOfClass:[AllNullableTypes class]]) { [self writeByte:129]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[AllTypes class]]) { + } else if ([value isKindOfClass:[AllNullableTypesWithoutRecursion class]]) { [self writeByte:130]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[TestMessage class]]) { + } else if ([value isKindOfClass:[AllTypes class]]) { [self writeByte:131]; [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[TestMessage class]]) { + [self writeByte:132]; + [self writeValue:[value toList]]; } else { [super writeValue:value]; } @@ -764,6 +875,30 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, [channel setMessageHandler:nil]; } } + /// Returns the passed object, to test serialization and deserialization. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." + @"echoAllNullableTypesWithoutRecursion" + binaryMessenger:binaryMessenger + codec:HostIntegrationCoreApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(echoAllNullableTypesWithoutRecursion:error:)], + @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"@selector(echoAllNullableTypesWithoutRecursion:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AllNullableTypesWithoutRecursion *arg_everything = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + AllNullableTypesWithoutRecursion *output = + [api echoAllNullableTypesWithoutRecursion:arg_everything error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } /// Returns the inner `aString` value from the wrapped object, to test /// sending of nested objects. { @@ -842,6 +977,36 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, [channel setMessageHandler:nil]; } } + /// Returns passed in arguments of multiple types. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." + @"sendMultipleNullableTypesWithoutRecursion" + binaryMessenger:binaryMessenger + codec:HostIntegrationCoreApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (sendMultipleNullableTypesWithoutRecursionABool:anInt:aString:error:)], + @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"@selector(sendMultipleNullableTypesWithoutRecursionABool:anInt:aString:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_aNullableBool = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_aNullableInt = GetNullableObjectAtIndex(args, 1); + NSString *arg_aNullableString = GetNullableObjectAtIndex(args, 2); + FlutterError *error; + AllNullableTypesWithoutRecursion *output = + [api sendMultipleNullableTypesWithoutRecursionABool:arg_aNullableBool + anInt:arg_aNullableInt + aString:arg_aNullableString + error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } /// Returns passed in int. { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] @@ -1455,6 +1620,33 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, [channel setMessageHandler:nil]; } } + /// Returns the passed object, to test serialization and deserialization. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." + @"echoAsyncNullableAllNullableTypesWithoutRecursion" + binaryMessenger:binaryMessenger + codec:HostIntegrationCoreApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (echoAsyncNullableAllNullableTypesWithoutRecursion:completion:)], + @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"@selector(echoAsyncNullableAllNullableTypesWithoutRecursion:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AllNullableTypesWithoutRecursion *arg_everything = GetNullableObjectAtIndex(args, 0); + [api echoAsyncNullableAllNullableTypesWithoutRecursion:arg_everything + completion:^(AllNullableTypesWithoutRecursion + *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } /// Returns passed in int asynchronously. { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] @@ -1817,6 +2009,66 @@ void SetUpHostIntegrationCoreApi(id binaryMessenger, [channel setMessageHandler:nil]; } } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." + @"callFlutterEchoAllNullableTypesWithoutRecursion" + binaryMessenger:binaryMessenger + codec:HostIntegrationCoreApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (callFlutterEchoAllNullableTypesWithoutRecursion:completion:)], + @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"@selector(callFlutterEchoAllNullableTypesWithoutRecursion:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + AllNullableTypesWithoutRecursion *arg_everything = GetNullableObjectAtIndex(args, 0); + [api callFlutterEchoAllNullableTypesWithoutRecursion:arg_everything + completion:^(AllNullableTypesWithoutRecursion + *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." + @"callFlutterSendMultipleNullableTypesWithoutRecursion" + binaryMessenger:binaryMessenger + codec:HostIntegrationCoreApiGetCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector + (callFlutterSendMultipleNullableTypesWithoutRecursionABool: + anInt:aString:completion:)], + @"HostIntegrationCoreApi api (%@) doesn't respond to " + @"@selector(callFlutterSendMultipleNullableTypesWithoutRecursionABool:anInt:aString:" + @"completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_aNullableBool = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_aNullableInt = GetNullableObjectAtIndex(args, 1); + NSString *arg_aNullableString = GetNullableObjectAtIndex(args, 2); + [api callFlutterSendMultipleNullableTypesWithoutRecursionABool:arg_aNullableBool + anInt:arg_aNullableInt + aString:arg_aNullableString + completion:^( + AllNullableTypesWithoutRecursion + *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] initWithName:@"dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." @@ -2215,8 +2467,10 @@ - (nullable id)readValueOfType:(UInt8)type { case 129: return [AllNullableTypes fromList:[self readValue]]; case 130: - return [AllTypes fromList:[self readValue]]; + return [AllNullableTypesWithoutRecursion fromList:[self readValue]]; case 131: + return [AllTypes fromList:[self readValue]]; + case 132: return [TestMessage fromList:[self readValue]]; default: return [super readValueOfType:type]; @@ -2234,12 +2488,15 @@ - (void)writeValue:(id)value { } else if ([value isKindOfClass:[AllNullableTypes class]]) { [self writeByte:129]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[AllTypes class]]) { + } else if ([value isKindOfClass:[AllNullableTypesWithoutRecursion class]]) { [self writeByte:130]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[TestMessage class]]) { + } else if ([value isKindOfClass:[AllTypes class]]) { [self writeByte:131]; [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[TestMessage class]]) { + [self writeByte:132]; + [self writeValue:[value toList]]; } else { [super writeValue:value]; } @@ -2427,6 +2684,66 @@ - (void)sendMultipleNullableTypesABool:(nullable NSNumber *)arg_aNullableBool } }]; } +- (void)echoAllNullableTypesWithoutRecursion: + (nullable AllNullableTypesWithoutRecursion *)arg_everything + completion:(void (^)(AllNullableTypesWithoutRecursion *_Nullable, + FlutterError *_Nullable))completion { + NSString *channelName = @"dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." + @"echoAllNullableTypesWithoutRecursion"; + FlutterBasicMessageChannel *channel = + [FlutterBasicMessageChannel messageChannelWithName:channelName + binaryMessenger:self.binaryMessenger + codec:FlutterIntegrationCoreApiGetCodec()]; + [channel sendMessage:@[ arg_everything ?: [NSNull null] ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion(nil, [FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + AllNullableTypesWithoutRecursion *output = + reply[0] == [NSNull null] ? nil : reply[0]; + completion(output, nil); + } + } else { + completion(nil, createConnectionError(channelName)); + } + }]; +} +- (void) + sendMultipleNullableTypesWithoutRecursionABool:(nullable NSNumber *)arg_aNullableBool + anInt:(nullable NSNumber *)arg_aNullableInt + aString:(nullable NSString *)arg_aNullableString + completion: + (void (^)(AllNullableTypesWithoutRecursion *_Nullable, + FlutterError *_Nullable))completion { + NSString *channelName = @"dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." + @"sendMultipleNullableTypesWithoutRecursion"; + FlutterBasicMessageChannel *channel = + [FlutterBasicMessageChannel messageChannelWithName:channelName + binaryMessenger:self.binaryMessenger + codec:FlutterIntegrationCoreApiGetCodec()]; + [channel sendMessage:@[ + arg_aNullableBool ?: [NSNull null], arg_aNullableInt ?: [NSNull null], + arg_aNullableString ?: [NSNull null] + ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion(nil, [FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + AllNullableTypesWithoutRecursion *output = + reply[0] == [NSNull null] ? nil : reply[0]; + completion(output, nil); + } + } else { + completion(nil, createConnectionError(channelName)); + } + }]; +} - (void)echoBool:(BOOL)arg_aBool completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion { NSString *channelName = diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart index 4ebee6b740a6..876c3b149d7e 100644 --- a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart +++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart @@ -106,6 +106,62 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) { allNullableTypesTwo.aNullableObject); expect( allNullableTypesOne.aNullableEnum, allNullableTypesTwo.aNullableEnum); + compareAllNullableTypes(allNullableTypesOne.allNullableTypes, + allNullableTypesTwo.allNullableTypes); + } + + void compareAllNullableTypesWithoutRecursion( + AllNullableTypesWithoutRecursion? allNullableTypesOne, + AllNullableTypesWithoutRecursion? allNullableTypesTwo) { + expect(allNullableTypesOne == null, allNullableTypesTwo == null); + if (allNullableTypesOne == null || allNullableTypesTwo == null) { + return; + } + expect( + allNullableTypesOne.aNullableBool, allNullableTypesTwo.aNullableBool); + expect(allNullableTypesOne.aNullableInt, allNullableTypesTwo.aNullableInt); + expect( + allNullableTypesOne.aNullableInt64, allNullableTypesTwo.aNullableInt64); + expect(allNullableTypesOne.aNullableDouble, + allNullableTypesTwo.aNullableDouble); + expect(allNullableTypesOne.aNullableString, + allNullableTypesTwo.aNullableString); + expect(allNullableTypesOne.aNullableByteArray, + allNullableTypesTwo.aNullableByteArray); + expect(allNullableTypesOne.aNullable4ByteArray, + allNullableTypesTwo.aNullable4ByteArray); + expect(allNullableTypesOne.aNullable8ByteArray, + allNullableTypesTwo.aNullable8ByteArray); + expect(allNullableTypesOne.aNullableFloatArray, + allNullableTypesTwo.aNullableFloatArray); + expect( + listEquals(allNullableTypesOne.aNullableList, + allNullableTypesTwo.aNullableList), + true); + expect( + mapEquals( + allNullableTypesOne.aNullableMap, allNullableTypesTwo.aNullableMap), + true); + expect(allNullableTypesOne.nullableNestedList?.length, + allNullableTypesTwo.nullableNestedList?.length); + // TODO(stuartmorgan): Enable this once the Dart types are fixed; see + // https://github.com/flutter/flutter/issues/116117 + //for (int i = 0; i < allNullableTypesOne.nullableNestedList!.length; i++) { + // expect(listEquals(allNullableTypesOne.nullableNestedList![i], allNullableTypesTwo.nullableNestedList![i]), + // true); + //} + expect( + mapEquals(allNullableTypesOne.nullableMapWithAnnotations, + allNullableTypesTwo.nullableMapWithAnnotations), + true); + expect( + mapEquals(allNullableTypesOne.nullableMapWithObject, + allNullableTypesTwo.nullableMapWithObject), + true); + expect(allNullableTypesOne.aNullableObject, + allNullableTypesTwo.aNullableObject); + expect( + allNullableTypesOne.aNullableEnum, allNullableTypesTwo.aNullableEnum); } void compareAllClassesWrapper( @@ -117,6 +173,10 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) { compareAllNullableTypes( wrapperOne.allNullableTypes, wrapperTwo.allNullableTypes); + compareAllNullableTypesWithoutRecursion( + wrapperOne.allNullableTypesWithoutRecursion, + wrapperTwo.allNullableTypesWithoutRecursion, + ); compareAllTypes(wrapperOne.allTypes, wrapperTwo.allTypes); } @@ -170,6 +230,65 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) { aNullableObject: 0, ); + final AllNullableTypes recursiveAllNullableTypes = AllNullableTypes( + aNullableBool: true, + aNullableInt: _regularInt, + aNullableInt64: _biggerThanBigInt, + aNullableDouble: _doublePi, + aNullableString: 'Hello host!', + aNullableByteArray: Uint8List.fromList([1, 2, 3]), + aNullable4ByteArray: Int32List.fromList([4, 5, 6]), + aNullable8ByteArray: Int64List.fromList([7, 8, 9]), + aNullableFloatArray: Float64List.fromList([2.71828, _doublePi]), + aNullableList: ['Thing 1', 2, true, 3.14, null], + aNullableMap: { + 'a': 1, + 'b': 2.0, + 'c': 'three', + 'd': false, + 'e': null + }, + nullableNestedList: >[ + [true, false], + [false, true] + ], + nullableMapWithAnnotations: {}, + nullableMapWithObject: {}, + aNullableEnum: AnEnum.fourHundredTwentyTwo, + aNullableObject: 0, + allNullableTypes: genericAllNullableTypes, + ); + + final AllNullableTypesWithoutRecursion + genericAllNullableTypesWithoutRecursion = + AllNullableTypesWithoutRecursion( + aNullableBool: true, + aNullableInt: _regularInt, + aNullableInt64: _biggerThanBigInt, + aNullableDouble: _doublePi, + aNullableString: 'Hello host!', + aNullableByteArray: Uint8List.fromList([1, 2, 3]), + aNullable4ByteArray: Int32List.fromList([4, 5, 6]), + aNullable8ByteArray: Int64List.fromList([7, 8, 9]), + aNullableFloatArray: Float64List.fromList([2.71828, _doublePi]), + aNullableList: ['Thing 1', 2, true, 3.14, null], + aNullableMap: { + 'a': 1, + 'b': 2.0, + 'c': 'three', + 'd': false, + 'e': null + }, + nullableNestedList: >[ + [true, false], + [false, true] + ], + nullableMapWithAnnotations: {}, + nullableMapWithObject: {}, + aNullableEnum: AnEnum.fourHundredTwentyTwo, + aNullableObject: 0, + ); + group('Host sync API tests', () { testWidgets('basic void->void call works', (WidgetTester _) async { final HostIntegrationCoreApi api = HostIntegrationCoreApi(); @@ -190,9 +309,9 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) { final HostIntegrationCoreApi api = HostIntegrationCoreApi(); final AllNullableTypes? echoObject = - await api.echoAllNullableTypes(genericAllNullableTypes); + await api.echoAllNullableTypes(recursiveAllNullableTypes); - compareAllNullableTypes(echoObject, genericAllNullableTypes); + compareAllNullableTypes(echoObject, recursiveAllNullableTypes); }); testWidgets('all null datatypes serialize and deserialize correctly', @@ -232,6 +351,67 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) { compareAllNullableTypes(nullableListTypes, echoNullFilledClass); }); + testWidgets( + 'all nullable datatypes without recursion serialize and deserialize correctly', + (WidgetTester _) async { + final HostIntegrationCoreApi api = HostIntegrationCoreApi(); + + final AllNullableTypesWithoutRecursion? echoObject = + await api.echoAllNullableTypesWithoutRecursion( + genericAllNullableTypesWithoutRecursion); + + compareAllNullableTypesWithoutRecursion( + echoObject, genericAllNullableTypesWithoutRecursion); + }); + + testWidgets( + 'all null datatypes without recursion serialize and deserialize correctly', + (WidgetTester _) async { + final HostIntegrationCoreApi api = HostIntegrationCoreApi(); + + final AllNullableTypesWithoutRecursion allTypesNull = + AllNullableTypesWithoutRecursion(); + + final AllNullableTypesWithoutRecursion? echoNullFilledClass = + await api.echoAllNullableTypesWithoutRecursion(allTypesNull); + compareAllNullableTypesWithoutRecursion( + allTypesNull, echoNullFilledClass); + }); + + testWidgets( + 'Classes without recursion with list of null serialize and deserialize correctly', + (WidgetTester _) async { + final HostIntegrationCoreApi api = HostIntegrationCoreApi(); + + final AllNullableTypesWithoutRecursion nullableListTypes = + AllNullableTypesWithoutRecursion( + aNullableList: ['String', null]); + + final AllNullableTypesWithoutRecursion? echoNullFilledClass = + await api.echoAllNullableTypesWithoutRecursion(nullableListTypes); + + compareAllNullableTypesWithoutRecursion( + nullableListTypes, echoNullFilledClass); + }); + + testWidgets( + 'Classes without recursion with map of null serialize and deserialize correctly', + (WidgetTester _) async { + final HostIntegrationCoreApi api = HostIntegrationCoreApi(); + + final AllNullableTypesWithoutRecursion nullableListTypes = + AllNullableTypesWithoutRecursion(aNullableMap: { + 'String': 'string', + 'null': null + }); + + final AllNullableTypesWithoutRecursion? echoNullFilledClass = + await api.echoAllNullableTypesWithoutRecursion(nullableListTypes); + + compareAllNullableTypesWithoutRecursion( + nullableListTypes, echoNullFilledClass); + }); + testWidgets('errors are returned correctly', (WidgetTester _) async { final HostIntegrationCoreApi api = HostIntegrationCoreApi(); @@ -266,7 +446,10 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) { final HostIntegrationCoreApi api = HostIntegrationCoreApi(); final AllClassesWrapper sentObject = AllClassesWrapper( - allNullableTypes: genericAllNullableTypes, allTypes: genericAllTypes); + allNullableTypes: recursiveAllNullableTypes, + allNullableTypesWithoutRecursion: + genericAllNullableTypesWithoutRecursion, + allTypes: genericAllTypes); final String? receivedString = await api.extractNestedNullableString(sentObject); @@ -288,7 +471,10 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) { final HostIntegrationCoreApi api = HostIntegrationCoreApi(); final AllClassesWrapper sentWrapper = AllClassesWrapper( - allNullableTypes: AllNullableTypes(), allTypes: genericAllTypes); + allNullableTypes: AllNullableTypes(), + allNullableTypesWithoutRecursion: AllNullableTypesWithoutRecursion(), + allTypes: genericAllTypes, + ); final AllClassesWrapper receivedClassWrapper = await api.echoClassWrapper(sentWrapper); @@ -299,8 +485,10 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) { (WidgetTester _) async { final HostIntegrationCoreApi api = HostIntegrationCoreApi(); - final AllClassesWrapper sentWrapper = - AllClassesWrapper(allNullableTypes: AllNullableTypes()); + final AllClassesWrapper sentWrapper = AllClassesWrapper( + allNullableTypes: AllNullableTypes(), + allNullableTypesWithoutRecursion: AllNullableTypesWithoutRecursion(), + ); final AllClassesWrapper receivedClassWrapper = await api.echoClassWrapper(sentWrapper); @@ -334,6 +522,34 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) { expect(echoNullFilledClass.aNullableString, null); }); + testWidgets( + 'Arguments of multiple types serialize and deserialize correctly (WithoutRecursion)', + (WidgetTester _) async { + final HostIntegrationCoreApi api = HostIntegrationCoreApi(); + const String aNullableString = 'this is a String'; + const bool aNullableBool = false; + const int aNullableInt = _regularInt; + + final AllNullableTypesWithoutRecursion echoObject = + await api.sendMultipleNullableTypesWithoutRecursion( + aNullableBool, aNullableInt, aNullableString); + expect(echoObject.aNullableInt, aNullableInt); + expect(echoObject.aNullableBool, aNullableBool); + expect(echoObject.aNullableString, aNullableString); + }); + + testWidgets( + 'Arguments of multiple null types serialize and deserialize correctly (WithoutRecursion)', + (WidgetTester _) async { + final HostIntegrationCoreApi api = HostIntegrationCoreApi(); + + final AllNullableTypesWithoutRecursion echoNullFilledClass = + await api.sendMultipleNullableTypesWithoutRecursion(null, null, null); + expect(echoNullFilledClass.aNullableInt, null); + expect(echoNullFilledClass.aNullableBool, null); + expect(echoNullFilledClass.aNullableString, null); + }); + testWidgets('Int serialize and deserialize correctly', (WidgetTester _) async { final HostIntegrationCoreApi api = HostIntegrationCoreApi(); @@ -775,10 +991,10 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) { (WidgetTester _) async { final HostIntegrationCoreApi api = HostIntegrationCoreApi(); - final AllNullableTypes? echoObject = - await api.echoAsyncNullableAllNullableTypes(genericAllNullableTypes); + final AllNullableTypes? echoObject = await api + .echoAsyncNullableAllNullableTypes(recursiveAllNullableTypes); - compareAllNullableTypes(echoObject, genericAllNullableTypes); + compareAllNullableTypes(echoObject, recursiveAllNullableTypes); }); testWidgets('all null datatypes async serialize and deserialize correctly', @@ -792,6 +1008,33 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) { compareAllNullableTypes(echoNullFilledClass, allTypesNull); }); + testWidgets( + 'all nullable async datatypes without recursion serialize and deserialize correctly', + (WidgetTester _) async { + final HostIntegrationCoreApi api = HostIntegrationCoreApi(); + + final AllNullableTypesWithoutRecursion? echoObject = + await api.echoAsyncNullableAllNullableTypesWithoutRecursion( + genericAllNullableTypesWithoutRecursion); + + compareAllNullableTypesWithoutRecursion( + echoObject, genericAllNullableTypesWithoutRecursion); + }); + + testWidgets( + 'all null datatypes without recursion async serialize and deserialize correctly', + (WidgetTester _) async { + final HostIntegrationCoreApi api = HostIntegrationCoreApi(); + + final AllNullableTypesWithoutRecursion allTypesNull = + AllNullableTypesWithoutRecursion(); + + final AllNullableTypesWithoutRecursion? echoNullFilledClass = await api + .echoAsyncNullableAllNullableTypesWithoutRecursion(allTypesNull); + compareAllNullableTypesWithoutRecursion( + echoNullFilledClass, allTypesNull); + }); + testWidgets('Int async serialize and deserialize correctly', (WidgetTester _) async { final HostIntegrationCoreApi api = HostIntegrationCoreApi(); @@ -1187,6 +1430,35 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) { expect(compositeObject.aNullableString, null); }); + testWidgets( + 'Arguments of multiple types serialize and deserialize correctly (WithoutRecursion)', + (WidgetTester _) async { + final HostIntegrationCoreApi api = HostIntegrationCoreApi(); + const String aNullableString = 'this is a String'; + const bool aNullableBool = false; + const int aNullableInt = _regularInt; + + final AllNullableTypesWithoutRecursion compositeObject = + await api.callFlutterSendMultipleNullableTypesWithoutRecursion( + aNullableBool, aNullableInt, aNullableString); + expect(compositeObject.aNullableInt, aNullableInt); + expect(compositeObject.aNullableBool, aNullableBool); + expect(compositeObject.aNullableString, aNullableString); + }); + + testWidgets( + 'Arguments of multiple null types serialize and deserialize correctly (WithoutRecursion)', + (WidgetTester _) async { + final HostIntegrationCoreApi api = HostIntegrationCoreApi(); + + final AllNullableTypesWithoutRecursion compositeObject = + await api.callFlutterSendMultipleNullableTypesWithoutRecursion( + null, null, null); + expect(compositeObject.aNullableInt, null); + expect(compositeObject.aNullableBool, null); + expect(compositeObject.aNullableString, null); + }); + testWidgets('booleans serialize and deserialize correctly', (WidgetTester _) async { final HostIntegrationCoreApi api = HostIntegrationCoreApi(); @@ -1483,6 +1755,12 @@ class _FlutterApiTestImplementation implements FlutterIntegrationCoreApi { return everything; } + @override + AllNullableTypesWithoutRecursion? echoAllNullableTypesWithoutRecursion( + AllNullableTypesWithoutRecursion? everything) { + return everything; + } + @override void noop() {} @@ -1505,6 +1783,15 @@ class _FlutterApiTestImplementation implements FlutterIntegrationCoreApi { aNullableString: aNullableString); } + @override + AllNullableTypesWithoutRecursion sendMultipleNullableTypesWithoutRecursion( + bool? aNullableBool, int? aNullableInt, String? aNullableString) { + return AllNullableTypesWithoutRecursion( + aNullableBool: aNullableBool, + aNullableInt: aNullableInt, + aNullableString: aNullableString); + } + @override bool echoBool(bool aBool) => aBool; diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/core_tests.gen.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/core_tests.gen.dart index 98c1aea5c084..1b3bc1f5304e 100644 --- a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/core_tests.gen.dart +++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/core_tests.gen.dart @@ -139,6 +139,7 @@ class AllNullableTypes { this.aNullableEnum, this.aNullableString, this.aNullableObject, + this.allNullableTypes, }); bool? aNullableBool; @@ -173,6 +174,8 @@ class AllNullableTypes { Object? aNullableObject; + AllNullableTypes? allNullableTypes; + Object encode() { return [ aNullableBool, @@ -191,6 +194,7 @@ class AllNullableTypes { aNullableEnum?.index, aNullableString, aNullableObject, + allNullableTypes?.encode(), ]; } @@ -216,6 +220,111 @@ class AllNullableTypes { result[13] != null ? AnEnum.values[result[13]! as int] : null, aNullableString: result[14] as String?, aNullableObject: result[15], + allNullableTypes: result[16] != null + ? AllNullableTypes.decode(result[16]! as List) + : null, + ); + } +} + +/// The primary purpose for this class is to ensure coverage of Swift structs +/// with nullable items, as the primary [AllNullableTypes] class is being used to +/// test Swift classes. +class AllNullableTypesWithoutRecursion { + AllNullableTypesWithoutRecursion({ + this.aNullableBool, + this.aNullableInt, + this.aNullableInt64, + this.aNullableDouble, + this.aNullableByteArray, + this.aNullable4ByteArray, + this.aNullable8ByteArray, + this.aNullableFloatArray, + this.aNullableList, + this.aNullableMap, + this.nullableNestedList, + this.nullableMapWithAnnotations, + this.nullableMapWithObject, + this.aNullableEnum, + this.aNullableString, + this.aNullableObject, + }); + + bool? aNullableBool; + + int? aNullableInt; + + int? aNullableInt64; + + double? aNullableDouble; + + Uint8List? aNullableByteArray; + + Int32List? aNullable4ByteArray; + + Int64List? aNullable8ByteArray; + + Float64List? aNullableFloatArray; + + List? aNullableList; + + Map? aNullableMap; + + List?>? nullableNestedList; + + Map? nullableMapWithAnnotations; + + Map? nullableMapWithObject; + + AnEnum? aNullableEnum; + + String? aNullableString; + + Object? aNullableObject; + + Object encode() { + return [ + aNullableBool, + aNullableInt, + aNullableInt64, + aNullableDouble, + aNullableByteArray, + aNullable4ByteArray, + aNullable8ByteArray, + aNullableFloatArray, + aNullableList, + aNullableMap, + nullableNestedList, + nullableMapWithAnnotations, + nullableMapWithObject, + aNullableEnum?.index, + aNullableString, + aNullableObject, + ]; + } + + static AllNullableTypesWithoutRecursion decode(Object result) { + result as List; + return AllNullableTypesWithoutRecursion( + aNullableBool: result[0] as bool?, + aNullableInt: result[1] as int?, + aNullableInt64: result[2] as int?, + aNullableDouble: result[3] as double?, + aNullableByteArray: result[4] as Uint8List?, + aNullable4ByteArray: result[5] as Int32List?, + aNullable8ByteArray: result[6] as Int64List?, + aNullableFloatArray: result[7] as Float64List?, + aNullableList: result[8] as List?, + aNullableMap: result[9] as Map?, + nullableNestedList: (result[10] as List?)?.cast?>(), + nullableMapWithAnnotations: + (result[11] as Map?)?.cast(), + nullableMapWithObject: + (result[12] as Map?)?.cast(), + aNullableEnum: + result[13] != null ? AnEnum.values[result[13]! as int] : null, + aNullableString: result[14] as String?, + aNullableObject: result[15], ); } } @@ -228,16 +337,20 @@ class AllNullableTypes { class AllClassesWrapper { AllClassesWrapper({ required this.allNullableTypes, + this.allNullableTypesWithoutRecursion, this.allTypes, }); AllNullableTypes allNullableTypes; + AllNullableTypesWithoutRecursion? allNullableTypesWithoutRecursion; + AllTypes? allTypes; Object encode() { return [ allNullableTypes.encode(), + allNullableTypesWithoutRecursion?.encode(), allTypes?.encode(), ]; } @@ -246,8 +359,11 @@ class AllClassesWrapper { result as List; return AllClassesWrapper( allNullableTypes: AllNullableTypes.decode(result[0]! as List), - allTypes: result[1] != null - ? AllTypes.decode(result[1]! as List) + allNullableTypesWithoutRecursion: result[1] != null + ? AllNullableTypesWithoutRecursion.decode(result[1]! as List) + : null, + allTypes: result[2] != null + ? AllTypes.decode(result[2]! as List) : null, ); } @@ -285,12 +401,15 @@ class _HostIntegrationCoreApiCodec extends StandardMessageCodec { } else if (value is AllNullableTypes) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is AllTypes) { + } else if (value is AllNullableTypesWithoutRecursion) { buffer.putUint8(130); writeValue(buffer, value.encode()); - } else if (value is TestMessage) { + } else if (value is AllTypes) { buffer.putUint8(131); writeValue(buffer, value.encode()); + } else if (value is TestMessage) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -304,8 +423,10 @@ class _HostIntegrationCoreApiCodec extends StandardMessageCodec { case 129: return AllNullableTypes.decode(readValue(buffer)!); case 130: - return AllTypes.decode(readValue(buffer)!); + return AllNullableTypesWithoutRecursion.decode(readValue(buffer)!); case 131: + return AllTypes.decode(readValue(buffer)!); + case 132: return TestMessage.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -874,6 +995,33 @@ class HostIntegrationCoreApi { } } + /// Returns the passed object, to test serialization and deserialization. + Future + echoAllNullableTypesWithoutRecursion( + AllNullableTypesWithoutRecursion? everything) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAllNullableTypesWithoutRecursion'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([everything]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as AllNullableTypesWithoutRecursion?); + } + } + /// Returns the inner `aString` value from the wrapped object, to test /// sending of nested objects. Future extractNestedNullableString(AllClassesWrapper wrapper) async { @@ -964,6 +1112,39 @@ class HostIntegrationCoreApi { } } + /// Returns passed in arguments of multiple types. + Future + sendMultipleNullableTypesWithoutRecursion(bool? aNullableBool, + int? aNullableInt, String? aNullableString) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.sendMultipleNullableTypesWithoutRecursion'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([aNullableBool, aNullableInt, aNullableString]) + as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as AllNullableTypesWithoutRecursion?)!; + } + } + /// Returns passed in int. Future echoNullableInt(int? aNullableInt) async { const String __pigeon_channelName = @@ -1671,6 +1852,33 @@ class HostIntegrationCoreApi { } } + /// Returns the passed object, to test serialization and deserialization. + Future + echoAsyncNullableAllNullableTypesWithoutRecursion( + AllNullableTypesWithoutRecursion? everything) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncNullableAllNullableTypesWithoutRecursion'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([everything]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as AllNullableTypesWithoutRecursion?); + } + } + /// Returns passed in int asynchronously. Future echoAsyncNullableInt(int? anInt) async { const String __pigeon_channelName = @@ -2057,6 +2265,64 @@ class HostIntegrationCoreApi { } } + Future + callFlutterEchoAllNullableTypesWithoutRecursion( + AllNullableTypesWithoutRecursion? everything) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoAllNullableTypesWithoutRecursion'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([everything]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as AllNullableTypesWithoutRecursion?); + } + } + + Future + callFlutterSendMultipleNullableTypesWithoutRecursion(bool? aNullableBool, + int? aNullableInt, String? aNullableString) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterSendMultipleNullableTypesWithoutRecursion'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([aNullableBool, aNullableInt, aNullableString]) + as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as AllNullableTypesWithoutRecursion?)!; + } + } + Future callFlutterEchoBool(bool aBool) async { const String __pigeon_channelName = 'dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoBool'; @@ -2499,12 +2765,15 @@ class _FlutterIntegrationCoreApiCodec extends StandardMessageCodec { } else if (value is AllNullableTypes) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is AllTypes) { + } else if (value is AllNullableTypesWithoutRecursion) { buffer.putUint8(130); writeValue(buffer, value.encode()); - } else if (value is TestMessage) { + } else if (value is AllTypes) { buffer.putUint8(131); writeValue(buffer, value.encode()); + } else if (value is TestMessage) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -2518,8 +2787,10 @@ class _FlutterIntegrationCoreApiCodec extends StandardMessageCodec { case 129: return AllNullableTypes.decode(readValue(buffer)!); case 130: - return AllTypes.decode(readValue(buffer)!); + return AllNullableTypesWithoutRecursion.decode(readValue(buffer)!); case 131: + return AllTypes.decode(readValue(buffer)!); + case 132: return TestMessage.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -2555,6 +2826,16 @@ abstract class FlutterIntegrationCoreApi { AllNullableTypes sendMultipleNullableTypes( bool? aNullableBool, int? aNullableInt, String? aNullableString); + /// Returns the passed object, to test serialization and deserialization. + AllNullableTypesWithoutRecursion? echoAllNullableTypesWithoutRecursion( + AllNullableTypesWithoutRecursion? everything); + + /// Returns passed in arguments of multiple types. + /// + /// Tests multiple-arity FlutterApi handling. + AllNullableTypesWithoutRecursion sendMultipleNullableTypesWithoutRecursion( + bool? aNullableBool, int? aNullableInt, String? aNullableString); + /// Returns the passed boolean, to test serialization and deserialization. bool echoBool(bool aBool); @@ -2763,6 +3044,64 @@ abstract class FlutterIntegrationCoreApi { }); } } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAllNullableTypesWithoutRecursion', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAllNullableTypesWithoutRecursion was null.'); + final List args = (message as List?)!; + final AllNullableTypesWithoutRecursion? arg_everything = + (args[0] as AllNullableTypesWithoutRecursion?); + try { + final AllNullableTypesWithoutRecursion? output = + api.echoAllNullableTypesWithoutRecursion(arg_everything); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.sendMultipleNullableTypesWithoutRecursion', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.sendMultipleNullableTypesWithoutRecursion was null.'); + final List args = (message as List?)!; + final bool? arg_aNullableBool = (args[0] as bool?); + final int? arg_aNullableInt = (args[1] as int?); + final String? arg_aNullableString = (args[2] as String?); + try { + final AllNullableTypesWithoutRecursion output = + api.sendMultipleNullableTypesWithoutRecursion( + arg_aNullableBool, arg_aNullableInt, arg_aNullableString); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt index b97e72c01469..002c621784a8 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt @@ -142,7 +142,8 @@ data class AllNullableTypes( val nullableMapWithObject: Map? = null, val aNullableEnum: AnEnum? = null, val aNullableString: String? = null, - val aNullableObject: Any? = null + val aNullableObject: Any? = null, + val allNullableTypes: AllNullableTypes? = null ) { companion object { @Suppress("UNCHECKED_CAST") @@ -163,7 +164,96 @@ data class AllNullableTypes( val aNullableEnum: AnEnum? = (list[13] as Int?)?.let { AnEnum.ofRaw(it) } val aNullableString = list[14] as String? val aNullableObject = list[15] + val allNullableTypes: AllNullableTypes? = + (list[16] as List?)?.let { AllNullableTypes.fromList(it) } return AllNullableTypes( + aNullableBool, + aNullableInt, + aNullableInt64, + aNullableDouble, + aNullableByteArray, + aNullable4ByteArray, + aNullable8ByteArray, + aNullableFloatArray, + aNullableList, + aNullableMap, + nullableNestedList, + nullableMapWithAnnotations, + nullableMapWithObject, + aNullableEnum, + aNullableString, + aNullableObject, + allNullableTypes) + } + } + + fun toList(): List { + return listOf( + aNullableBool, + aNullableInt, + aNullableInt64, + aNullableDouble, + aNullableByteArray, + aNullable4ByteArray, + aNullable8ByteArray, + aNullableFloatArray, + aNullableList, + aNullableMap, + nullableNestedList, + nullableMapWithAnnotations, + nullableMapWithObject, + aNullableEnum?.raw, + aNullableString, + aNullableObject, + allNullableTypes?.toList(), + ) + } +} + +/** + * The primary purpose for this class is to ensure coverage of Swift structs with nullable items, as + * the primary [AllNullableTypes] class is being used to test Swift classes. + * + * Generated class from Pigeon that represents data sent in messages. + */ +data class AllNullableTypesWithoutRecursion( + val aNullableBool: Boolean? = null, + val aNullableInt: Long? = null, + val aNullableInt64: Long? = null, + val aNullableDouble: Double? = null, + val aNullableByteArray: ByteArray? = null, + val aNullable4ByteArray: IntArray? = null, + val aNullable8ByteArray: LongArray? = null, + val aNullableFloatArray: DoubleArray? = null, + val aNullableList: List? = null, + val aNullableMap: Map? = null, + val nullableNestedList: List?>? = null, + val nullableMapWithAnnotations: Map? = null, + val nullableMapWithObject: Map? = null, + val aNullableEnum: AnEnum? = null, + val aNullableString: String? = null, + val aNullableObject: Any? = null +) { + companion object { + @Suppress("UNCHECKED_CAST") + fun fromList(list: List): AllNullableTypesWithoutRecursion { + val aNullableBool = list[0] as Boolean? + val aNullableInt = list[1].let { if (it is Int) it.toLong() else it as Long? } + val aNullableInt64 = list[2].let { if (it is Int) it.toLong() else it as Long? } + val aNullableDouble = list[3] as Double? + val aNullableByteArray = list[4] as ByteArray? + val aNullable4ByteArray = list[5] as IntArray? + val aNullable8ByteArray = list[6] as LongArray? + val aNullableFloatArray = list[7] as DoubleArray? + val aNullableList = list[8] as List? + val aNullableMap = list[9] as Map? + val nullableNestedList = list[10] as List?>? + val nullableMapWithAnnotations = list[11] as Map? + val nullableMapWithObject = list[12] as Map? + val aNullableEnum: AnEnum? = (list[13] as Int?)?.let { AnEnum.ofRaw(it) } + val aNullableString = list[14] as String? + val aNullableObject = list[15] + return AllNullableTypesWithoutRecursion( aNullableBool, aNullableInt, aNullableInt64, @@ -216,20 +306,24 @@ data class AllNullableTypes( */ data class AllClassesWrapper( val allNullableTypes: AllNullableTypes, + val allNullableTypesWithoutRecursion: AllNullableTypesWithoutRecursion? = null, val allTypes: AllTypes? = null ) { companion object { @Suppress("UNCHECKED_CAST") fun fromList(list: List): AllClassesWrapper { val allNullableTypes = AllNullableTypes.fromList(list[0] as List) - val allTypes: AllTypes? = (list[1] as List?)?.let { AllTypes.fromList(it) } - return AllClassesWrapper(allNullableTypes, allTypes) + val allNullableTypesWithoutRecursion: AllNullableTypesWithoutRecursion? = + (list[1] as List?)?.let { AllNullableTypesWithoutRecursion.fromList(it) } + val allTypes: AllTypes? = (list[2] as List?)?.let { AllTypes.fromList(it) } + return AllClassesWrapper(allNullableTypes, allNullableTypesWithoutRecursion, allTypes) } } fun toList(): List { return listOf( allNullableTypes.toList(), + allNullableTypesWithoutRecursion?.toList(), allTypes?.toList(), ) } @@ -268,9 +362,14 @@ private object HostIntegrationCoreApiCodec : StandardMessageCodec() { return (readValue(buffer) as? List)?.let { AllNullableTypes.fromList(it) } } 130.toByte() -> { - return (readValue(buffer) as? List)?.let { AllTypes.fromList(it) } + return (readValue(buffer) as? List)?.let { + AllNullableTypesWithoutRecursion.fromList(it) + } } 131.toByte() -> { + return (readValue(buffer) as? List)?.let { AllTypes.fromList(it) } + } + 132.toByte() -> { return (readValue(buffer) as? List)?.let { TestMessage.fromList(it) } } else -> super.readValueOfType(type, buffer) @@ -287,14 +386,18 @@ private object HostIntegrationCoreApiCodec : StandardMessageCodec() { stream.write(129) writeValue(stream, value.toList()) } - is AllTypes -> { + is AllNullableTypesWithoutRecursion -> { stream.write(130) writeValue(stream, value.toList()) } - is TestMessage -> { + is AllTypes -> { stream.write(131) writeValue(stream, value.toList()) } + is TestMessage -> { + stream.write(132) + writeValue(stream, value.toList()) + } else -> super.writeValue(stream, value) } } @@ -345,6 +448,10 @@ interface HostIntegrationCoreApi { fun echoRequiredInt(anInt: Long): Long /** Returns the passed object, to test serialization and deserialization. */ fun echoAllNullableTypes(everything: AllNullableTypes?): AllNullableTypes? + /** Returns the passed object, to test serialization and deserialization. */ + fun echoAllNullableTypesWithoutRecursion( + everything: AllNullableTypesWithoutRecursion? + ): AllNullableTypesWithoutRecursion? /** * Returns the inner `aString` value from the wrapped object, to test sending of nested objects. */ @@ -359,6 +466,12 @@ interface HostIntegrationCoreApi { aNullableInt: Long?, aNullableString: String? ): AllNullableTypes + /** Returns passed in arguments of multiple types. */ + fun sendMultipleNullableTypesWithoutRecursion( + aNullableBool: Boolean?, + aNullableInt: Long?, + aNullableString: String? + ): AllNullableTypesWithoutRecursion /** Returns passed in int. */ fun echoNullableInt(aNullableInt: Long?): Long? /** Returns passed in double. */ @@ -417,6 +530,11 @@ interface HostIntegrationCoreApi { everything: AllNullableTypes?, callback: (Result) -> Unit ) + /** Returns the passed object, to test serialization and deserialization. */ + fun echoAsyncNullableAllNullableTypesWithoutRecursion( + everything: AllNullableTypesWithoutRecursion?, + callback: (Result) -> Unit + ) /** Returns passed in int asynchronously. */ fun echoAsyncNullableInt(anInt: Long?, callback: (Result) -> Unit) /** Returns passed in double asynchronously. */ @@ -459,6 +577,18 @@ interface HostIntegrationCoreApi { callback: (Result) -> Unit ) + fun callFlutterEchoAllNullableTypesWithoutRecursion( + everything: AllNullableTypesWithoutRecursion?, + callback: (Result) -> Unit + ) + + fun callFlutterSendMultipleNullableTypesWithoutRecursion( + aNullableBool: Boolean?, + aNullableInt: Long?, + aNullableString: String?, + callback: (Result) -> Unit + ) + fun callFlutterEchoBool(aBool: Boolean, callback: (Result) -> Unit) fun callFlutterEchoInt(anInt: Long, callback: (Result) -> Unit) @@ -915,6 +1045,28 @@ interface HostIntegrationCoreApi { channel.setMessageHandler(null) } } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAllNullableTypesWithoutRecursion", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val everythingArg = args[0] as AllNullableTypesWithoutRecursion? + var wrapped: List + try { + wrapped = listOf(api.echoAllNullableTypesWithoutRecursion(everythingArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } run { val channel = BasicMessageChannel( @@ -986,6 +1138,33 @@ interface HostIntegrationCoreApi { channel.setMessageHandler(null) } } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.sendMultipleNullableTypesWithoutRecursion", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val aNullableBoolArg = args[0] as Boolean? + val aNullableIntArg = args[1].let { if (it is Int) it.toLong() else it as Long? } + val aNullableStringArg = args[2] as String? + var wrapped: List + try { + wrapped = + listOf( + api.sendMultipleNullableTypesWithoutRecursion( + aNullableBoolArg, aNullableIntArg, aNullableStringArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } run { val channel = BasicMessageChannel( @@ -1579,6 +1758,31 @@ interface HostIntegrationCoreApi { channel.setMessageHandler(null) } } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncNullableAllNullableTypesWithoutRecursion", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val everythingArg = args[0] as AllNullableTypesWithoutRecursion? + api.echoAsyncNullableAllNullableTypesWithoutRecursion(everythingArg) { + result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } run { val channel = BasicMessageChannel( @@ -1936,6 +2140,59 @@ interface HostIntegrationCoreApi { channel.setMessageHandler(null) } } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoAllNullableTypesWithoutRecursion", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val everythingArg = args[0] as AllNullableTypesWithoutRecursion? + api.callFlutterEchoAllNullableTypesWithoutRecursion(everythingArg) { + result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterSendMultipleNullableTypesWithoutRecursion", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val aNullableBoolArg = args[0] as Boolean? + val aNullableIntArg = args[1].let { if (it is Int) it.toLong() else it as Long? } + val aNullableStringArg = args[2] as String? + api.callFlutterSendMultipleNullableTypesWithoutRecursion( + aNullableBoolArg, aNullableIntArg, aNullableStringArg) { + result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } run { val channel = BasicMessageChannel( @@ -2335,9 +2592,14 @@ private object FlutterIntegrationCoreApiCodec : StandardMessageCodec() { return (readValue(buffer) as? List)?.let { AllNullableTypes.fromList(it) } } 130.toByte() -> { - return (readValue(buffer) as? List)?.let { AllTypes.fromList(it) } + return (readValue(buffer) as? List)?.let { + AllNullableTypesWithoutRecursion.fromList(it) + } } 131.toByte() -> { + return (readValue(buffer) as? List)?.let { AllTypes.fromList(it) } + } + 132.toByte() -> { return (readValue(buffer) as? List)?.let { TestMessage.fromList(it) } } else -> super.readValueOfType(type, buffer) @@ -2354,14 +2616,18 @@ private object FlutterIntegrationCoreApiCodec : StandardMessageCodec() { stream.write(129) writeValue(stream, value.toList()) } - is AllTypes -> { + is AllNullableTypesWithoutRecursion -> { stream.write(130) writeValue(stream, value.toList()) } - is TestMessage -> { + is AllTypes -> { stream.write(131) writeValue(stream, value.toList()) } + is TestMessage -> { + stream.write(132) + writeValue(stream, value.toList()) + } else -> super.writeValue(stream, value) } } @@ -2510,6 +2776,61 @@ class FlutterIntegrationCoreApi(private val binaryMessenger: BinaryMessenger) { } } } + /** Returns the passed object, to test serialization and deserialization. */ + fun echoAllNullableTypesWithoutRecursion( + everythingArg: AllNullableTypesWithoutRecursion?, + callback: (Result) -> Unit + ) { + val channelName = + "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAllNullableTypesWithoutRecursion" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(everythingArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + val output = it[0] as AllNullableTypesWithoutRecursion? + callback(Result.success(output)) + } + } else { + callback(Result.failure(createConnectionError(channelName))) + } + } + } + /** + * Returns passed in arguments of multiple types. + * + * Tests multiple-arity FlutterApi handling. + */ + fun sendMultipleNullableTypesWithoutRecursion( + aNullableBoolArg: Boolean?, + aNullableIntArg: Long?, + aNullableStringArg: String?, + callback: (Result) -> Unit + ) { + val channelName = + "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.sendMultipleNullableTypesWithoutRecursion" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(aNullableBoolArg, aNullableIntArg, aNullableStringArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else if (it[0] == null) { + callback( + Result.failure( + FlutterError( + "null-error", + "Flutter api returned null value for non-null return value.", + ""))) + } else { + val output = it[0] as AllNullableTypesWithoutRecursion + callback(Result.success(output)) + } + } else { + callback(Result.failure(createConnectionError(channelName))) + } + } + } /** Returns the passed boolean, to test serialization and deserialization. */ fun echoBool(aBoolArg: Boolean, callback: (Result) -> Unit) { val channelName = diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt index 8b4e8bc4fb18..c64aab7d34f5 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt @@ -4,19 +4,18 @@ package com.example.test_plugin -import androidx.annotation.NonNull import io.flutter.embedding.engine.plugins.FlutterPlugin /** This plugin handles the native side of the integration tests in example/integration_test/. */ class TestPlugin : FlutterPlugin, HostIntegrationCoreApi { var flutterApi: FlutterIntegrationCoreApi? = null - override fun onAttachedToEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { - HostIntegrationCoreApi.setUp(binding.getBinaryMessenger(), this) - flutterApi = FlutterIntegrationCoreApi(binding.getBinaryMessenger()) + override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { + HostIntegrationCoreApi.setUp(binding.binaryMessenger, this) + flutterApi = FlutterIntegrationCoreApi(binding.binaryMessenger) } - override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {} + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {} // HostIntegrationCoreApi @@ -30,6 +29,12 @@ class TestPlugin : FlutterPlugin, HostIntegrationCoreApi { return everything } + override fun echoAllNullableTypesWithoutRecursion( + everything: AllNullableTypesWithoutRecursion? + ): AllNullableTypesWithoutRecursion? { + return everything + } + override fun throwError(): Any? { throw Exception("An error") } @@ -113,6 +118,17 @@ class TestPlugin : FlutterPlugin, HostIntegrationCoreApi { aNullableString = aNullableString) } + override fun sendMultipleNullableTypesWithoutRecursion( + aNullableBool: Boolean?, + aNullableInt: Long?, + aNullableString: String? + ): AllNullableTypesWithoutRecursion { + return AllNullableTypesWithoutRecursion( + aNullableBool = aNullableBool, + aNullableInt = aNullableInt, + aNullableString = aNullableString) + } + override fun echoNullableInt(aNullableInt: Long?): Long? { return aNullableInt } @@ -184,6 +200,13 @@ class TestPlugin : FlutterPlugin, HostIntegrationCoreApi { callback(Result.success(everything)) } + override fun echoAsyncNullableAllNullableTypesWithoutRecursion( + everything: AllNullableTypesWithoutRecursion?, + callback: (Result) -> Unit + ) { + callback(Result.success(everything)) + } + override fun echoAsyncInt(anInt: Long, callback: (Result) -> Unit) { callback(Result.success(anInt)) } @@ -292,6 +315,25 @@ class TestPlugin : FlutterPlugin, HostIntegrationCoreApi { } } + override fun callFlutterEchoAllNullableTypesWithoutRecursion( + everything: AllNullableTypesWithoutRecursion?, + callback: (Result) -> Unit + ) { + flutterApi!!.echoAllNullableTypesWithoutRecursion(everything) { echo -> callback(echo) } + } + + override fun callFlutterSendMultipleNullableTypesWithoutRecursion( + aNullableBool: Boolean?, + aNullableInt: Long?, + aNullableString: String?, + callback: (Result) -> Unit + ) { + flutterApi!!.sendMultipleNullableTypesWithoutRecursion( + aNullableBool, aNullableInt, aNullableString) { echo -> + callback(echo) + } + } + override fun callFlutterEchoBool(aBool: Boolean, callback: (Result) -> Unit) { flutterApi!!.echoBool(aBool) { echo -> callback(echo) } } diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/test/kotlin/com/example/test_plugin/AllDatatypesTest.kt b/packages/pigeon/platform_tests/test_plugin/android/src/test/kotlin/com/example/test_plugin/AllDatatypesTest.kt index 7a506d3d1307..81207b03f586 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/test/kotlin/com/example/test_plugin/AllDatatypesTest.kt +++ b/packages/pigeon/platform_tests/test_plugin/android/src/test/kotlin/com/example/test_plugin/AllDatatypesTest.kt @@ -160,6 +160,7 @@ internal class AllDatatypesTest : TestCase() { null, null, null, + null, null) val everything2 = AllNullableTypes.fromList(list2) diff --git a/packages/pigeon/platform_tests/test_plugin/ios/Classes/CoreTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/ios/Classes/CoreTests.gen.swift index 62682b44c2c9..3a45a12a40fe 100644 --- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/CoreTests.gen.swift +++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/CoreTests.gen.swift @@ -128,7 +128,139 @@ struct AllTypes { /// A class containing all supported nullable types. /// /// Generated class from Pigeon that represents data sent in messages. -struct AllNullableTypes { +class AllNullableTypes { + init( + aNullableBool: Bool? = nil, + aNullableInt: Int64? = nil, + aNullableInt64: Int64? = nil, + aNullableDouble: Double? = nil, + aNullableByteArray: FlutterStandardTypedData? = nil, + aNullable4ByteArray: FlutterStandardTypedData? = nil, + aNullable8ByteArray: FlutterStandardTypedData? = nil, + aNullableFloatArray: FlutterStandardTypedData? = nil, + aNullableList: [Any?]? = nil, + aNullableMap: [AnyHashable: Any?]? = nil, + nullableNestedList: [[Bool?]?]? = nil, + nullableMapWithAnnotations: [String?: String?]? = nil, + nullableMapWithObject: [String?: Any?]? = nil, + aNullableEnum: AnEnum? = nil, + aNullableString: String? = nil, + aNullableObject: Any? = nil, + allNullableTypes: AllNullableTypes? = nil + ) { + self.aNullableBool = aNullableBool + self.aNullableInt = aNullableInt + self.aNullableInt64 = aNullableInt64 + self.aNullableDouble = aNullableDouble + self.aNullableByteArray = aNullableByteArray + self.aNullable4ByteArray = aNullable4ByteArray + self.aNullable8ByteArray = aNullable8ByteArray + self.aNullableFloatArray = aNullableFloatArray + self.aNullableList = aNullableList + self.aNullableMap = aNullableMap + self.nullableNestedList = nullableNestedList + self.nullableMapWithAnnotations = nullableMapWithAnnotations + self.nullableMapWithObject = nullableMapWithObject + self.aNullableEnum = aNullableEnum + self.aNullableString = aNullableString + self.aNullableObject = aNullableObject + self.allNullableTypes = allNullableTypes + } + var aNullableBool: Bool? + var aNullableInt: Int64? + var aNullableInt64: Int64? + var aNullableDouble: Double? + var aNullableByteArray: FlutterStandardTypedData? + var aNullable4ByteArray: FlutterStandardTypedData? + var aNullable8ByteArray: FlutterStandardTypedData? + var aNullableFloatArray: FlutterStandardTypedData? + var aNullableList: [Any?]? + var aNullableMap: [AnyHashable: Any?]? + var nullableNestedList: [[Bool?]?]? + var nullableMapWithAnnotations: [String?: String?]? + var nullableMapWithObject: [String?: Any?]? + var aNullableEnum: AnEnum? + var aNullableString: String? + var aNullableObject: Any? + var allNullableTypes: AllNullableTypes? + + static func fromList(_ list: [Any?]) -> AllNullableTypes? { + let aNullableBool: Bool? = nilOrValue(list[0]) + let aNullableInt: Int64? = + isNullish(list[1]) ? nil : (list[1] is Int64? ? list[1] as! Int64? : Int64(list[1] as! Int32)) + let aNullableInt64: Int64? = + isNullish(list[2]) ? nil : (list[2] is Int64? ? list[2] as! Int64? : Int64(list[2] as! Int32)) + let aNullableDouble: Double? = nilOrValue(list[3]) + let aNullableByteArray: FlutterStandardTypedData? = nilOrValue(list[4]) + let aNullable4ByteArray: FlutterStandardTypedData? = nilOrValue(list[5]) + let aNullable8ByteArray: FlutterStandardTypedData? = nilOrValue(list[6]) + let aNullableFloatArray: FlutterStandardTypedData? = nilOrValue(list[7]) + let aNullableList: [Any?]? = nilOrValue(list[8]) + let aNullableMap: [AnyHashable: Any?]? = nilOrValue(list[9]) + let nullableNestedList: [[Bool?]?]? = nilOrValue(list[10]) + let nullableMapWithAnnotations: [String?: String?]? = nilOrValue(list[11]) + let nullableMapWithObject: [String?: Any?]? = nilOrValue(list[12]) + var aNullableEnum: AnEnum? = nil + let aNullableEnumEnumVal: Int? = nilOrValue(list[13]) + if let aNullableEnumRawValue = aNullableEnumEnumVal { + aNullableEnum = AnEnum(rawValue: aNullableEnumRawValue)! + } + let aNullableString: String? = nilOrValue(list[14]) + let aNullableObject: Any? = list[15] + var allNullableTypes: AllNullableTypes? = nil + if let allNullableTypesList: [Any?] = nilOrValue(list[16]) { + allNullableTypes = AllNullableTypes.fromList(allNullableTypesList) + } + + return AllNullableTypes( + aNullableBool: aNullableBool, + aNullableInt: aNullableInt, + aNullableInt64: aNullableInt64, + aNullableDouble: aNullableDouble, + aNullableByteArray: aNullableByteArray, + aNullable4ByteArray: aNullable4ByteArray, + aNullable8ByteArray: aNullable8ByteArray, + aNullableFloatArray: aNullableFloatArray, + aNullableList: aNullableList, + aNullableMap: aNullableMap, + nullableNestedList: nullableNestedList, + nullableMapWithAnnotations: nullableMapWithAnnotations, + nullableMapWithObject: nullableMapWithObject, + aNullableEnum: aNullableEnum, + aNullableString: aNullableString, + aNullableObject: aNullableObject, + allNullableTypes: allNullableTypes + ) + } + func toList() -> [Any?] { + return [ + aNullableBool, + aNullableInt, + aNullableInt64, + aNullableDouble, + aNullableByteArray, + aNullable4ByteArray, + aNullable8ByteArray, + aNullableFloatArray, + aNullableList, + aNullableMap, + nullableNestedList, + nullableMapWithAnnotations, + nullableMapWithObject, + aNullableEnum?.rawValue, + aNullableString, + aNullableObject, + allNullableTypes?.toList(), + ] + } +} + +/// The primary purpose for this class is to ensure coverage of Swift structs +/// with nullable items, as the primary [AllNullableTypes] class is being used to +/// test Swift classes. +/// +/// Generated class from Pigeon that represents data sent in messages. +struct AllNullableTypesWithoutRecursion { var aNullableBool: Bool? = nil var aNullableInt: Int64? = nil var aNullableInt64: Int64? = nil @@ -146,7 +278,7 @@ struct AllNullableTypes { var aNullableString: String? = nil var aNullableObject: Any? = nil - static func fromList(_ list: [Any?]) -> AllNullableTypes? { + static func fromList(_ list: [Any?]) -> AllNullableTypesWithoutRecursion? { let aNullableBool: Bool? = nilOrValue(list[0]) let aNullableInt: Int64? = isNullish(list[1]) ? nil : (list[1] is Int64? ? list[1] as! Int64? : Int64(list[1] as! Int32)) @@ -170,7 +302,7 @@ struct AllNullableTypes { let aNullableString: String? = nilOrValue(list[14]) let aNullableObject: Any? = list[15] - return AllNullableTypes( + return AllNullableTypesWithoutRecursion( aNullableBool: aNullableBool, aNullableInt: aNullableInt, aNullableInt64: aNullableInt64, @@ -220,23 +352,31 @@ struct AllNullableTypes { /// Generated class from Pigeon that represents data sent in messages. struct AllClassesWrapper { var allNullableTypes: AllNullableTypes + var allNullableTypesWithoutRecursion: AllNullableTypesWithoutRecursion? = nil var allTypes: AllTypes? = nil static func fromList(_ list: [Any?]) -> AllClassesWrapper? { let allNullableTypes = AllNullableTypes.fromList(list[0] as! [Any?])! + var allNullableTypesWithoutRecursion: AllNullableTypesWithoutRecursion? = nil + if let allNullableTypesWithoutRecursionList: [Any?] = nilOrValue(list[1]) { + allNullableTypesWithoutRecursion = AllNullableTypesWithoutRecursion.fromList( + allNullableTypesWithoutRecursionList) + } var allTypes: AllTypes? = nil - if let allTypesList: [Any?] = nilOrValue(list[1]) { + if let allTypesList: [Any?] = nilOrValue(list[2]) { allTypes = AllTypes.fromList(allTypesList) } return AllClassesWrapper( allNullableTypes: allNullableTypes, + allNullableTypesWithoutRecursion: allNullableTypesWithoutRecursion, allTypes: allTypes ) } func toList() -> [Any?] { return [ allNullableTypes.toList(), + allNullableTypesWithoutRecursion?.toList(), allTypes?.toList(), ] } @@ -270,8 +410,10 @@ private class HostIntegrationCoreApiCodecReader: FlutterStandardReader { case 129: return AllNullableTypes.fromList(self.readValue() as! [Any?]) case 130: - return AllTypes.fromList(self.readValue() as! [Any?]) + return AllNullableTypesWithoutRecursion.fromList(self.readValue() as! [Any?]) case 131: + return AllTypes.fromList(self.readValue() as! [Any?]) + case 132: return TestMessage.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) @@ -287,12 +429,15 @@ private class HostIntegrationCoreApiCodecWriter: FlutterStandardWriter { } else if let value = value as? AllNullableTypes { super.writeByte(129) super.writeValue(value.toList()) - } else if let value = value as? AllTypes { + } else if let value = value as? AllNullableTypesWithoutRecursion { super.writeByte(130) super.writeValue(value.toList()) - } else if let value = value as? TestMessage { + } else if let value = value as? AllTypes { super.writeByte(131) super.writeValue(value.toList()) + } else if let value = value as? TestMessage { + super.writeByte(132) + super.writeValue(value.toList()) } else { super.writeValue(value) } @@ -358,6 +503,9 @@ protocol HostIntegrationCoreApi { func echoRequired(_ anInt: Int64) throws -> Int64 /// Returns the passed object, to test serialization and deserialization. func echo(_ everything: AllNullableTypes?) throws -> AllNullableTypes? + /// Returns the passed object, to test serialization and deserialization. + func echo(_ everything: AllNullableTypesWithoutRecursion?) throws + -> AllNullableTypesWithoutRecursion? /// Returns the inner `aString` value from the wrapped object, to test /// sending of nested objects. func extractNestedNullableString(from wrapper: AllClassesWrapper) throws -> String? @@ -368,6 +516,10 @@ protocol HostIntegrationCoreApi { func sendMultipleNullableTypes( aBool aNullableBool: Bool?, anInt aNullableInt: Int64?, aString aNullableString: String? ) throws -> AllNullableTypes + /// Returns passed in arguments of multiple types. + func sendMultipleNullableTypesWithoutRecursion( + aBool aNullableBool: Bool?, anInt aNullableInt: Int64?, aString aNullableString: String? + ) throws -> AllNullableTypesWithoutRecursion /// Returns passed in int. func echo(_ aNullableInt: Int64?) throws -> Int64? /// Returns passed in double. @@ -425,6 +577,10 @@ protocol HostIntegrationCoreApi { func echoAsync( _ everything: AllNullableTypes?, completion: @escaping (Result) -> Void) + /// Returns the passed object, to test serialization and deserialization. + func echoAsync( + _ everything: AllNullableTypesWithoutRecursion?, + completion: @escaping (Result) -> Void) /// Returns passed in int asynchronously. func echoAsyncNullable(_ anInt: Int64?, completion: @escaping (Result) -> Void) /// Returns passed in double asynchronously. @@ -457,6 +613,12 @@ protocol HostIntegrationCoreApi { func callFlutterSendMultipleNullableTypes( aBool aNullableBool: Bool?, anInt aNullableInt: Int64?, aString aNullableString: String?, completion: @escaping (Result) -> Void) + func callFlutterEcho( + _ everything: AllNullableTypesWithoutRecursion?, + completion: @escaping (Result) -> Void) + func callFlutterSendMultipleNullableTypesWithoutRecursion( + aBool aNullableBool: Bool?, anInt aNullableInt: Int64?, aString aNullableString: String?, + completion: @escaping (Result) -> Void) func callFlutterEcho(_ aBool: Bool, completion: @escaping (Result) -> Void) func callFlutterEcho(_ anInt: Int64, completion: @escaping (Result) -> Void) func callFlutterEcho(_ aDouble: Double, completion: @escaping (Result) -> Void) @@ -830,6 +992,25 @@ class HostIntegrationCoreApiSetup { } else { echoAllNullableTypesChannel.setMessageHandler(nil) } + /// Returns the passed object, to test serialization and deserialization. + let echoAllNullableTypesWithoutRecursionChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAllNullableTypesWithoutRecursion", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + echoAllNullableTypesWithoutRecursionChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let everythingArg: AllNullableTypesWithoutRecursion? = nilOrValue(args[0]) + do { + let result = try api.echo(everythingArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + echoAllNullableTypesWithoutRecursionChannel.setMessageHandler(nil) + } /// Returns the inner `aString` value from the wrapped object, to test /// sending of nested objects. let extractNestedNullableStringChannel = FlutterBasicMessageChannel( @@ -894,6 +1075,30 @@ class HostIntegrationCoreApiSetup { } else { sendMultipleNullableTypesChannel.setMessageHandler(nil) } + /// Returns passed in arguments of multiple types. + let sendMultipleNullableTypesWithoutRecursionChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.sendMultipleNullableTypesWithoutRecursion", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + sendMultipleNullableTypesWithoutRecursionChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let aNullableBoolArg: Bool? = nilOrValue(args[0]) + let aNullableIntArg: Int64? = + isNullish(args[1]) + ? nil : (args[1] is Int64? ? args[1] as! Int64? : Int64(args[1] as! Int32)) + let aNullableStringArg: String? = nilOrValue(args[2]) + do { + let result = try api.sendMultipleNullableTypesWithoutRecursion( + aBool: aNullableBoolArg, anInt: aNullableIntArg, aString: aNullableStringArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + sendMultipleNullableTypesWithoutRecursionChannel.setMessageHandler(nil) + } /// Returns passed in int. let echoNullableIntChannel = FlutterBasicMessageChannel( name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoNullableInt", @@ -1394,6 +1599,27 @@ class HostIntegrationCoreApiSetup { } else { echoAsyncNullableAllNullableTypesChannel.setMessageHandler(nil) } + /// Returns the passed object, to test serialization and deserialization. + let echoAsyncNullableAllNullableTypesWithoutRecursionChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncNullableAllNullableTypesWithoutRecursion", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + echoAsyncNullableAllNullableTypesWithoutRecursionChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let everythingArg: AllNullableTypesWithoutRecursion? = nilOrValue(args[0]) + api.echoAsync(everythingArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + echoAsyncNullableAllNullableTypesWithoutRecursionChannel.setMessageHandler(nil) + } /// Returns passed in int asynchronously. let echoAsyncNullableIntChannel = FlutterBasicMessageChannel( name: @@ -1704,6 +1930,53 @@ class HostIntegrationCoreApiSetup { } else { callFlutterSendMultipleNullableTypesChannel.setMessageHandler(nil) } + let callFlutterEchoAllNullableTypesWithoutRecursionChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoAllNullableTypesWithoutRecursion", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + callFlutterEchoAllNullableTypesWithoutRecursionChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let everythingArg: AllNullableTypesWithoutRecursion? = nilOrValue(args[0]) + api.callFlutterEcho(everythingArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + callFlutterEchoAllNullableTypesWithoutRecursionChannel.setMessageHandler(nil) + } + let callFlutterSendMultipleNullableTypesWithoutRecursionChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterSendMultipleNullableTypesWithoutRecursion", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + callFlutterSendMultipleNullableTypesWithoutRecursionChannel.setMessageHandler { + message, reply in + let args = message as! [Any?] + let aNullableBoolArg: Bool? = nilOrValue(args[0]) + let aNullableIntArg: Int64? = + isNullish(args[1]) + ? nil : (args[1] is Int64? ? args[1] as! Int64? : Int64(args[1] as! Int32)) + let aNullableStringArg: String? = nilOrValue(args[2]) + api.callFlutterSendMultipleNullableTypesWithoutRecursion( + aBool: aNullableBoolArg, anInt: aNullableIntArg, aString: aNullableStringArg + ) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + callFlutterSendMultipleNullableTypesWithoutRecursionChannel.setMessageHandler(nil) + } let callFlutterEchoBoolChannel = FlutterBasicMessageChannel( name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoBool", @@ -2034,8 +2307,10 @@ private class FlutterIntegrationCoreApiCodecReader: FlutterStandardReader { case 129: return AllNullableTypes.fromList(self.readValue() as! [Any?]) case 130: - return AllTypes.fromList(self.readValue() as! [Any?]) + return AllNullableTypesWithoutRecursion.fromList(self.readValue() as! [Any?]) case 131: + return AllTypes.fromList(self.readValue() as! [Any?]) + case 132: return TestMessage.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) @@ -2051,12 +2326,15 @@ private class FlutterIntegrationCoreApiCodecWriter: FlutterStandardWriter { } else if let value = value as? AllNullableTypes { super.writeByte(129) super.writeValue(value.toList()) - } else if let value = value as? AllTypes { + } else if let value = value as? AllNullableTypesWithoutRecursion { super.writeByte(130) super.writeValue(value.toList()) - } else if let value = value as? TestMessage { + } else if let value = value as? AllTypes { super.writeByte(131) super.writeValue(value.toList()) + } else if let value = value as? TestMessage { + super.writeByte(132) + super.writeValue(value.toList()) } else { super.writeValue(value) } @@ -2104,6 +2382,17 @@ protocol FlutterIntegrationCoreApiProtocol { aBool aNullableBoolArg: Bool?, anInt aNullableIntArg: Int64?, aString aNullableStringArg: String?, completion: @escaping (Result) -> Void) + /// Returns the passed object, to test serialization and deserialization. + func echoNullable( + _ everythingArg: AllNullableTypesWithoutRecursion?, + completion: @escaping (Result) -> Void) + /// Returns passed in arguments of multiple types. + /// + /// Tests multiple-arity FlutterApi handling. + func sendMultipleNullableTypesWithoutRecursion( + aBool aNullableBoolArg: Bool?, anInt aNullableIntArg: Int64?, + aString aNullableStringArg: String?, + completion: @escaping (Result) -> Void) /// Returns the passed boolean, to test serialization and deserialization. func echo(_ aBoolArg: Bool, completion: @escaping (Result) -> Void) /// Returns the passed int, to test serialization and deserialization. @@ -2318,6 +2607,66 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } } } + /// Returns the passed object, to test serialization and deserialization. + func echoNullable( + _ everythingArg: AllNullableTypesWithoutRecursion?, + completion: @escaping (Result) -> Void + ) { + let channelName: String = + "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAllNullableTypesWithoutRecursion" + let channel = FlutterBasicMessageChannel( + name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([everythingArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(FlutterError(code: code, message: message, details: details))) + } else { + let result: AllNullableTypesWithoutRecursion? = nilOrValue(listResponse[0]) + completion(.success(result)) + } + } + } + /// Returns passed in arguments of multiple types. + /// + /// Tests multiple-arity FlutterApi handling. + func sendMultipleNullableTypesWithoutRecursion( + aBool aNullableBoolArg: Bool?, anInt aNullableIntArg: Int64?, + aString aNullableStringArg: String?, + completion: @escaping (Result) -> Void + ) { + let channelName: String = + "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.sendMultipleNullableTypesWithoutRecursion" + let channel = FlutterBasicMessageChannel( + name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([aNullableBoolArg, aNullableIntArg, aNullableStringArg] as [Any?]) { + response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(FlutterError(code: code, message: message, details: details))) + } else if listResponse[0] == nil { + completion( + .failure( + FlutterError( + code: "null-error", + message: "Flutter api returned null value for non-null return value.", details: ""))) + } else { + let result = listResponse[0] as! AllNullableTypesWithoutRecursion + completion(.success(result)) + } + } + } /// Returns the passed boolean, to test serialization and deserialization. func echo(_ aBoolArg: Bool, completion: @escaping (Result) -> Void) { let channelName: String = diff --git a/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift b/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift index 0d04c9544ca1..d444e5c0bacc 100644 --- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift +++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift @@ -10,6 +10,7 @@ extension FlutterError: Error {} /// This plugin handles the native side of the integration tests in /// example/integration_test/. public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { + var flutterAPI: FlutterIntegrationCoreApi public static func register(with registrar: FlutterPluginRegistrar) { @@ -34,6 +35,11 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { func echo(_ everything: AllNullableTypes?) -> AllNullableTypes? { return everything } + func echo(_ everything: AllNullableTypesWithoutRecursion?) throws + -> AllNullableTypesWithoutRecursion? + { + return everything + } func throwError() throws -> Any? { throw FlutterError(code: "code", message: "message", details: "details") @@ -103,6 +109,14 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { return someThings } + func sendMultipleNullableTypesWithoutRecursion( + aBool aNullableBool: Bool?, anInt aNullableInt: Int64?, aString aNullableString: String? + ) throws -> AllNullableTypesWithoutRecursion { + let someThings = AllNullableTypesWithoutRecursion( + aNullableBool: aNullableBool, aNullableInt: aNullableInt, aNullableString: aNullableString) + return someThings + } + func echo(_ aNullableInt: Int64?) -> Int64? { return aNullableInt } @@ -186,6 +200,13 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { completion(.success(everything)) } + func echoAsync( + _ everything: AllNullableTypesWithoutRecursion?, + completion: @escaping (Result) -> Void + ) { + completion(.success(everything)) + } + func echoAsync(_ anInt: Int64, completion: @escaping (Result) -> Void) { completion(.success(anInt)) } @@ -331,6 +352,20 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { } } + func callFlutterEcho( + _ everything: AllNullableTypesWithoutRecursion?, + completion: @escaping (Result) -> Void + ) { + flutterAPI.echoNullable(everything) { response in + switch response { + case .success(let res): + completion(.success(res)) + case .failure(let error): + completion(.failure(error)) + } + } + } + func callFlutterSendMultipleNullableTypes( aBool aNullableBool: Bool?, anInt aNullableInt: Int64?, @@ -351,6 +386,24 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { } } + func callFlutterSendMultipleNullableTypesWithoutRecursion( + aBool aNullableBool: Bool?, anInt aNullableInt: Int64?, aString aNullableString: String?, + completion: @escaping (Result) -> Void + ) { + flutterAPI.sendMultipleNullableTypesWithoutRecursion( + aBool: aNullableBool, + anInt: aNullableInt, + aString: aNullableString + ) { response in + switch response { + case .success(let res): + completion(.success(res)) + case .failure(let error): + completion(.failure(error)) + } + } + } + func callFlutterEcho(_ aBool: Bool, completion: @escaping (Result) -> Void) { flutterAPI.echo(aBool) { response in switch response { diff --git a/packages/pigeon/platform_tests/test_plugin/macos/Classes/CoreTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/macos/Classes/CoreTests.gen.swift index 62682b44c2c9..3a45a12a40fe 100644 --- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/CoreTests.gen.swift +++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/CoreTests.gen.swift @@ -128,7 +128,139 @@ struct AllTypes { /// A class containing all supported nullable types. /// /// Generated class from Pigeon that represents data sent in messages. -struct AllNullableTypes { +class AllNullableTypes { + init( + aNullableBool: Bool? = nil, + aNullableInt: Int64? = nil, + aNullableInt64: Int64? = nil, + aNullableDouble: Double? = nil, + aNullableByteArray: FlutterStandardTypedData? = nil, + aNullable4ByteArray: FlutterStandardTypedData? = nil, + aNullable8ByteArray: FlutterStandardTypedData? = nil, + aNullableFloatArray: FlutterStandardTypedData? = nil, + aNullableList: [Any?]? = nil, + aNullableMap: [AnyHashable: Any?]? = nil, + nullableNestedList: [[Bool?]?]? = nil, + nullableMapWithAnnotations: [String?: String?]? = nil, + nullableMapWithObject: [String?: Any?]? = nil, + aNullableEnum: AnEnum? = nil, + aNullableString: String? = nil, + aNullableObject: Any? = nil, + allNullableTypes: AllNullableTypes? = nil + ) { + self.aNullableBool = aNullableBool + self.aNullableInt = aNullableInt + self.aNullableInt64 = aNullableInt64 + self.aNullableDouble = aNullableDouble + self.aNullableByteArray = aNullableByteArray + self.aNullable4ByteArray = aNullable4ByteArray + self.aNullable8ByteArray = aNullable8ByteArray + self.aNullableFloatArray = aNullableFloatArray + self.aNullableList = aNullableList + self.aNullableMap = aNullableMap + self.nullableNestedList = nullableNestedList + self.nullableMapWithAnnotations = nullableMapWithAnnotations + self.nullableMapWithObject = nullableMapWithObject + self.aNullableEnum = aNullableEnum + self.aNullableString = aNullableString + self.aNullableObject = aNullableObject + self.allNullableTypes = allNullableTypes + } + var aNullableBool: Bool? + var aNullableInt: Int64? + var aNullableInt64: Int64? + var aNullableDouble: Double? + var aNullableByteArray: FlutterStandardTypedData? + var aNullable4ByteArray: FlutterStandardTypedData? + var aNullable8ByteArray: FlutterStandardTypedData? + var aNullableFloatArray: FlutterStandardTypedData? + var aNullableList: [Any?]? + var aNullableMap: [AnyHashable: Any?]? + var nullableNestedList: [[Bool?]?]? + var nullableMapWithAnnotations: [String?: String?]? + var nullableMapWithObject: [String?: Any?]? + var aNullableEnum: AnEnum? + var aNullableString: String? + var aNullableObject: Any? + var allNullableTypes: AllNullableTypes? + + static func fromList(_ list: [Any?]) -> AllNullableTypes? { + let aNullableBool: Bool? = nilOrValue(list[0]) + let aNullableInt: Int64? = + isNullish(list[1]) ? nil : (list[1] is Int64? ? list[1] as! Int64? : Int64(list[1] as! Int32)) + let aNullableInt64: Int64? = + isNullish(list[2]) ? nil : (list[2] is Int64? ? list[2] as! Int64? : Int64(list[2] as! Int32)) + let aNullableDouble: Double? = nilOrValue(list[3]) + let aNullableByteArray: FlutterStandardTypedData? = nilOrValue(list[4]) + let aNullable4ByteArray: FlutterStandardTypedData? = nilOrValue(list[5]) + let aNullable8ByteArray: FlutterStandardTypedData? = nilOrValue(list[6]) + let aNullableFloatArray: FlutterStandardTypedData? = nilOrValue(list[7]) + let aNullableList: [Any?]? = nilOrValue(list[8]) + let aNullableMap: [AnyHashable: Any?]? = nilOrValue(list[9]) + let nullableNestedList: [[Bool?]?]? = nilOrValue(list[10]) + let nullableMapWithAnnotations: [String?: String?]? = nilOrValue(list[11]) + let nullableMapWithObject: [String?: Any?]? = nilOrValue(list[12]) + var aNullableEnum: AnEnum? = nil + let aNullableEnumEnumVal: Int? = nilOrValue(list[13]) + if let aNullableEnumRawValue = aNullableEnumEnumVal { + aNullableEnum = AnEnum(rawValue: aNullableEnumRawValue)! + } + let aNullableString: String? = nilOrValue(list[14]) + let aNullableObject: Any? = list[15] + var allNullableTypes: AllNullableTypes? = nil + if let allNullableTypesList: [Any?] = nilOrValue(list[16]) { + allNullableTypes = AllNullableTypes.fromList(allNullableTypesList) + } + + return AllNullableTypes( + aNullableBool: aNullableBool, + aNullableInt: aNullableInt, + aNullableInt64: aNullableInt64, + aNullableDouble: aNullableDouble, + aNullableByteArray: aNullableByteArray, + aNullable4ByteArray: aNullable4ByteArray, + aNullable8ByteArray: aNullable8ByteArray, + aNullableFloatArray: aNullableFloatArray, + aNullableList: aNullableList, + aNullableMap: aNullableMap, + nullableNestedList: nullableNestedList, + nullableMapWithAnnotations: nullableMapWithAnnotations, + nullableMapWithObject: nullableMapWithObject, + aNullableEnum: aNullableEnum, + aNullableString: aNullableString, + aNullableObject: aNullableObject, + allNullableTypes: allNullableTypes + ) + } + func toList() -> [Any?] { + return [ + aNullableBool, + aNullableInt, + aNullableInt64, + aNullableDouble, + aNullableByteArray, + aNullable4ByteArray, + aNullable8ByteArray, + aNullableFloatArray, + aNullableList, + aNullableMap, + nullableNestedList, + nullableMapWithAnnotations, + nullableMapWithObject, + aNullableEnum?.rawValue, + aNullableString, + aNullableObject, + allNullableTypes?.toList(), + ] + } +} + +/// The primary purpose for this class is to ensure coverage of Swift structs +/// with nullable items, as the primary [AllNullableTypes] class is being used to +/// test Swift classes. +/// +/// Generated class from Pigeon that represents data sent in messages. +struct AllNullableTypesWithoutRecursion { var aNullableBool: Bool? = nil var aNullableInt: Int64? = nil var aNullableInt64: Int64? = nil @@ -146,7 +278,7 @@ struct AllNullableTypes { var aNullableString: String? = nil var aNullableObject: Any? = nil - static func fromList(_ list: [Any?]) -> AllNullableTypes? { + static func fromList(_ list: [Any?]) -> AllNullableTypesWithoutRecursion? { let aNullableBool: Bool? = nilOrValue(list[0]) let aNullableInt: Int64? = isNullish(list[1]) ? nil : (list[1] is Int64? ? list[1] as! Int64? : Int64(list[1] as! Int32)) @@ -170,7 +302,7 @@ struct AllNullableTypes { let aNullableString: String? = nilOrValue(list[14]) let aNullableObject: Any? = list[15] - return AllNullableTypes( + return AllNullableTypesWithoutRecursion( aNullableBool: aNullableBool, aNullableInt: aNullableInt, aNullableInt64: aNullableInt64, @@ -220,23 +352,31 @@ struct AllNullableTypes { /// Generated class from Pigeon that represents data sent in messages. struct AllClassesWrapper { var allNullableTypes: AllNullableTypes + var allNullableTypesWithoutRecursion: AllNullableTypesWithoutRecursion? = nil var allTypes: AllTypes? = nil static func fromList(_ list: [Any?]) -> AllClassesWrapper? { let allNullableTypes = AllNullableTypes.fromList(list[0] as! [Any?])! + var allNullableTypesWithoutRecursion: AllNullableTypesWithoutRecursion? = nil + if let allNullableTypesWithoutRecursionList: [Any?] = nilOrValue(list[1]) { + allNullableTypesWithoutRecursion = AllNullableTypesWithoutRecursion.fromList( + allNullableTypesWithoutRecursionList) + } var allTypes: AllTypes? = nil - if let allTypesList: [Any?] = nilOrValue(list[1]) { + if let allTypesList: [Any?] = nilOrValue(list[2]) { allTypes = AllTypes.fromList(allTypesList) } return AllClassesWrapper( allNullableTypes: allNullableTypes, + allNullableTypesWithoutRecursion: allNullableTypesWithoutRecursion, allTypes: allTypes ) } func toList() -> [Any?] { return [ allNullableTypes.toList(), + allNullableTypesWithoutRecursion?.toList(), allTypes?.toList(), ] } @@ -270,8 +410,10 @@ private class HostIntegrationCoreApiCodecReader: FlutterStandardReader { case 129: return AllNullableTypes.fromList(self.readValue() as! [Any?]) case 130: - return AllTypes.fromList(self.readValue() as! [Any?]) + return AllNullableTypesWithoutRecursion.fromList(self.readValue() as! [Any?]) case 131: + return AllTypes.fromList(self.readValue() as! [Any?]) + case 132: return TestMessage.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) @@ -287,12 +429,15 @@ private class HostIntegrationCoreApiCodecWriter: FlutterStandardWriter { } else if let value = value as? AllNullableTypes { super.writeByte(129) super.writeValue(value.toList()) - } else if let value = value as? AllTypes { + } else if let value = value as? AllNullableTypesWithoutRecursion { super.writeByte(130) super.writeValue(value.toList()) - } else if let value = value as? TestMessage { + } else if let value = value as? AllTypes { super.writeByte(131) super.writeValue(value.toList()) + } else if let value = value as? TestMessage { + super.writeByte(132) + super.writeValue(value.toList()) } else { super.writeValue(value) } @@ -358,6 +503,9 @@ protocol HostIntegrationCoreApi { func echoRequired(_ anInt: Int64) throws -> Int64 /// Returns the passed object, to test serialization and deserialization. func echo(_ everything: AllNullableTypes?) throws -> AllNullableTypes? + /// Returns the passed object, to test serialization and deserialization. + func echo(_ everything: AllNullableTypesWithoutRecursion?) throws + -> AllNullableTypesWithoutRecursion? /// Returns the inner `aString` value from the wrapped object, to test /// sending of nested objects. func extractNestedNullableString(from wrapper: AllClassesWrapper) throws -> String? @@ -368,6 +516,10 @@ protocol HostIntegrationCoreApi { func sendMultipleNullableTypes( aBool aNullableBool: Bool?, anInt aNullableInt: Int64?, aString aNullableString: String? ) throws -> AllNullableTypes + /// Returns passed in arguments of multiple types. + func sendMultipleNullableTypesWithoutRecursion( + aBool aNullableBool: Bool?, anInt aNullableInt: Int64?, aString aNullableString: String? + ) throws -> AllNullableTypesWithoutRecursion /// Returns passed in int. func echo(_ aNullableInt: Int64?) throws -> Int64? /// Returns passed in double. @@ -425,6 +577,10 @@ protocol HostIntegrationCoreApi { func echoAsync( _ everything: AllNullableTypes?, completion: @escaping (Result) -> Void) + /// Returns the passed object, to test serialization and deserialization. + func echoAsync( + _ everything: AllNullableTypesWithoutRecursion?, + completion: @escaping (Result) -> Void) /// Returns passed in int asynchronously. func echoAsyncNullable(_ anInt: Int64?, completion: @escaping (Result) -> Void) /// Returns passed in double asynchronously. @@ -457,6 +613,12 @@ protocol HostIntegrationCoreApi { func callFlutterSendMultipleNullableTypes( aBool aNullableBool: Bool?, anInt aNullableInt: Int64?, aString aNullableString: String?, completion: @escaping (Result) -> Void) + func callFlutterEcho( + _ everything: AllNullableTypesWithoutRecursion?, + completion: @escaping (Result) -> Void) + func callFlutterSendMultipleNullableTypesWithoutRecursion( + aBool aNullableBool: Bool?, anInt aNullableInt: Int64?, aString aNullableString: String?, + completion: @escaping (Result) -> Void) func callFlutterEcho(_ aBool: Bool, completion: @escaping (Result) -> Void) func callFlutterEcho(_ anInt: Int64, completion: @escaping (Result) -> Void) func callFlutterEcho(_ aDouble: Double, completion: @escaping (Result) -> Void) @@ -830,6 +992,25 @@ class HostIntegrationCoreApiSetup { } else { echoAllNullableTypesChannel.setMessageHandler(nil) } + /// Returns the passed object, to test serialization and deserialization. + let echoAllNullableTypesWithoutRecursionChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAllNullableTypesWithoutRecursion", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + echoAllNullableTypesWithoutRecursionChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let everythingArg: AllNullableTypesWithoutRecursion? = nilOrValue(args[0]) + do { + let result = try api.echo(everythingArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + echoAllNullableTypesWithoutRecursionChannel.setMessageHandler(nil) + } /// Returns the inner `aString` value from the wrapped object, to test /// sending of nested objects. let extractNestedNullableStringChannel = FlutterBasicMessageChannel( @@ -894,6 +1075,30 @@ class HostIntegrationCoreApiSetup { } else { sendMultipleNullableTypesChannel.setMessageHandler(nil) } + /// Returns passed in arguments of multiple types. + let sendMultipleNullableTypesWithoutRecursionChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.sendMultipleNullableTypesWithoutRecursion", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + sendMultipleNullableTypesWithoutRecursionChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let aNullableBoolArg: Bool? = nilOrValue(args[0]) + let aNullableIntArg: Int64? = + isNullish(args[1]) + ? nil : (args[1] is Int64? ? args[1] as! Int64? : Int64(args[1] as! Int32)) + let aNullableStringArg: String? = nilOrValue(args[2]) + do { + let result = try api.sendMultipleNullableTypesWithoutRecursion( + aBool: aNullableBoolArg, anInt: aNullableIntArg, aString: aNullableStringArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + sendMultipleNullableTypesWithoutRecursionChannel.setMessageHandler(nil) + } /// Returns passed in int. let echoNullableIntChannel = FlutterBasicMessageChannel( name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoNullableInt", @@ -1394,6 +1599,27 @@ class HostIntegrationCoreApiSetup { } else { echoAsyncNullableAllNullableTypesChannel.setMessageHandler(nil) } + /// Returns the passed object, to test serialization and deserialization. + let echoAsyncNullableAllNullableTypesWithoutRecursionChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.echoAsyncNullableAllNullableTypesWithoutRecursion", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + echoAsyncNullableAllNullableTypesWithoutRecursionChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let everythingArg: AllNullableTypesWithoutRecursion? = nilOrValue(args[0]) + api.echoAsync(everythingArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + echoAsyncNullableAllNullableTypesWithoutRecursionChannel.setMessageHandler(nil) + } /// Returns passed in int asynchronously. let echoAsyncNullableIntChannel = FlutterBasicMessageChannel( name: @@ -1704,6 +1930,53 @@ class HostIntegrationCoreApiSetup { } else { callFlutterSendMultipleNullableTypesChannel.setMessageHandler(nil) } + let callFlutterEchoAllNullableTypesWithoutRecursionChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoAllNullableTypesWithoutRecursion", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + callFlutterEchoAllNullableTypesWithoutRecursionChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let everythingArg: AllNullableTypesWithoutRecursion? = nilOrValue(args[0]) + api.callFlutterEcho(everythingArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + callFlutterEchoAllNullableTypesWithoutRecursionChannel.setMessageHandler(nil) + } + let callFlutterSendMultipleNullableTypesWithoutRecursionChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterSendMultipleNullableTypesWithoutRecursion", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + callFlutterSendMultipleNullableTypesWithoutRecursionChannel.setMessageHandler { + message, reply in + let args = message as! [Any?] + let aNullableBoolArg: Bool? = nilOrValue(args[0]) + let aNullableIntArg: Int64? = + isNullish(args[1]) + ? nil : (args[1] is Int64? ? args[1] as! Int64? : Int64(args[1] as! Int32)) + let aNullableStringArg: String? = nilOrValue(args[2]) + api.callFlutterSendMultipleNullableTypesWithoutRecursion( + aBool: aNullableBoolArg, anInt: aNullableIntArg, aString: aNullableStringArg + ) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + callFlutterSendMultipleNullableTypesWithoutRecursionChannel.setMessageHandler(nil) + } let callFlutterEchoBoolChannel = FlutterBasicMessageChannel( name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterEchoBool", @@ -2034,8 +2307,10 @@ private class FlutterIntegrationCoreApiCodecReader: FlutterStandardReader { case 129: return AllNullableTypes.fromList(self.readValue() as! [Any?]) case 130: - return AllTypes.fromList(self.readValue() as! [Any?]) + return AllNullableTypesWithoutRecursion.fromList(self.readValue() as! [Any?]) case 131: + return AllTypes.fromList(self.readValue() as! [Any?]) + case 132: return TestMessage.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) @@ -2051,12 +2326,15 @@ private class FlutterIntegrationCoreApiCodecWriter: FlutterStandardWriter { } else if let value = value as? AllNullableTypes { super.writeByte(129) super.writeValue(value.toList()) - } else if let value = value as? AllTypes { + } else if let value = value as? AllNullableTypesWithoutRecursion { super.writeByte(130) super.writeValue(value.toList()) - } else if let value = value as? TestMessage { + } else if let value = value as? AllTypes { super.writeByte(131) super.writeValue(value.toList()) + } else if let value = value as? TestMessage { + super.writeByte(132) + super.writeValue(value.toList()) } else { super.writeValue(value) } @@ -2104,6 +2382,17 @@ protocol FlutterIntegrationCoreApiProtocol { aBool aNullableBoolArg: Bool?, anInt aNullableIntArg: Int64?, aString aNullableStringArg: String?, completion: @escaping (Result) -> Void) + /// Returns the passed object, to test serialization and deserialization. + func echoNullable( + _ everythingArg: AllNullableTypesWithoutRecursion?, + completion: @escaping (Result) -> Void) + /// Returns passed in arguments of multiple types. + /// + /// Tests multiple-arity FlutterApi handling. + func sendMultipleNullableTypesWithoutRecursion( + aBool aNullableBoolArg: Bool?, anInt aNullableIntArg: Int64?, + aString aNullableStringArg: String?, + completion: @escaping (Result) -> Void) /// Returns the passed boolean, to test serialization and deserialization. func echo(_ aBoolArg: Bool, completion: @escaping (Result) -> Void) /// Returns the passed int, to test serialization and deserialization. @@ -2318,6 +2607,66 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } } } + /// Returns the passed object, to test serialization and deserialization. + func echoNullable( + _ everythingArg: AllNullableTypesWithoutRecursion?, + completion: @escaping (Result) -> Void + ) { + let channelName: String = + "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAllNullableTypesWithoutRecursion" + let channel = FlutterBasicMessageChannel( + name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([everythingArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(FlutterError(code: code, message: message, details: details))) + } else { + let result: AllNullableTypesWithoutRecursion? = nilOrValue(listResponse[0]) + completion(.success(result)) + } + } + } + /// Returns passed in arguments of multiple types. + /// + /// Tests multiple-arity FlutterApi handling. + func sendMultipleNullableTypesWithoutRecursion( + aBool aNullableBoolArg: Bool?, anInt aNullableIntArg: Int64?, + aString aNullableStringArg: String?, + completion: @escaping (Result) -> Void + ) { + let channelName: String = + "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.sendMultipleNullableTypesWithoutRecursion" + let channel = FlutterBasicMessageChannel( + name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([aNullableBoolArg, aNullableIntArg, aNullableStringArg] as [Any?]) { + response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(FlutterError(code: code, message: message, details: details))) + } else if listResponse[0] == nil { + completion( + .failure( + FlutterError( + code: "null-error", + message: "Flutter api returned null value for non-null return value.", details: ""))) + } else { + let result = listResponse[0] as! AllNullableTypesWithoutRecursion + completion(.success(result)) + } + } + } /// Returns the passed boolean, to test serialization and deserialization. func echo(_ aBoolArg: Bool, completion: @escaping (Result) -> Void) { let channelName: String = diff --git a/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift b/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift index 5da16290c7a0..019864394650 100644 --- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift +++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift @@ -34,6 +34,11 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { func echo(_ everything: AllNullableTypes?) -> AllNullableTypes? { return everything } + func echo(_ everything: AllNullableTypesWithoutRecursion?) throws + -> AllNullableTypesWithoutRecursion? + { + return everything + } func throwError() throws -> Any? { throw FlutterError(code: "code", message: "message", details: "details") @@ -87,18 +92,6 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { return anEnum } - func echoNamedDefault(_ aString: String) throws -> String { - return aString - } - - func echoOptionalDefault(_ aDouble: Double) throws -> Double { - return aDouble - } - - func echoRequired(_ anInt: Int64) throws -> Int64 { - return anInt - } - func extractNestedNullableString(from wrapper: AllClassesWrapper) -> String? { return wrapper.allNullableTypes.aNullableString } @@ -115,6 +108,14 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { return someThings } + func sendMultipleNullableTypesWithoutRecursion( + aBool aNullableBool: Bool?, anInt aNullableInt: Int64?, aString aNullableString: String? + ) throws -> AllNullableTypesWithoutRecursion { + let someThings = AllNullableTypesWithoutRecursion( + aNullableBool: aNullableBool, aNullableInt: aNullableInt, aNullableString: aNullableString) + return someThings + } + func echo(_ aNullableInt: Int64?) -> Int64? { return aNullableInt } @@ -139,6 +140,18 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { return aNullableObject } + func echoNamedDefault(_ aString: String) throws -> String { + return aString + } + + func echoOptionalDefault(_ aDouble: Double) throws -> Double { + return aDouble + } + + func echoRequired(_ anInt: Int64) throws -> Int64 { + return anInt + } + func echoNullable(_ aNullableList: [Any?]?) throws -> [Any?]? { return aNullableList } @@ -186,6 +199,13 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { completion(.success(everything)) } + func echoAsync( + _ everything: AllNullableTypesWithoutRecursion?, + completion: @escaping (Result) -> Void + ) { + completion(.success(everything)) + } + func echoAsync(_ anInt: Int64, completion: @escaping (Result) -> Void) { completion(.success(anInt)) } @@ -331,6 +351,20 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { } } + func callFlutterEcho( + _ everything: AllNullableTypesWithoutRecursion?, + completion: @escaping (Result) -> Void + ) { + flutterAPI.echoNullable(everything) { response in + switch response { + case .success(let res): + completion(.success(res)) + case .failure(let error): + completion(.failure(error)) + } + } + } + func callFlutterSendMultipleNullableTypes( aBool aNullableBool: Bool?, anInt aNullableInt: Int64?, @@ -351,6 +385,24 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { } } + func callFlutterSendMultipleNullableTypesWithoutRecursion( + aBool aNullableBool: Bool?, anInt aNullableInt: Int64?, aString aNullableString: String?, + completion: @escaping (Result) -> Void + ) { + flutterAPI.sendMultipleNullableTypesWithoutRecursion( + aBool: aNullableBool, + anInt: aNullableInt, + aString: aNullableString + ) { response in + switch response { + case .success(let res): + completion(.success(res)) + case .failure(let error): + completion(.failure(error)) + } + } + } + func callFlutterEcho(_ aBool: Bool, completion: @escaping (Result) -> Void) { flutterAPI.echo(aBool) { response in switch response { diff --git a/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.cpp b/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.cpp index eee8003a7445..fd12459454a1 100644 --- a/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.cpp +++ b/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.cpp @@ -177,7 +177,8 @@ AllNullableTypes::AllNullableTypes( const EncodableMap* nullable_map_with_annotations, const EncodableMap* nullable_map_with_object, const AnEnum* a_nullable_enum, const std::string* a_nullable_string, - const EncodableValue* a_nullable_object) + const EncodableValue* a_nullable_object, + const AllNullableTypes* all_nullable_types) : a_nullable_bool_(a_nullable_bool ? std::optional(*a_nullable_bool) : std::nullopt), a_nullable_int_(a_nullable_int ? std::optional(*a_nullable_int) @@ -228,7 +229,99 @@ AllNullableTypes::AllNullableTypes( : std::nullopt), a_nullable_object_(a_nullable_object ? std::optional(*a_nullable_object) - : std::nullopt) {} + : std::nullopt), + all_nullable_types_( + all_nullable_types + ? std::make_unique(*all_nullable_types) + : nullptr) {} + +AllNullableTypes::AllNullableTypes(const AllNullableTypes& other) + : a_nullable_bool_(other.a_nullable_bool_ + ? std::optional(*other.a_nullable_bool_) + : std::nullopt), + a_nullable_int_(other.a_nullable_int_ + ? std::optional(*other.a_nullable_int_) + : std::nullopt), + a_nullable_int64_(other.a_nullable_int64_ + ? std::optional(*other.a_nullable_int64_) + : std::nullopt), + a_nullable_double_(other.a_nullable_double_ + ? std::optional(*other.a_nullable_double_) + : std::nullopt), + a_nullable_byte_array_(other.a_nullable_byte_array_ + ? std::optional>( + *other.a_nullable_byte_array_) + : std::nullopt), + a_nullable4_byte_array_(other.a_nullable4_byte_array_ + ? std::optional>( + *other.a_nullable4_byte_array_) + : std::nullopt), + a_nullable8_byte_array_(other.a_nullable8_byte_array_ + ? std::optional>( + *other.a_nullable8_byte_array_) + : std::nullopt), + a_nullable_float_array_(other.a_nullable_float_array_ + ? std::optional>( + *other.a_nullable_float_array_) + : std::nullopt), + a_nullable_list_(other.a_nullable_list_ ? std::optional( + *other.a_nullable_list_) + : std::nullopt), + a_nullable_map_(other.a_nullable_map_ + ? std::optional(*other.a_nullable_map_) + : std::nullopt), + nullable_nested_list_( + other.nullable_nested_list_ + ? std::optional(*other.nullable_nested_list_) + : std::nullopt), + nullable_map_with_annotations_( + other.nullable_map_with_annotations_ + ? std::optional( + *other.nullable_map_with_annotations_) + : std::nullopt), + nullable_map_with_object_( + other.nullable_map_with_object_ + ? std::optional(*other.nullable_map_with_object_) + : std::nullopt), + a_nullable_enum_(other.a_nullable_enum_ + ? std::optional(*other.a_nullable_enum_) + : std::nullopt), + a_nullable_string_( + other.a_nullable_string_ + ? std::optional(*other.a_nullable_string_) + : std::nullopt), + a_nullable_object_( + other.a_nullable_object_ + ? std::optional(*other.a_nullable_object_) + : std::nullopt), + all_nullable_types_( + other.all_nullable_types_ + ? std::make_unique(*other.all_nullable_types_) + : nullptr) {} + +AllNullableTypes& AllNullableTypes::operator=(const AllNullableTypes& other) { + a_nullable_bool_ = other.a_nullable_bool_; + a_nullable_int_ = other.a_nullable_int_; + a_nullable_int64_ = other.a_nullable_int64_; + a_nullable_double_ = other.a_nullable_double_; + a_nullable_byte_array_ = other.a_nullable_byte_array_; + a_nullable4_byte_array_ = other.a_nullable4_byte_array_; + a_nullable8_byte_array_ = other.a_nullable8_byte_array_; + a_nullable_float_array_ = other.a_nullable_float_array_; + a_nullable_list_ = other.a_nullable_list_; + a_nullable_map_ = other.a_nullable_map_; + nullable_nested_list_ = other.nullable_nested_list_; + nullable_map_with_annotations_ = other.nullable_map_with_annotations_; + nullable_map_with_object_ = other.nullable_map_with_object_; + a_nullable_enum_ = other.a_nullable_enum_; + a_nullable_string_ = other.a_nullable_string_; + a_nullable_object_ = other.a_nullable_object_; + all_nullable_types_ = + other.all_nullable_types_ + ? std::make_unique(*other.all_nullable_types_) + : nullptr; + return *this; +} const bool* AllNullableTypes::a_nullable_bool() const { return a_nullable_bool_ ? &(*a_nullable_bool_) : nullptr; @@ -456,9 +549,24 @@ void AllNullableTypes::set_a_nullable_object(const EncodableValue& value_arg) { a_nullable_object_ = value_arg; } +const AllNullableTypes* AllNullableTypes::all_nullable_types() const { + return all_nullable_types_.get(); +} + +void AllNullableTypes::set_all_nullable_types( + const AllNullableTypes* value_arg) { + all_nullable_types_ = + value_arg ? std::make_unique(*value_arg) : nullptr; +} + +void AllNullableTypes::set_all_nullable_types( + const AllNullableTypes& value_arg) { + all_nullable_types_ = std::make_unique(value_arg); +} + EncodableList AllNullableTypes::ToEncodableList() const { EncodableList list; - list.reserve(16); + list.reserve(17); list.push_back(a_nullable_bool_ ? EncodableValue(*a_nullable_bool_) : EncodableValue()); list.push_back(a_nullable_int_ ? EncodableValue(*a_nullable_int_) @@ -496,6 +604,9 @@ EncodableList AllNullableTypes::ToEncodableList() const { list.push_back(a_nullable_string_ ? EncodableValue(*a_nullable_string_) : EncodableValue()); list.push_back(a_nullable_object_ ? *a_nullable_object_ : EncodableValue()); + list.push_back(all_nullable_types_ + ? EncodableValue(all_nullable_types_->ToEncodableList()) + : EncodableValue()); return list; } @@ -578,45 +689,549 @@ AllNullableTypes AllNullableTypes::FromEncodableList( if (!encodable_a_nullable_object.IsNull()) { decoded.set_a_nullable_object(encodable_a_nullable_object); } + auto& encodable_all_nullable_types = list[16]; + if (!encodable_all_nullable_types.IsNull()) { + decoded.set_all_nullable_types(AllNullableTypes::FromEncodableList( + std::get(encodable_all_nullable_types))); + } + return decoded; +} + +// AllNullableTypesWithoutRecursion + +AllNullableTypesWithoutRecursion::AllNullableTypesWithoutRecursion() {} + +AllNullableTypesWithoutRecursion::AllNullableTypesWithoutRecursion( + const bool* a_nullable_bool, const int64_t* a_nullable_int, + const int64_t* a_nullable_int64, const double* a_nullable_double, + const std::vector* a_nullable_byte_array, + const std::vector* a_nullable4_byte_array, + const std::vector* a_nullable8_byte_array, + const std::vector* a_nullable_float_array, + const EncodableList* a_nullable_list, const EncodableMap* a_nullable_map, + const EncodableList* nullable_nested_list, + const EncodableMap* nullable_map_with_annotations, + const EncodableMap* nullable_map_with_object, const AnEnum* a_nullable_enum, + const std::string* a_nullable_string, + const EncodableValue* a_nullable_object) + : a_nullable_bool_(a_nullable_bool ? std::optional(*a_nullable_bool) + : std::nullopt), + a_nullable_int_(a_nullable_int ? std::optional(*a_nullable_int) + : std::nullopt), + a_nullable_int64_(a_nullable_int64 + ? std::optional(*a_nullable_int64) + : std::nullopt), + a_nullable_double_(a_nullable_double + ? std::optional(*a_nullable_double) + : std::nullopt), + a_nullable_byte_array_( + a_nullable_byte_array + ? std::optional>(*a_nullable_byte_array) + : std::nullopt), + a_nullable4_byte_array_( + a_nullable4_byte_array + ? std::optional>(*a_nullable4_byte_array) + : std::nullopt), + a_nullable8_byte_array_( + a_nullable8_byte_array + ? std::optional>(*a_nullable8_byte_array) + : std::nullopt), + a_nullable_float_array_( + a_nullable_float_array + ? std::optional>(*a_nullable_float_array) + : std::nullopt), + a_nullable_list_(a_nullable_list + ? std::optional(*a_nullable_list) + : std::nullopt), + a_nullable_map_(a_nullable_map + ? std::optional(*a_nullable_map) + : std::nullopt), + nullable_nested_list_(nullable_nested_list ? std::optional( + *nullable_nested_list) + : std::nullopt), + nullable_map_with_annotations_( + nullable_map_with_annotations + ? std::optional(*nullable_map_with_annotations) + : std::nullopt), + nullable_map_with_object_( + nullable_map_with_object + ? std::optional(*nullable_map_with_object) + : std::nullopt), + a_nullable_enum_(a_nullable_enum ? std::optional(*a_nullable_enum) + : std::nullopt), + a_nullable_string_(a_nullable_string + ? std::optional(*a_nullable_string) + : std::nullopt), + a_nullable_object_(a_nullable_object + ? std::optional(*a_nullable_object) + : std::nullopt) {} + +const bool* AllNullableTypesWithoutRecursion::a_nullable_bool() const { + return a_nullable_bool_ ? &(*a_nullable_bool_) : nullptr; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable_bool( + const bool* value_arg) { + a_nullable_bool_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable_bool(bool value_arg) { + a_nullable_bool_ = value_arg; +} + +const int64_t* AllNullableTypesWithoutRecursion::a_nullable_int() const { + return a_nullable_int_ ? &(*a_nullable_int_) : nullptr; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable_int( + const int64_t* value_arg) { + a_nullable_int_ = + value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable_int(int64_t value_arg) { + a_nullable_int_ = value_arg; +} + +const int64_t* AllNullableTypesWithoutRecursion::a_nullable_int64() const { + return a_nullable_int64_ ? &(*a_nullable_int64_) : nullptr; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable_int64( + const int64_t* value_arg) { + a_nullable_int64_ = + value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable_int64(int64_t value_arg) { + a_nullable_int64_ = value_arg; +} + +const double* AllNullableTypesWithoutRecursion::a_nullable_double() const { + return a_nullable_double_ ? &(*a_nullable_double_) : nullptr; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable_double( + const double* value_arg) { + a_nullable_double_ = + value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable_double(double value_arg) { + a_nullable_double_ = value_arg; +} + +const std::vector* +AllNullableTypesWithoutRecursion::a_nullable_byte_array() const { + return a_nullable_byte_array_ ? &(*a_nullable_byte_array_) : nullptr; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable_byte_array( + const std::vector* value_arg) { + a_nullable_byte_array_ = value_arg + ? std::optional>(*value_arg) + : std::nullopt; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable_byte_array( + const std::vector& value_arg) { + a_nullable_byte_array_ = value_arg; +} + +const std::vector* +AllNullableTypesWithoutRecursion::a_nullable4_byte_array() const { + return a_nullable4_byte_array_ ? &(*a_nullable4_byte_array_) : nullptr; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable4_byte_array( + const std::vector* value_arg) { + a_nullable4_byte_array_ = + value_arg ? std::optional>(*value_arg) + : std::nullopt; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable4_byte_array( + const std::vector& value_arg) { + a_nullable4_byte_array_ = value_arg; +} + +const std::vector* +AllNullableTypesWithoutRecursion::a_nullable8_byte_array() const { + return a_nullable8_byte_array_ ? &(*a_nullable8_byte_array_) : nullptr; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable8_byte_array( + const std::vector* value_arg) { + a_nullable8_byte_array_ = + value_arg ? std::optional>(*value_arg) + : std::nullopt; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable8_byte_array( + const std::vector& value_arg) { + a_nullable8_byte_array_ = value_arg; +} + +const std::vector* +AllNullableTypesWithoutRecursion::a_nullable_float_array() const { + return a_nullable_float_array_ ? &(*a_nullable_float_array_) : nullptr; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable_float_array( + const std::vector* value_arg) { + a_nullable_float_array_ = + value_arg ? std::optional>(*value_arg) : std::nullopt; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable_float_array( + const std::vector& value_arg) { + a_nullable_float_array_ = value_arg; +} + +const EncodableList* AllNullableTypesWithoutRecursion::a_nullable_list() const { + return a_nullable_list_ ? &(*a_nullable_list_) : nullptr; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable_list( + const EncodableList* value_arg) { + a_nullable_list_ = + value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable_list( + const EncodableList& value_arg) { + a_nullable_list_ = value_arg; +} + +const EncodableMap* AllNullableTypesWithoutRecursion::a_nullable_map() const { + return a_nullable_map_ ? &(*a_nullable_map_) : nullptr; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable_map( + const EncodableMap* value_arg) { + a_nullable_map_ = + value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable_map( + const EncodableMap& value_arg) { + a_nullable_map_ = value_arg; +} + +const EncodableList* AllNullableTypesWithoutRecursion::nullable_nested_list() + const { + return nullable_nested_list_ ? &(*nullable_nested_list_) : nullptr; +} + +void AllNullableTypesWithoutRecursion::set_nullable_nested_list( + const EncodableList* value_arg) { + nullable_nested_list_ = + value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void AllNullableTypesWithoutRecursion::set_nullable_nested_list( + const EncodableList& value_arg) { + nullable_nested_list_ = value_arg; +} + +const EncodableMap* +AllNullableTypesWithoutRecursion::nullable_map_with_annotations() const { + return nullable_map_with_annotations_ ? &(*nullable_map_with_annotations_) + : nullptr; +} + +void AllNullableTypesWithoutRecursion::set_nullable_map_with_annotations( + const EncodableMap* value_arg) { + nullable_map_with_annotations_ = + value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void AllNullableTypesWithoutRecursion::set_nullable_map_with_annotations( + const EncodableMap& value_arg) { + nullable_map_with_annotations_ = value_arg; +} + +const EncodableMap* AllNullableTypesWithoutRecursion::nullable_map_with_object() + const { + return nullable_map_with_object_ ? &(*nullable_map_with_object_) : nullptr; +} + +void AllNullableTypesWithoutRecursion::set_nullable_map_with_object( + const EncodableMap* value_arg) { + nullable_map_with_object_ = + value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void AllNullableTypesWithoutRecursion::set_nullable_map_with_object( + const EncodableMap& value_arg) { + nullable_map_with_object_ = value_arg; +} + +const AnEnum* AllNullableTypesWithoutRecursion::a_nullable_enum() const { + return a_nullable_enum_ ? &(*a_nullable_enum_) : nullptr; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable_enum( + const AnEnum* value_arg) { + a_nullable_enum_ = + value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable_enum( + const AnEnum& value_arg) { + a_nullable_enum_ = value_arg; +} + +const std::string* AllNullableTypesWithoutRecursion::a_nullable_string() const { + return a_nullable_string_ ? &(*a_nullable_string_) : nullptr; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable_string( + const std::string_view* value_arg) { + a_nullable_string_ = + value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable_string( + std::string_view value_arg) { + a_nullable_string_ = value_arg; +} + +const EncodableValue* AllNullableTypesWithoutRecursion::a_nullable_object() + const { + return a_nullable_object_ ? &(*a_nullable_object_) : nullptr; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable_object( + const EncodableValue* value_arg) { + a_nullable_object_ = + value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void AllNullableTypesWithoutRecursion::set_a_nullable_object( + const EncodableValue& value_arg) { + a_nullable_object_ = value_arg; +} + +EncodableList AllNullableTypesWithoutRecursion::ToEncodableList() const { + EncodableList list; + list.reserve(16); + list.push_back(a_nullable_bool_ ? EncodableValue(*a_nullable_bool_) + : EncodableValue()); + list.push_back(a_nullable_int_ ? EncodableValue(*a_nullable_int_) + : EncodableValue()); + list.push_back(a_nullable_int64_ ? EncodableValue(*a_nullable_int64_) + : EncodableValue()); + list.push_back(a_nullable_double_ ? EncodableValue(*a_nullable_double_) + : EncodableValue()); + list.push_back(a_nullable_byte_array_ + ? EncodableValue(*a_nullable_byte_array_) + : EncodableValue()); + list.push_back(a_nullable4_byte_array_ + ? EncodableValue(*a_nullable4_byte_array_) + : EncodableValue()); + list.push_back(a_nullable8_byte_array_ + ? EncodableValue(*a_nullable8_byte_array_) + : EncodableValue()); + list.push_back(a_nullable_float_array_ + ? EncodableValue(*a_nullable_float_array_) + : EncodableValue()); + list.push_back(a_nullable_list_ ? EncodableValue(*a_nullable_list_) + : EncodableValue()); + list.push_back(a_nullable_map_ ? EncodableValue(*a_nullable_map_) + : EncodableValue()); + list.push_back(nullable_nested_list_ ? EncodableValue(*nullable_nested_list_) + : EncodableValue()); + list.push_back(nullable_map_with_annotations_ + ? EncodableValue(*nullable_map_with_annotations_) + : EncodableValue()); + list.push_back(nullable_map_with_object_ + ? EncodableValue(*nullable_map_with_object_) + : EncodableValue()); + list.push_back(a_nullable_enum_ ? EncodableValue((int)(*a_nullable_enum_)) + : EncodableValue()); + list.push_back(a_nullable_string_ ? EncodableValue(*a_nullable_string_) + : EncodableValue()); + list.push_back(a_nullable_object_ ? *a_nullable_object_ : EncodableValue()); + return list; +} + +AllNullableTypesWithoutRecursion +AllNullableTypesWithoutRecursion::FromEncodableList(const EncodableList& list) { + AllNullableTypesWithoutRecursion decoded; + auto& encodable_a_nullable_bool = list[0]; + if (!encodable_a_nullable_bool.IsNull()) { + decoded.set_a_nullable_bool(std::get(encodable_a_nullable_bool)); + } + auto& encodable_a_nullable_int = list[1]; + if (!encodable_a_nullable_int.IsNull()) { + decoded.set_a_nullable_int(encodable_a_nullable_int.LongValue()); + } + auto& encodable_a_nullable_int64 = list[2]; + if (!encodable_a_nullable_int64.IsNull()) { + decoded.set_a_nullable_int64(encodable_a_nullable_int64.LongValue()); + } + auto& encodable_a_nullable_double = list[3]; + if (!encodable_a_nullable_double.IsNull()) { + decoded.set_a_nullable_double( + std::get(encodable_a_nullable_double)); + } + auto& encodable_a_nullable_byte_array = list[4]; + if (!encodable_a_nullable_byte_array.IsNull()) { + decoded.set_a_nullable_byte_array( + std::get>(encodable_a_nullable_byte_array)); + } + auto& encodable_a_nullable4_byte_array = list[5]; + if (!encodable_a_nullable4_byte_array.IsNull()) { + decoded.set_a_nullable4_byte_array( + std::get>(encodable_a_nullable4_byte_array)); + } + auto& encodable_a_nullable8_byte_array = list[6]; + if (!encodable_a_nullable8_byte_array.IsNull()) { + decoded.set_a_nullable8_byte_array( + std::get>(encodable_a_nullable8_byte_array)); + } + auto& encodable_a_nullable_float_array = list[7]; + if (!encodable_a_nullable_float_array.IsNull()) { + decoded.set_a_nullable_float_array( + std::get>(encodable_a_nullable_float_array)); + } + auto& encodable_a_nullable_list = list[8]; + if (!encodable_a_nullable_list.IsNull()) { + decoded.set_a_nullable_list( + std::get(encodable_a_nullable_list)); + } + auto& encodable_a_nullable_map = list[9]; + if (!encodable_a_nullable_map.IsNull()) { + decoded.set_a_nullable_map( + std::get(encodable_a_nullable_map)); + } + auto& encodable_nullable_nested_list = list[10]; + if (!encodable_nullable_nested_list.IsNull()) { + decoded.set_nullable_nested_list( + std::get(encodable_nullable_nested_list)); + } + auto& encodable_nullable_map_with_annotations = list[11]; + if (!encodable_nullable_map_with_annotations.IsNull()) { + decoded.set_nullable_map_with_annotations( + std::get(encodable_nullable_map_with_annotations)); + } + auto& encodable_nullable_map_with_object = list[12]; + if (!encodable_nullable_map_with_object.IsNull()) { + decoded.set_nullable_map_with_object( + std::get(encodable_nullable_map_with_object)); + } + auto& encodable_a_nullable_enum = list[13]; + if (!encodable_a_nullable_enum.IsNull()) { + decoded.set_a_nullable_enum( + (AnEnum)(std::get(encodable_a_nullable_enum))); + } + auto& encodable_a_nullable_string = list[14]; + if (!encodable_a_nullable_string.IsNull()) { + decoded.set_a_nullable_string( + std::get(encodable_a_nullable_string)); + } + auto& encodable_a_nullable_object = list[15]; + if (!encodable_a_nullable_object.IsNull()) { + decoded.set_a_nullable_object(encodable_a_nullable_object); + } return decoded; } // AllClassesWrapper AllClassesWrapper::AllClassesWrapper(const AllNullableTypes& all_nullable_types) - : all_nullable_types_(all_nullable_types) {} + : all_nullable_types_( + std::make_unique(all_nullable_types)) {} AllClassesWrapper::AllClassesWrapper(const AllNullableTypes& all_nullable_types, + const AllNullableTypesWithoutRecursion* + all_nullable_types_without_recursion, const AllTypes* all_types) - : all_nullable_types_(all_nullable_types), - all_types_(all_types ? std::optional(*all_types) - : std::nullopt) {} + : all_nullable_types_( + std::make_unique(all_nullable_types)), + all_nullable_types_without_recursion_( + all_nullable_types_without_recursion + ? std::make_unique( + *all_nullable_types_without_recursion) + : nullptr), + all_types_(all_types ? std::make_unique(*all_types) : nullptr) { +} + +AllClassesWrapper::AllClassesWrapper(const AllClassesWrapper& other) + : all_nullable_types_( + std::make_unique(*other.all_nullable_types_)), + all_nullable_types_without_recursion_( + other.all_nullable_types_without_recursion_ + ? std::make_unique( + *other.all_nullable_types_without_recursion_) + : nullptr), + all_types_(other.all_types_ + ? std::make_unique(*other.all_types_) + : nullptr) {} + +AllClassesWrapper& AllClassesWrapper::operator=( + const AllClassesWrapper& other) { + all_nullable_types_ = + std::make_unique(*other.all_nullable_types_); + all_nullable_types_without_recursion_ = + other.all_nullable_types_without_recursion_ + ? std::make_unique( + *other.all_nullable_types_without_recursion_) + : nullptr; + all_types_ = other.all_types_ ? std::make_unique(*other.all_types_) + : nullptr; + return *this; +} const AllNullableTypes& AllClassesWrapper::all_nullable_types() const { - return all_nullable_types_; + return *all_nullable_types_; } void AllClassesWrapper::set_all_nullable_types( const AllNullableTypes& value_arg) { - all_nullable_types_ = value_arg; + all_nullable_types_ = std::make_unique(value_arg); +} + +const AllNullableTypesWithoutRecursion* +AllClassesWrapper::all_nullable_types_without_recursion() const { + return all_nullable_types_without_recursion_.get(); +} + +void AllClassesWrapper::set_all_nullable_types_without_recursion( + const AllNullableTypesWithoutRecursion* value_arg) { + all_nullable_types_without_recursion_ = + value_arg ? std::make_unique(*value_arg) + : nullptr; +} + +void AllClassesWrapper::set_all_nullable_types_without_recursion( + const AllNullableTypesWithoutRecursion& value_arg) { + all_nullable_types_without_recursion_ = + std::make_unique(value_arg); } const AllTypes* AllClassesWrapper::all_types() const { - return all_types_ ? &(*all_types_) : nullptr; + return all_types_.get(); } void AllClassesWrapper::set_all_types(const AllTypes* value_arg) { - all_types_ = value_arg ? std::optional(*value_arg) : std::nullopt; + all_types_ = value_arg ? std::make_unique(*value_arg) : nullptr; } void AllClassesWrapper::set_all_types(const AllTypes& value_arg) { - all_types_ = value_arg; + all_types_ = std::make_unique(value_arg); } EncodableList AllClassesWrapper::ToEncodableList() const { EncodableList list; - list.reserve(2); - list.push_back(EncodableValue(all_nullable_types_.ToEncodableList())); + list.reserve(3); + list.push_back(EncodableValue(all_nullable_types_->ToEncodableList())); + list.push_back( + all_nullable_types_without_recursion_ + ? EncodableValue( + all_nullable_types_without_recursion_->ToEncodableList()) + : EncodableValue()); list.push_back(all_types_ ? EncodableValue(all_types_->ToEncodableList()) : EncodableValue()); return list; @@ -626,7 +1241,14 @@ AllClassesWrapper AllClassesWrapper::FromEncodableList( const EncodableList& list) { AllClassesWrapper decoded( AllNullableTypes::FromEncodableList(std::get(list[0]))); - auto& encodable_all_types = list[1]; + auto& encodable_all_nullable_types_without_recursion = list[1]; + if (!encodable_all_nullable_types_without_recursion.IsNull()) { + decoded.set_all_nullable_types_without_recursion( + AllNullableTypesWithoutRecursion::FromEncodableList( + std::get( + encodable_all_nullable_types_without_recursion))); + } + auto& encodable_all_types = list[2]; if (!encodable_all_types.IsNull()) { decoded.set_all_types(AllTypes::FromEncodableList( std::get(encodable_all_types))); @@ -684,9 +1306,13 @@ EncodableValue HostIntegrationCoreApiCodecSerializer::ReadValueOfType( return CustomEncodableValue(AllNullableTypes::FromEncodableList( std::get(ReadValue(stream)))); case 130: + return CustomEncodableValue( + AllNullableTypesWithoutRecursion::FromEncodableList( + std::get(ReadValue(stream)))); + case 131: return CustomEncodableValue(AllTypes::FromEncodableList( std::get(ReadValue(stream)))); - case 131: + case 132: return CustomEncodableValue(TestMessage::FromEncodableList( std::get(ReadValue(stream)))); default: @@ -713,15 +1339,23 @@ void HostIntegrationCoreApiCodecSerializer::WriteValue( stream); return; } - if (custom_value->type() == typeid(AllTypes)) { + if (custom_value->type() == typeid(AllNullableTypesWithoutRecursion)) { stream->WriteByte(130); + WriteValue(EncodableValue(std::any_cast( + *custom_value) + .ToEncodableList()), + stream); + return; + } + if (custom_value->type() == typeid(AllTypes)) { + stream->WriteByte(131); WriteValue(EncodableValue( std::any_cast(*custom_value).ToEncodableList()), stream); return; } if (custom_value->type() == typeid(TestMessage)) { - stream->WriteByte(131); + stream->WriteByte(132); WriteValue( EncodableValue( std::any_cast(*custom_value).ToEncodableList()), @@ -1353,11 +1987,51 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, const auto& args = std::get(message); const auto& encodable_everything_arg = args.at(0); const auto* everything_arg = - &(std::any_cast( + &(std::any_cast( + std::get( + encodable_everything_arg))); + ErrorOr> output = + api->EchoAllNullableTypes(everything_arg); + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + auto output_optional = std::move(output).TakeValue(); + if (output_optional) { + wrapped.push_back( + CustomEncodableValue(std::move(output_optional).value())); + } else { + wrapped.push_back(EncodableValue()); + } + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." + "echoAllNullableTypesWithoutRecursion", + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_everything_arg = args.at(0); + const auto* everything_arg = + &(std::any_cast( std::get( encodable_everything_arg))); - ErrorOr> output = - api->EchoAllNullableTypes(everything_arg); + ErrorOr> output = + api->EchoAllNullableTypesWithoutRecursion(everything_arg); if (output.has_error()) { reply(WrapError(output.error())); return; @@ -1500,6 +2174,53 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, channel.SetMessageHandler(nullptr); } } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." + "sendMultipleNullableTypesWithoutRecursion", + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_a_nullable_bool_arg = args.at(0); + const auto* a_nullable_bool_arg = + std::get_if(&encodable_a_nullable_bool_arg); + const auto& encodable_a_nullable_int_arg = args.at(1); + const int64_t a_nullable_int_arg_value = + encodable_a_nullable_int_arg.IsNull() + ? 0 + : encodable_a_nullable_int_arg.LongValue(); + const auto* a_nullable_int_arg = + encodable_a_nullable_int_arg.IsNull() + ? nullptr + : &a_nullable_int_arg_value; + const auto& encodable_a_nullable_string_arg = args.at(2); + const auto* a_nullable_string_arg = + std::get_if(&encodable_a_nullable_string_arg); + ErrorOr output = + api->SendMultipleNullableTypesWithoutRecursion( + a_nullable_bool_arg, a_nullable_int_arg, + a_nullable_string_arg); + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back( + CustomEncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } { BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests." @@ -2463,6 +3184,50 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, channel.SetMessageHandler(nullptr); } } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." + "echoAsyncNullableAllNullableTypesWithoutRecursion", + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_everything_arg = args.at(0); + const auto* everything_arg = + &(std::any_cast( + std::get( + encodable_everything_arg))); + api->EchoAsyncNullableAllNullableTypesWithoutRecursion( + everything_arg, + [reply]( + ErrorOr>&& + output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + auto output_optional = std::move(output).TakeValue(); + if (output_optional) { + wrapped.push_back(CustomEncodableValue( + std::move(output_optional).value())); + } else { + wrapped.push_back(EncodableValue()); + } + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } { BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests." @@ -3050,6 +3815,98 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, channel.SetMessageHandler(nullptr); } } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." + "callFlutterEchoAllNullableTypesWithoutRecursion", + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_everything_arg = args.at(0); + const auto* everything_arg = + &(std::any_cast( + std::get( + encodable_everything_arg))); + api->CallFlutterEchoAllNullableTypesWithoutRecursion( + everything_arg, + [reply]( + ErrorOr>&& + output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + auto output_optional = std::move(output).TakeValue(); + if (output_optional) { + wrapped.push_back(CustomEncodableValue( + std::move(output_optional).value())); + } else { + wrapped.push_back(EncodableValue()); + } + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." + "callFlutterSendMultipleNullableTypesWithoutRecursion", + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_a_nullable_bool_arg = args.at(0); + const auto* a_nullable_bool_arg = + std::get_if(&encodable_a_nullable_bool_arg); + const auto& encodable_a_nullable_int_arg = args.at(1); + const int64_t a_nullable_int_arg_value = + encodable_a_nullable_int_arg.IsNull() + ? 0 + : encodable_a_nullable_int_arg.LongValue(); + const auto* a_nullable_int_arg = + encodable_a_nullable_int_arg.IsNull() + ? nullptr + : &a_nullable_int_arg_value; + const auto& encodable_a_nullable_string_arg = args.at(2); + const auto* a_nullable_string_arg = + std::get_if(&encodable_a_nullable_string_arg); + api->CallFlutterSendMultipleNullableTypesWithoutRecursion( + a_nullable_bool_arg, a_nullable_int_arg, + a_nullable_string_arg, + [reply](ErrorOr&& output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back( + CustomEncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } { BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests." @@ -3705,9 +4562,13 @@ EncodableValue FlutterIntegrationCoreApiCodecSerializer::ReadValueOfType( return CustomEncodableValue(AllNullableTypes::FromEncodableList( std::get(ReadValue(stream)))); case 130: + return CustomEncodableValue( + AllNullableTypesWithoutRecursion::FromEncodableList( + std::get(ReadValue(stream)))); + case 131: return CustomEncodableValue(AllTypes::FromEncodableList( std::get(ReadValue(stream)))); - case 131: + case 132: return CustomEncodableValue(TestMessage::FromEncodableList( std::get(ReadValue(stream)))); default: @@ -3734,15 +4595,23 @@ void FlutterIntegrationCoreApiCodecSerializer::WriteValue( stream); return; } - if (custom_value->type() == typeid(AllTypes)) { + if (custom_value->type() == typeid(AllNullableTypesWithoutRecursion)) { stream->WriteByte(130); + WriteValue(EncodableValue(std::any_cast( + *custom_value) + .ToEncodableList()), + stream); + return; + } + if (custom_value->type() == typeid(AllTypes)) { + stream->WriteByte(131); WriteValue(EncodableValue( std::any_cast(*custom_value).ToEncodableList()), stream); return; } if (custom_value->type() == typeid(TestMessage)) { - stream->WriteByte(131); + stream->WriteByte(132); WriteValue( EncodableValue( std::any_cast(*custom_value).ToEncodableList()), @@ -3978,6 +4847,88 @@ void FlutterIntegrationCoreApi::SendMultipleNullableTypes( }); } +void FlutterIntegrationCoreApi::EchoAllNullableTypesWithoutRecursion( + const AllNullableTypesWithoutRecursion* everything_arg, + std::function&& on_success, + std::function&& on_error) { + const std::string channel_name = + "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." + "echoAllNullableTypesWithoutRecursion"; + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); + EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ + everything_arg ? CustomEncodableValue(*everything_arg) : EncodableValue(), + }); + channel.Send( + encoded_api_arguments, [channel_name, on_success = std::move(on_success), + on_error = std::move(on_error)]( + const uint8_t* reply, size_t reply_size) { + std::unique_ptr response = + GetCodec().DecodeMessage(reply, reply_size); + const auto& encodable_return_value = *response; + const auto* list_return_value = + std::get_if(&encodable_return_value); + if (list_return_value) { + if (list_return_value->size() > 1) { + on_error( + FlutterError(std::get(list_return_value->at(0)), + std::get(list_return_value->at(1)), + list_return_value->at(2))); + } else { + const auto* return_value = + &(std::any_cast( + std::get(list_return_value->at(0)))); + on_success(return_value); + } + } else { + on_error(CreateConnectionError(channel_name)); + } + }); +} + +void FlutterIntegrationCoreApi::SendMultipleNullableTypesWithoutRecursion( + const bool* a_nullable_bool_arg, const int64_t* a_nullable_int_arg, + const std::string* a_nullable_string_arg, + std::function&& on_success, + std::function&& on_error) { + const std::string channel_name = + "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi." + "sendMultipleNullableTypesWithoutRecursion"; + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); + EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ + a_nullable_bool_arg ? EncodableValue(*a_nullable_bool_arg) + : EncodableValue(), + a_nullable_int_arg ? EncodableValue(*a_nullable_int_arg) + : EncodableValue(), + a_nullable_string_arg ? EncodableValue(*a_nullable_string_arg) + : EncodableValue(), + }); + channel.Send( + encoded_api_arguments, [channel_name, on_success = std::move(on_success), + on_error = std::move(on_error)]( + const uint8_t* reply, size_t reply_size) { + std::unique_ptr response = + GetCodec().DecodeMessage(reply, reply_size); + const auto& encodable_return_value = *response; + const auto* list_return_value = + std::get_if(&encodable_return_value); + if (list_return_value) { + if (list_return_value->size() > 1) { + on_error( + FlutterError(std::get(list_return_value->at(0)), + std::get(list_return_value->at(1)), + list_return_value->at(2))); + } else { + const auto& return_value = + std::any_cast( + std::get(list_return_value->at(0))); + on_success(return_value); + } + } else { + on_error(CreateConnectionError(channel_name)); + } + }); +} + void FlutterIntegrationCoreApi::EchoBool( bool a_bool_arg, std::function&& on_success, std::function&& on_error) { diff --git a/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.h b/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.h index 484d5e349bcd..4b15d9dbbe70 100644 --- a/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.h +++ b/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.h @@ -168,6 +168,142 @@ class AllNullableTypes { // Constructs an object setting all fields. explicit AllNullableTypes( + const bool* a_nullable_bool, const int64_t* a_nullable_int, + const int64_t* a_nullable_int64, const double* a_nullable_double, + const std::vector* a_nullable_byte_array, + const std::vector* a_nullable4_byte_array, + const std::vector* a_nullable8_byte_array, + const std::vector* a_nullable_float_array, + const flutter::EncodableList* a_nullable_list, + const flutter::EncodableMap* a_nullable_map, + const flutter::EncodableList* nullable_nested_list, + const flutter::EncodableMap* nullable_map_with_annotations, + const flutter::EncodableMap* nullable_map_with_object, + const AnEnum* a_nullable_enum, const std::string* a_nullable_string, + const flutter::EncodableValue* a_nullable_object, + const AllNullableTypes* all_nullable_types); + + ~AllNullableTypes() = default; + AllNullableTypes(const AllNullableTypes& other); + AllNullableTypes& operator=(const AllNullableTypes& other); + AllNullableTypes(AllNullableTypes&& other) = default; + AllNullableTypes& operator=(AllNullableTypes&& other) noexcept = default; + const bool* a_nullable_bool() const; + void set_a_nullable_bool(const bool* value_arg); + void set_a_nullable_bool(bool value_arg); + + const int64_t* a_nullable_int() const; + void set_a_nullable_int(const int64_t* value_arg); + void set_a_nullable_int(int64_t value_arg); + + const int64_t* a_nullable_int64() const; + void set_a_nullable_int64(const int64_t* value_arg); + void set_a_nullable_int64(int64_t value_arg); + + const double* a_nullable_double() const; + void set_a_nullable_double(const double* value_arg); + void set_a_nullable_double(double value_arg); + + const std::vector* a_nullable_byte_array() const; + void set_a_nullable_byte_array(const std::vector* value_arg); + void set_a_nullable_byte_array(const std::vector& value_arg); + + const std::vector* a_nullable4_byte_array() const; + void set_a_nullable4_byte_array(const std::vector* value_arg); + void set_a_nullable4_byte_array(const std::vector& value_arg); + + const std::vector* a_nullable8_byte_array() const; + void set_a_nullable8_byte_array(const std::vector* value_arg); + void set_a_nullable8_byte_array(const std::vector& value_arg); + + const std::vector* a_nullable_float_array() const; + void set_a_nullable_float_array(const std::vector* value_arg); + void set_a_nullable_float_array(const std::vector& value_arg); + + const flutter::EncodableList* a_nullable_list() const; + void set_a_nullable_list(const flutter::EncodableList* value_arg); + void set_a_nullable_list(const flutter::EncodableList& value_arg); + + const flutter::EncodableMap* a_nullable_map() const; + void set_a_nullable_map(const flutter::EncodableMap* value_arg); + void set_a_nullable_map(const flutter::EncodableMap& value_arg); + + const flutter::EncodableList* nullable_nested_list() const; + void set_nullable_nested_list(const flutter::EncodableList* value_arg); + void set_nullable_nested_list(const flutter::EncodableList& value_arg); + + const flutter::EncodableMap* nullable_map_with_annotations() const; + void set_nullable_map_with_annotations( + const flutter::EncodableMap* value_arg); + void set_nullable_map_with_annotations( + const flutter::EncodableMap& value_arg); + + const flutter::EncodableMap* nullable_map_with_object() const; + void set_nullable_map_with_object(const flutter::EncodableMap* value_arg); + void set_nullable_map_with_object(const flutter::EncodableMap& value_arg); + + const AnEnum* a_nullable_enum() const; + void set_a_nullable_enum(const AnEnum* value_arg); + void set_a_nullable_enum(const AnEnum& value_arg); + + const std::string* a_nullable_string() const; + void set_a_nullable_string(const std::string_view* value_arg); + void set_a_nullable_string(std::string_view value_arg); + + const flutter::EncodableValue* a_nullable_object() const; + void set_a_nullable_object(const flutter::EncodableValue* value_arg); + void set_a_nullable_object(const flutter::EncodableValue& value_arg); + + const AllNullableTypes* all_nullable_types() const; + void set_all_nullable_types(const AllNullableTypes* value_arg); + void set_all_nullable_types(const AllNullableTypes& value_arg); + + private: + static AllNullableTypes FromEncodableList(const flutter::EncodableList& list); + flutter::EncodableList ToEncodableList() const; + friend class AllClassesWrapper; + friend class HostIntegrationCoreApi; + friend class HostIntegrationCoreApiCodecSerializer; + friend class FlutterIntegrationCoreApi; + friend class FlutterIntegrationCoreApiCodecSerializer; + friend class HostTrivialApi; + friend class HostTrivialApiCodecSerializer; + friend class HostSmallApi; + friend class HostSmallApiCodecSerializer; + friend class FlutterSmallApi; + friend class FlutterSmallApiCodecSerializer; + friend class CoreTestsTest; + std::optional a_nullable_bool_; + std::optional a_nullable_int_; + std::optional a_nullable_int64_; + std::optional a_nullable_double_; + std::optional> a_nullable_byte_array_; + std::optional> a_nullable4_byte_array_; + std::optional> a_nullable8_byte_array_; + std::optional> a_nullable_float_array_; + std::optional a_nullable_list_; + std::optional a_nullable_map_; + std::optional nullable_nested_list_; + std::optional nullable_map_with_annotations_; + std::optional nullable_map_with_object_; + std::optional a_nullable_enum_; + std::optional a_nullable_string_; + std::optional a_nullable_object_; + std::unique_ptr all_nullable_types_; +}; + +// The primary purpose for this class is to ensure coverage of Swift structs +// with nullable items, as the primary [AllNullableTypes] class is being used to +// test Swift classes. +// +// Generated class from Pigeon that represents data sent in messages. +class AllNullableTypesWithoutRecursion { + public: + // Constructs an object setting all non-nullable fields. + AllNullableTypesWithoutRecursion(); + + // Constructs an object setting all fields. + explicit AllNullableTypesWithoutRecursion( const bool* a_nullable_bool, const int64_t* a_nullable_int, const int64_t* a_nullable_int64, const double* a_nullable_double, const std::vector* a_nullable_byte_array, @@ -249,7 +385,8 @@ class AllNullableTypes { void set_a_nullable_object(const flutter::EncodableValue& value_arg); private: - static AllNullableTypes FromEncodableList(const flutter::EncodableList& list); + static AllNullableTypesWithoutRecursion FromEncodableList( + const flutter::EncodableList& list); flutter::EncodableList ToEncodableList() const; friend class AllClassesWrapper; friend class HostIntegrationCoreApi; @@ -295,11 +432,25 @@ class AllClassesWrapper { // Constructs an object setting all fields. explicit AllClassesWrapper(const AllNullableTypes& all_nullable_types, + const AllNullableTypesWithoutRecursion* + all_nullable_types_without_recursion, const AllTypes* all_types); + ~AllClassesWrapper() = default; + AllClassesWrapper(const AllClassesWrapper& other); + AllClassesWrapper& operator=(const AllClassesWrapper& other); + AllClassesWrapper(AllClassesWrapper&& other) = default; + AllClassesWrapper& operator=(AllClassesWrapper&& other) noexcept = default; const AllNullableTypes& all_nullable_types() const; void set_all_nullable_types(const AllNullableTypes& value_arg); + const AllNullableTypesWithoutRecursion* all_nullable_types_without_recursion() + const; + void set_all_nullable_types_without_recursion( + const AllNullableTypesWithoutRecursion* value_arg); + void set_all_nullable_types_without_recursion( + const AllNullableTypesWithoutRecursion& value_arg); + const AllTypes* all_types() const; void set_all_types(const AllTypes* value_arg); void set_all_types(const AllTypes& value_arg); @@ -319,8 +470,10 @@ class AllClassesWrapper { friend class FlutterSmallApi; friend class FlutterSmallApiCodecSerializer; friend class CoreTestsTest; - AllNullableTypes all_nullable_types_; - std::optional all_types_; + std::unique_ptr all_nullable_types_; + std::unique_ptr + all_nullable_types_without_recursion_; + std::unique_ptr all_types_; }; // A data class containing a List, used in unit tests. @@ -430,6 +583,10 @@ class HostIntegrationCoreApi { // Returns the passed object, to test serialization and deserialization. virtual ErrorOr> EchoAllNullableTypes( const AllNullableTypes* everything) = 0; + // Returns the passed object, to test serialization and deserialization. + virtual ErrorOr> + EchoAllNullableTypesWithoutRecursion( + const AllNullableTypesWithoutRecursion* everything) = 0; // Returns the inner `aString` value from the wrapped object, to test // sending of nested objects. virtual ErrorOr> ExtractNestedNullableString( @@ -442,6 +599,11 @@ class HostIntegrationCoreApi { virtual ErrorOr SendMultipleNullableTypes( const bool* a_nullable_bool, const int64_t* a_nullable_int, const std::string* a_nullable_string) = 0; + // Returns passed in arguments of multiple types. + virtual ErrorOr + SendMultipleNullableTypesWithoutRecursion( + const bool* a_nullable_bool, const int64_t* a_nullable_int, + const std::string* a_nullable_string) = 0; // Returns passed in int. virtual ErrorOr> EchoNullableInt( const int64_t* a_nullable_int) = 0; @@ -534,6 +696,12 @@ class HostIntegrationCoreApi { const AllNullableTypes* everything, std::function> reply)> result) = 0; + // Returns the passed object, to test serialization and deserialization. + virtual void EchoAsyncNullableAllNullableTypesWithoutRecursion( + const AllNullableTypesWithoutRecursion* everything, + std::function< + void(ErrorOr> reply)> + result) = 0; // Returns passed in int asynchronously. virtual void EchoAsyncNullableInt( const int64_t* an_int, @@ -596,6 +764,16 @@ class HostIntegrationCoreApi { const bool* a_nullable_bool, const int64_t* a_nullable_int, const std::string* a_nullable_string, std::function reply)> result) = 0; + virtual void CallFlutterEchoAllNullableTypesWithoutRecursion( + const AllNullableTypesWithoutRecursion* everything, + std::function< + void(ErrorOr> reply)> + result) = 0; + virtual void CallFlutterSendMultipleNullableTypesWithoutRecursion( + const bool* a_nullable_bool, const int64_t* a_nullable_int, + const std::string* a_nullable_string, + std::function reply)> + result) = 0; virtual void CallFlutterEchoBool( bool a_bool, std::function reply)> result) = 0; virtual void CallFlutterEchoInt( @@ -712,6 +890,19 @@ class FlutterIntegrationCoreApi { const std::string* a_nullable_string, std::function&& on_success, std::function&& on_error); + // Returns the passed object, to test serialization and deserialization. + void EchoAllNullableTypesWithoutRecursion( + const AllNullableTypesWithoutRecursion* everything, + std::function&& on_success, + std::function&& on_error); + // Returns passed in arguments of multiple types. + // + // Tests multiple-arity FlutterApi handling. + void SendMultipleNullableTypesWithoutRecursion( + const bool* a_nullable_bool, const int64_t* a_nullable_int, + const std::string* a_nullable_string, + std::function&& on_success, + std::function&& on_error); // Returns the passed boolean, to test serialization and deserialization. void EchoBool(bool a_bool, std::function&& on_success, std::function&& on_error); diff --git a/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.cpp b/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.cpp index 46535b3be806..3b22d1e36202 100644 --- a/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.cpp +++ b/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.cpp @@ -18,6 +18,7 @@ namespace test_plugin { using core_tests_pigeontest::AllClassesWrapper; using core_tests_pigeontest::AllNullableTypes; +using core_tests_pigeontest::AllNullableTypesWithoutRecursion; using core_tests_pigeontest::AllTypes; using core_tests_pigeontest::AnEnum; using core_tests_pigeontest::ErrorOr; @@ -58,6 +59,15 @@ ErrorOr> TestPlugin::EchoAllNullableTypes( return *everything; } +ErrorOr> +TestPlugin::EchoAllNullableTypesWithoutRecursion( + const AllNullableTypesWithoutRecursion* everything) { + if (!everything) { + return std::nullopt; + } + return *everything; +} + ErrorOr> TestPlugin::ThrowError() { return FlutterError("An error"); } @@ -157,6 +167,24 @@ ErrorOr TestPlugin::SendMultipleNullableTypes( return someTypes; }; +ErrorOr +TestPlugin::SendMultipleNullableTypesWithoutRecursion( + const bool* a_nullable_bool, const int64_t* a_nullable_int, + const std::string* a_nullable_string) { + AllNullableTypesWithoutRecursion someTypes; + someTypes.set_a_nullable_bool(a_nullable_bool); + someTypes.set_a_nullable_int(a_nullable_int); + // The string pointer can't be passed through directly since the setter for + // a string takes a std::string_view rather than std::string so the pointer + // types don't match. + if (a_nullable_string) { + someTypes.set_a_nullable_string(*a_nullable_string); + } else { + someTypes.set_a_nullable_string(nullptr); + } + return someTypes; +}; + ErrorOr> TestPlugin::EchoNullableInt( const int64_t* a_nullable_int) { if (!a_nullable_int) { @@ -328,6 +356,16 @@ void TestPlugin::EchoAsyncNullableAllNullableTypes( : std::nullopt); } +void TestPlugin::EchoAsyncNullableAllNullableTypesWithoutRecursion( + const AllNullableTypesWithoutRecursion* everything, + std::function< + void(ErrorOr> reply)> + result) { + result(everything + ? std::optional(*everything) + : std::nullopt); +} + void TestPlugin::EchoAsyncNullableInt( const int64_t* an_int, std::function> reply)> result) { @@ -437,6 +475,31 @@ void TestPlugin::CallFlutterSendMultipleNullableTypes( [result](const FlutterError& error) { result(error); }); } +void TestPlugin::CallFlutterEchoAllNullableTypesWithoutRecursion( + const AllNullableTypesWithoutRecursion* everything, + std::function< + void(ErrorOr> reply)> + result) { + flutter_api_->EchoAllNullableTypesWithoutRecursion( + everything, + [result](const AllNullableTypesWithoutRecursion* echo) { + result(echo ? std::optional(*echo) + : std::nullopt); + }, + [result](const FlutterError& error) { result(error); }); +} + +void TestPlugin::CallFlutterSendMultipleNullableTypesWithoutRecursion( + const bool* a_nullable_bool, const int64_t* a_nullable_int, + const std::string* a_nullable_string, + std::function reply)> + result) { + flutter_api_->SendMultipleNullableTypesWithoutRecursion( + a_nullable_bool, a_nullable_int, a_nullable_string, + [result](const AllNullableTypesWithoutRecursion& echo) { result(echo); }, + [result](const FlutterError& error) { result(error); }); +} + void TestPlugin::CallFlutterEchoBool( bool a_bool, std::function reply)> result) { flutter_api_->EchoBool( diff --git a/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.h b/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.h index e9fd9c6f2de5..372f7440d53f 100644 --- a/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.h +++ b/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.h @@ -39,6 +39,11 @@ class TestPlugin : public flutter::Plugin, std::optional> EchoAllNullableTypes( const core_tests_pigeontest::AllNullableTypes* everything) override; + core_tests_pigeontest::ErrorOr< + std::optional> + EchoAllNullableTypesWithoutRecursion( + const core_tests_pigeontest::AllNullableTypesWithoutRecursion* everything) + override; core_tests_pigeontest::ErrorOr> ThrowError() override; std::optional ThrowErrorFromVoid() @@ -78,6 +83,11 @@ class TestPlugin : public flutter::Plugin, SendMultipleNullableTypes(const bool* a_nullable_bool, const int64_t* a_nullable_int, const std::string* a_nullable_string) override; + core_tests_pigeontest::ErrorOr< + core_tests_pigeontest::AllNullableTypesWithoutRecursion> + SendMultipleNullableTypesWithoutRecursion( + const bool* a_nullable_bool, const int64_t* a_nullable_int, + const std::string* a_nullable_string) override; core_tests_pigeontest::ErrorOr> EchoNullableInt( const int64_t* a_nullable_int) override; core_tests_pigeontest::ErrorOr> EchoNullableDouble( @@ -130,6 +140,13 @@ class TestPlugin : public flutter::Plugin, std::optional> reply)> result) override; + void EchoAsyncNullableAllNullableTypesWithoutRecursion( + const core_tests_pigeontest::AllNullableTypesWithoutRecursion* everything, + std::function< + void(core_tests_pigeontest::ErrorOr> + reply)> + result) override; void EchoAsyncInt( int64_t an_int, std::function reply)> result) @@ -253,6 +270,21 @@ class TestPlugin : public flutter::Plugin, core_tests_pigeontest::AllNullableTypes> reply)> result) override; + void CallFlutterEchoAllNullableTypesWithoutRecursion( + const core_tests_pigeontest::AllNullableTypesWithoutRecursion* everything, + std::function< + void(core_tests_pigeontest::ErrorOr> + reply)> + result) override; + void CallFlutterSendMultipleNullableTypesWithoutRecursion( + const bool* a_nullable_bool, const int64_t* a_nullable_int, + const std::string* a_nullable_string, + std::function< + void(core_tests_pigeontest::ErrorOr< + core_tests_pigeontest::AllNullableTypesWithoutRecursion> + reply)> + result) override; void CallFlutterEchoBool( bool a_bool, std::function reply)> result) diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml index d5b579405d45..b4912fd32620 100644 --- a/packages/pigeon/pubspec.yaml +++ b/packages/pigeon/pubspec.yaml @@ -2,7 +2,7 @@ name: pigeon description: Code generator tool to make communication between Flutter and the host platform type-safe and easier. repository: https://github.com/flutter/packages/tree/main/packages/pigeon issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+pigeon%22 -version: 17.2.0 # This must match the version in lib/generator_tools.dart +version: 17.3.0 # This must match the version in lib/generator_tools.dart environment: sdk: ^3.1.0 diff --git a/packages/pigeon/test/cpp_generator_test.dart b/packages/pigeon/test/cpp_generator_test.dart index 9c1b9cbd091a..c2fdcd68f689 100644 --- a/packages/pigeon/test/cpp_generator_test.dart +++ b/packages/pigeon/test/cpp_generator_test.dart @@ -584,11 +584,12 @@ void main() { contains('void set_nullable_string(std::string_view value_arg)')); expect( code, contains('void set_nullable_nested(const Nested& value_arg)')); - // Instance variables should be std::optionals. + // Most instance variables should be std::optionals. expect(code, contains('std::optional nullable_bool_')); expect(code, contains('std::optional nullable_int_')); expect(code, contains('std::optional nullable_string_')); - expect(code, contains('std::optional nullable_nested_')); + // Custom classes are the exception, to avoid inline storage. + expect(code, contains('std::unique_ptr nullable_nested_')); } { final StringBuffer sink = StringBuffer(); @@ -624,10 +625,7 @@ void main() { code, contains( 'return nullable_string_ ? &(*nullable_string_) : nullptr;')); - expect( - code, - contains( - 'return nullable_nested_ ? &(*nullable_nested_) : nullptr;')); + expect(code, contains('return nullable_nested_.get();')); // Setters convert to optionals. expect( code, @@ -643,8 +641,8 @@ void main() { 'std::optional(*value_arg) : std::nullopt;')); expect( code, - contains('nullable_nested_ = value_arg ? ' - 'std::optional(*value_arg) : std::nullopt;')); + contains( + 'nullable_nested_ = value_arg ? std::make_unique(*value_arg) : nullptr;')); // Serialization handles optionals. expect( code, @@ -763,7 +761,8 @@ void main() { expect(code, contains('bool non_nullable_bool_;')); expect(code, contains('int64_t non_nullable_int_;')); expect(code, contains('std::string non_nullable_string_;')); - expect(code, contains('Nested non_nullable_nested_;')); + // Except for custom classes. + expect(code, contains('std::unique_ptr non_nullable_nested_;')); } { final StringBuffer sink = StringBuffer(); @@ -793,15 +792,20 @@ void main() { expect(code, contains('return non_nullable_bool_;')); expect(code, contains('return non_nullable_int_;')); expect(code, contains('return non_nullable_string_;')); - expect(code, contains('return non_nullable_nested_;')); + // Unless it's a custom class. + expect(code, contains('return *non_nullable_nested_;')); // Setters just assign the value. expect(code, contains('non_nullable_bool_ = value_arg;')); expect(code, contains('non_nullable_int_ = value_arg;')); expect(code, contains('non_nullable_string_ = value_arg;')); - expect(code, contains('non_nullable_nested_ = value_arg;')); + // Unless it's a custom class. + expect( + code, + contains( + 'non_nullable_nested_ = std::make_unique(value_arg);')); // Serialization uses the value directly. expect(code, contains('EncodableValue(non_nullable_bool_)')); - expect(code, contains('non_nullable_nested_.ToEncodableList()')); + expect(code, contains('non_nullable_nested_->ToEncodableList()')); // Serialization should use push_back, not initializer lists, to avoid // copies. From 333d43c9bd83536a57c4033d40685ed177d4f8bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Mar 2024 18:10:26 +0000 Subject: [PATCH 104/126] [in_app_pur]: Bump org.json:json from 20231013 to 20240303 in /packages/in_app_purchase/in_app_purchase_android/example/android/app (#6255) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [org.json:json](https://github.com/douglascrockford/JSON-java) from 20231013 to 20240303.
Release notes

Sourced from org.json:json's releases.

20240303

Pull Request Description
#875 20240303-pre-release-updates
#874 Deployment and Pipeline action updates
#869 Revert recent obj long get long changes
#860 Added missing Javadocs for Java 21
#858 [cleanup-after-commit for #854 and #856](stleary/JSON-java#858)
#856 add ability for custom delimiters

20240205

Pull Request Description
#855 Fix JSONArrayTest testRecursiveDepthArrayFor1000Levels() 
#846 Cleanup warnings and merge new unit tests
#845 improved annotation search performance
#835 Delete redundant .toString() call in README
#832 Add a config flag to disable whitespace trimming
#831 Refactor NumberConversionUtil and toString() of CookieList & XML Classes
#830 Upgrade json-path's version to 2.4.0 to avoid dependency conflict
#828 Fixed flaky tests in XMLTest.java
#824 Ignore tests that fail due to resource differences
#823 JSON parsing self reference object and array
#820 Close XML tag explicitly for empty tags with configuration
#815 Ensure java 6 compatable 
#814 Refactor duplicate code for stringToNumber() in JSONObject, JSONArray, and XML
#812 docs: use syntax highlighting
#808 Fix compiler warnings
#806 Add new deployment pipeline
#803 ci: test with Java 21 
#801 Updates the pipeline to validate that packaging a jar works properly
#798 fix: flakiness in org.json.junit.XMLTest#testIndentComplicatedJsonObjectWithArrayAndWithConfig
#794 XML optLong/getLong equivalent updates for string to number conversion.
Changelog

Sourced from org.json:json's changelog.

20240303 Revert optLong/getLong changes, and recent commits.

20240205 Recent commits.

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.json:json&package-manager=gradle&previous-version=20231013&new-version=20240303)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- .../in_app_purchase_android/example/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/in_app_purchase/in_app_purchase_android/example/android/app/build.gradle b/packages/in_app_purchase/in_app_purchase_android/example/android/app/build.gradle index dfccaac37d6e..9279ec8b2520 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/android/app/build.gradle +++ b/packages/in_app_purchase/in_app_purchase_android/example/android/app/build.gradle @@ -110,7 +110,7 @@ dependencies { implementation 'com.android.billingclient:billing:6.1.0' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:5.1.1' - testImplementation 'org.json:json:20231013' + testImplementation 'org.json:json:20240303' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } From c16c95687305ef3682c551dbb93e1c3ea6fcf5c9 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 26 Mar 2024 14:11:53 -0400 Subject: [PATCH 105/126] Roll Flutter from 14774b95c250 to dbdcead93225 (38 revisions) (#6399) Roll Flutter from 14774b95c250 to dbdcead93225 (38 revisions) https://github.com/flutter/flutter/compare/14774b95c250...dbdcead93225 2024-03-25 engine-flutter-autoroll@skia.org Roll Flutter Engine from ef896e942f44 to 04191a122e9a (1 revision) (flutter/flutter#145709) 2024-03-25 eltociear@gmail.com Update semantics.dart (flutter/flutter#145683) 2024-03-25 sokolovskyi.konstantin@gmail.com TwoDimensionalChildDelegate should dispatch creation and disposal events (flutter/flutter#145684) 2024-03-25 sokolovskyi.konstantin@gmail.com Memory leaks clean up 1 (flutter/flutter#145691) 2024-03-25 engine-flutter-autoroll@skia.org Roll Flutter Engine from 2e8d77dbb0f0 to ef896e942f44 (1 revision) (flutter/flutter#145705) 2024-03-25 Sahil.kachhap111989@gmail.com [Fix]: Searchbar doesn't lose focus when tapped outside (flutter/flutter#145232) 2024-03-25 sokolovskyi.konstantin@gmail.com Fix typo in hitTest docs (flutter/flutter#145677) 2024-03-25 engine-flutter-autoroll@skia.org Roll Packages from 611aea1657fb to 28d126c54c63 (1 revision) (flutter/flutter#145690) 2024-03-25 dacoharkes@google.com [deps] Bump native assets dependencies (flutter/flutter#145612) 2024-03-25 engine-flutter-autoroll@skia.org Roll Flutter Engine from 857584c458ff to 2e8d77dbb0f0 (1 revision) (flutter/flutter#145678) 2024-03-25 tessertaha@gmail.com Add `AnimationStyle` to `showBottomSheet` and `showModalBottomSheet` (flutter/flutter#145536) 2024-03-25 engine-flutter-autoroll@skia.org Roll Flutter Engine from ae758d54630d to 857584c458ff (2 revisions) (flutter/flutter#145673) 2024-03-25 engine-flutter-autoroll@skia.org Roll Flutter Engine from efdde39a2a57 to ae758d54630d (1 revision) (flutter/flutter#145669) 2024-03-25 andrewrkolos@gmail.com make hot reload reflect changes to asset transformer configurations (flutter/flutter#144660) 2024-03-25 engine-flutter-autoroll@skia.org Roll Flutter Engine from 0734f47f2a3d to efdde39a2a57 (2 revisions) (flutter/flutter#145668) 2024-03-24 engine-flutter-autoroll@skia.org Roll Flutter Engine from 7c139e0a4897 to 0734f47f2a3d (1 revision) (flutter/flutter#145662) 2024-03-24 polinach@google.com Leak clean up. (flutter/flutter#144803) 2024-03-24 polinach@google.com Turn off randomization for leak detection bots. (flutter/flutter#145624) 2024-03-24 engine-flutter-autoroll@skia.org Roll Flutter Engine from 2f5afdf62365 to 7c139e0a4897 (1 revision) (flutter/flutter#145655) 2024-03-24 engine-flutter-autoroll@skia.org Roll Flutter Engine from 7aa8521e640e to 2f5afdf62365 (2 revisions) (flutter/flutter#145651) 2024-03-23 engine-flutter-autoroll@skia.org Roll Flutter Engine from 689ea4b10b17 to 7aa8521e640e (1 revision) (flutter/flutter#145643) 2024-03-23 engine-flutter-autoroll@skia.org Roll Flutter Engine from 42d4262b4b20 to 689ea4b10b17 (1 revision) (flutter/flutter#145636) 2024-03-23 6655696+guidezpl@users.noreply.github.com Reland #128236 "Improve build output for all platforms" (flutter/flutter#145495) 2024-03-23 engine-flutter-autoroll@skia.org Roll Flutter Engine from 7690eb12ded5 to 42d4262b4b20 (1 revision) (flutter/flutter#145635) 2024-03-23 engine-flutter-autoroll@skia.org Roll Flutter Engine from 8a51e124fbf1 to 7690eb12ded5 (1 revision) (flutter/flutter#145634) 2024-03-23 engine-flutter-autoroll@skia.org Roll Flutter Engine from bc4fa4367edb to 8a51e124fbf1 (4 revisions) (flutter/flutter#145630) 2024-03-23 49699333+dependabot[bot]@users.noreply.github.com Bump github/codeql-action from 3.24.8 to 3.24.9 (flutter/flutter#145627) 2024-03-23 godofredoc@google.com Add workaround for bug adding unicode strings to test reports. (flutter/flutter#145607) 2024-03-23 engine-flutter-autoroll@skia.org Roll Flutter Engine from 52142e428760 to bc4fa4367edb (1 revision) (flutter/flutter#145626) 2024-03-23 ditman@gmail.com [web][docs] Improve HtmlElementView widget docs. (flutter/flutter#145192) 2024-03-23 jacksongardner@google.com Disable flaky text_editing_integration tests temporarily. (flutter/flutter#145629) 2024-03-22 engine-flutter-autoroll@skia.org Roll Flutter Engine from cc8f752b81e5 to 52142e428760 (4 revisions) (flutter/flutter#145625) 2024-03-22 katelovett@google.com Fix skipping golden comparator for CI environments (flutter/flutter#145619) 2024-03-22 engine-flutter-autoroll@skia.org Roll Flutter Engine from 68301f268278 to cc8f752b81e5 (1 revision) (flutter/flutter#145621) 2024-03-22 engine-flutter-autoroll@skia.org Roll Packages from b7fbe68d4ec2 to 611aea1657fb (4 revisions) (flutter/flutter#145615) 2024-03-22 engine-flutter-autoroll@skia.org Roll Flutter Engine from 63ff0dedd8e6 to 68301f268278 (2 revisions) (flutter/flutter#145613) 2024-03-22 engine-flutter-autoroll@skia.org Roll Flutter Engine from 09dadce76828 to 63ff0dedd8e6 (2 revisions) (flutter/flutter#145611) 2024-03-22 katelovett@google.com 2DScrollView - Fix drag when one axis does not have enough content (flutter/flutter#145566) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC dit@google.com,rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose ... --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index c43d284b50cf..460e1b5f8eb8 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -14774b95c250d59b1a1bbfa7b3da63d24b59e008 +dbdcead93225bf701bdf03d1acb1b98a98f59334 From 014cf20185671de8d12b9c3fd80fc523c837ac9b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Mar 2024 18:27:07 +0000 Subject: [PATCH 106/126] Bump github/codeql-action from 3.24.7 to 3.24.9 (#6386) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.24.7 to 3.24.9.
Changelog

Sourced from github/codeql-action's changelog.

CodeQL Action Changelog

See the releases page for the relevant changes to the CodeQL CLI and language packs.

Note that the only difference between v2 and v3 of the CodeQL Action is the node version they support, with v3 running on node 20 while we continue to release v2 to support running on node 16. For example 3.22.11 was the first v3 release and is functionally identical to 2.22.11. This approach ensures an easy way to track exactly which features are included in different versions, indicated by the minor and patch version numbers.

[UNRELEASED]

No user facing changes.

3.24.9 - 22 Mar 2024

  • Update default CodeQL bundle version to 2.16.5. #2203

3.24.8 - 18 Mar 2024

  • Improve the ease of debugging extraction issues by increasing the verbosity of the extractor logs when running in debug mode. #2195

3.24.7 - 12 Mar 2024

  • Update default CodeQL bundle version to 2.16.4. #2185

3.24.6 - 29 Feb 2024

No user facing changes.

3.24.5 - 23 Feb 2024

  • Update default CodeQL bundle version to 2.16.3. #2156

3.24.4 - 21 Feb 2024

  • Fix an issue where an existing, but empty, /sys/fs/cgroup/cpuset.cpus file always resulted in a single-threaded run. #2151

3.24.3 - 15 Feb 2024

  • Fix an issue where the CodeQL Action would fail to load a configuration specified by the config input to the init Action. #2147

3.24.2 - 15 Feb 2024

  • Enable improved multi-threaded performance on larger runners for GitHub Enterprise Server users. This feature is already available to GitHub.com users. #2141

3.24.1 - 13 Feb 2024

  • Update default CodeQL bundle version to 2.16.2. #2124
  • The CodeQL action no longer fails if it can't write to the telemetry api endpoint. #2121

3.24.0 - 02 Feb 2024

  • CodeQL Python analysis will no longer install dependencies on GitHub Enterprise Server, as is already the case for GitHub.com. See release notes for 3.23.0 for more details. #2106

... (truncated)

Commits
  • 1b1aada Merge pull request #2208 from github/update-v3.24.9-09d4101d2
  • 6505708 Update changelog for v3.24.9
  • 09d4101 Merge pull request #2203 from github/update-bundle/codeql-bundle-v2.16.5
  • a3ab02e Merge branch 'main' into update-bundle/codeql-bundle-v2.16.5
  • 9cf4574 Add changelog note
  • 964f5e7 Merge pull request #2207 from github/henrymercer/more-processing-error-catego...
  • 9c0c35b Merge pull request #2206 from github/henrymercer/improved-autobuild-error-wit...
  • c84e4c8 Mark some more processing errors as invalid SARIF upload requests
  • 4aca720 Improve error message when using build modes and autobuild fails
  • 7f375ae Wrap configuration errors for all CLI commands
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github/codeql-action&package-manager=github_actions&previous-version=3.24.7&new-version=3.24.9)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- .github/workflows/scorecards-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index d8b96951cc50..2cf05fe701cd 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -49,6 +49,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@3ab4101902695724f9365a384f86c1074d94e18c # v1.0.26 + uses: github/codeql-action/upload-sarif@1b1aada464948af03b950897e5eb522f92603cc2 # v1.0.26 with: sarif_file: results.sarif From 01d16a467d9e2f8d759f15bcf4b0c856113cd705 Mon Sep 17 00:00:00 2001 From: Michael Goderbauer Date: Tue, 26 Mar 2024 11:27:09 -0700 Subject: [PATCH 107/126] [flutter_lints] Updates various links (#6400) --- packages/flutter_lints/CHANGELOG.md | 3 ++- packages/flutter_lints/README.md | 6 +++--- packages/flutter_lints/example/analysis_options.yaml | 4 ++-- packages/flutter_lints/pubspec.yaml | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/flutter_lints/CHANGELOG.md b/packages/flutter_lints/CHANGELOG.md index 959080a0e1c6..5e5343cc446e 100644 --- a/packages/flutter_lints/CHANGELOG.md +++ b/packages/flutter_lints/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 3.0.2 * Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. +* Updates various links in comments and documentation. ## 3.0.1 diff --git a/packages/flutter_lints/README.md b/packages/flutter_lints/README.md index 941fb47e7452..6617deecc816 100644 --- a/packages/flutter_lints/README.md +++ b/packages/flutter_lints/README.md @@ -42,7 +42,7 @@ linter: # The lint rules applied to this project can be customized in the # section below to disable rules from the `package:flutter_lints/flutter.yaml` # included above or to enable additional rules. A list of all available lints - # and their documentation is published at https://dart.dev/lints. + # and their documentation is published at https://dart.dev/tools/linter-rules. # # Instead of disabling a lint rule for the entire project in the # section below, it can also be suppressed for a single line of code @@ -54,7 +54,7 @@ linter: # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule # Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options +# https://dart.dev/tools/analysis ``` ## Adding new lints @@ -71,7 +71,7 @@ bump. To keep churn low, lints are not added one-by-one, but in one batch following a review of all accumulated suggestions since the previous review. [Flutter]: https://flutter.dev -[dart analyzer]: https://dart.dev/guides/language/analysis-options +[dart analyzer]: https://dart.dev/tools/analysis [Dart-enabled IDEs]: https://dart.dev/tools#ides-and-editors [package:lints]: https://pub.dev/packages/lints [lint proposal]: https://github.com/dart-lang/lints/issues/new?&labels=type-lint&template=lint-propoposal.md diff --git a/packages/flutter_lints/example/analysis_options.yaml b/packages/flutter_lints/example/analysis_options.yaml index 0d2902135cae..6e937f6ea890 100644 --- a/packages/flutter_lints/example/analysis_options.yaml +++ b/packages/flutter_lints/example/analysis_options.yaml @@ -13,7 +13,7 @@ linter: # The lint rules applied to this project can be customized in the # section below to disable rules from the `package:flutter_lints/flutter.yaml` # included above or to enable additional rules. A list of all available lints - # and their documentation is published at https://dart.dev/lints. + # and their documentation is published at https://dart.dev/tools/linter-rules. # # Instead of disabling a lint rule for the entire project in the # section below, it can also be suppressed for a single line of code @@ -25,4 +25,4 @@ linter: # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule # Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options +# https://dart.dev/tools/analysis diff --git a/packages/flutter_lints/pubspec.yaml b/packages/flutter_lints/pubspec.yaml index 860eedbb55ee..051e5cf2a095 100644 --- a/packages/flutter_lints/pubspec.yaml +++ b/packages/flutter_lints/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_lints description: Recommended lints for Flutter apps, packages, and plugins to encourage good coding practices. repository: https://github.com/flutter/packages/tree/main/packages/flutter_lints issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_lints%22 -version: 3.0.1 +version: 3.0.2 environment: sdk: ^3.1.0 From 00d29b18501bd088070e13cedb1fb49e03ae6b2f Mon Sep 17 00:00:00 2001 From: LouiseHsu Date: Tue, 26 Mar 2024 23:54:53 -0700 Subject: [PATCH 108/126] [google_maps_flutter] Fix ios tile overlay not loading when dealing with wide gamut images (#6377) Fixes https://github.com/flutter/flutter/issues/135252 Google maps does not currently support the use of wide gamut images. Since engine uses 16 bit floats when dealing with wide gamut images ([here](https://github.com/flutter/engine/blob/8a51e124fbf168295a1b05f8d66459bfb29ae8a9/shell/platform/darwin/ios/framework/Source/FlutterView.mm#L125)), when we detect that an image is using that format, we downsample it. https://github.com/flutter/packages/assets/36148254/2d67418d-53a9-472b-b03e-86fb24f9610c ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [relevant style guides] and ran the auto-formatter. (Unlike the flutter/flutter repo, the flutter/packages repo does use `dart format`.) - [x] I signed the [CLA]. - [x] The title of the PR starts with the name of the package surrounded by square brackets, e.g. `[shared_preferences]` - [x] I [linked to at least one issue that this PR fixes] in the description above. - [x] I updated `pubspec.yaml` with an appropriate new version according to the [pub versioning philosophy], or this PR is [exempt from version changes]. - [x] I updated `CHANGELOG.md` to add a description of the change, [following repository CHANGELOG style]. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [Contributor Guide]: https://github.com/flutter/packages/blob/main/CONTRIBUTING.md [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [relevant style guides]: https://github.com/flutter/packages/blob/main/CONTRIBUTING.md#style [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat [linked to at least one issue that this PR fixes]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [pub versioning philosophy]: https://dart.dev/tools/pub/versioning [exempt from version changes]: https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#version-and-changelog-updates [following repository CHANGELOG style]: https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changelog-style [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests --- .../google_maps_flutter_ios/CHANGELOG.md | 4 +++ .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- .../ios/Runner.xcodeproj/project.pbxproj | 6 ++++ .../ios14/ios/RunnerTests/GoogleMapsTests.m | 23 ++++++++++++++++ .../ios/RunnerTests/assets/widegamut.png | Bin 0 -> 3025 bytes .../FLTGoogleMapTileOverlayController.m | 26 ++++++++++++++++-- .../google_maps_flutter_ios/pubspec.yaml | 2 +- 7 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/assets/widegamut.png diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md index 96468dc393fc..2d1f96e1db09 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.5.2 + +* Fixes the tile overlay not correctly displaying on physical ios devices. + ## 2.5.1 * Makes the tile overlay callback invoke the platform channel on the platform thread. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios12/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios12/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index e271ffe8da9a..f8d70602adf7 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios12/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios12/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ od`25nQR-ORux1~&Vd3PXb2NR9e4SF1 zUI|5tljKb&Ig&Sr)O%1AIh9T)r5vZyInPk^R&VdK-@W_VbzlGgb^WjVe(v8gSgXDD zkS0g~0Q7u)7(w97(+nLg@LM{N6AS>5SOJ~R64Jftl4yyHE#+|e41ru4&DqMb2LP+g zSarjy%PgZS%<`b0DXO`PO-UK_u1;iJOq8{eCovdN&OWehjws0z7ZhW-4mZ9e;Cns6?V*M&7MVqvVupjMA^GyR*6)pVV|pSXR; zVRl(Wb?H$X%;0sqC&fyh|=Z175x1^u*wK=}>MUr;jPX5W7?Z5OD z-dvY=#dJ8ZjbpeBy^|G~3P;>aesg25+g@AQx^NbLAJnFm;XB{3x@q+1BOC)#j1FyB zeNO&(+q{#nuZl!Z7vzhi*9kp69x&%aNA}t9D$#;dR))U$baa%61wP@GCeK^1m@r?)w z0G5Nc4uF6d05H&kfFA&20w5-B0Puq>dT$3o>;Nd}Hv`A|9RdJ89g_skn#TtmAbM)1 z@g$r1SXPuoA(2N(q*&4lydBmmfWsAtH69!o2Q7lW=in8=W^8Z`IDI0{5uD~oeM02` zplhfZ5a2)o8UWyNg5VHENPs_uD-q#2nnZY|NUA{rR3!y8MSKMZs}x0xxhL^Kh602}A zH`gS3|4h}X5Jb#m63eG$fdUDd6@nd}Nca>C6dMrmzQPZ1N{)0ACK7!TDOUvXKa=qt zQXafj$|nTz<&sz#mk)+pe5Qo|UBLUEfRd>iHIz^dXLPKlFRo4`>ZHUEoHGrY0)%`D zFx&ax0H#d92lr$$gWYt3*}hrvlP#n$_%cBppXVi$h$gp6%8|+W8vQQj?;0>$hepa| z2Pp!vT)`1@`MwNSkO?mk@F;W!ZH2v)lan*e%gN5plR;pE}rNMw*4h-8u@gG%@a zI#bUKGf%>e6@e6Px=vbR??|8-_Lf5 z{3Y61&<|-TbQzzckjR21l4#d2-8YlsKR`^AQ3;wibJm+Wbq!2l*#NAlULt5%a@>`%;{;9ls6{t}6ES%b=Ajj-# zN|LW}yIQJT@a%`^cG#BV;)I$tck&QvEY*wS(QelwCbp^0r@pC+?r*J4F`7SCk^ZbW z?AS!Z<95@9yPpn4SfiU_>d1_N{g~qLyW88U(gVvb7^}NEVMrf1DrtT1()0=|bgq0y z_XL z)bcSxRkr0LTZ$WjR=J=eer=ik?cnRL#KR?Cxx3i&LWgmOiT6j>nw>nw)P-}<7aKUr zcFYDfE|t;fo*#v7QqKh$np(m&l#P+gR>3ThUg?~&-}P~z!~NT6q6%jUMwk?Kcz}?` z$Ov7S0m5RCxF%k$57rl@h(k9$oNEd78si}KN$aqI#E2GDJXSZ|@@XC(Qyu=otSfQ{ zEF*;mi!>>@tx58r&C!N_G`#HqW|%=wu|a43G1h4cDm&nk7@>3MQC6)E`uPd18oyJR zx@PtD@SA4>QuRY}+Zz!(mDF2kW2a>ih%pB=6YYF^QAsVaWv;Mzbrbi}@qel!FavtU z|EP*D8QWaA^3}@$GN*v2cZMw(;%;FV99+;@IYDsw+xmVg~tx zG-QLSJdPU}J$uAG%7?(NB_`~7U>YlcF8OoC8aI{_#eQfh-_W^?hTBS=*yz~TTl#G1 zVtq*>g8sVRslb_b$h%*sp!DsCt=L`VZGh zK1vkBztVLn>ic`-)I{)rr2c!;MTzylA}^-j`!n*@QBG0lc~nMP3{$)Mg41IC1l<0& zEp0^|3FnJpylMqK_rZd-{3FNU&(jD0swcGj zXFe?@IjB|s5XSwH91~Rn4(if6;(!1>mm(P@{t!zN`+jeE0n&$20{m#tE`c81lrTi* z`(aJ`aV|?TTu7kF^D{EyKPr{2#|E-SR+88yS_TNu?u=yN!LM}Ee2T2waCv~a)$+*0 z4+$_hI{!Zwc3%gWJcF!nWum^$X&Bs5#2U8Wx3VlTUMN&0#dlnV<-vo55Tk@MLwRR} zcN{w{Pk-m~Q9wV3+Ncj8Y*agViu7hdfdl#%%dN b>|umk_w!q81@)IS|E7FBS2Ip|gs1%tSx05K literal 0 HcmV?d00001 diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m index ffa646bf870f..73eab6c1eadb 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m @@ -118,6 +118,29 @@ - (instancetype)init:(FlutterMethodChannel *)methodChannel #pragma mark - GMSTileLayer method +- (UIImage *)handleResultTile:(nullable UIImage *)tile { + CGImageRef imageRef = tile.CGImage; + CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef); + BOOL isFloat = bitmapInfo && kCGBitmapFloatComponents; + size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef); + + // Engine use f16 pixel format for wide gamut images + // If it is wide gamut, we want to downsample it + if (isFloat & (bitsPerComponent == 16)) { + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGContextRef context = CGBitmapContextCreate(nil, tile.size.width, tile.size.height, 8, 0, + colorSpace, kCGImageAlphaPremultipliedLast); + CGContextDrawImage(context, CGRectMake(0, 0, tile.size.width, tile.size.height), tile.CGImage); + CGImageRef image = CGBitmapContextCreateImage(context); + tile = [UIImage imageWithCGImage:image]; + + CGImageRelease(image); + CGContextRelease(context); + CGColorSpaceRelease(colorSpace); + } + return tile; +} + - (void)requestTileForX:(NSUInteger)x y:(NSUInteger)y zoom:(NSUInteger)zoom @@ -137,7 +160,7 @@ - (void)requestTileForX:(NSUInteger)x if (typedData == nil) { tileImage = kGMSTileLayerNoTile; } else { - tileImage = [UIImage imageWithData:typedData.data]; + tileImage = [self handleResultTile:[UIImage imageWithData:typedData.data]]; } } else { if ([result isKindOfClass:[FlutterError class]]) { @@ -150,7 +173,6 @@ - (void)requestTileForX:(NSUInteger)x } tileImage = kGMSTileLayerNoTile; } - [receiver receiveTileWithX:x y:y zoom:zoom image:tileImage]; }]; }); diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml index c1ebb4e2cea2..0a3d3e0894fa 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_ios description: iOS implementation of the google_maps_flutter plugin. repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.5.1 +version: 2.5.2 environment: sdk: ^3.2.3 From e6b3e11d9d5a64ec51f90eefc0fd01c826e1ed63 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:29:11 +0000 Subject: [PATCH 109/126] [camera]: Bump androidx.annotation:annotation from 1.7.0 to 1.7.1 in /packages/camera/camera_android/android (#5707) Bumps androidx.annotation:annotation from 1.7.0 to 1.7.1. [![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=androidx.annotation:annotation&package-manager=gradle&previous-version=1.7.0&new-version=1.7.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) You can trigger a rebase of this PR by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
> **Note** > Automatic rebases have been disabled on this pull request as it has been open for over 30 days. --- packages/camera/camera_android/CHANGELOG.md | 4 ++++ packages/camera/camera_android/android/build.gradle | 2 +- packages/camera/camera_android/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index 37790bd7f5b1..82d27a401e85 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.10.8+18 + +* Updates annotations lib to 1.7.1. + ## 0.10.8+17 * Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. diff --git a/packages/camera/camera_android/android/build.gradle b/packages/camera/camera_android/android/build.gradle index 824931536ec8..dfa21b72bb86 100644 --- a/packages/camera/camera_android/android/build.gradle +++ b/packages/camera/camera_android/android/build.gradle @@ -65,7 +65,7 @@ buildFeatures { } dependencies { - implementation 'androidx.annotation:annotation:1.7.0' + implementation 'androidx.annotation:annotation:1.7.1' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:5.0.0' testImplementation 'androidx.test:core:1.4.0' diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index fadf4b6bcc69..e45547bc0a92 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -3,7 +3,7 @@ description: Android implementation of the camera plugin. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.10.8+17 +version: 0.10.8+18 environment: sdk: ^3.1.0 From f61723ba2acc85cf9c579290a2efb64ea3f4cf80 Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Wed, 27 Mar 2024 13:06:06 -0400 Subject: [PATCH 110/126] [camerax] Use `AspectRatioStrategy` to help automatic selection of expected resolution (#6357) Defines `AspectRatioStategy`s that will help CameraX select the resolution we expect. Fixes https://github.com/flutter/flutter/issues/144363. --- .../camera_android_camerax/CHANGELOG.md | 6 + .../camerax/CameraAndroidCameraxPlugin.java | 2 + .../camerax/GeneratedCameraXLibrary.java | 80 ++- .../camerax/ResolutionFilterHostApiImpl.java | 94 +++ .../ResolutionSelectorHostApiImpl.java | 17 +- .../plugins/camerax/ResolutionFilterTest.java | 76 +++ .../camerax/ResolutionSelectorTest.java | 13 +- .../lib/src/android_camera_camerax.dart | 22 +- .../lib/src/camerax_library.g.dart | 65 +- .../lib/src/camerax_proxy.dart | 45 +- .../lib/src/resolution_filter.dart | 107 +++ .../lib/src/resolution_selector.dart | 16 +- .../lib/src/zoom_state.dart | 2 +- .../pigeons/camerax_library.dart | 7 + .../camera_android_camerax/pubspec.yaml | 2 +- .../test/android_camera_camerax_test.dart | 386 +++++++---- .../android_camera_camerax_test.mocks.dart | 613 ++++++++++-------- .../test/resolution_filter_test.dart | 90 +++ .../test/resolution_filter_test.mocks.dart | 67 ++ .../test/resolution_selector_test.dart | 26 +- .../test/resolution_selector_test.mocks.dart | 48 +- .../test/test_camerax_library.g.dart | 75 ++- 22 files changed, 1431 insertions(+), 428 deletions(-) create mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ResolutionFilterHostApiImpl.java create mode 100644 packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ResolutionFilterTest.java create mode 100644 packages/camera/camera_android_camerax/lib/src/resolution_filter.dart create mode 100644 packages/camera/camera_android_camerax/test/resolution_filter_test.dart create mode 100644 packages/camera/camera_android_camerax/test/resolution_filter_test.mocks.dart diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index 2226ba4352c7..68fdea1e7554 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.6.1 + +* Modifies resolution selection logic to use an `AspectRatioStrategy` for all aspect ratios supported by CameraX. +* Adds `ResolutionFilter` to resolution selection logic to prioritize resolutions that match + the defined `ResolutionPreset`s. + ## 0.6.0+1 * Updates `README.md` to encourage developers to opt into this implementation of the camera plugin. diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java index 6a30074a2f87..d281bbe65a8c 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java @@ -134,6 +134,8 @@ public void setUp( binaryMessenger, new FocusMeteringResultHostApiImpl(instanceManager)); meteringPointHostApiImpl = new MeteringPointHostApiImpl(instanceManager); GeneratedCameraXLibrary.MeteringPointHostApi.setup(binaryMessenger, meteringPointHostApiImpl); + GeneratedCameraXLibrary.ResolutionFilterHostApi.setup( + binaryMessenger, new ResolutionFilterHostApiImpl(instanceManager)); } @Override diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java index 75b4a8c276bc..fa6be7920961 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java @@ -2507,6 +2507,7 @@ public interface ResolutionSelectorHostApi { void create( @NonNull Long identifier, @Nullable Long resolutionStrategyIdentifier, + @Nullable Long resolutionSelectorIdentifier, @Nullable Long aspectRatioStrategyIdentifier); /** The codec used by ResolutionSelectorHostApi. */ @@ -2530,13 +2531,17 @@ static void setup( ArrayList args = (ArrayList) message; Number identifierArg = (Number) args.get(0); Number resolutionStrategyIdentifierArg = (Number) args.get(1); - Number aspectRatioStrategyIdentifierArg = (Number) args.get(2); + Number resolutionSelectorIdentifierArg = (Number) args.get(2); + Number aspectRatioStrategyIdentifierArg = (Number) args.get(3); try { api.create( (identifierArg == null) ? null : identifierArg.longValue(), (resolutionStrategyIdentifierArg == null) ? null : resolutionStrategyIdentifierArg.longValue(), + (resolutionSelectorIdentifierArg == null) + ? null + : resolutionSelectorIdentifierArg.longValue(), (aspectRatioStrategyIdentifierArg == null) ? null : aspectRatioStrategyIdentifierArg.longValue()); @@ -4189,4 +4194,77 @@ public void error(Throwable error) { } } } + + private static class ResolutionFilterHostApiCodec extends StandardMessageCodec { + public static final ResolutionFilterHostApiCodec INSTANCE = new ResolutionFilterHostApiCodec(); + + private ResolutionFilterHostApiCodec() {} + + @Override + protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { + switch (type) { + case (byte) 128: + return ResolutionInfo.fromList((ArrayList) readValue(buffer)); + default: + return super.readValueOfType(type, buffer); + } + } + + @Override + protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { + if (value instanceof ResolutionInfo) { + stream.write(128); + writeValue(stream, ((ResolutionInfo) value).toList()); + } else { + super.writeValue(stream, value); + } + } + } + + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ + public interface ResolutionFilterHostApi { + + void createWithOnePreferredSize( + @NonNull Long identifier, @NonNull ResolutionInfo preferredResolution); + + /** The codec used by ResolutionFilterHostApi. */ + static @NonNull MessageCodec getCodec() { + return ResolutionFilterHostApiCodec.INSTANCE; + } + /** + * Sets up an instance of `ResolutionFilterHostApi` to handle messages through the + * `binaryMessenger`. + */ + static void setup( + @NonNull BinaryMessenger binaryMessenger, @Nullable ResolutionFilterHostApi api) { + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.ResolutionFilterHostApi.createWithOnePreferredSize", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + ResolutionInfo preferredResolutionArg = (ResolutionInfo) args.get(1); + try { + api.createWithOnePreferredSize( + (identifierArg == null) ? null : identifierArg.longValue(), + preferredResolutionArg); + wrapped.add(0, null); + } catch (Throwable exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ResolutionFilterHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ResolutionFilterHostApiImpl.java new file mode 100644 index 000000000000..b2c3c9e69123 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ResolutionFilterHostApiImpl.java @@ -0,0 +1,94 @@ +// 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. + +package io.flutter.plugins.camerax; + +import android.util.Size; +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; +import androidx.camera.core.resolutionselector.ResolutionFilter; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.ResolutionFilterHostApi; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.ResolutionInfo; +import java.util.List; + +/** + * Host API implementation for {@link ResolutionFilter}. + * + *

This class handles instantiating and adding native object instances that are attached to a + * Dart instance or handle method calls on the associated native class or an instance of the class. + */ +public class ResolutionFilterHostApiImpl implements ResolutionFilterHostApi { + private final InstanceManager instanceManager; + private final ResolutionFilterFactory resolutionFilterFactory; + + /** + * Proxy for constructing {@link ResolutionFilter}s with particular attributes, as detailed by + * documentation below. + */ + @VisibleForTesting + public static class ResolutionFilterFactory { + /** + * Creates an instance of {@link ResolutionFilter} that moves the {@code preferredSize} to the + * front of the list of supported resolutions so that it can be prioritized by CameraX. + * + *

If the preferred {@code Size} is not found, then this creates a {@link ResolutionFilter} + * that leaves the priority of supported resolutions unadjusted. + */ + @NonNull + public ResolutionFilter createWithOnePreferredSize(@NonNull Size preferredSize) { + return new ResolutionFilter() { + @Override + @NonNull + public List filter(@NonNull List supportedSizes, int rotationDegrees) { + int preferredSizeIndex = supportedSizes.indexOf(preferredSize); + + if (preferredSizeIndex > -1) { + supportedSizes.remove(preferredSizeIndex); + supportedSizes.add(0, preferredSize); + } + + return supportedSizes; + } + }; + } + } + + /** + * Constructs a {@link ResolutionFilterHostApiImpl}. + * + * @param instanceManager maintains instances stored to communicate with attached Dart objects + */ + public ResolutionFilterHostApiImpl(@NonNull InstanceManager instanceManager) { + this(instanceManager, new ResolutionFilterFactory()); + } + + /** + * Constructs a {@link ResolutionFilterHostApiImpl}. + * + * @param instanceManager maintains instances stored to communicate with attached Dart objects + * @param resolutionFilterFactory proxy for constructing different kinds of {@link + * ResolutionFilter}s + */ + @VisibleForTesting + ResolutionFilterHostApiImpl( + @NonNull InstanceManager instanceManager, + @NonNull ResolutionFilterFactory resolutionFilterFactory) { + this.instanceManager = instanceManager; + this.resolutionFilterFactory = resolutionFilterFactory; + } + + /** + * Creates a {@link ResolutionFilter} that prioritizes the specified {@code preferredResolution} + * over all other resolutions. + */ + @Override + public void createWithOnePreferredSize( + @NonNull Long identifier, @NonNull ResolutionInfo preferredResolution) { + Size preferredSize = + new Size( + preferredResolution.getWidth().intValue(), preferredResolution.getHeight().intValue()); + instanceManager.addDartCreatedInstance( + resolutionFilterFactory.createWithOnePreferredSize(preferredSize), identifier); + } +} diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ResolutionSelectorHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ResolutionSelectorHostApiImpl.java index 4aa11cd593f2..0a5fe750d163 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ResolutionSelectorHostApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ResolutionSelectorHostApiImpl.java @@ -8,6 +8,7 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.camera.core.resolutionselector.AspectRatioStrategy; +import androidx.camera.core.resolutionselector.ResolutionFilter; import androidx.camera.core.resolutionselector.ResolutionSelector; import androidx.camera.core.resolutionselector.ResolutionStrategy; import io.flutter.plugins.camerax.GeneratedCameraXLibrary.ResolutionSelectorHostApi; @@ -30,7 +31,8 @@ public static class ResolutionSelectorProxy { @NonNull public ResolutionSelector create( @Nullable ResolutionStrategy resolutionStrategy, - @Nullable AspectRatioStrategy aspectRatioStrategy) { + @Nullable AspectRatioStrategy aspectRatioStrategy, + @Nullable ResolutionFilter resolutionFilter) { final ResolutionSelector.Builder builder = new ResolutionSelector.Builder(); if (resolutionStrategy != null) { builder.setResolutionStrategy(resolutionStrategy); @@ -38,6 +40,9 @@ public ResolutionSelector create( if (aspectRatioStrategy != null) { builder.setAspectRatioStrategy(aspectRatioStrategy); } + if (resolutionFilter != null) { + builder.setResolutionFilter(resolutionFilter); + } return builder.build(); } } @@ -65,13 +70,14 @@ public ResolutionSelectorHostApiImpl(@NonNull InstanceManager instanceManager) { } /** - * Creates a {@link ResolutionSelector} instance with the {@link ResolutionStrategy} and {@link - * AspectRatio} that have the identifiers specified if provided. + * Creates a {@link ResolutionSelector} instance with the {@link ResolutionStrategy}, {@link + * ResolutionFilter}, and {@link AspectRatio} that have the identifiers specified if provided. */ @Override public void create( @NonNull Long identifier, @Nullable Long resolutionStrategyIdentifier, + @Nullable Long resolutionFilterIdentifier, @Nullable Long aspectRatioStrategyIdentifier) { instanceManager.addDartCreatedInstance( proxy.create( @@ -81,7 +87,10 @@ public void create( aspectRatioStrategyIdentifier == null ? null : Objects.requireNonNull( - instanceManager.getInstance(aspectRatioStrategyIdentifier))), + instanceManager.getInstance(aspectRatioStrategyIdentifier)), + resolutionFilterIdentifier == null + ? null + : Objects.requireNonNull(instanceManager.getInstance(resolutionFilterIdentifier))), identifier); } } diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ResolutionFilterTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ResolutionFilterTest.java new file mode 100644 index 000000000000..150f5676739a --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ResolutionFilterTest.java @@ -0,0 +1,76 @@ +// 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. + +package io.flutter.plugins.camerax; + +import static org.junit.Assert.assertEquals; + +import android.util.Size; +import androidx.camera.core.resolutionselector.ResolutionFilter; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.ResolutionInfo; +import java.util.ArrayList; +import java.util.List; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class ResolutionFilterTest { + @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); + + InstanceManager instanceManager; + + @Before + public void setUp() { + instanceManager = InstanceManager.create(identifier -> {}); + } + + @After + public void tearDown() { + instanceManager.stopFinalizationListener(); + } + + @Test + public void hostApiCreateWithOnePreferredSize_createsExpectedResolutionFilterInstance() { + final ResolutionFilterHostApiImpl hostApi = new ResolutionFilterHostApiImpl(instanceManager); + final long instanceIdentifier = 50; + final long preferredResolutionWidth = 20; + final long preferredResolutionHeight = 80; + final ResolutionInfo preferredResolution = + new ResolutionInfo.Builder() + .setWidth(preferredResolutionWidth) + .setHeight(preferredResolutionHeight) + .build(); + + hostApi.createWithOnePreferredSize(instanceIdentifier, preferredResolution); + + // Test that instance filters supported resolutions as expected. + final ResolutionFilter resolutionFilter = instanceManager.getInstance(instanceIdentifier); + final Size fakeSupportedSize1 = new Size(720, 480); + final Size fakeSupportedSize2 = new Size(20, 80); + final Size fakeSupportedSize3 = new Size(2, 8); + final Size preferredSize = + new Size((int) preferredResolutionWidth, (int) preferredResolutionHeight); + + final ArrayList fakeSupportedSizes = new ArrayList(); + fakeSupportedSizes.add(fakeSupportedSize1); + fakeSupportedSizes.add(fakeSupportedSize2); + fakeSupportedSizes.add(preferredSize); + fakeSupportedSizes.add(fakeSupportedSize3); + + // Test the case where preferred resolution is supported. + List filteredSizes = resolutionFilter.filter(fakeSupportedSizes, 90); + assertEquals(filteredSizes.get(0), preferredSize); + + // Test the case where preferred resolution is not supported. + fakeSupportedSizes.remove(0); + filteredSizes = resolutionFilter.filter(fakeSupportedSizes, 90); + assertEquals(filteredSizes, fakeSupportedSizes); + } +} diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ResolutionSelectorTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ResolutionSelectorTest.java index f323e4706c9b..0f45f07b4f7a 100644 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ResolutionSelectorTest.java +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ResolutionSelectorTest.java @@ -9,6 +9,7 @@ import static org.mockito.Mockito.when; import androidx.camera.core.resolutionselector.AspectRatioStrategy; +import androidx.camera.core.resolutionselector.ResolutionFilter; import androidx.camera.core.resolutionselector.ResolutionSelector; import androidx.camera.core.resolutionselector.ResolutionStrategy; import org.junit.After; @@ -46,13 +47,21 @@ public void hostApiCreate_createsExpectedResolutionSelectorInstance() { final long aspectRatioStrategyIdentifier = 15; instanceManager.addDartCreatedInstance(mockAspectRatioStrategy, aspectRatioStrategyIdentifier); - when(mockProxy.create(mockResolutionStrategy, mockAspectRatioStrategy)) + final ResolutionFilter mockResolutionFilter = mock(ResolutionFilter.class); + final long resolutionFilterIdentifier = 33; + instanceManager.addDartCreatedInstance(mockResolutionFilter, resolutionFilterIdentifier); + + when(mockProxy.create(mockResolutionStrategy, mockAspectRatioStrategy, mockResolutionFilter)) .thenReturn(mockResolutionSelector); final ResolutionSelectorHostApiImpl hostApi = new ResolutionSelectorHostApiImpl(instanceManager, mockProxy); final long instanceIdentifier = 0; - hostApi.create(instanceIdentifier, resolutionStrategyIdentifier, aspectRatioStrategyIdentifier); + hostApi.create( + instanceIdentifier, + resolutionStrategyIdentifier, + resolutionFilterIdentifier, + aspectRatioStrategyIdentifier); assertEquals(instanceManager.getInstance(instanceIdentifier), mockResolutionSelector); } diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart index 1612ab512d32..19aaea83fa3d 100644 --- a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart @@ -9,10 +9,12 @@ import 'package:async/async.dart'; import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/services.dart' show DeviceOrientation, PlatformException; -import 'package:flutter/widgets.dart'; +import 'package:flutter/widgets.dart' + show Size, Texture, Widget, visibleForTesting; import 'package:stream_transform/stream_transform.dart'; import 'analyzer.dart'; +import 'aspect_ratio_strategy.dart'; import 'camera.dart'; import 'camera2_camera_control.dart'; import 'camera_control.dart'; @@ -40,6 +42,7 @@ import 'process_camera_provider.dart'; import 'quality_selector.dart'; import 'recorder.dart'; import 'recording.dart'; +import 'resolution_filter.dart'; import 'resolution_selector.dart'; import 'resolution_strategy.dart'; import 'surface.dart'; @@ -1150,23 +1153,29 @@ class AndroidCameraCameraX extends CameraPlatform { ResolutionStrategy.fallbackRuleClosestLowerThenHigher; Size? boundSize; + int? aspectRatio; ResolutionStrategy? resolutionStrategy; switch (preset) { case ResolutionPreset.low: boundSize = const Size(320, 240); + aspectRatio = AspectRatio.ratio4To3; case ResolutionPreset.medium: boundSize = const Size(720, 480); case ResolutionPreset.high: boundSize = const Size(1280, 720); + aspectRatio = AspectRatio.ratio16To9; case ResolutionPreset.veryHigh: boundSize = const Size(1920, 1080); + aspectRatio = AspectRatio.ratio16To9; case ResolutionPreset.ultraHigh: boundSize = const Size(3840, 2160); + aspectRatio = AspectRatio.ratio16To9; case ResolutionPreset.max: // Automatically set strategy to choose highest available. resolutionStrategy = proxy.createResolutionStrategy(highestAvailable: true); - return proxy.createResolutionSelector(resolutionStrategy); + return proxy.createResolutionSelector(resolutionStrategy, + /* ResolutionFilter */ null, /* AspectRatioStrategy */ null); case null: // If no preset is specified, default to CameraX's default behavior // for each UseCase. @@ -1175,7 +1184,14 @@ class AndroidCameraCameraX extends CameraPlatform { resolutionStrategy = proxy.createResolutionStrategy( boundSize: boundSize, fallbackRule: fallbackRule); - return proxy.createResolutionSelector(resolutionStrategy); + final ResolutionFilter resolutionFilter = + proxy.createResolutionFilterWithOnePreferredSize(boundSize); + final AspectRatioStrategy? aspectRatioStrategy = aspectRatio == null + ? null + : proxy.createAspectRatioStrategy( + aspectRatio, AspectRatioStrategy.fallbackRuleAuto); + return proxy.createResolutionSelector( + resolutionStrategy, resolutionFilter, aspectRatioStrategy); } /// Returns the [QualitySelector] that maps to the specified resolution diff --git a/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart b/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart index 78c167bc4a11..ac66dfd0ae77 100644 --- a/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart +++ b/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart @@ -1914,7 +1914,10 @@ class ResolutionSelectorHostApi { static const MessageCodec codec = StandardMessageCodec(); - Future create(int arg_identifier, int? arg_resolutionStrategyIdentifier, + Future create( + int arg_identifier, + int? arg_resolutionStrategyIdentifier, + int? arg_resolutionSelectorIdentifier, int? arg_aspectRatioStrategyIdentifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.ResolutionSelectorHostApi.create', codec, @@ -1922,6 +1925,7 @@ class ResolutionSelectorHostApi { final List? replyList = await channel.send([ arg_identifier, arg_resolutionStrategyIdentifier, + arg_resolutionSelectorIdentifier, arg_aspectRatioStrategyIdentifier ]) as List?; if (replyList == null) { @@ -3340,3 +3344,62 @@ class Camera2CameraControlHostApi { } } } + +class _ResolutionFilterHostApiCodec extends StandardMessageCodec { + const _ResolutionFilterHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is ResolutionInfo) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return ResolutionInfo.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +class ResolutionFilterHostApi { + /// Constructor for [ResolutionFilterHostApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + ResolutionFilterHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _ResolutionFilterHostApiCodec(); + + Future createWithOnePreferredSize( + int arg_identifier, ResolutionInfo arg_preferredResolution) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ResolutionFilterHostApi.createWithOnePreferredSize', + codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_identifier, arg_preferredResolution]) + as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return; + } + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/camerax_proxy.dart b/packages/camera/camera_android_camerax/lib/src/camerax_proxy.dart index fb100ddb20a2..6fec50ce3983 100644 --- a/packages/camera/camera_android_camerax/lib/src/camerax_proxy.dart +++ b/packages/camera/camera_android_camerax/lib/src/camerax_proxy.dart @@ -5,6 +5,7 @@ import 'dart:ui' show Size; import 'analyzer.dart'; +import 'aspect_ratio_strategy.dart'; import 'camera2_camera_control.dart'; import 'camera_control.dart'; import 'camera_info.dart'; @@ -24,6 +25,7 @@ import 'preview.dart'; import 'process_camera_provider.dart'; import 'quality_selector.dart'; import 'recorder.dart'; +import 'resolution_filter.dart'; import 'resolution_selector.dart'; import 'resolution_strategy.dart'; import 'system_services.dart'; @@ -59,6 +61,9 @@ class CameraXProxy { this.createCaptureRequestOptions = _createAttachedCaptureRequestOptions, this.createMeteringPoint = _createAttachedMeteringPoint, this.createFocusMeteringAction = _createAttachedFocusMeteringAction, + this.createAspectRatioStrategy = _createAttachedAspectRatioStrategy, + this.createResolutionFilterWithOnePreferredSize = + _createAttachedResolutionFilterWithOnePreferredSize, }); /// Returns a [ProcessCameraProvider] instance. @@ -115,9 +120,11 @@ class CameraXProxy { int? fallbackRule}) createResolutionStrategy; /// Returns a [ResolutionSelector] configured with the specified - /// [ResolutionStrategy]. - ResolutionSelector Function(ResolutionStrategy resolutionStrategy) - createResolutionSelector; + /// [ResolutionStrategy], [ResolutionFilter], and [AspectRatioStrategy]. + ResolutionSelector Function( + ResolutionStrategy resolutionStrategy, + ResolutionFilter? resolutionFilter, + AspectRatioStrategy? aspectRatioStrategy) createResolutionSelector; /// Returns a [FallbackStrategy] configured with the specified [VideoQuality] /// and [VideoResolutionFallbackRule]. @@ -151,7 +158,7 @@ class CameraXProxy { Camera2CameraControl Function(CameraControl cameraControl) getCamera2CameraControl; - /// Create [CapureRequestOptions] with specified options. + /// Creates a [CaptureRequestOptions] with specified options. CaptureRequestOptions Function( List<(CaptureRequestKeySupportedType, Object?)> options) createCaptureRequestOptions; @@ -167,6 +174,15 @@ class CameraXProxy { FocusMeteringAction Function(List<(MeteringPoint, int?)> meteringPointInfos, bool? disableAutoCancel) createFocusMeteringAction; + /// Creates an [AspectRatioStrategy] with specified aspect ratio and fallback + /// rule. + AspectRatioStrategy Function(int aspectRatio, int fallbackRule) + createAspectRatioStrategy; + + /// Creates a [ResolutionFilter] that prioritizes specified resolution. + ResolutionFilter Function(Size preferredResolution) + createResolutionFilterWithOnePreferredSize; + static Future _getProcessCameraProvider() { return ProcessCameraProvider.getInstance(); } @@ -234,8 +250,13 @@ class CameraXProxy { } static ResolutionSelector _createAttachedResolutionSelector( - ResolutionStrategy resolutionStrategy) { - return ResolutionSelector(resolutionStrategy: resolutionStrategy); + ResolutionStrategy resolutionStrategy, + ResolutionFilter? resolutionFilter, + AspectRatioStrategy? aspectRatioStrategy) { + return ResolutionSelector( + resolutionStrategy: resolutionStrategy, + resolutionFilter: resolutionFilter, + aspectRatioStrategy: aspectRatioStrategy); } static FallbackStrategy _createAttachedFallbackStrategy( @@ -291,4 +312,16 @@ class CameraXProxy { meteringPointInfos: meteringPointInfos, disableAutoCancel: disableAutoCancel); } + + static AspectRatioStrategy _createAttachedAspectRatioStrategy( + int preferredAspectRatio, int fallbackRule) { + return AspectRatioStrategy( + preferredAspectRatio: preferredAspectRatio, fallbackRule: fallbackRule); + } + + static ResolutionFilter _createAttachedResolutionFilterWithOnePreferredSize( + Size preferredSize) { + return ResolutionFilter.onePreferredSize( + preferredResolution: preferredSize); + } } diff --git a/packages/camera/camera_android_camerax/lib/src/resolution_filter.dart b/packages/camera/camera_android_camerax/lib/src/resolution_filter.dart new file mode 100644 index 000000000000..3a428a00e73a --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/src/resolution_filter.dart @@ -0,0 +1,107 @@ +// 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/services.dart'; +import 'package:meta/meta.dart' show immutable; + +import 'camerax_library.g.dart'; +import 'instance_manager.dart'; +import 'java_object.dart'; + +/// Filterer for applications to specify preferred resolutions. +/// +/// This is an indirect wrapping of the native Android `ResolutionFilter`, +/// an interface that requires a synchronous response. Achieving such is not +/// possible through pigeon. Thus, constructing a [ResolutionFilter] with a +/// particular constructor will create a native `ResolutionFilter` with the +/// characteristics described in the documentation for that constructor, +/// respectively. +/// +/// If the provided constructors do not meet your needs, feel free to add a new +/// constructor; see CONTRIBUTING.MD for more information on how to do so. +/// +/// See https://developer.android.com/reference/androidx/camera/core/ResolutionFilter/ResolutionFilter. +@immutable +class ResolutionFilter extends JavaObject { + /// Constructs a [ResolutionFilter]. + /// + /// This will construct a native `ResolutionFilter` that will prioritize the + /// specified [preferredResolution] (if supported) over other supported + /// resolutions, whose priorities (as determined by CameraX) will remain the + /// same. + ResolutionFilter.onePreferredSize({ + required this.preferredResolution, + super.binaryMessenger, + super.instanceManager, + }) : _api = _ResolutionFilterHostApiImpl( + instanceManager: instanceManager, + binaryMessenger: binaryMessenger, + ), + super.detached() { + _api.createWithOnePreferredSizeFromInstances(this, preferredResolution); + } + + /// Instantiates a [ResolutionFilter.onePreferredSize] that is not + /// automatically attached to a native object. + ResolutionFilter.onePreferredSizeDetached({ + required this.preferredResolution, + super.binaryMessenger, + super.instanceManager, + }) : _api = _ResolutionFilterHostApiImpl( + instanceManager: instanceManager, + binaryMessenger: binaryMessenger, + ), + super.detached(); + + final _ResolutionFilterHostApiImpl _api; + + /// The resolution for a [ResolutionFilter.onePreferredSize] to prioritize. + final Size preferredResolution; +} + +/// Host API implementation of [ResolutionFilter]. +class _ResolutionFilterHostApiImpl extends ResolutionFilterHostApi { + /// Constructs an [_ResolutionFilterHostApiImpl]. + /// + /// If [binaryMessenger] is null, the default [BinaryMessenger] will be used, + /// which routes to the host platform. + /// + /// An [instanceManager] is typically passed when a copy of an instance + /// contained by an [InstanceManager] is being created. If left null, it + /// will default to the global instance defined in [JavaObject]. + _ResolutionFilterHostApiImpl({ + this.binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); + + /// Receives binary data across the Flutter platform barrier. + final BinaryMessenger? binaryMessenger; + + /// Maintains instances stored to communicate with native language objects. + final InstanceManager instanceManager; + + /// Creates a [ResolutionFilter] on the native side that will prioritize + /// the specified [preferredResolution]. + Future createWithOnePreferredSizeFromInstances( + ResolutionFilter instance, + Size preferredResolution, + ) { + return createWithOnePreferredSize( + instanceManager.addDartCreatedInstance( + instance, + onCopy: (ResolutionFilter original) => + ResolutionFilter.onePreferredSizeDetached( + preferredResolution: original.preferredResolution, + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ), + ), + ResolutionInfo( + width: preferredResolution.width.toInt(), + height: preferredResolution.height.toInt(), + ), + ); + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/resolution_selector.dart b/packages/camera/camera_android_camerax/lib/src/resolution_selector.dart index 74191724e5ce..3017daab811a 100644 --- a/packages/camera/camera_android_camerax/lib/src/resolution_selector.dart +++ b/packages/camera/camera_android_camerax/lib/src/resolution_selector.dart @@ -9,6 +9,7 @@ import 'aspect_ratio_strategy.dart'; import 'camerax_library.g.dart'; import 'instance_manager.dart'; import 'java_object.dart'; +import 'resolution_filter.dart'; import 'resolution_strategy.dart'; /// A set of requirements and priorities used to select a resolution for a @@ -20,6 +21,7 @@ class ResolutionSelector extends JavaObject { /// Construct a [ResolutionSelector]. ResolutionSelector({ this.resolutionStrategy, + this.resolutionFilter, this.aspectRatioStrategy, super.binaryMessenger, super.instanceManager, @@ -28,7 +30,8 @@ class ResolutionSelector extends JavaObject { binaryMessenger: binaryMessenger, ), super.detached() { - _api.createFromInstances(this, resolutionStrategy, aspectRatioStrategy); + _api.createFromInstances( + this, resolutionStrategy, resolutionFilter, aspectRatioStrategy); } /// Instantiates a [ResolutionSelector] without creating and attaching to an @@ -38,6 +41,7 @@ class ResolutionSelector extends JavaObject { /// library or to create a copy for an [InstanceManager]. ResolutionSelector.detached({ this.resolutionStrategy, + this.resolutionFilter, this.aspectRatioStrategy, super.binaryMessenger, super.instanceManager, @@ -53,6 +57,9 @@ class ResolutionSelector extends JavaObject { /// image. final ResolutionStrategy? resolutionStrategy; + /// Filter for CameraX to automatically select a desirable resolution. + final ResolutionFilter? resolutionFilter; + /// Determines how the UseCase will choose the aspect ratio of the captured /// image. final AspectRatioStrategy? aspectRatioStrategy; @@ -81,10 +88,12 @@ class _ResolutionSelectorHostApiImpl extends ResolutionSelectorHostApi { final InstanceManager instanceManager; /// Creates a [ResolutionSelector] on the native side with the - /// [ResolutionStrategy] and [AspectRatioStrategy] if specified. + /// [ResolutionStrategy], [ResolutionFilter], and [AspectRatioStrategy] if + /// specified. Future createFromInstances( ResolutionSelector instance, ResolutionStrategy? resolutionStrategy, + ResolutionFilter? resolutionFilter, AspectRatioStrategy? aspectRatioStrategy, ) { return create( @@ -100,6 +109,9 @@ class _ResolutionSelectorHostApiImpl extends ResolutionSelectorHostApi { resolutionStrategy == null ? null : instanceManager.getIdentifier(resolutionStrategy)!, + resolutionFilter == null + ? null + : instanceManager.getIdentifier(resolutionFilter)!, aspectRatioStrategy == null ? null : instanceManager.getIdentifier(aspectRatioStrategy)!, diff --git a/packages/camera/camera_android_camerax/lib/src/zoom_state.dart b/packages/camera/camera_android_camerax/lib/src/zoom_state.dart index 4c93d41e0249..6fe5321389b6 100644 --- a/packages/camera/camera_android_camerax/lib/src/zoom_state.dart +++ b/packages/camera/camera_android_camerax/lib/src/zoom_state.dart @@ -15,7 +15,7 @@ import 'java_object.dart'; /// See https://developer.android.com/reference/androidx/camera/core/ZoomState. @immutable class ZoomState extends JavaObject { - /// Constructs a [CameraInfo] that is not automatically attached to a native object. + /// Constructs a [ZoomState] that is not automatically attached to a native object. ZoomState.detached( {super.binaryMessenger, super.instanceManager, diff --git a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart index 103c260c25c1..18741904a8d0 100644 --- a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart +++ b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart @@ -366,6 +366,7 @@ abstract class ResolutionSelectorHostApi { void create( int identifier, int? resolutionStrategyIdentifier, + int? resolutionSelectorIdentifier, int? aspectRatioStrategyIdentifier, ); } @@ -536,3 +537,9 @@ abstract class Camera2CameraControlHostApi { void addCaptureRequestOptions( int identifier, int captureRequestOptionsIdentifier); } + +@HostApi(dartHostTestHandler: 'TestResolutionFilterHostApi') +abstract class ResolutionFilterHostApi { + void createWithOnePreferredSize( + int identifier, ResolutionInfo preferredResolution); +} diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index 248da8bbd97d..0f4756be1bb7 100644 --- a/packages/camera/camera_android_camerax/pubspec.yaml +++ b/packages/camera/camera_android_camerax/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android_camerax description: Android implementation of the camera plugin using the CameraX library. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android_camerax issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.6.0+1 +version: 0.6.1 environment: sdk: ^3.1.0 diff --git a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart index d9ff081383e9..f08a2e02ad49 100644 --- a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart +++ b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart @@ -8,6 +8,7 @@ import 'dart:math' show Point; import 'package:async/async.dart'; import 'package:camera_android_camerax/camera_android_camerax.dart'; import 'package:camera_android_camerax/src/analyzer.dart'; +import 'package:camera_android_camerax/src/aspect_ratio_strategy.dart'; import 'package:camera_android_camerax/src/camera.dart'; import 'package:camera_android_camerax/src/camera2_camera_control.dart'; import 'package:camera_android_camerax/src/camera_control.dart'; @@ -36,6 +37,7 @@ import 'package:camera_android_camerax/src/process_camera_provider.dart'; import 'package:camera_android_camerax/src/quality_selector.dart'; import 'package:camera_android_camerax/src/recorder.dart'; import 'package:camera_android_camerax/src/recording.dart'; +import 'package:camera_android_camerax/src/resolution_filter.dart'; import 'package:camera_android_camerax/src/resolution_selector.dart'; import 'package:camera_android_camerax/src/resolution_strategy.dart'; import 'package:camera_android_camerax/src/surface.dart'; @@ -46,7 +48,7 @@ import 'package:camera_android_camerax/src/zoom_state.dart'; import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/services.dart' show DeviceOrientation, PlatformException, Uint8List; -import 'package:flutter/widgets.dart'; +import 'package:flutter/widgets.dart' show BuildContext, Size, Texture, Widget; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; @@ -56,6 +58,7 @@ import 'test_camerax_library.g.dart'; @GenerateNiceMocks(>[ MockSpec(), + MockSpec(), MockSpec(), MockSpec(), MockSpec(), @@ -75,6 +78,7 @@ import 'test_camerax_library.g.dart'; MockSpec(), MockSpec(), MockSpec(), + MockSpec(), MockSpec(), MockSpec(), MockSpec(), @@ -122,6 +126,76 @@ void main() { return cameraClosingEventSent && cameraErrorSent; } + /// CameraXProxy for testing functionality related to the camera resolution + /// preset (setting expected ResolutionSelectors, QualitySelectors, etc.). + CameraXProxy getProxyForTestingResolutionPreset( + MockProcessCameraProvider mockProcessCameraProvider) => + CameraXProxy( + getProcessCameraProvider: () => + Future.value(mockProcessCameraProvider), + createCameraSelector: (int cameraSelectorLensDirection) => + MockCameraSelector(), + createPreview: + (ResolutionSelector? resolutionSelector, int? targetRotation) => + Preview.detached( + initialTargetRotation: targetRotation, + resolutionSelector: resolutionSelector), + createImageCapture: + (ResolutionSelector? resolutionSelector, int? targetRotation) => + ImageCapture.detached( + resolutionSelector: resolutionSelector, + initialTargetRotation: targetRotation), + createRecorder: (QualitySelector? qualitySelector) => + Recorder.detached(qualitySelector: qualitySelector), + createVideoCapture: (_) => + Future.value(MockVideoCapture()), + createImageAnalysis: + (ResolutionSelector? resolutionSelector, int? targetRotation) => + ImageAnalysis.detached( + resolutionSelector: resolutionSelector, + initialTargetRotation: targetRotation), + createResolutionStrategy: ( + {bool highestAvailable = false, + Size? boundSize, + int? fallbackRule}) { + if (highestAvailable) { + return ResolutionStrategy.detachedHighestAvailableStrategy(); + } + return ResolutionStrategy.detached( + boundSize: boundSize, fallbackRule: fallbackRule); + }, + createResolutionSelector: (ResolutionStrategy resolutionStrategy, + ResolutionFilter? resolutionFilter, + AspectRatioStrategy? aspectRatioStrategy) => + ResolutionSelector.detached( + resolutionStrategy: resolutionStrategy, + resolutionFilter: resolutionFilter, + aspectRatioStrategy: aspectRatioStrategy), + createFallbackStrategy: ( + {required VideoQuality quality, + required VideoResolutionFallbackRule fallbackRule}) => + FallbackStrategy.detached( + quality: quality, fallbackRule: fallbackRule), + createQualitySelector: ( + {required VideoQuality videoQuality, + required FallbackStrategy fallbackStrategy}) => + QualitySelector.detached(qualityList: [ + VideoQualityData(quality: videoQuality) + ], fallbackStrategy: fallbackStrategy), + createCameraStateObserver: (_) => MockObserver(), + requestCameraPermissions: (_) => Future.value(), + startListeningForDeviceOrientationChange: (_, __) {}, + setPreviewSurfaceProvider: (_) => Future.value( + 3), // 3 is a random Flutter SurfaceTexture ID for testing, + createAspectRatioStrategy: (int aspectRatio, int fallbackRule) => + AspectRatioStrategy.detached( + preferredAspectRatio: aspectRatio, fallbackRule: fallbackRule), + createResolutionFilterWithOnePreferredSize: + (Size preferredResolution) => + ResolutionFilter.onePreferredSizeDetached( + preferredResolution: preferredResolution), + ); + /// CameraXProxy for testing exposure and focus related controls. /// /// Modifies the creation of [MeteringPoint]s and [FocusMeteringAction]s to @@ -289,7 +363,7 @@ void main() { Size? boundSize, int? fallbackRule}) => MockResolutionStrategy(), - createResolutionSelector: (_) => MockResolutionSelector(), + createResolutionSelector: (_, __, ___) => MockResolutionSelector(), createFallbackStrategy: ( {required VideoQuality quality, required VideoResolutionFallbackRule fallbackRule}) => @@ -307,6 +381,8 @@ void main() { startListeningForDeviceOrientationChange: (_, __) { startedListeningForDeviceOrientationChanges = true; }, + createAspectRatioStrategy: (_, __) => MockAspectRatioStrategy(), + createResolutionFilterWithOnePreferredSize: (_) => MockResolutionFilter(), ); when(mockPreview.setSurfaceProvider()) @@ -405,7 +481,7 @@ void main() { Size? boundSize, int? fallbackRule}) => MockResolutionStrategy(), - createResolutionSelector: (_) => MockResolutionSelector(), + createResolutionSelector: (_, __, ___) => MockResolutionSelector(), createFallbackStrategy: ( {required VideoQuality quality, required VideoResolutionFallbackRule fallbackRule}) => @@ -418,6 +494,8 @@ void main() { Observer.detached(onChanged: onChanged), requestCameraPermissions: (_) => Future.value(), startListeningForDeviceOrientationChange: (_, __) {}, + createAspectRatioStrategy: (_, __) => MockAspectRatioStrategy(), + createResolutionFilterWithOnePreferredSize: (_) => MockResolutionFilter(), ); when(mockProcessCameraProvider.bindToLifecycle(mockBackCameraSelector, @@ -449,7 +527,7 @@ void main() { }); test( - 'createCamera properly sets preset resolution for non-video capture use cases', + 'createCamera properly sets preset resolution selection strategy for non-video capture use cases', () async { final AndroidCameraCameraX camera = AndroidCameraCameraX(); const CameraLensDirection testLensDirection = CameraLensDirection.back; @@ -466,69 +544,13 @@ void main() { final MockProcessCameraProvider mockProcessCameraProvider = MockProcessCameraProvider(); final MockCameraInfo mockCameraInfo = MockCameraInfo(); - final MockCameraSelector mockBackCameraSelector = MockCameraSelector(); - final MockCameraSelector mockFrontCameraSelector = MockCameraSelector(); - final MockVideoCapture mockVideoCapture = MockVideoCapture(); - final MockRecorder mockRecorder = MockRecorder(); // Tell plugin to create mock/detached objects for testing createCamera // as needed. - camera.proxy = CameraXProxy( - getProcessCameraProvider: () => - Future.value(mockProcessCameraProvider), - createCameraSelector: (int cameraSelectorLensDirection) { - switch (cameraSelectorLensDirection) { - case CameraSelector.lensFacingFront: - return mockFrontCameraSelector; - case CameraSelector.lensFacingBack: - default: - return mockBackCameraSelector; - } - }, - createPreview: - (ResolutionSelector? resolutionSelector, int? targetRotation) => - Preview.detached( - initialTargetRotation: targetRotation, - resolutionSelector: resolutionSelector), - createImageCapture: - (ResolutionSelector? resolutionSelector, int? targetRotation) => - ImageCapture.detached( - resolutionSelector: resolutionSelector, - initialTargetRotation: targetRotation), - createRecorder: (_) => mockRecorder, - createVideoCapture: (_) => Future.value(mockVideoCapture), - createImageAnalysis: - (ResolutionSelector? resolutionSelector, int? targetRotation) => - ImageAnalysis.detached( - resolutionSelector: resolutionSelector, - initialTargetRotation: targetRotation), - createResolutionStrategy: ( - {bool highestAvailable = false, Size? boundSize, int? fallbackRule}) { - if (highestAvailable) { - return ResolutionStrategy.detachedHighestAvailableStrategy(); - } - - return ResolutionStrategy.detached( - boundSize: boundSize, fallbackRule: fallbackRule); - }, - createResolutionSelector: (ResolutionStrategy resolutionStrategy) => - ResolutionSelector.detached(resolutionStrategy: resolutionStrategy), - createFallbackStrategy: ( - {required VideoQuality quality, - required VideoResolutionFallbackRule fallbackRule}) => - MockFallbackStrategy(), - createQualitySelector: ( - {required VideoQuality videoQuality, - required FallbackStrategy fallbackStrategy}) => - MockQualitySelector(), - createCameraStateObserver: (_) => MockObserver(), - requestCameraPermissions: (_) => Future.value(), - startListeningForDeviceOrientationChange: (_, __) {}, - setPreviewSurfaceProvider: (_) => Future.value( - 3), // 3 is a random Flutter SurfaceTexture ID for testing}, - ); + camera.proxy = + getProxyForTestingResolutionPreset(mockProcessCameraProvider); - when(mockProcessCameraProvider.bindToLifecycle(mockBackCameraSelector, any)) + when(mockProcessCameraProvider.bindToLifecycle(any, any)) .thenAnswer((_) async => mockCamera); when(mockCamera.getCameraInfo()).thenAnswer((_) async => mockCameraInfo); when(mockCameraInfo.getCameraState()) @@ -595,6 +617,189 @@ void main() { expect(camera.imageAnalysis!.resolutionSelector, isNull); }); + test( + 'createCamera properly sets filter for resolution preset for non-video capture use cases', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const CameraLensDirection testLensDirection = CameraLensDirection.front; + const int testSensorOrientation = 180; + const CameraDescription testCameraDescription = CameraDescription( + name: 'cameraName', + lensDirection: testLensDirection, + sensorOrientation: testSensorOrientation); + const bool enableAudio = true; + final MockCamera mockCamera = MockCamera(); + + // Mock/Detached objects for (typically attached) objects created by + // createCamera. + final MockProcessCameraProvider mockProcessCameraProvider = + MockProcessCameraProvider(); + final MockCameraInfo mockCameraInfo = MockCameraInfo(); + + // Tell plugin to create mock/detached objects for testing createCamera + // as needed. + camera.proxy = + getProxyForTestingResolutionPreset(mockProcessCameraProvider); + + when(mockProcessCameraProvider.bindToLifecycle(any, any)) + .thenAnswer((_) async => mockCamera); + when(mockCamera.getCameraInfo()).thenAnswer((_) async => mockCameraInfo); + when(mockCameraInfo.getCameraState()) + .thenAnswer((_) async => MockLiveCameraState()); + camera.processCameraProvider = mockProcessCameraProvider; + + // Test non-null resolution presets. + for (final ResolutionPreset resolutionPreset in ResolutionPreset.values) { + await camera.createCamera(testCameraDescription, resolutionPreset, + enableAudio: enableAudio); + + Size? expectedPreferredResolution; + switch (resolutionPreset) { + case ResolutionPreset.low: + expectedPreferredResolution = const Size(320, 240); + case ResolutionPreset.medium: + expectedPreferredResolution = const Size(720, 480); + case ResolutionPreset.high: + expectedPreferredResolution = const Size(1280, 720); + case ResolutionPreset.veryHigh: + expectedPreferredResolution = const Size(1920, 1080); + case ResolutionPreset.ultraHigh: + expectedPreferredResolution = const Size(3840, 2160); + case ResolutionPreset.max: + expectedPreferredResolution = null; + } + + if (expectedPreferredResolution == null) { + expect(camera.preview!.resolutionSelector!.resolutionFilter, isNull); + expect( + camera.imageCapture!.resolutionSelector!.resolutionFilter, isNull); + expect( + camera.imageAnalysis!.resolutionSelector!.resolutionFilter, isNull); + continue; + } + + expect( + camera.preview!.resolutionSelector!.resolutionFilter! + .preferredResolution, + equals(expectedPreferredResolution)); + expect( + camera + .imageCapture!.resolutionSelector!.resolutionStrategy!.boundSize, + equals(expectedPreferredResolution)); + expect( + camera + .imageAnalysis!.resolutionSelector!.resolutionStrategy!.boundSize, + equals(expectedPreferredResolution)); + } + + // Test null case. + await camera.createCamera(testCameraDescription, null); + expect(camera.preview!.resolutionSelector, isNull); + expect(camera.imageCapture!.resolutionSelector, isNull); + expect(camera.imageAnalysis!.resolutionSelector, isNull); + }); + + test( + 'createCamera properly sets aspect ratio based on preset resolution for non-video capture use cases', + () async { + final AndroidCameraCameraX camera = AndroidCameraCameraX(); + const CameraLensDirection testLensDirection = CameraLensDirection.back; + const int testSensorOrientation = 90; + const CameraDescription testCameraDescription = CameraDescription( + name: 'cameraName', + lensDirection: testLensDirection, + sensorOrientation: testSensorOrientation); + const bool enableAudio = true; + final MockCamera mockCamera = MockCamera(); + + // Mock/Detached objects for (typically attached) objects created by + // createCamera. + final MockProcessCameraProvider mockProcessCameraProvider = + MockProcessCameraProvider(); + final MockCameraInfo mockCameraInfo = MockCameraInfo(); + + // Tell plugin to create mock/detached objects for testing createCamera + // as needed. + camera.proxy = + getProxyForTestingResolutionPreset(mockProcessCameraProvider); + when(mockProcessCameraProvider.bindToLifecycle(any, any)) + .thenAnswer((_) async => mockCamera); + when(mockCamera.getCameraInfo()).thenAnswer((_) async => mockCameraInfo); + when(mockCameraInfo.getCameraState()) + .thenAnswer((_) async => MockLiveCameraState()); + camera.processCameraProvider = mockProcessCameraProvider; + + // Test non-null resolution presets. + for (final ResolutionPreset resolutionPreset in ResolutionPreset.values) { + await camera.createCamera(testCameraDescription, resolutionPreset, + enableAudio: enableAudio); + + int? expectedAspectRatio; + AspectRatioStrategy? expectedAspectRatioStrategy; + switch (resolutionPreset) { + case ResolutionPreset.low: + expectedAspectRatio = AspectRatio.ratio4To3; + case ResolutionPreset.high: + case ResolutionPreset.veryHigh: + case ResolutionPreset.ultraHigh: + expectedAspectRatio = AspectRatio.ratio16To9; + case ResolutionPreset.medium: + // Medium resolution preset uses aspect ratio 3:2 which is unsupported + // by CameraX. + case ResolutionPreset.max: + expectedAspectRatioStrategy = null; + } + + expectedAspectRatioStrategy = expectedAspectRatio == null + ? null + : AspectRatioStrategy.detached( + preferredAspectRatio: expectedAspectRatio, + fallbackRule: AspectRatioStrategy.fallbackRuleAuto); + + if (expectedAspectRatio == null) { + expect(camera.preview!.resolutionSelector!.aspectRatioStrategy, isNull); + expect(camera.imageCapture!.resolutionSelector!.aspectRatioStrategy, + isNull); + expect(camera.imageAnalysis!.resolutionSelector!.aspectRatioStrategy, + isNull); + continue; + } + + // Check aspect ratio. + expect( + camera.preview!.resolutionSelector!.aspectRatioStrategy! + .preferredAspectRatio, + equals(expectedAspectRatioStrategy!.preferredAspectRatio)); + expect( + camera.imageCapture!.resolutionSelector!.aspectRatioStrategy! + .preferredAspectRatio, + equals(expectedAspectRatioStrategy.preferredAspectRatio)); + expect( + camera.imageAnalysis!.resolutionSelector!.aspectRatioStrategy! + .preferredAspectRatio, + equals(expectedAspectRatioStrategy.preferredAspectRatio)); + + // Check fallback rule. + expect( + camera.preview!.resolutionSelector!.aspectRatioStrategy!.fallbackRule, + equals(expectedAspectRatioStrategy.fallbackRule)); + expect( + camera.imageCapture!.resolutionSelector!.aspectRatioStrategy! + .fallbackRule, + equals(expectedAspectRatioStrategy.fallbackRule)); + expect( + camera.imageAnalysis!.resolutionSelector!.aspectRatioStrategy! + .fallbackRule, + equals(expectedAspectRatioStrategy.fallbackRule)); + } + + // Test null case. + await camera.createCamera(testCameraDescription, null); + expect(camera.preview!.resolutionSelector, isNull); + expect(camera.imageCapture!.resolutionSelector, isNull); + expect(camera.imageAnalysis!.resolutionSelector, isNull); + }); + test( 'createCamera properly sets preset resolution for video capture use case', () async { @@ -613,58 +818,13 @@ void main() { final MockProcessCameraProvider mockProcessCameraProvider = MockProcessCameraProvider(); final MockCameraInfo mockCameraInfo = MockCameraInfo(); - final MockCameraSelector mockBackCameraSelector = MockCameraSelector(); - final MockCameraSelector mockFrontCameraSelector = MockCameraSelector(); - final MockPreview mockPreview = MockPreview(); - final MockImageCapture mockImageCapture = MockImageCapture(); - final MockVideoCapture mockVideoCapture = MockVideoCapture(); - final MockImageAnalysis mockImageAnalysis = MockImageAnalysis(); // Tell plugin to create mock/detached objects for testing createCamera // as needed. - camera.proxy = CameraXProxy( - getProcessCameraProvider: () => - Future.value(mockProcessCameraProvider), - createCameraSelector: (int cameraSelectorLensDirection) { - switch (cameraSelectorLensDirection) { - case CameraSelector.lensFacingFront: - return mockFrontCameraSelector; - case CameraSelector.lensFacingBack: - default: - return mockBackCameraSelector; - } - }, - createPreview: (_, __) => mockPreview, - createImageCapture: (_, __) => mockImageCapture, - createRecorder: (QualitySelector? qualitySelector) => - Recorder.detached(qualitySelector: qualitySelector), - createVideoCapture: (_) => Future.value(mockVideoCapture), - createImageAnalysis: (_, __) => mockImageAnalysis, - createResolutionStrategy: ( - {bool highestAvailable = false, - Size? boundSize, - int? fallbackRule}) => - MockResolutionStrategy(), - createResolutionSelector: (_) => MockResolutionSelector(), - createFallbackStrategy: ( - {required VideoQuality quality, - required VideoResolutionFallbackRule fallbackRule}) => - FallbackStrategy.detached( - quality: quality, fallbackRule: fallbackRule), - createQualitySelector: ( - {required VideoQuality videoQuality, - required FallbackStrategy fallbackStrategy}) => - QualitySelector.detached(qualityList: [ - VideoQualityData(quality: videoQuality) - ], fallbackStrategy: fallbackStrategy), - createCameraStateObserver: (void Function(Object) onChanged) => - Observer.detached(onChanged: onChanged), - requestCameraPermissions: (_) => Future.value(), - startListeningForDeviceOrientationChange: (_, __) {}, - ); + camera.proxy = + getProxyForTestingResolutionPreset(mockProcessCameraProvider); - when(mockProcessCameraProvider.bindToLifecycle(mockBackCameraSelector, - [mockPreview, mockImageCapture, mockImageAnalysis])) + when(mockProcessCameraProvider.bindToLifecycle(any, any)) .thenAnswer((_) async => mockCamera); when(mockCamera.getCameraInfo()).thenAnswer((_) async => mockCameraInfo); when(mockCameraInfo.getCameraState()) @@ -773,7 +933,7 @@ void main() { Size? boundSize, int? fallbackRule}) => MockResolutionStrategy(), - createResolutionSelector: (_) => MockResolutionSelector(), + createResolutionSelector: (_, __, ___) => MockResolutionSelector(), createFallbackStrategy: ( {required VideoQuality quality, required VideoResolutionFallbackRule fallbackRule}) => @@ -786,6 +946,8 @@ void main() { Observer.detached(onChanged: onChanged), requestCameraPermissions: (_) => Future.value(), startListeningForDeviceOrientationChange: (_, __) {}, + createAspectRatioStrategy: (_, __) => MockAspectRatioStrategy(), + createResolutionFilterWithOnePreferredSize: (_) => MockResolutionFilter(), ); final CameraInitializedEvent testCameraInitializedEvent = diff --git a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.mocks.dart b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.mocks.dart index 80d76d8339ca..5d6d2051212d 100644 --- a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.mocks.dart +++ b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.mocks.dart @@ -3,50 +3,53 @@ // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i16; -import 'dart:typed_data' as _i31; +import 'dart:async' as _i17; +import 'dart:typed_data' as _i33; +import 'dart:ui' as _i11; -import 'package:camera_android_camerax/src/analyzer.dart' as _i15; +import 'package:camera_android_camerax/src/analyzer.dart' as _i16; +import 'package:camera_android_camerax/src/aspect_ratio_strategy.dart' as _i19; import 'package:camera_android_camerax/src/camera.dart' as _i9; -import 'package:camera_android_camerax/src/camera2_camera_control.dart' as _i22; +import 'package:camera_android_camerax/src/camera2_camera_control.dart' as _i24; import 'package:camera_android_camerax/src/camera_control.dart' as _i3; import 'package:camera_android_camerax/src/camera_info.dart' as _i2; -import 'package:camera_android_camerax/src/camera_selector.dart' as _i24; -import 'package:camera_android_camerax/src/camera_state.dart' as _i18; +import 'package:camera_android_camerax/src/camera_selector.dart' as _i26; +import 'package:camera_android_camerax/src/camera_state.dart' as _i20; import 'package:camera_android_camerax/src/camerax_library.g.dart' as _i7; import 'package:camera_android_camerax/src/capture_request_options.dart' - as _i23; + as _i25; import 'package:camera_android_camerax/src/exposure_state.dart' as _i5; -import 'package:camera_android_camerax/src/fallback_strategy.dart' as _i25; -import 'package:camera_android_camerax/src/focus_metering_action.dart' as _i21; -import 'package:camera_android_camerax/src/focus_metering_result.dart' as _i20; -import 'package:camera_android_camerax/src/image_analysis.dart' as _i26; -import 'package:camera_android_camerax/src/image_capture.dart' as _i27; -import 'package:camera_android_camerax/src/image_proxy.dart' as _i17; +import 'package:camera_android_camerax/src/fallback_strategy.dart' as _i27; +import 'package:camera_android_camerax/src/focus_metering_action.dart' as _i23; +import 'package:camera_android_camerax/src/focus_metering_result.dart' as _i22; +import 'package:camera_android_camerax/src/image_analysis.dart' as _i28; +import 'package:camera_android_camerax/src/image_capture.dart' as _i29; +import 'package:camera_android_camerax/src/image_proxy.dart' as _i18; import 'package:camera_android_camerax/src/live_data.dart' as _i4; -import 'package:camera_android_camerax/src/observer.dart' as _i30; +import 'package:camera_android_camerax/src/observer.dart' as _i32; import 'package:camera_android_camerax/src/pending_recording.dart' as _i10; -import 'package:camera_android_camerax/src/plane_proxy.dart' as _i29; -import 'package:camera_android_camerax/src/preview.dart' as _i32; +import 'package:camera_android_camerax/src/plane_proxy.dart' as _i31; +import 'package:camera_android_camerax/src/preview.dart' as _i34; import 'package:camera_android_camerax/src/process_camera_provider.dart' - as _i33; -import 'package:camera_android_camerax/src/quality_selector.dart' as _i35; -import 'package:camera_android_camerax/src/recorder.dart' as _i11; + as _i35; +import 'package:camera_android_camerax/src/quality_selector.dart' as _i37; +import 'package:camera_android_camerax/src/recorder.dart' as _i12; import 'package:camera_android_camerax/src/recording.dart' as _i8; -import 'package:camera_android_camerax/src/resolution_selector.dart' as _i36; -import 'package:camera_android_camerax/src/resolution_strategy.dart' as _i37; -import 'package:camera_android_camerax/src/use_case.dart' as _i34; -import 'package:camera_android_camerax/src/video_capture.dart' as _i38; -import 'package:camera_android_camerax/src/zoom_state.dart' as _i19; +import 'package:camera_android_camerax/src/resolution_filter.dart' as _i38; +import 'package:camera_android_camerax/src/resolution_selector.dart' as _i39; +import 'package:camera_android_camerax/src/resolution_strategy.dart' as _i40; +import 'package:camera_android_camerax/src/use_case.dart' as _i36; +import 'package:camera_android_camerax/src/video_capture.dart' as _i41; +import 'package:camera_android_camerax/src/zoom_state.dart' as _i21; import 'package:camera_platform_interface/camera_platform_interface.dart' as _i6; -import 'package:flutter/foundation.dart' as _i14; -import 'package:flutter/services.dart' as _i13; -import 'package:flutter/widgets.dart' as _i12; +import 'package:flutter/foundation.dart' as _i15; +import 'package:flutter/services.dart' as _i14; +import 'package:flutter/widgets.dart' as _i13; import 'package:mockito/mockito.dart' as _i1; -import 'package:mockito/src/dummies.dart' as _i28; +import 'package:mockito/src/dummies.dart' as _i30; -import 'test_camerax_library.g.dart' as _i39; +import 'test_camerax_library.g.dart' as _i42; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -166,8 +169,8 @@ class _FakePendingRecording_9 extends _i1.SmartFake ); } -class _FakeRecorder_10 extends _i1.SmartFake implements _i11.Recorder { - _FakeRecorder_10( +class _FakeSize_10 extends _i1.SmartFake implements _i11.Size { + _FakeSize_10( Object parent, Invocation parentInvocation, ) : super( @@ -176,8 +179,18 @@ class _FakeRecorder_10 extends _i1.SmartFake implements _i11.Recorder { ); } -class _FakeWidget_11 extends _i1.SmartFake implements _i12.Widget { - _FakeWidget_11( +class _FakeRecorder_11 extends _i1.SmartFake implements _i12.Recorder { + _FakeRecorder_11( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeWidget_12 extends _i1.SmartFake implements _i13.Widget { + _FakeWidget_12( Object parent, Invocation parentInvocation, ) : super( @@ -187,13 +200,13 @@ class _FakeWidget_11 extends _i1.SmartFake implements _i12.Widget { @override String toString( - {_i13.DiagnosticLevel? minLevel = _i13.DiagnosticLevel.info}) => + {_i14.DiagnosticLevel? minLevel = _i14.DiagnosticLevel.info}) => super.toString(); } -class _FakeInheritedWidget_12 extends _i1.SmartFake - implements _i12.InheritedWidget { - _FakeInheritedWidget_12( +class _FakeInheritedWidget_13 extends _i1.SmartFake + implements _i13.InheritedWidget { + _FakeInheritedWidget_13( Object parent, Invocation parentInvocation, ) : super( @@ -203,13 +216,13 @@ class _FakeInheritedWidget_12 extends _i1.SmartFake @override String toString( - {_i13.DiagnosticLevel? minLevel = _i13.DiagnosticLevel.info}) => + {_i14.DiagnosticLevel? minLevel = _i14.DiagnosticLevel.info}) => super.toString(); } -class _FakeDiagnosticsNode_13 extends _i1.SmartFake - implements _i14.DiagnosticsNode { - _FakeDiagnosticsNode_13( +class _FakeDiagnosticsNode_14 extends _i1.SmartFake + implements _i15.DiagnosticsNode { + _FakeDiagnosticsNode_14( Object parent, Invocation parentInvocation, ) : super( @@ -219,8 +232,8 @@ class _FakeDiagnosticsNode_13 extends _i1.SmartFake @override String toString({ - _i14.TextTreeConfiguration? parentConfiguration, - _i13.DiagnosticLevel? minLevel = _i13.DiagnosticLevel.info, + _i15.TextTreeConfiguration? parentConfiguration, + _i14.DiagnosticLevel? minLevel = _i14.DiagnosticLevel.info, }) => super.toString(); } @@ -229,15 +242,36 @@ class _FakeDiagnosticsNode_13 extends _i1.SmartFake /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockAnalyzer extends _i1.Mock implements _i15.Analyzer { +class MockAnalyzer extends _i1.Mock implements _i16.Analyzer { @override - _i16.Future Function(_i17.ImageProxy) get analyze => + _i17.Future Function(_i18.ImageProxy) get analyze => (super.noSuchMethod( Invocation.getter(#analyze), - returnValue: (_i17.ImageProxy imageProxy) => _i16.Future.value(), - returnValueForMissingStub: (_i17.ImageProxy imageProxy) => - _i16.Future.value(), - ) as _i16.Future Function(_i17.ImageProxy)); + returnValue: (_i18.ImageProxy imageProxy) => _i17.Future.value(), + returnValueForMissingStub: (_i18.ImageProxy imageProxy) => + _i17.Future.value(), + ) as _i17.Future Function(_i18.ImageProxy)); +} + +/// A class which mocks [AspectRatioStrategy]. +/// +/// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable +class MockAspectRatioStrategy extends _i1.Mock + implements _i19.AspectRatioStrategy { + @override + int get preferredAspectRatio => (super.noSuchMethod( + Invocation.getter(#preferredAspectRatio), + returnValue: 0, + returnValueForMissingStub: 0, + ) as int); + + @override + int get fallbackRule => (super.noSuchMethod( + Invocation.getter(#fallbackRule), + returnValue: 0, + returnValueForMissingStub: 0, + ) as int); } /// A class which mocks [Camera]. @@ -246,12 +280,12 @@ class MockAnalyzer extends _i1.Mock implements _i15.Analyzer { // ignore: must_be_immutable class MockCamera extends _i1.Mock implements _i9.Camera { @override - _i16.Future<_i2.CameraInfo> getCameraInfo() => (super.noSuchMethod( + _i17.Future<_i2.CameraInfo> getCameraInfo() => (super.noSuchMethod( Invocation.method( #getCameraInfo, [], ), - returnValue: _i16.Future<_i2.CameraInfo>.value(_FakeCameraInfo_0( + returnValue: _i17.Future<_i2.CameraInfo>.value(_FakeCameraInfo_0( this, Invocation.method( #getCameraInfo, @@ -259,22 +293,22 @@ class MockCamera extends _i1.Mock implements _i9.Camera { ), )), returnValueForMissingStub: - _i16.Future<_i2.CameraInfo>.value(_FakeCameraInfo_0( + _i17.Future<_i2.CameraInfo>.value(_FakeCameraInfo_0( this, Invocation.method( #getCameraInfo, [], ), )), - ) as _i16.Future<_i2.CameraInfo>); + ) as _i17.Future<_i2.CameraInfo>); @override - _i16.Future<_i3.CameraControl> getCameraControl() => (super.noSuchMethod( + _i17.Future<_i3.CameraControl> getCameraControl() => (super.noSuchMethod( Invocation.method( #getCameraControl, [], ), - returnValue: _i16.Future<_i3.CameraControl>.value(_FakeCameraControl_1( + returnValue: _i17.Future<_i3.CameraControl>.value(_FakeCameraControl_1( this, Invocation.method( #getCameraControl, @@ -282,14 +316,14 @@ class MockCamera extends _i1.Mock implements _i9.Camera { ), )), returnValueForMissingStub: - _i16.Future<_i3.CameraControl>.value(_FakeCameraControl_1( + _i17.Future<_i3.CameraControl>.value(_FakeCameraControl_1( this, Invocation.method( #getCameraControl, [], ), )), - ) as _i16.Future<_i3.CameraControl>); + ) as _i17.Future<_i3.CameraControl>); } /// A class which mocks [CameraInfo]. @@ -298,24 +332,24 @@ class MockCamera extends _i1.Mock implements _i9.Camera { // ignore: must_be_immutable class MockCameraInfo extends _i1.Mock implements _i2.CameraInfo { @override - _i16.Future getSensorRotationDegrees() => (super.noSuchMethod( + _i17.Future getSensorRotationDegrees() => (super.noSuchMethod( Invocation.method( #getSensorRotationDegrees, [], ), - returnValue: _i16.Future.value(0), - returnValueForMissingStub: _i16.Future.value(0), - ) as _i16.Future); + returnValue: _i17.Future.value(0), + returnValueForMissingStub: _i17.Future.value(0), + ) as _i17.Future); @override - _i16.Future<_i4.LiveData<_i18.CameraState>> getCameraState() => + _i17.Future<_i4.LiveData<_i20.CameraState>> getCameraState() => (super.noSuchMethod( Invocation.method( #getCameraState, [], ), - returnValue: _i16.Future<_i4.LiveData<_i18.CameraState>>.value( - _FakeLiveData_2<_i18.CameraState>( + returnValue: _i17.Future<_i4.LiveData<_i20.CameraState>>.value( + _FakeLiveData_2<_i20.CameraState>( this, Invocation.method( #getCameraState, @@ -323,23 +357,23 @@ class MockCameraInfo extends _i1.Mock implements _i2.CameraInfo { ), )), returnValueForMissingStub: - _i16.Future<_i4.LiveData<_i18.CameraState>>.value( - _FakeLiveData_2<_i18.CameraState>( + _i17.Future<_i4.LiveData<_i20.CameraState>>.value( + _FakeLiveData_2<_i20.CameraState>( this, Invocation.method( #getCameraState, [], ), )), - ) as _i16.Future<_i4.LiveData<_i18.CameraState>>); + ) as _i17.Future<_i4.LiveData<_i20.CameraState>>); @override - _i16.Future<_i5.ExposureState> getExposureState() => (super.noSuchMethod( + _i17.Future<_i5.ExposureState> getExposureState() => (super.noSuchMethod( Invocation.method( #getExposureState, [], ), - returnValue: _i16.Future<_i5.ExposureState>.value(_FakeExposureState_3( + returnValue: _i17.Future<_i5.ExposureState>.value(_FakeExposureState_3( this, Invocation.method( #getExposureState, @@ -347,24 +381,24 @@ class MockCameraInfo extends _i1.Mock implements _i2.CameraInfo { ), )), returnValueForMissingStub: - _i16.Future<_i5.ExposureState>.value(_FakeExposureState_3( + _i17.Future<_i5.ExposureState>.value(_FakeExposureState_3( this, Invocation.method( #getExposureState, [], ), )), - ) as _i16.Future<_i5.ExposureState>); + ) as _i17.Future<_i5.ExposureState>); @override - _i16.Future<_i4.LiveData<_i19.ZoomState>> getZoomState() => + _i17.Future<_i4.LiveData<_i21.ZoomState>> getZoomState() => (super.noSuchMethod( Invocation.method( #getZoomState, [], ), - returnValue: _i16.Future<_i4.LiveData<_i19.ZoomState>>.value( - _FakeLiveData_2<_i19.ZoomState>( + returnValue: _i17.Future<_i4.LiveData<_i21.ZoomState>>.value( + _FakeLiveData_2<_i21.ZoomState>( this, Invocation.method( #getZoomState, @@ -372,15 +406,15 @@ class MockCameraInfo extends _i1.Mock implements _i2.CameraInfo { ), )), returnValueForMissingStub: - _i16.Future<_i4.LiveData<_i19.ZoomState>>.value( - _FakeLiveData_2<_i19.ZoomState>( + _i17.Future<_i4.LiveData<_i21.ZoomState>>.value( + _FakeLiveData_2<_i21.ZoomState>( this, Invocation.method( #getZoomState, [], ), )), - ) as _i16.Future<_i4.LiveData<_i19.ZoomState>>); + ) as _i17.Future<_i4.LiveData<_i21.ZoomState>>); } /// A class which mocks [CameraControl]. @@ -389,58 +423,58 @@ class MockCameraInfo extends _i1.Mock implements _i2.CameraInfo { // ignore: must_be_immutable class MockCameraControl extends _i1.Mock implements _i3.CameraControl { @override - _i16.Future enableTorch(bool? torch) => (super.noSuchMethod( + _i17.Future enableTorch(bool? torch) => (super.noSuchMethod( Invocation.method( #enableTorch, [torch], ), - returnValue: _i16.Future.value(), - returnValueForMissingStub: _i16.Future.value(), - ) as _i16.Future); + returnValue: _i17.Future.value(), + returnValueForMissingStub: _i17.Future.value(), + ) as _i17.Future); @override - _i16.Future setZoomRatio(double? ratio) => (super.noSuchMethod( + _i17.Future setZoomRatio(double? ratio) => (super.noSuchMethod( Invocation.method( #setZoomRatio, [ratio], ), - returnValue: _i16.Future.value(), - returnValueForMissingStub: _i16.Future.value(), - ) as _i16.Future); + returnValue: _i17.Future.value(), + returnValueForMissingStub: _i17.Future.value(), + ) as _i17.Future); @override - _i16.Future<_i20.FocusMeteringResult?> startFocusAndMetering( - _i21.FocusMeteringAction? action) => + _i17.Future<_i22.FocusMeteringResult?> startFocusAndMetering( + _i23.FocusMeteringAction? action) => (super.noSuchMethod( Invocation.method( #startFocusAndMetering, [action], ), - returnValue: _i16.Future<_i20.FocusMeteringResult?>.value(), + returnValue: _i17.Future<_i22.FocusMeteringResult?>.value(), returnValueForMissingStub: - _i16.Future<_i20.FocusMeteringResult?>.value(), - ) as _i16.Future<_i20.FocusMeteringResult?>); + _i17.Future<_i22.FocusMeteringResult?>.value(), + ) as _i17.Future<_i22.FocusMeteringResult?>); @override - _i16.Future cancelFocusAndMetering() => (super.noSuchMethod( + _i17.Future cancelFocusAndMetering() => (super.noSuchMethod( Invocation.method( #cancelFocusAndMetering, [], ), - returnValue: _i16.Future.value(), - returnValueForMissingStub: _i16.Future.value(), - ) as _i16.Future); + returnValue: _i17.Future.value(), + returnValueForMissingStub: _i17.Future.value(), + ) as _i17.Future); @override - _i16.Future setExposureCompensationIndex(int? index) => + _i17.Future setExposureCompensationIndex(int? index) => (super.noSuchMethod( Invocation.method( #setExposureCompensationIndex, [index], ), - returnValue: _i16.Future.value(), - returnValueForMissingStub: _i16.Future.value(), - ) as _i16.Future); + returnValue: _i17.Future.value(), + returnValueForMissingStub: _i17.Future.value(), + ) as _i17.Future); } /// A class which mocks [Camera2CameraControl]. @@ -448,7 +482,7 @@ class MockCameraControl extends _i1.Mock implements _i3.CameraControl { /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable class MockCamera2CameraControl extends _i1.Mock - implements _i22.Camera2CameraControl { + implements _i24.Camera2CameraControl { @override _i3.CameraControl get cameraControl => (super.noSuchMethod( Invocation.getter(#cameraControl), @@ -463,16 +497,16 @@ class MockCamera2CameraControl extends _i1.Mock ) as _i3.CameraControl); @override - _i16.Future addCaptureRequestOptions( - _i23.CaptureRequestOptions? captureRequestOptions) => + _i17.Future addCaptureRequestOptions( + _i25.CaptureRequestOptions? captureRequestOptions) => (super.noSuchMethod( Invocation.method( #addCaptureRequestOptions, [captureRequestOptions], ), - returnValue: _i16.Future.value(), - returnValueForMissingStub: _i16.Future.value(), - ) as _i16.Future); + returnValue: _i17.Future.value(), + returnValueForMissingStub: _i17.Future.value(), + ) as _i17.Future); } /// A class which mocks [CameraImageData]. @@ -519,19 +553,19 @@ class MockCameraImageData extends _i1.Mock implements _i6.CameraImageData { /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockCameraSelector extends _i1.Mock implements _i24.CameraSelector { +class MockCameraSelector extends _i1.Mock implements _i26.CameraSelector { @override - _i16.Future> filter(List<_i2.CameraInfo>? cameraInfos) => + _i17.Future> filter(List<_i2.CameraInfo>? cameraInfos) => (super.noSuchMethod( Invocation.method( #filter, [cameraInfos], ), returnValue: - _i16.Future>.value(<_i2.CameraInfo>[]), + _i17.Future>.value(<_i2.CameraInfo>[]), returnValueForMissingStub: - _i16.Future>.value(<_i2.CameraInfo>[]), - ) as _i16.Future>); + _i17.Future>.value(<_i2.CameraInfo>[]), + ) as _i17.Future>); } /// A class which mocks [ExposureState]. @@ -565,7 +599,7 @@ class MockExposureState extends _i1.Mock implements _i5.ExposureState { /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockFallbackStrategy extends _i1.Mock implements _i25.FallbackStrategy { +class MockFallbackStrategy extends _i1.Mock implements _i27.FallbackStrategy { @override _i7.VideoQuality get quality => (super.noSuchMethod( Invocation.getter(#quality), @@ -587,86 +621,86 @@ class MockFallbackStrategy extends _i1.Mock implements _i25.FallbackStrategy { /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable class MockFocusMeteringResult extends _i1.Mock - implements _i20.FocusMeteringResult { + implements _i22.FocusMeteringResult { @override - _i16.Future isFocusSuccessful() => (super.noSuchMethod( + _i17.Future isFocusSuccessful() => (super.noSuchMethod( Invocation.method( #isFocusSuccessful, [], ), - returnValue: _i16.Future.value(false), - returnValueForMissingStub: _i16.Future.value(false), - ) as _i16.Future); + returnValue: _i17.Future.value(false), + returnValueForMissingStub: _i17.Future.value(false), + ) as _i17.Future); } /// A class which mocks [ImageAnalysis]. /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockImageAnalysis extends _i1.Mock implements _i26.ImageAnalysis { +class MockImageAnalysis extends _i1.Mock implements _i28.ImageAnalysis { @override - _i16.Future setTargetRotation(int? rotation) => (super.noSuchMethod( + _i17.Future setTargetRotation(int? rotation) => (super.noSuchMethod( Invocation.method( #setTargetRotation, [rotation], ), - returnValue: _i16.Future.value(), - returnValueForMissingStub: _i16.Future.value(), - ) as _i16.Future); + returnValue: _i17.Future.value(), + returnValueForMissingStub: _i17.Future.value(), + ) as _i17.Future); @override - _i16.Future setAnalyzer(_i15.Analyzer? analyzer) => (super.noSuchMethod( + _i17.Future setAnalyzer(_i16.Analyzer? analyzer) => (super.noSuchMethod( Invocation.method( #setAnalyzer, [analyzer], ), - returnValue: _i16.Future.value(), - returnValueForMissingStub: _i16.Future.value(), - ) as _i16.Future); + returnValue: _i17.Future.value(), + returnValueForMissingStub: _i17.Future.value(), + ) as _i17.Future); @override - _i16.Future clearAnalyzer() => (super.noSuchMethod( + _i17.Future clearAnalyzer() => (super.noSuchMethod( Invocation.method( #clearAnalyzer, [], ), - returnValue: _i16.Future.value(), - returnValueForMissingStub: _i16.Future.value(), - ) as _i16.Future); + returnValue: _i17.Future.value(), + returnValueForMissingStub: _i17.Future.value(), + ) as _i17.Future); } /// A class which mocks [ImageCapture]. /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockImageCapture extends _i1.Mock implements _i27.ImageCapture { +class MockImageCapture extends _i1.Mock implements _i29.ImageCapture { @override - _i16.Future setTargetRotation(int? rotation) => (super.noSuchMethod( + _i17.Future setTargetRotation(int? rotation) => (super.noSuchMethod( Invocation.method( #setTargetRotation, [rotation], ), - returnValue: _i16.Future.value(), - returnValueForMissingStub: _i16.Future.value(), - ) as _i16.Future); + returnValue: _i17.Future.value(), + returnValueForMissingStub: _i17.Future.value(), + ) as _i17.Future); @override - _i16.Future setFlashMode(int? newFlashMode) => (super.noSuchMethod( + _i17.Future setFlashMode(int? newFlashMode) => (super.noSuchMethod( Invocation.method( #setFlashMode, [newFlashMode], ), - returnValue: _i16.Future.value(), - returnValueForMissingStub: _i16.Future.value(), - ) as _i16.Future); + returnValue: _i17.Future.value(), + returnValueForMissingStub: _i17.Future.value(), + ) as _i17.Future); @override - _i16.Future takePicture() => (super.noSuchMethod( + _i17.Future takePicture() => (super.noSuchMethod( Invocation.method( #takePicture, [], ), - returnValue: _i16.Future.value(_i28.dummyValue( + returnValue: _i17.Future.value(_i30.dummyValue( this, Invocation.method( #takePicture, @@ -674,21 +708,21 @@ class MockImageCapture extends _i1.Mock implements _i27.ImageCapture { ), )), returnValueForMissingStub: - _i16.Future.value(_i28.dummyValue( + _i17.Future.value(_i30.dummyValue( this, Invocation.method( #takePicture, [], ), )), - ) as _i16.Future); + ) as _i17.Future); } /// A class which mocks [ImageProxy]. /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockImageProxy extends _i1.Mock implements _i17.ImageProxy { +class MockImageProxy extends _i1.Mock implements _i18.ImageProxy { @override int get format => (super.noSuchMethod( Invocation.getter(#format), @@ -711,33 +745,33 @@ class MockImageProxy extends _i1.Mock implements _i17.ImageProxy { ) as int); @override - _i16.Future> getPlanes() => (super.noSuchMethod( + _i17.Future> getPlanes() => (super.noSuchMethod( Invocation.method( #getPlanes, [], ), returnValue: - _i16.Future>.value(<_i29.PlaneProxy>[]), + _i17.Future>.value(<_i31.PlaneProxy>[]), returnValueForMissingStub: - _i16.Future>.value(<_i29.PlaneProxy>[]), - ) as _i16.Future>); + _i17.Future>.value(<_i31.PlaneProxy>[]), + ) as _i17.Future>); @override - _i16.Future close() => (super.noSuchMethod( + _i17.Future close() => (super.noSuchMethod( Invocation.method( #close, [], ), - returnValue: _i16.Future.value(), - returnValueForMissingStub: _i16.Future.value(), - ) as _i16.Future); + returnValue: _i17.Future.value(), + returnValueForMissingStub: _i17.Future.value(), + ) as _i17.Future); } /// A class which mocks [Observer]. /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockObserver extends _i1.Mock implements _i30.Observer<_i18.CameraState> { +class MockObserver extends _i1.Mock implements _i32.Observer<_i20.CameraState> { @override void Function(Object) get onChanged => (super.noSuchMethod( Invocation.getter(#onChanged), @@ -761,12 +795,12 @@ class MockObserver extends _i1.Mock implements _i30.Observer<_i18.CameraState> { // ignore: must_be_immutable class MockPendingRecording extends _i1.Mock implements _i10.PendingRecording { @override - _i16.Future<_i8.Recording> start() => (super.noSuchMethod( + _i17.Future<_i8.Recording> start() => (super.noSuchMethod( Invocation.method( #start, [], ), - returnValue: _i16.Future<_i8.Recording>.value(_FakeRecording_6( + returnValue: _i17.Future<_i8.Recording>.value(_FakeRecording_6( this, Invocation.method( #start, @@ -774,27 +808,27 @@ class MockPendingRecording extends _i1.Mock implements _i10.PendingRecording { ), )), returnValueForMissingStub: - _i16.Future<_i8.Recording>.value(_FakeRecording_6( + _i17.Future<_i8.Recording>.value(_FakeRecording_6( this, Invocation.method( #start, [], ), )), - ) as _i16.Future<_i8.Recording>); + ) as _i17.Future<_i8.Recording>); } /// A class which mocks [PlaneProxy]. /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockPlaneProxy extends _i1.Mock implements _i29.PlaneProxy { +class MockPlaneProxy extends _i1.Mock implements _i31.PlaneProxy { @override - _i31.Uint8List get buffer => (super.noSuchMethod( + _i33.Uint8List get buffer => (super.noSuchMethod( Invocation.getter(#buffer), - returnValue: _i31.Uint8List(0), - returnValueForMissingStub: _i31.Uint8List(0), - ) as _i31.Uint8List); + returnValue: _i33.Uint8List(0), + returnValueForMissingStub: _i33.Uint8List(0), + ) as _i33.Uint8List); @override int get pixelStride => (super.noSuchMethod( @@ -815,26 +849,26 @@ class MockPlaneProxy extends _i1.Mock implements _i29.PlaneProxy { /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockPreview extends _i1.Mock implements _i32.Preview { +class MockPreview extends _i1.Mock implements _i34.Preview { @override - _i16.Future setTargetRotation(int? rotation) => (super.noSuchMethod( + _i17.Future setTargetRotation(int? rotation) => (super.noSuchMethod( Invocation.method( #setTargetRotation, [rotation], ), - returnValue: _i16.Future.value(), - returnValueForMissingStub: _i16.Future.value(), - ) as _i16.Future); + returnValue: _i17.Future.value(), + returnValueForMissingStub: _i17.Future.value(), + ) as _i17.Future); @override - _i16.Future setSurfaceProvider() => (super.noSuchMethod( + _i17.Future setSurfaceProvider() => (super.noSuchMethod( Invocation.method( #setSurfaceProvider, [], ), - returnValue: _i16.Future.value(0), - returnValueForMissingStub: _i16.Future.value(0), - ) as _i16.Future); + returnValue: _i17.Future.value(0), + returnValueForMissingStub: _i17.Future.value(0), + ) as _i17.Future); @override void releaseFlutterSurfaceTexture() => super.noSuchMethod( @@ -846,13 +880,13 @@ class MockPreview extends _i1.Mock implements _i32.Preview { ); @override - _i16.Future<_i7.ResolutionInfo> getResolutionInfo() => (super.noSuchMethod( + _i17.Future<_i7.ResolutionInfo> getResolutionInfo() => (super.noSuchMethod( Invocation.method( #getResolutionInfo, [], ), returnValue: - _i16.Future<_i7.ResolutionInfo>.value(_FakeResolutionInfo_7( + _i17.Future<_i7.ResolutionInfo>.value(_FakeResolutionInfo_7( this, Invocation.method( #getResolutionInfo, @@ -860,14 +894,14 @@ class MockPreview extends _i1.Mock implements _i32.Preview { ), )), returnValueForMissingStub: - _i16.Future<_i7.ResolutionInfo>.value(_FakeResolutionInfo_7( + _i17.Future<_i7.ResolutionInfo>.value(_FakeResolutionInfo_7( this, Invocation.method( #getResolutionInfo, [], ), )), - ) as _i16.Future<_i7.ResolutionInfo>); + ) as _i17.Future<_i7.ResolutionInfo>); } /// A class which mocks [ProcessCameraProvider]. @@ -875,24 +909,24 @@ class MockPreview extends _i1.Mock implements _i32.Preview { /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable class MockProcessCameraProvider extends _i1.Mock - implements _i33.ProcessCameraProvider { + implements _i35.ProcessCameraProvider { @override - _i16.Future> getAvailableCameraInfos() => + _i17.Future> getAvailableCameraInfos() => (super.noSuchMethod( Invocation.method( #getAvailableCameraInfos, [], ), returnValue: - _i16.Future>.value(<_i2.CameraInfo>[]), + _i17.Future>.value(<_i2.CameraInfo>[]), returnValueForMissingStub: - _i16.Future>.value(<_i2.CameraInfo>[]), - ) as _i16.Future>); + _i17.Future>.value(<_i2.CameraInfo>[]), + ) as _i17.Future>); @override - _i16.Future<_i9.Camera> bindToLifecycle( - _i24.CameraSelector? cameraSelector, - List<_i34.UseCase>? useCases, + _i17.Future<_i9.Camera> bindToLifecycle( + _i26.CameraSelector? cameraSelector, + List<_i36.UseCase>? useCases, ) => (super.noSuchMethod( Invocation.method( @@ -902,7 +936,7 @@ class MockProcessCameraProvider extends _i1.Mock useCases, ], ), - returnValue: _i16.Future<_i9.Camera>.value(_FakeCamera_8( + returnValue: _i17.Future<_i9.Camera>.value(_FakeCamera_8( this, Invocation.method( #bindToLifecycle, @@ -912,7 +946,7 @@ class MockProcessCameraProvider extends _i1.Mock ], ), )), - returnValueForMissingStub: _i16.Future<_i9.Camera>.value(_FakeCamera_8( + returnValueForMissingStub: _i17.Future<_i9.Camera>.value(_FakeCamera_8( this, Invocation.method( #bindToLifecycle, @@ -922,20 +956,20 @@ class MockProcessCameraProvider extends _i1.Mock ], ), )), - ) as _i16.Future<_i9.Camera>); + ) as _i17.Future<_i9.Camera>); @override - _i16.Future isBound(_i34.UseCase? useCase) => (super.noSuchMethod( + _i17.Future isBound(_i36.UseCase? useCase) => (super.noSuchMethod( Invocation.method( #isBound, [useCase], ), - returnValue: _i16.Future.value(false), - returnValueForMissingStub: _i16.Future.value(false), - ) as _i16.Future); + returnValue: _i17.Future.value(false), + returnValueForMissingStub: _i17.Future.value(false), + ) as _i17.Future); @override - void unbind(List<_i34.UseCase>? useCases) => super.noSuchMethod( + void unbind(List<_i36.UseCase>? useCases) => super.noSuchMethod( Invocation.method( #unbind, [useCases], @@ -957,7 +991,7 @@ class MockProcessCameraProvider extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockQualitySelector extends _i1.Mock implements _i35.QualitySelector { +class MockQualitySelector extends _i1.Mock implements _i37.QualitySelector { @override List<_i7.VideoQualityData> get qualityList => (super.noSuchMethod( Invocation.getter(#qualityList), @@ -970,16 +1004,16 @@ class MockQualitySelector extends _i1.Mock implements _i35.QualitySelector { /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockRecorder extends _i1.Mock implements _i11.Recorder { +class MockRecorder extends _i1.Mock implements _i12.Recorder { @override - _i16.Future<_i10.PendingRecording> prepareRecording(String? path) => + _i17.Future<_i10.PendingRecording> prepareRecording(String? path) => (super.noSuchMethod( Invocation.method( #prepareRecording, [path], ), returnValue: - _i16.Future<_i10.PendingRecording>.value(_FakePendingRecording_9( + _i17.Future<_i10.PendingRecording>.value(_FakePendingRecording_9( this, Invocation.method( #prepareRecording, @@ -987,14 +1021,33 @@ class MockRecorder extends _i1.Mock implements _i11.Recorder { ), )), returnValueForMissingStub: - _i16.Future<_i10.PendingRecording>.value(_FakePendingRecording_9( + _i17.Future<_i10.PendingRecording>.value(_FakePendingRecording_9( this, Invocation.method( #prepareRecording, [path], ), )), - ) as _i16.Future<_i10.PendingRecording>); + ) as _i17.Future<_i10.PendingRecording>); +} + +/// A class which mocks [ResolutionFilter]. +/// +/// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable +class MockResolutionFilter extends _i1.Mock implements _i38.ResolutionFilter { + @override + _i11.Size get preferredResolution => (super.noSuchMethod( + Invocation.getter(#preferredResolution), + returnValue: _FakeSize_10( + this, + Invocation.getter(#preferredResolution), + ), + returnValueForMissingStub: _FakeSize_10( + this, + Invocation.getter(#preferredResolution), + ), + ) as _i11.Size); } /// A class which mocks [ResolutionSelector]. @@ -1002,14 +1055,14 @@ class MockRecorder extends _i1.Mock implements _i11.Recorder { /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable class MockResolutionSelector extends _i1.Mock - implements _i36.ResolutionSelector {} + implements _i39.ResolutionSelector {} /// A class which mocks [ResolutionStrategy]. /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable class MockResolutionStrategy extends _i1.Mock - implements _i37.ResolutionStrategy {} + implements _i40.ResolutionStrategy {} /// A class which mocks [Recording]. /// @@ -1017,68 +1070,68 @@ class MockResolutionStrategy extends _i1.Mock // ignore: must_be_immutable class MockRecording extends _i1.Mock implements _i8.Recording { @override - _i16.Future close() => (super.noSuchMethod( + _i17.Future close() => (super.noSuchMethod( Invocation.method( #close, [], ), - returnValue: _i16.Future.value(), - returnValueForMissingStub: _i16.Future.value(), - ) as _i16.Future); + returnValue: _i17.Future.value(), + returnValueForMissingStub: _i17.Future.value(), + ) as _i17.Future); @override - _i16.Future pause() => (super.noSuchMethod( + _i17.Future pause() => (super.noSuchMethod( Invocation.method( #pause, [], ), - returnValue: _i16.Future.value(), - returnValueForMissingStub: _i16.Future.value(), - ) as _i16.Future); + returnValue: _i17.Future.value(), + returnValueForMissingStub: _i17.Future.value(), + ) as _i17.Future); @override - _i16.Future resume() => (super.noSuchMethod( + _i17.Future resume() => (super.noSuchMethod( Invocation.method( #resume, [], ), - returnValue: _i16.Future.value(), - returnValueForMissingStub: _i16.Future.value(), - ) as _i16.Future); + returnValue: _i17.Future.value(), + returnValueForMissingStub: _i17.Future.value(), + ) as _i17.Future); @override - _i16.Future stop() => (super.noSuchMethod( + _i17.Future stop() => (super.noSuchMethod( Invocation.method( #stop, [], ), - returnValue: _i16.Future.value(), - returnValueForMissingStub: _i16.Future.value(), - ) as _i16.Future); + returnValue: _i17.Future.value(), + returnValueForMissingStub: _i17.Future.value(), + ) as _i17.Future); } /// A class which mocks [VideoCapture]. /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockVideoCapture extends _i1.Mock implements _i38.VideoCapture { +class MockVideoCapture extends _i1.Mock implements _i41.VideoCapture { @override - _i16.Future setTargetRotation(int? rotation) => (super.noSuchMethod( + _i17.Future setTargetRotation(int? rotation) => (super.noSuchMethod( Invocation.method( #setTargetRotation, [rotation], ), - returnValue: _i16.Future.value(), - returnValueForMissingStub: _i16.Future.value(), - ) as _i16.Future); + returnValue: _i17.Future.value(), + returnValueForMissingStub: _i17.Future.value(), + ) as _i17.Future); @override - _i16.Future<_i11.Recorder> getOutput() => (super.noSuchMethod( + _i17.Future<_i12.Recorder> getOutput() => (super.noSuchMethod( Invocation.method( #getOutput, [], ), - returnValue: _i16.Future<_i11.Recorder>.value(_FakeRecorder_10( + returnValue: _i17.Future<_i12.Recorder>.value(_FakeRecorder_11( this, Invocation.method( #getOutput, @@ -1086,32 +1139,32 @@ class MockVideoCapture extends _i1.Mock implements _i38.VideoCapture { ), )), returnValueForMissingStub: - _i16.Future<_i11.Recorder>.value(_FakeRecorder_10( + _i17.Future<_i12.Recorder>.value(_FakeRecorder_11( this, Invocation.method( #getOutput, [], ), )), - ) as _i16.Future<_i11.Recorder>); + ) as _i17.Future<_i12.Recorder>); } /// A class which mocks [BuildContext]. /// /// See the documentation for Mockito's code generation for more information. -class MockBuildContext extends _i1.Mock implements _i12.BuildContext { +class MockBuildContext extends _i1.Mock implements _i13.BuildContext { @override - _i12.Widget get widget => (super.noSuchMethod( + _i13.Widget get widget => (super.noSuchMethod( Invocation.getter(#widget), - returnValue: _FakeWidget_11( + returnValue: _FakeWidget_12( this, Invocation.getter(#widget), ), - returnValueForMissingStub: _FakeWidget_11( + returnValueForMissingStub: _FakeWidget_12( this, Invocation.getter(#widget), ), - ) as _i12.Widget); + ) as _i13.Widget); @override bool get mounted => (super.noSuchMethod( @@ -1128,8 +1181,8 @@ class MockBuildContext extends _i1.Mock implements _i12.BuildContext { ) as bool); @override - _i12.InheritedWidget dependOnInheritedElement( - _i12.InheritedElement? ancestor, { + _i13.InheritedWidget dependOnInheritedElement( + _i13.InheritedElement? ancestor, { Object? aspect, }) => (super.noSuchMethod( @@ -1138,7 +1191,7 @@ class MockBuildContext extends _i1.Mock implements _i12.BuildContext { [ancestor], {#aspect: aspect}, ), - returnValue: _FakeInheritedWidget_12( + returnValue: _FakeInheritedWidget_13( this, Invocation.method( #dependOnInheritedElement, @@ -1146,7 +1199,7 @@ class MockBuildContext extends _i1.Mock implements _i12.BuildContext { {#aspect: aspect}, ), ), - returnValueForMissingStub: _FakeInheritedWidget_12( + returnValueForMissingStub: _FakeInheritedWidget_13( this, Invocation.method( #dependOnInheritedElement, @@ -1154,10 +1207,10 @@ class MockBuildContext extends _i1.Mock implements _i12.BuildContext { {#aspect: aspect}, ), ), - ) as _i12.InheritedWidget); + ) as _i13.InheritedWidget); @override - void visitAncestorElements(_i12.ConditionalElementVisitor? visitor) => + void visitAncestorElements(_i13.ConditionalElementVisitor? visitor) => super.noSuchMethod( Invocation.method( #visitAncestorElements, @@ -1167,7 +1220,7 @@ class MockBuildContext extends _i1.Mock implements _i12.BuildContext { ); @override - void visitChildElements(_i12.ElementVisitor? visitor) => super.noSuchMethod( + void visitChildElements(_i13.ElementVisitor? visitor) => super.noSuchMethod( Invocation.method( #visitChildElements, [visitor], @@ -1176,7 +1229,7 @@ class MockBuildContext extends _i1.Mock implements _i12.BuildContext { ); @override - void dispatchNotification(_i12.Notification? notification) => + void dispatchNotification(_i13.Notification? notification) => super.noSuchMethod( Invocation.method( #dispatchNotification, @@ -1186,9 +1239,9 @@ class MockBuildContext extends _i1.Mock implements _i12.BuildContext { ); @override - _i14.DiagnosticsNode describeElement( + _i15.DiagnosticsNode describeElement( String? name, { - _i14.DiagnosticsTreeStyle? style = _i14.DiagnosticsTreeStyle.errorProperty, + _i15.DiagnosticsTreeStyle? style = _i15.DiagnosticsTreeStyle.errorProperty, }) => (super.noSuchMethod( Invocation.method( @@ -1196,7 +1249,7 @@ class MockBuildContext extends _i1.Mock implements _i12.BuildContext { [name], {#style: style}, ), - returnValue: _FakeDiagnosticsNode_13( + returnValue: _FakeDiagnosticsNode_14( this, Invocation.method( #describeElement, @@ -1204,7 +1257,7 @@ class MockBuildContext extends _i1.Mock implements _i12.BuildContext { {#style: style}, ), ), - returnValueForMissingStub: _FakeDiagnosticsNode_13( + returnValueForMissingStub: _FakeDiagnosticsNode_14( this, Invocation.method( #describeElement, @@ -1212,12 +1265,12 @@ class MockBuildContext extends _i1.Mock implements _i12.BuildContext { {#style: style}, ), ), - ) as _i14.DiagnosticsNode); + ) as _i15.DiagnosticsNode); @override - _i14.DiagnosticsNode describeWidget( + _i15.DiagnosticsNode describeWidget( String? name, { - _i14.DiagnosticsTreeStyle? style = _i14.DiagnosticsTreeStyle.errorProperty, + _i15.DiagnosticsTreeStyle? style = _i15.DiagnosticsTreeStyle.errorProperty, }) => (super.noSuchMethod( Invocation.method( @@ -1225,7 +1278,7 @@ class MockBuildContext extends _i1.Mock implements _i12.BuildContext { [name], {#style: style}, ), - returnValue: _FakeDiagnosticsNode_13( + returnValue: _FakeDiagnosticsNode_14( this, Invocation.method( #describeWidget, @@ -1233,7 +1286,7 @@ class MockBuildContext extends _i1.Mock implements _i12.BuildContext { {#style: style}, ), ), - returnValueForMissingStub: _FakeDiagnosticsNode_13( + returnValueForMissingStub: _FakeDiagnosticsNode_14( this, Invocation.method( #describeWidget, @@ -1241,10 +1294,10 @@ class MockBuildContext extends _i1.Mock implements _i12.BuildContext { {#style: style}, ), ), - ) as _i14.DiagnosticsNode); + ) as _i15.DiagnosticsNode); @override - List<_i14.DiagnosticsNode> describeMissingAncestor( + List<_i15.DiagnosticsNode> describeMissingAncestor( {required Type? expectedAncestorType}) => (super.noSuchMethod( Invocation.method( @@ -1252,39 +1305,39 @@ class MockBuildContext extends _i1.Mock implements _i12.BuildContext { [], {#expectedAncestorType: expectedAncestorType}, ), - returnValue: <_i14.DiagnosticsNode>[], - returnValueForMissingStub: <_i14.DiagnosticsNode>[], - ) as List<_i14.DiagnosticsNode>); + returnValue: <_i15.DiagnosticsNode>[], + returnValueForMissingStub: <_i15.DiagnosticsNode>[], + ) as List<_i15.DiagnosticsNode>); @override - _i14.DiagnosticsNode describeOwnershipChain(String? name) => + _i15.DiagnosticsNode describeOwnershipChain(String? name) => (super.noSuchMethod( Invocation.method( #describeOwnershipChain, [name], ), - returnValue: _FakeDiagnosticsNode_13( + returnValue: _FakeDiagnosticsNode_14( this, Invocation.method( #describeOwnershipChain, [name], ), ), - returnValueForMissingStub: _FakeDiagnosticsNode_13( + returnValueForMissingStub: _FakeDiagnosticsNode_14( this, Invocation.method( #describeOwnershipChain, [name], ), ), - ) as _i14.DiagnosticsNode); + ) as _i15.DiagnosticsNode); } /// A class which mocks [TestInstanceManagerHostApi]. /// /// See the documentation for Mockito's code generation for more information. class MockTestInstanceManagerHostApi extends _i1.Mock - implements _i39.TestInstanceManagerHostApi { + implements _i42.TestInstanceManagerHostApi { @override void clear() => super.noSuchMethod( Invocation.method( @@ -1299,19 +1352,19 @@ class MockTestInstanceManagerHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestSystemServicesHostApi extends _i1.Mock - implements _i39.TestSystemServicesHostApi { + implements _i42.TestSystemServicesHostApi { @override - _i16.Future<_i7.CameraPermissionsErrorData?> requestCameraPermissions( + _i17.Future<_i7.CameraPermissionsErrorData?> requestCameraPermissions( bool? enableAudio) => (super.noSuchMethod( Invocation.method( #requestCameraPermissions, [enableAudio], ), - returnValue: _i16.Future<_i7.CameraPermissionsErrorData?>.value(), + returnValue: _i17.Future<_i7.CameraPermissionsErrorData?>.value(), returnValueForMissingStub: - _i16.Future<_i7.CameraPermissionsErrorData?>.value(), - ) as _i16.Future<_i7.CameraPermissionsErrorData?>); + _i17.Future<_i7.CameraPermissionsErrorData?>.value(), + ) as _i17.Future<_i7.CameraPermissionsErrorData?>); @override String getTempFilePath( @@ -1326,7 +1379,7 @@ class MockTestSystemServicesHostApi extends _i1.Mock suffix, ], ), - returnValue: _i28.dummyValue( + returnValue: _i30.dummyValue( this, Invocation.method( #getTempFilePath, @@ -1336,7 +1389,7 @@ class MockTestSystemServicesHostApi extends _i1.Mock ], ), ), - returnValueForMissingStub: _i28.dummyValue( + returnValueForMissingStub: _i30.dummyValue( this, Invocation.method( #getTempFilePath, @@ -1353,7 +1406,7 @@ class MockTestSystemServicesHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable -class MockZoomState extends _i1.Mock implements _i19.ZoomState { +class MockZoomState extends _i1.Mock implements _i21.ZoomState { @override double get minZoomRatio => (super.noSuchMethod( Invocation.getter(#minZoomRatio), @@ -1374,31 +1427,31 @@ class MockZoomState extends _i1.Mock implements _i19.ZoomState { /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable class MockLiveCameraState extends _i1.Mock - implements _i4.LiveData<_i18.CameraState> { + implements _i4.LiveData<_i20.CameraState> { MockLiveCameraState() { _i1.throwOnMissingStub(this); } @override - _i16.Future observe(_i30.Observer<_i18.CameraState>? observer) => + _i17.Future observe(_i32.Observer<_i20.CameraState>? observer) => (super.noSuchMethod( Invocation.method( #observe, [observer], ), - returnValue: _i16.Future.value(), - returnValueForMissingStub: _i16.Future.value(), - ) as _i16.Future); + returnValue: _i17.Future.value(), + returnValueForMissingStub: _i17.Future.value(), + ) as _i17.Future); @override - _i16.Future removeObservers() => (super.noSuchMethod( + _i17.Future removeObservers() => (super.noSuchMethod( Invocation.method( #removeObservers, [], ), - returnValue: _i16.Future.value(), - returnValueForMissingStub: _i16.Future.value(), - ) as _i16.Future); + returnValue: _i17.Future.value(), + returnValueForMissingStub: _i17.Future.value(), + ) as _i17.Future); } /// A class which mocks [LiveData]. @@ -1406,29 +1459,29 @@ class MockLiveCameraState extends _i1.Mock /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable class MockLiveZoomState extends _i1.Mock - implements _i4.LiveData<_i19.ZoomState> { + implements _i4.LiveData<_i21.ZoomState> { MockLiveZoomState() { _i1.throwOnMissingStub(this); } @override - _i16.Future observe(_i30.Observer<_i19.ZoomState>? observer) => + _i17.Future observe(_i32.Observer<_i21.ZoomState>? observer) => (super.noSuchMethod( Invocation.method( #observe, [observer], ), - returnValue: _i16.Future.value(), - returnValueForMissingStub: _i16.Future.value(), - ) as _i16.Future); + returnValue: _i17.Future.value(), + returnValueForMissingStub: _i17.Future.value(), + ) as _i17.Future); @override - _i16.Future removeObservers() => (super.noSuchMethod( + _i17.Future removeObservers() => (super.noSuchMethod( Invocation.method( #removeObservers, [], ), - returnValue: _i16.Future.value(), - returnValueForMissingStub: _i16.Future.value(), - ) as _i16.Future); + returnValue: _i17.Future.value(), + returnValueForMissingStub: _i17.Future.value(), + ) as _i17.Future); } diff --git a/packages/camera/camera_android_camerax/test/resolution_filter_test.dart b/packages/camera/camera_android_camerax/test/resolution_filter_test.dart new file mode 100644 index 000000000000..07a96ff68027 --- /dev/null +++ b/packages/camera/camera_android_camerax/test/resolution_filter_test.dart @@ -0,0 +1,90 @@ +// 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 'dart:ui'; + +import 'package:camera_android_camerax/src/camerax_library.g.dart'; +import 'package:camera_android_camerax/src/instance_manager.dart'; +import 'package:camera_android_camerax/src/resolution_filter.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'resolution_filter_test.mocks.dart'; +import 'test_camerax_library.g.dart'; + +@GenerateMocks([ + TestResolutionFilterHostApi, + TestInstanceManagerHostApi, +]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('ResolutionFilter', () { + tearDown(() { + TestResolutionFilterHostApi.setup(null); + TestInstanceManagerHostApi.setup(null); + }); + + test( + 'detached ResolutionFilter.onePreferredSize constructor does not make call to Host API createWithOnePreferredSize', + () { + final MockTestResolutionFilterHostApi mockApi = + MockTestResolutionFilterHostApi(); + TestResolutionFilterHostApi.setup(mockApi); + TestInstanceManagerHostApi.setup(MockTestInstanceManagerHostApi()); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + + const double preferredWidth = 270; + const double preferredHeight = 720; + const Size preferredResolution = Size(preferredWidth, preferredHeight); + + ResolutionFilter.onePreferredSizeDetached( + preferredResolution: preferredResolution, + instanceManager: instanceManager, + ); + + verifyNever(mockApi.createWithOnePreferredSize( + argThat(isA()), + argThat(isA() + .having( + (ResolutionInfo size) => size.width, 'width', preferredWidth) + .having((ResolutionInfo size) => size.height, 'height', + preferredHeight)), + )); + }); + + test('HostApi createWithOnePreferredSize creates expected ResolutionFilter', + () { + final MockTestResolutionFilterHostApi mockApi = + MockTestResolutionFilterHostApi(); + TestResolutionFilterHostApi.setup(mockApi); + TestInstanceManagerHostApi.setup(MockTestInstanceManagerHostApi()); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + + const double preferredWidth = 890; + const double preferredHeight = 980; + const Size preferredResolution = Size(preferredWidth, preferredHeight); + + final ResolutionFilter instance = ResolutionFilter.onePreferredSize( + preferredResolution: preferredResolution, + instanceManager: instanceManager, + ); + verify(mockApi.createWithOnePreferredSize( + instanceManager.getIdentifier(instance), + argThat(isA() + .having( + (ResolutionInfo size) => size.width, 'width', preferredWidth) + .having((ResolutionInfo size) => size.height, 'height', + preferredHeight)), + )); + }); + }); +} diff --git a/packages/camera/camera_android_camerax/test/resolution_filter_test.mocks.dart b/packages/camera/camera_android_camerax/test/resolution_filter_test.mocks.dart new file mode 100644 index 000000000000..cf859c7b6378 --- /dev/null +++ b/packages/camera/camera_android_camerax/test/resolution_filter_test.mocks.dart @@ -0,0 +1,67 @@ +// Mocks generated by Mockito 5.4.4 from annotations +// in camera_android_camerax/test/resolution_filter_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:camera_android_camerax/src/camerax_library.g.dart' as _i3; +import 'package:mockito/mockito.dart' as _i1; + +import 'test_camerax_library.g.dart' as _i2; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +/// A class which mocks [TestResolutionFilterHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestResolutionFilterHostApi extends _i1.Mock + implements _i2.TestResolutionFilterHostApi { + MockTestResolutionFilterHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void createWithOnePreferredSize( + int? identifier, + _i3.ResolutionInfo? preferredResolution, + ) => + super.noSuchMethod( + Invocation.method( + #createWithOnePreferredSize, + [ + identifier, + preferredResolution, + ], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [TestInstanceManagerHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestInstanceManagerHostApi extends _i1.Mock + implements _i2.TestInstanceManagerHostApi { + MockTestInstanceManagerHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void clear() => super.noSuchMethod( + Invocation.method( + #clear, + [], + ), + returnValueForMissingStub: null, + ); +} diff --git a/packages/camera/camera_android_camerax/test/resolution_selector_test.dart b/packages/camera/camera_android_camerax/test/resolution_selector_test.dart index 45ecef576a3a..7aaa37ae1ea0 100644 --- a/packages/camera/camera_android_camerax/test/resolution_selector_test.dart +++ b/packages/camera/camera_android_camerax/test/resolution_selector_test.dart @@ -6,6 +6,7 @@ import 'dart:ui'; import 'package:camera_android_camerax/src/aspect_ratio_strategy.dart'; import 'package:camera_android_camerax/src/instance_manager.dart'; +import 'package:camera_android_camerax/src/resolution_filter.dart'; import 'package:camera_android_camerax/src/resolution_selector.dart'; import 'package:camera_android_camerax/src/resolution_strategy.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -17,6 +18,7 @@ import 'test_camerax_library.g.dart'; @GenerateMocks([ AspectRatioStrategy, + ResolutionFilter, ResolutionStrategy, TestResolutionSelectorHostApi, TestInstanceManagerHostApi, @@ -54,15 +56,13 @@ void main() { ResolutionSelector.detached( resolutionStrategy: MockResolutionStrategy(), + resolutionFilter: MockResolutionFilter(), aspectRatioStrategy: MockAspectRatioStrategy(), instanceManager: instanceManager, ); - verifyNever(mockApi.create( - argThat(isA()), - argThat(isA()), - argThat(isA()), - )); + verifyNever(mockApi.create(argThat(isA()), argThat(isA()), + argThat(isA()), argThat(isA()))); }); test('HostApi create creates expected ResolutionSelector instance', () { @@ -91,6 +91,20 @@ void main() { ), ); + final ResolutionFilter resolutionFilter = + ResolutionFilter.onePreferredSizeDetached( + preferredResolution: const Size(30, 40)); + const int resolutionFilterIdentifier = 54; + instanceManager.addHostCreatedInstance( + resolutionFilter, + resolutionFilterIdentifier, + onCopy: (ResolutionFilter original) => + ResolutionFilter.onePreferredSizeDetached( + preferredResolution: original.preferredResolution, + instanceManager: instanceManager, + ), + ); + final AspectRatioStrategy aspectRatioStrategy = AspectRatioStrategy.detached( preferredAspectRatio: AspectRatio.ratio4To3, @@ -110,6 +124,7 @@ void main() { final ResolutionSelector instance = ResolutionSelector( resolutionStrategy: resolutionStrategy, + resolutionFilter: resolutionFilter, aspectRatioStrategy: aspectRatioStrategy, instanceManager: instanceManager, ); @@ -117,6 +132,7 @@ void main() { verify(mockApi.create( instanceManager.getIdentifier(instance), resolutionStrategyIdentifier, + resolutionFilterIdentifier, aspectRatioStrategyIdentifier, )); }); diff --git a/packages/camera/camera_android_camerax/test/resolution_selector_test.mocks.dart b/packages/camera/camera_android_camerax/test/resolution_selector_test.mocks.dart index 541092f089a9..c36ae699e8ad 100644 --- a/packages/camera/camera_android_camerax/test/resolution_selector_test.mocks.dart +++ b/packages/camera/camera_android_camerax/test/resolution_selector_test.mocks.dart @@ -3,11 +3,14 @@ // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:camera_android_camerax/src/aspect_ratio_strategy.dart' as _i2; -import 'package:camera_android_camerax/src/resolution_strategy.dart' as _i3; +import 'dart:ui' as _i2; + +import 'package:camera_android_camerax/src/aspect_ratio_strategy.dart' as _i3; +import 'package:camera_android_camerax/src/resolution_filter.dart' as _i4; +import 'package:camera_android_camerax/src/resolution_strategy.dart' as _i5; import 'package:mockito/mockito.dart' as _i1; -import 'test_camerax_library.g.dart' as _i4; +import 'test_camerax_library.g.dart' as _i6; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -22,12 +25,22 @@ import 'test_camerax_library.g.dart' as _i4; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class +class _FakeSize_0 extends _i1.SmartFake implements _i2.Size { + _FakeSize_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + /// A class which mocks [AspectRatioStrategy]. /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable class MockAspectRatioStrategy extends _i1.Mock - implements _i2.AspectRatioStrategy { + implements _i3.AspectRatioStrategy { MockAspectRatioStrategy() { _i1.throwOnMissingStub(this); } @@ -45,12 +58,31 @@ class MockAspectRatioStrategy extends _i1.Mock ) as int); } +/// A class which mocks [ResolutionFilter]. +/// +/// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable +class MockResolutionFilter extends _i1.Mock implements _i4.ResolutionFilter { + MockResolutionFilter() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.Size get preferredResolution => (super.noSuchMethod( + Invocation.getter(#preferredResolution), + returnValue: _FakeSize_0( + this, + Invocation.getter(#preferredResolution), + ), + ) as _i2.Size); +} + /// A class which mocks [ResolutionStrategy]. /// /// See the documentation for Mockito's code generation for more information. // ignore: must_be_immutable class MockResolutionStrategy extends _i1.Mock - implements _i3.ResolutionStrategy { + implements _i5.ResolutionStrategy { MockResolutionStrategy() { _i1.throwOnMissingStub(this); } @@ -60,7 +92,7 @@ class MockResolutionStrategy extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestResolutionSelectorHostApi extends _i1.Mock - implements _i4.TestResolutionSelectorHostApi { + implements _i6.TestResolutionSelectorHostApi { MockTestResolutionSelectorHostApi() { _i1.throwOnMissingStub(this); } @@ -69,6 +101,7 @@ class MockTestResolutionSelectorHostApi extends _i1.Mock void create( int? identifier, int? resolutionStrategyIdentifier, + int? resolutionSelectorIdentifier, int? aspectRatioStrategyIdentifier, ) => super.noSuchMethod( @@ -77,6 +110,7 @@ class MockTestResolutionSelectorHostApi extends _i1.Mock [ identifier, resolutionStrategyIdentifier, + resolutionSelectorIdentifier, aspectRatioStrategyIdentifier, ], ), @@ -88,7 +122,7 @@ class MockTestResolutionSelectorHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestInstanceManagerHostApi extends _i1.Mock - implements _i4.TestInstanceManagerHostApi { + implements _i6.TestInstanceManagerHostApi { MockTestInstanceManagerHostApi() { _i1.throwOnMissingStub(this); } diff --git a/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart b/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart index b69825928dd9..8080e4276fc8 100644 --- a/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart +++ b/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart @@ -1321,7 +1321,7 @@ abstract class TestResolutionSelectorHostApi { static const MessageCodec codec = StandardMessageCodec(); void create(int identifier, int? resolutionStrategyIdentifier, - int? aspectRatioStrategyIdentifier); + int? resolutionSelectorIdentifier, int? aspectRatioStrategyIdentifier); static void setup(TestResolutionSelectorHostApi? api, {BinaryMessenger? binaryMessenger}) { @@ -1343,8 +1343,12 @@ abstract class TestResolutionSelectorHostApi { assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.ResolutionSelectorHostApi.create was null, expected non-null int.'); final int? arg_resolutionStrategyIdentifier = (args[1] as int?); - final int? arg_aspectRatioStrategyIdentifier = (args[2] as int?); - api.create(arg_identifier!, arg_resolutionStrategyIdentifier, + final int? arg_resolutionSelectorIdentifier = (args[2] as int?); + final int? arg_aspectRatioStrategyIdentifier = (args[3] as int?); + api.create( + arg_identifier!, + arg_resolutionStrategyIdentifier, + arg_resolutionSelectorIdentifier, arg_aspectRatioStrategyIdentifier); return []; }); @@ -2359,3 +2363,68 @@ abstract class TestCamera2CameraControlHostApi { } } } + +class _TestResolutionFilterHostApiCodec extends StandardMessageCodec { + const _TestResolutionFilterHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is ResolutionInfo) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return ResolutionInfo.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestResolutionFilterHostApi { + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => + TestDefaultBinaryMessengerBinding.instance; + static const MessageCodec codec = + _TestResolutionFilterHostApiCodec(); + + void createWithOnePreferredSize( + int identifier, ResolutionInfo preferredResolution); + + static void setup(TestResolutionFilterHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ResolutionFilterHostApi.createWithOnePreferredSize', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.ResolutionFilterHostApi.createWithOnePreferredSize was null.'); + final List args = (message as List?)!; + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.ResolutionFilterHostApi.createWithOnePreferredSize was null, expected non-null int.'); + final ResolutionInfo? arg_preferredResolution = + (args[1] as ResolutionInfo?); + assert(arg_preferredResolution != null, + 'Argument for dev.flutter.pigeon.ResolutionFilterHostApi.createWithOnePreferredSize was null, expected non-null ResolutionInfo.'); + api.createWithOnePreferredSize( + arg_identifier!, arg_preferredResolution!); + return []; + }); + } + } + } +} From 72669d5eed65f850c91a297dc75e2b80cd960cf3 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 27 Mar 2024 18:31:45 -0400 Subject: [PATCH 111/126] [interactive_media_ads] Creates and adds the `interactive_media_ads` plugin (#6060) Part of https://github.com/flutter/flutter/issues/134228 Initial implementation of the plugin. * Adds minimum changes to pass CI (licenses, gradle, podpec changes, etc..). * Adds platform interface and tests for it. --- .github/dependabot.yml | 28 + CODEOWNERS | 1 + README.md | 1 + packages/interactive_media_ads/AUTHORS | 6 + packages/interactive_media_ads/CHANGELOG.md | 3 + packages/interactive_media_ads/LICENSE | 25 + packages/interactive_media_ads/README.md | 15 + .../android/build.gradle | 78 +++ .../android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 3 + .../InteractiveMediaAdsPlugin.kt | 37 ++ .../InteractiveMediaAdsPluginTest.kt | 31 + .../interactive_media_ads/example/README.md | 9 + .../example/android/app/build.gradle | 68 ++ .../MainActivityTest.kt | 17 + .../android/app/src/debug/AndroidManifest.xml | 20 + .../android/app/src/main/AndroidManifest.xml | 44 ++ .../DriverExtensionActivity.kt | 10 + .../MainActivity.kt | 9 + .../io/flutter/plugins/DartIntegrationTest.kt | 16 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + .../app/src/main/res/values/styles.xml | 18 + .../example/android/build.gradle | 39 ++ .../example/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + .../example/android/settings.gradle | 39 ++ .../interactive_media_ads_test.dart | 23 + .../ios/Flutter/AppFrameworkInfo.plist | 26 + .../example/ios/Flutter/Debug.xcconfig | 2 + .../example/ios/Flutter/Release.xcconfig | 2 + .../interactive_media_ads/example/ios/Podfile | 44 ++ .../ios/Runner.xcodeproj/project.pbxproj | 619 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 98 +++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../example/ios/Runner/AppDelegate.swift | 17 + .../AppIcon.appiconset/Contents.json | 122 ++++ .../Icon-App-1024x1024@1x.png | Bin 0 -> 10932 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 295 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 450 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 282 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 462 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 704 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 586 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 1674 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 762 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 1226 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 1418 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../Runner/Base.lproj/LaunchScreen.storyboard | 37 ++ .../ios/Runner/Base.lproj/Main.storyboard | 26 + .../example/ios/Runner/Info.plist | 49 ++ .../ios/Runner/Runner-Bridging-Header.h | 5 + .../example/ios/RunnerTests/RunnerTests.swift | 30 + .../example/lib/main.dart | 44 ++ .../example/pubspec.yaml | 25 + .../example/test_driver/integration_test.dart | 7 + .../interactive_media_ads/ios/Assets/.gitkeep | 0 .../Classes/InteractiveMediaAdsPlugin.swift | 24 + .../ios/Resources/PrivacyInfo.xcprivacy | 6 + .../ios/interactive_media_ads.podspec | 29 + .../lib/interactive_media_ads.dart | 10 + .../lib/src/ad_display_container.dart | 102 +++ .../lib/src/ads_loader.dart | 162 +++++ .../lib/src/ads_manager_delegate.dart | 88 +++ .../lib/src/platform_interface/ad_error.dart | 156 +++++ .../lib/src/platform_interface/ad_event.dart | 53 ++ .../src/platform_interface/ads_request.dart | 12 + .../interactive_media_ads_platform.dart | 32 + .../platform_ad_display_container.dart | 95 +++ .../platform_ads_loader.dart | 119 ++++ .../platform_ads_manager.dart | 34 + .../platform_ads_manager_delegate.dart | 88 +++ .../platform_interface.dart | 12 + packages/interactive_media_ads/pubspec.yaml | 31 + .../test/ad_display_container_test.dart | 59 ++ .../test/ads_loader_test.dart | 51 ++ .../test/ads_manager_delegate_test.dart | 38 ++ .../test/ads_manager_test.dart | 93 +++ .../test/test_stubs.dart | 127 ++++ 98 files changed, 3234 insertions(+) create mode 100644 packages/interactive_media_ads/AUTHORS create mode 100644 packages/interactive_media_ads/CHANGELOG.md create mode 100644 packages/interactive_media_ads/LICENSE create mode 100644 packages/interactive_media_ads/README.md create mode 100644 packages/interactive_media_ads/android/build.gradle create mode 100644 packages/interactive_media_ads/android/settings.gradle create mode 100644 packages/interactive_media_ads/android/src/main/AndroidManifest.xml create mode 100644 packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsPlugin.kt create mode 100644 packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsPluginTest.kt create mode 100644 packages/interactive_media_ads/example/README.md create mode 100644 packages/interactive_media_ads/example/android/app/build.gradle create mode 100644 packages/interactive_media_ads/example/android/app/src/androidTest/kotlin/dev/flutter/packages/interactive_media_ads_example/MainActivityTest.kt create mode 100644 packages/interactive_media_ads/example/android/app/src/debug/AndroidManifest.xml create mode 100644 packages/interactive_media_ads/example/android/app/src/main/AndroidManifest.xml create mode 100644 packages/interactive_media_ads/example/android/app/src/main/kotlin/dev/flutter/packages/interactive_media_ads_example/DriverExtensionActivity.kt create mode 100644 packages/interactive_media_ads/example/android/app/src/main/kotlin/dev/flutter/packages/interactive_media_ads_example/MainActivity.kt create mode 100644 packages/interactive_media_ads/example/android/app/src/main/kotlin/io/flutter/plugins/DartIntegrationTest.kt create mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/drawable/launch_background.xml create mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/values-night/styles.xml create mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/values/styles.xml create mode 100644 packages/interactive_media_ads/example/android/build.gradle create mode 100644 packages/interactive_media_ads/example/android/gradle.properties create mode 100644 packages/interactive_media_ads/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/interactive_media_ads/example/android/settings.gradle create mode 100644 packages/interactive_media_ads/example/integration_test/interactive_media_ads_test.dart create mode 100644 packages/interactive_media_ads/example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 packages/interactive_media_ads/example/ios/Flutter/Debug.xcconfig create mode 100644 packages/interactive_media_ads/example/ios/Flutter/Release.xcconfig create mode 100644 packages/interactive_media_ads/example/ios/Podfile create mode 100644 packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 packages/interactive_media_ads/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/interactive_media_ads/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/interactive_media_ads/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/interactive_media_ads/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 packages/interactive_media_ads/example/ios/Runner/AppDelegate.swift create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 packages/interactive_media_ads/example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 packages/interactive_media_ads/example/ios/Runner/Info.plist create mode 100644 packages/interactive_media_ads/example/ios/Runner/Runner-Bridging-Header.h create mode 100644 packages/interactive_media_ads/example/ios/RunnerTests/RunnerTests.swift create mode 100644 packages/interactive_media_ads/example/lib/main.dart create mode 100644 packages/interactive_media_ads/example/pubspec.yaml create mode 100644 packages/interactive_media_ads/example/test_driver/integration_test.dart create mode 100644 packages/interactive_media_ads/ios/Assets/.gitkeep create mode 100644 packages/interactive_media_ads/ios/Classes/InteractiveMediaAdsPlugin.swift create mode 100644 packages/interactive_media_ads/ios/Resources/PrivacyInfo.xcprivacy create mode 100644 packages/interactive_media_ads/ios/interactive_media_ads.podspec create mode 100644 packages/interactive_media_ads/lib/interactive_media_ads.dart create mode 100644 packages/interactive_media_ads/lib/src/ad_display_container.dart create mode 100644 packages/interactive_media_ads/lib/src/ads_loader.dart create mode 100644 packages/interactive_media_ads/lib/src/ads_manager_delegate.dart create mode 100644 packages/interactive_media_ads/lib/src/platform_interface/ad_error.dart create mode 100644 packages/interactive_media_ads/lib/src/platform_interface/ad_event.dart create mode 100644 packages/interactive_media_ads/lib/src/platform_interface/ads_request.dart create mode 100644 packages/interactive_media_ads/lib/src/platform_interface/interactive_media_ads_platform.dart create mode 100644 packages/interactive_media_ads/lib/src/platform_interface/platform_ad_display_container.dart create mode 100644 packages/interactive_media_ads/lib/src/platform_interface/platform_ads_loader.dart create mode 100644 packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager.dart create mode 100644 packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager_delegate.dart create mode 100644 packages/interactive_media_ads/lib/src/platform_interface/platform_interface.dart create mode 100644 packages/interactive_media_ads/pubspec.yaml create mode 100644 packages/interactive_media_ads/test/ad_display_container_test.dart create mode 100644 packages/interactive_media_ads/test/ads_loader_test.dart create mode 100644 packages/interactive_media_ads/test/ads_manager_delegate_test.dart create mode 100644 packages/interactive_media_ads/test/ads_manager_test.dart create mode 100644 packages/interactive_media_ads/test/test_stubs.dart diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d9157022847f..d1f55572e699 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -395,6 +395,34 @@ updates: - dependency-name: "*" update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - package-ecosystem: "gradle" + directory: "/packages/interactive_media_ads/android" + commit-message: + prefix: "[interactive_media_ads]" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + ignore: + - dependency-name: "com.android.tools.build:gradle" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "junit:junit" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.mockito:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "androidx.test:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + + - package-ecosystem: "gradle" + directory: "/packages/interactive_media_ads/example/android/app" + commit-message: + prefix: "[interactive_media_ads]" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - package-ecosystem: "gradle" directory: "/packages/image_picker/image_picker/example/android/app" commit-message: diff --git a/CODEOWNERS b/CODEOWNERS index 4d7c8b95b3f7..77f7adb749f2 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -23,6 +23,7 @@ packages/google_identity_services_web/** @ditman packages/google_maps_flutter/** @stuartmorgan packages/google_sign_in/** @stuartmorgan packages/image_picker/** @tarrinneal +packages/interactive_media_ads/** @bparrishMines packages/in_app_purchase/** @bparrishMines packages/local_auth/** @stuartmorgan packages/metrics_center/** @keyonghan diff --git a/README.md b/README.md index ac0078ff5f72..1996143171fb 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ These are the packages hosted in this repository: | [google\_maps\_flutter](./packages/google_maps_flutter/) | [![pub package](https://img.shields.io/pub/v/google_maps_flutter.svg)](https://pub.dev/packages/google_maps_flutter) | [![pub points](https://img.shields.io/pub/points/google_maps_flutter)](https://pub.dev/packages/google_maps_flutter/score) | [![popularity](https://img.shields.io/pub/popularity/google_maps_flutter)](https://pub.dev/packages/google_maps_flutter/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20maps?label=)](https://github.com/flutter/flutter/labels/p%3A%20maps) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20google_maps_flutter?label=)](https://github.com/flutter/packages/labels/p%3A%20google_maps_flutter) | | [google\_sign\_in](./packages/google_sign_in/) | [![pub package](https://img.shields.io/pub/v/google_sign_in.svg)](https://pub.dev/packages/google_sign_in) | [![pub points](https://img.shields.io/pub/points/google_sign_in)](https://pub.dev/packages/google_sign_in/score) | [![popularity](https://img.shields.io/pub/popularity/google_sign_in)](https://pub.dev/packages/google_sign_in/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20google_sign_in?label=)](https://github.com/flutter/flutter/labels/p%3A%20google_sign_in) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20google_sign_in?label=)](https://github.com/flutter/packages/labels/p%3A%20google_sign_in) | | [image\_picker](./packages/image_picker/) | [![pub package](https://img.shields.io/pub/v/image_picker.svg)](https://pub.dev/packages/image_picker) | [![pub points](https://img.shields.io/pub/points/image_picker)](https://pub.dev/packages/image_picker/score) | [![popularity](https://img.shields.io/pub/popularity/image_picker)](https://pub.dev/packages/image_picker/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20image_picker?label=)](https://github.com/flutter/flutter/labels/p%3A%20image_picker) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20image_picker?label=)](https://github.com/flutter/packages/labels/p%3A%20image_picker) | +| [interactive\_media\_ads](./packages/interactive_media_ads/) | [![pub package](https://img.shields.io/pub/v/interactive_media_ads.svg)](https://pub.dev/packages/interactive_media_ads) | [![pub points](https://img.shields.io/pub/points/interactive_media_ads)](https://pub.dev/packages/interactive_media_ads/score) | [![popularity](https://img.shields.io/pub/popularity/interactive_media_ads)](https://pub.dev/packages/interactive_media_ads/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20interactive_media_ads?label=)](https://github.com/flutter/flutter/labels/p%3A%20interactive_media_ads) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20interactive_media_ads?label=)](https://github.com/flutter/packages/labels/p%3A%20interactive_media_ads) | | [in\_app\_purchase](./packages/in_app_purchase/) | [![pub package](https://img.shields.io/pub/v/in_app_purchase.svg)](https://pub.dev/packages/in_app_purchase) | [![pub points](https://img.shields.io/pub/points/in_app_purchase)](https://pub.dev/packages/in_app_purchase/score) | [![popularity](https://img.shields.io/pub/popularity/in_app_purchase)](https://pub.dev/packages/in_app_purchase/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20in_app_purchase?label=)](https://github.com/flutter/flutter/labels/p%3A%20in_app_purchase) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20in_app_purchase?label=)](https://github.com/flutter/packages/labels/p%3A%20in_app_purchase) | | [ios\_platform\_images](./packages/ios_platform_images/) | [![pub package](https://img.shields.io/pub/v/ios_platform_images.svg)](https://pub.dev/packages/ios_platform_images) | [![pub points](https://img.shields.io/pub/points/ios_platform_images)](https://pub.dev/packages/ios_platform_images/score) | [![popularity](https://img.shields.io/pub/popularity/ios_platform_images)](https://pub.dev/packages/ios_platform_images/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20ios_platform_images?label=)](https://github.com/flutter/flutter/labels/p%3A%20ios_platform_images) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20ios_platform_images?label=)](https://github.com/flutter/packages/labels/p%3A%20ios_platform_images) | | [local\_auth](./packages/local_auth/) | [![pub package](https://img.shields.io/pub/v/local_auth.svg)](https://pub.dev/packages/local_auth) | [![pub points](https://img.shields.io/pub/points/local_auth)](https://pub.dev/packages/local_auth/score) | [![popularity](https://img.shields.io/pub/popularity/local_auth)](https://pub.dev/packages/local_auth/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20local_auth?label=)](https://github.com/flutter/flutter/labels/p%3A%20local_auth) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20local_auth?label=)](https://github.com/flutter/packages/labels/p%3A%20local_auth) | diff --git a/packages/interactive_media_ads/AUTHORS b/packages/interactive_media_ads/AUTHORS new file mode 100644 index 000000000000..557dff97933b --- /dev/null +++ b/packages/interactive_media_ads/AUTHORS @@ -0,0 +1,6 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. diff --git a/packages/interactive_media_ads/CHANGELOG.md b/packages/interactive_media_ads/CHANGELOG.md new file mode 100644 index 000000000000..477158a8871e --- /dev/null +++ b/packages/interactive_media_ads/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* Adds platform interface for Android and iOS. diff --git a/packages/interactive_media_ads/LICENSE b/packages/interactive_media_ads/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/interactive_media_ads/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/interactive_media_ads/README.md b/packages/interactive_media_ads/README.md new file mode 100644 index 000000000000..d1d17151f02b --- /dev/null +++ b/packages/interactive_media_ads/README.md @@ -0,0 +1,15 @@ +# interactive\_media\_ads + +Flutter plugin for the [Interactive Media Ads SDKs][1]. + +[![pub package](https://img.shields.io/pub/v/webview_flutter.svg)](https://pub.dev/packages/interactive_media_ads) + +A Flutter plugin for using the Interactive Media Ads SDKs on Android and iOS. + +| | Android | iOS | +|-------------|---------|-------| +| **Support** | SDK 19+ | 12.0+ | + +**This package is still in development.** + +[1]: https://developers.google.com/interactive-media-ads diff --git a/packages/interactive_media_ads/android/build.gradle b/packages/interactive_media_ads/android/build.gradle new file mode 100644 index 000000000000..3490e07ba38e --- /dev/null +++ b/packages/interactive_media_ads/android/build.gradle @@ -0,0 +1,78 @@ +group 'dev.flutter.packages.interactive_media_ads' +version '1.0-SNAPSHOT' + +buildscript { + ext.kotlin_version = '1.7.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:8.0.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + if (project.android.hasProperty("namespace")) { + namespace 'dev.flutter.packages.interactive_media_ads' + } + + compileSdk 34 + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + test.java.srcDirs += 'src/test/kotlin' + } + + defaultConfig { + minSdk 19 + } + + dependencies { + implementation 'androidx.annotation:annotation:1.5.0' + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.jetbrains.kotlin:kotlin-test' + testImplementation 'org.mockito:mockito-inline:5.1.0' + testImplementation 'androidx.test:core:1.3.0' + } + + lintOptions { + checkAllWarnings true + warningsAsErrors true + disable 'AndroidGradlePluginVersion', 'InvalidPackage', 'GradleDependency' + } + + testOptions { + unitTests.includeAndroidResources = true + unitTests.returnDefaultValues = true + unitTests.all { + useJUnitPlatform() + testLogging { + events "passed", "skipped", "failed", "standardOut", "standardError" + outputs.upToDateWhen {false} + showStandardStreams = true + } + } + } +} diff --git a/packages/interactive_media_ads/android/settings.gradle b/packages/interactive_media_ads/android/settings.gradle new file mode 100644 index 000000000000..388e84d5a359 --- /dev/null +++ b/packages/interactive_media_ads/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'interactive_media_ads' diff --git a/packages/interactive_media_ads/android/src/main/AndroidManifest.xml b/packages/interactive_media_ads/android/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..cde3df78e2ba --- /dev/null +++ b/packages/interactive_media_ads/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsPlugin.kt b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsPlugin.kt new file mode 100644 index 000000000000..50595236588a --- /dev/null +++ b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsPlugin.kt @@ -0,0 +1,37 @@ +// 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. + +package dev.flutter.packages.interactive_media_ads + +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import io.flutter.plugin.common.MethodChannel.MethodCallHandler +import io.flutter.plugin.common.MethodChannel.Result + +/** InteractiveMediaAdsPlugin */ +class InteractiveMediaAdsPlugin : FlutterPlugin, MethodCallHandler { + /// The MethodChannel that will the communication between Flutter and native Android + /// + /// This local reference serves to register the plugin with the Flutter Engine and unregister it + /// when the Flutter Engine is detached from the Activity + private lateinit var channel: MethodChannel + + override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + channel = MethodChannel(flutterPluginBinding.binaryMessenger, "interactive_media_ads") + channel.setMethodCallHandler(this) + } + + override fun onMethodCall(call: MethodCall, result: Result) { + if (call.method == "getPlatformVersion") { + result.success("Android ${android.os.Build.VERSION.RELEASE}") + } else { + result.notImplemented() + } + } + + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + channel.setMethodCallHandler(null) + } +} diff --git a/packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsPluginTest.kt b/packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsPluginTest.kt new file mode 100644 index 000000000000..3adc0d0a56b4 --- /dev/null +++ b/packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsPluginTest.kt @@ -0,0 +1,31 @@ +// 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. + +package dev.flutter.packages.interactive_media_ads + +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import kotlin.test.Test +import org.mockito.Mockito + +/* + * This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation. + * + * Once you have built the plugin's example app, you can run these tests from the command + * line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or + * you can run them directly from IDEs that support JUnit such as Android Studio. + */ + +internal class InteractiveMediaAdsPluginTest { + @Test + fun onMethodCall_getPlatformVersion_returnsExpectedValue() { + val plugin = InteractiveMediaAdsPlugin() + + val call = MethodCall("getPlatformVersion", null) + val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java) + plugin.onMethodCall(call, mockResult) + + Mockito.verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE) + } +} diff --git a/packages/interactive_media_ads/example/README.md b/packages/interactive_media_ads/example/README.md new file mode 100644 index 000000000000..96b8bb17dbff --- /dev/null +++ b/packages/interactive_media_ads/example/README.md @@ -0,0 +1,9 @@ +# Platform Implementation Test App + +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/interactive_media_ads/example/android/app/build.gradle b/packages/interactive_media_ads/example/android/app/build.gradle new file mode 100644 index 000000000000..326ce9b6246f --- /dev/null +++ b/packages/interactive_media_ads/example/android/app/build.gradle @@ -0,0 +1,68 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} + +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +android { + namespace "dev.flutter.packages.interactive_media_ads_example" + compileSdk flutter.compileSdkVersion + ndkVersion flutter.ndkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + applicationId "dev.flutter.packages.interactive_media_ads_example" + minSdk flutter.minSdkVersion + targetSdk flutter.targetSdkVersion + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + api 'androidx.test:core:1.4.0' +} diff --git a/packages/interactive_media_ads/example/android/app/src/androidTest/kotlin/dev/flutter/packages/interactive_media_ads_example/MainActivityTest.kt b/packages/interactive_media_ads/example/android/app/src/androidTest/kotlin/dev/flutter/packages/interactive_media_ads_example/MainActivityTest.kt new file mode 100644 index 000000000000..7f51c3fccf13 --- /dev/null +++ b/packages/interactive_media_ads/example/android/app/src/androidTest/kotlin/dev/flutter/packages/interactive_media_ads_example/MainActivityTest.kt @@ -0,0 +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. + +package dev.flutter.packages.interactive_media_ads_example + +import androidx.test.rule.ActivityTestRule +import dev.flutter.plugins.integration_test.FlutterTestRunner +import io.flutter.plugins.DartIntegrationTest +import org.junit.Rule +import org.junit.runner.RunWith + +@DartIntegrationTest +@RunWith(FlutterTestRunner::class) +class MainActivityTest { + @JvmField @Rule var rule = ActivityTestRule(MainActivity::class.java) +} diff --git a/packages/interactive_media_ads/example/android/app/src/debug/AndroidManifest.xml b/packages/interactive_media_ads/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000000..4665eaa3ae96 --- /dev/null +++ b/packages/interactive_media_ads/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/packages/interactive_media_ads/example/android/app/src/main/AndroidManifest.xml b/packages/interactive_media_ads/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..8733f2b862ab --- /dev/null +++ b/packages/interactive_media_ads/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/interactive_media_ads/example/android/app/src/main/kotlin/dev/flutter/packages/interactive_media_ads_example/DriverExtensionActivity.kt b/packages/interactive_media_ads/example/android/app/src/main/kotlin/dev/flutter/packages/interactive_media_ads_example/DriverExtensionActivity.kt new file mode 100644 index 000000000000..d43924d0054f --- /dev/null +++ b/packages/interactive_media_ads/example/android/app/src/main/kotlin/dev/flutter/packages/interactive_media_ads_example/DriverExtensionActivity.kt @@ -0,0 +1,10 @@ +// 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. + +package dev.flutter.packages.interactive_media_ads_example + +import io.flutter.embedding.android.FlutterActivity + +/** Test Activity that sets the name of the Dart method entrypoint in the manifest. */ +class DriverExtensionActivity : FlutterActivity() diff --git a/packages/interactive_media_ads/example/android/app/src/main/kotlin/dev/flutter/packages/interactive_media_ads_example/MainActivity.kt b/packages/interactive_media_ads/example/android/app/src/main/kotlin/dev/flutter/packages/interactive_media_ads_example/MainActivity.kt new file mode 100644 index 000000000000..3392748b8720 --- /dev/null +++ b/packages/interactive_media_ads/example/android/app/src/main/kotlin/dev/flutter/packages/interactive_media_ads_example/MainActivity.kt @@ -0,0 +1,9 @@ +// 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. + +package dev.flutter.packages.interactive_media_ads_example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity : FlutterActivity() diff --git a/packages/interactive_media_ads/example/android/app/src/main/kotlin/io/flutter/plugins/DartIntegrationTest.kt b/packages/interactive_media_ads/example/android/app/src/main/kotlin/io/flutter/plugins/DartIntegrationTest.kt new file mode 100644 index 000000000000..099fb761cc9d --- /dev/null +++ b/packages/interactive_media_ads/example/android/app/src/main/kotlin/io/flutter/plugins/DartIntegrationTest.kt @@ -0,0 +1,16 @@ +// 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. + +package io.flutter.plugins + +/* + * Annotation to aid repository tooling in determining if a test is + * a native java unit test or a java class with a dart integration. + * + * See: https://github.com/flutter/flutter/wiki/Plugin-Tests#enabling-android-ui-tests + * for more infomation. + */ +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) +annotation class DartIntegrationTest diff --git a/packages/interactive_media_ads/example/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/interactive_media_ads/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 000000000000..f74085f3f6a2 --- /dev/null +++ b/packages/interactive_media_ads/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/packages/interactive_media_ads/example/android/app/src/main/res/drawable/launch_background.xml b/packages/interactive_media_ads/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 000000000000..304732f88420 --- /dev/null +++ b/packages/interactive_media_ads/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/packages/interactive_media_ads/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/interactive_media_ads/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/interactive_media_ads/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/interactive_media_ads/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/interactive_media_ads/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/android/app/src/main/res/values-night/styles.xml b/packages/interactive_media_ads/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 000000000000..06952be745f9 --- /dev/null +++ b/packages/interactive_media_ads/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/packages/interactive_media_ads/example/android/app/src/main/res/values/styles.xml b/packages/interactive_media_ads/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000000..cb1ef88056ed --- /dev/null +++ b/packages/interactive_media_ads/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/packages/interactive_media_ads/example/android/build.gradle b/packages/interactive_media_ads/example/android/build.gradle new file mode 100644 index 000000000000..29a592fd9d6d --- /dev/null +++ b/packages/interactive_media_ads/example/android/build.gradle @@ -0,0 +1,39 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} + +allprojects { + repositories { + // See https://github.com/flutter/flutter/wiki/Plugins-and-Packages-repository-structure#gradle-structure for more info. + def artifactRepoKey = 'ARTIFACT_HUB_REPOSITORY' + if (System.getenv().containsKey(artifactRepoKey)) { + println "Using artifact hub" + maven { url System.getenv(artifactRepoKey) } + } + google() + mavenCentral() + } +} + +gradle.projectsEvaluated { + project(":interactive_media_ads") { + tasks.withType(JavaCompile) { + options.compilerArgs << "-Xlint:all" << "-Werror" + } + } +} diff --git a/packages/interactive_media_ads/example/android/gradle.properties b/packages/interactive_media_ads/example/android/gradle.properties new file mode 100644 index 000000000000..3b5b324f6e3f --- /dev/null +++ b/packages/interactive_media_ads/example/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true +android.enableJetifier=true diff --git a/packages/interactive_media_ads/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/interactive_media_ads/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..e1ca574ef017 --- /dev/null +++ b/packages/interactive_media_ads/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip diff --git a/packages/interactive_media_ads/example/android/settings.gradle b/packages/interactive_media_ads/example/android/settings.gradle new file mode 100644 index 000000000000..12cfac56b468 --- /dev/null +++ b/packages/interactive_media_ads/example/android/settings.gradle @@ -0,0 +1,39 @@ +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +// See https://github.com/flutter/flutter/wiki/Plugins-and-Packages-repository-structure#gradle-structure for more info. +buildscript { + repositories { + maven { + url "https://plugins.gradle.org/m2/" + } + } + dependencies { + classpath "gradle.plugin.com.google.cloud.artifactregistry:artifactregistry-gradle-plugin:2.2.1" + } +} + +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.3.0" apply false + id "org.jetbrains.kotlin.android" version "1.7.10" apply false +} + +include ":app" + +apply plugin: "com.google.cloud.artifactregistry.gradle-plugin" diff --git a/packages/interactive_media_ads/example/integration_test/interactive_media_ads_test.dart b/packages/interactive_media_ads/example/integration_test/interactive_media_ads_test.dart new file mode 100644 index 000000000000..d79165b3788b --- /dev/null +++ b/packages/interactive_media_ads/example/integration_test/interactive_media_ads_test.dart @@ -0,0 +1,23 @@ +// 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_driver/driver_extension.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:interactive_media_ads_example/main.dart' as app; + +/// Entry point for integration tests that require espresso. +@pragma('vm:entry-point') +void integrationTestMain() { + enableFlutterDriverExtension(); + app.main(); +} + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + // Since this test is lacking integration tests, this test ensures the example + // app can be launched on an emulator/device. + testWidgets('Launch Test', (WidgetTester tester) async {}); +} diff --git a/packages/interactive_media_ads/example/ios/Flutter/AppFrameworkInfo.plist b/packages/interactive_media_ads/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 000000000000..7c5696400627 --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 12.0 + + diff --git a/packages/interactive_media_ads/example/ios/Flutter/Debug.xcconfig b/packages/interactive_media_ads/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000000..ec97fc6f3021 --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/interactive_media_ads/example/ios/Flutter/Release.xcconfig b/packages/interactive_media_ads/example/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000000..c4855bfe2000 --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/interactive_media_ads/example/ios/Podfile b/packages/interactive_media_ads/example/ios/Podfile new file mode 100644 index 000000000000..d97f17e223fb --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Podfile @@ -0,0 +1,44 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '12.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.pbxproj b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..966e3a40fea7 --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,619 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = S8QB4VV633; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.packages.interactiveMediaAdsExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.packages.interactiveMediaAdsExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.packages.interactiveMediaAdsExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.packages.interactiveMediaAdsExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = S8QB4VV633; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.packages.interactiveMediaAdsExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = S8QB4VV633; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.packages.interactiveMediaAdsExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..919434a6254f --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000000..f9b0d7c5ea15 --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/interactive_media_ads/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000000..8e3ca5dfe193 --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/interactive_media_ads/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/interactive_media_ads/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..1d526a16ed0f --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/interactive_media_ads/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/interactive_media_ads/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/interactive_media_ads/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/interactive_media_ads/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000000..f9b0d7c5ea15 --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/interactive_media_ads/example/ios/Runner/AppDelegate.swift b/packages/interactive_media_ads/example/ios/Runner/AppDelegate.swift new file mode 100644 index 000000000000..d83c0ff0beea --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner/AppDelegate.swift @@ -0,0 +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. + +import Flutter +import UIKit + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000000..d36b1fab2d9d --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..dc9ada4725e9b0ddb1deab583e5b5102493aa332 GIT binary patch literal 10932 zcmeHN2~<R zh`|8`A_PQ1nSu(UMFx?8j8PC!!VDphaL#`F42fd#7Vlc`zIE4n%Y~eiz4y1j|NDpi z?<@|pSJ-HM`qifhf@m%MamgwK83`XpBA<+azdF#2QsT{X@z0A9Bq>~TVErigKH1~P zRX-!h-f0NJ4Mh++{D}J+K>~~rq}d%o%+4dogzXp7RxX4C>Km5XEI|PAFDmo;DFm6G zzjVoB`@qW98Yl0Kvc-9w09^PrsobmG*Eju^=3f?0o-t$U)TL1B3;sZ^!++3&bGZ!o-*6w?;oOhf z=A+Qb$scV5!RbG+&2S}BQ6YH!FKb0``VVX~T$dzzeSZ$&9=X$3)_7Z{SspSYJ!lGE z7yig_41zpQ)%5dr4ff0rh$@ky3-JLRk&DK)NEIHecf9c*?Z1bUB4%pZjQ7hD!A0r-@NF(^WKdr(LXj|=UE7?gBYGgGQV zidf2`ZT@pzXf7}!NH4q(0IMcxsUGDih(0{kRSez&z?CFA0RVXsVFw3^u=^KMtt95q z43q$b*6#uQDLoiCAF_{RFc{!H^moH_cmll#Fc^KXi{9GDl{>%+3qyfOE5;Zq|6#Hb zp^#1G+z^AXfRKaa9HK;%b3Ux~U@q?xg<2DXP%6k!3E)PA<#4$ui8eDy5|9hA5&{?v z(-;*1%(1~-NTQ`Is1_MGdQ{+i*ccd96ab$R$T3=% zw_KuNF@vI!A>>Y_2pl9L{9h1-C6H8<)J4gKI6{WzGBi<@u3P6hNsXG=bRq5c+z;Gc3VUCe;LIIFDmQAGy+=mRyF++u=drBWV8-^>0yE9N&*05XHZpPlE zxu@?8(ZNy7rm?|<+UNe0Vs6&o?l`Pt>P&WaL~M&#Eh%`rg@Mbb)J&@DA-wheQ>hRV z<(XhigZAT z>=M;URcdCaiO3d^?H<^EiEMDV+7HsTiOhoaMX%P65E<(5xMPJKxf!0u>U~uVqnPN7T!X!o@_gs3Ct1 zlZ_$5QXP4{Aj645wG_SNT&6m|O6~Tsl$q?nK*)(`{J4b=(yb^nOATtF1_aS978$x3 zx>Q@s4i3~IT*+l{@dx~Hst21fR*+5}S1@cf>&8*uLw-0^zK(+OpW?cS-YG1QBZ5q! zgTAgivzoF#`cSz&HL>Ti!!v#?36I1*l^mkrx7Y|K6L#n!-~5=d3;K<;Zqi|gpNUn_ z_^GaQDEQ*jfzh;`j&KXb66fWEk1K7vxQIMQ_#Wu_%3 z4Oeb7FJ`8I>Px;^S?)}2+4D_83gHEq>8qSQY0PVP?o)zAv3K~;R$fnwTmI-=ZLK`= zTm+0h*e+Yfr(IlH3i7gUclNH^!MU>id$Jw>O?2i0Cila#v|twub21@e{S2v}8Z13( zNDrTXZVgris|qYm<0NU(tAPouG!QF4ZNpZPkX~{tVf8xY690JqY1NVdiTtW+NqyRP zZ&;T0ikb8V{wxmFhlLTQ&?OP7 z;(z*<+?J2~z*6asSe7h`$8~Se(@t(#%?BGLVs$p``;CyvcT?7Y!{tIPva$LxCQ&4W z6v#F*);|RXvI%qnoOY&i4S*EL&h%hP3O zLsrFZhv&Hu5tF$Lx!8(hs&?!Kx5&L(fdu}UI5d*wn~A`nPUhG&Rv z2#ixiJdhSF-K2tpVL=)5UkXRuPAFrEW}7mW=uAmtVQ&pGE-&az6@#-(Te^n*lrH^m@X-ftVcwO_#7{WI)5v(?>uC9GG{lcGXYJ~Q8q zbMFl7;t+kV;|;KkBW2!P_o%Czhw&Q(nXlxK9ak&6r5t_KH8#1Mr-*0}2h8R9XNkr zto5-b7P_auqTJb(TJlmJ9xreA=6d=d)CVbYP-r4$hDn5|TIhB>SReMfh&OVLkMk-T zYf%$taLF0OqYF?V{+6Xkn>iX@TuqQ?&cN6UjC9YF&%q{Ut3zv{U2)~$>-3;Dp)*(? zg*$mu8^i=-e#acaj*T$pNowo{xiGEk$%DusaQiS!KjJH96XZ-hXv+jk%ard#fu=@Q z$AM)YWvE^{%tDfK%nD49=PI|wYu}lYVbB#a7wtN^Nml@CE@{Gv7+jo{_V?I*jkdLD zJE|jfdrmVbkfS>rN*+`#l%ZUi5_bMS<>=MBDNlpiSb_tAF|Zy`K7kcp@|d?yaTmB^ zo?(vg;B$vxS|SszusORgDg-*Uitzdi{dUV+glA~R8V(?`3GZIl^egW{a919!j#>f` znL1o_^-b`}xnU0+~KIFLQ)$Q6#ym%)(GYC`^XM*{g zv3AM5$+TtDRs%`2TyR^$(hqE7Y1b&`Jd6dS6B#hDVbJlUXcG3y*439D8MrK!2D~6gn>UD4Imctb z+IvAt0iaW73Iq$K?4}H`7wq6YkTMm`tcktXgK0lKPmh=>h+l}Y+pDtvHnG>uqBA)l zAH6BV4F}v$(o$8Gfo*PB>IuaY1*^*`OTx4|hM8jZ?B6HY;F6p4{`OcZZ(us-RVwDx zUzJrCQlp@mz1ZFiSZ*$yX3c_#h9J;yBE$2g%xjmGF4ca z&yL`nGVs!Zxsh^j6i%$a*I3ZD2SoNT`{D%mU=LKaEwbN(_J5%i-6Va?@*>=3(dQy` zOv%$_9lcy9+(t>qohkuU4r_P=R^6ME+wFu&LA9tw9RA?azGhjrVJKy&8=*qZT5Dr8g--d+S8zAyJ$1HlW3Olryt`yE zFIph~Z6oF&o64rw{>lgZISC6p^CBer9C5G6yq%?8tC+)7*d+ib^?fU!JRFxynRLEZ zj;?PwtS}Ao#9whV@KEmwQgM0TVP{hs>dg(1*DiMUOKHdQGIqa0`yZnHk9mtbPfoLx zo;^V6pKUJ!5#n`w2D&381#5#_t}AlTGEgDz$^;u;-vxDN?^#5!zN9ngytY@oTv!nc zp1Xn8uR$1Z;7vY`-<*?DfPHB;x|GUi_fI9@I9SVRv1)qETbNU_8{5U|(>Du84qP#7 z*l9Y$SgA&wGbj>R1YeT9vYjZuC@|{rajTL0f%N@>3$DFU=`lSPl=Iv;EjuGjBa$Gw zHD-;%YOE@<-!7-Mn`0WuO3oWuL6tB2cpPw~Nvuj|KM@))ixuDK`9;jGMe2d)7gHin zS<>k@!x;!TJEc#HdL#RF(`|4W+H88d4V%zlh(7#{q2d0OQX9*FW^`^_<3r$kabWAB z$9BONo5}*(%kx zOXi-yM_cmB3>inPpI~)duvZykJ@^^aWzQ=eQ&STUa}2uT@lV&WoRzkUoE`rR0)`=l zFT%f|LA9fCw>`enm$p7W^E@U7RNBtsh{_-7vVz3DtB*y#*~(L9+x9*wn8VjWw|Q~q zKFsj1Yl>;}%MG3=PY`$g$_mnyhuV&~O~u~)968$0b2!Jkd;2MtAP#ZDYw9hmK_+M$ zb3pxyYC&|CuAbtiG8HZjj?MZJBFbt`ryf+c1dXFuC z0*ZQhBzNBd*}s6K_G}(|Z_9NDV162#y%WSNe|FTDDhx)K!c(mMJh@h87@8(^YdK$&d*^WQe8Z53 z(|@MRJ$Lk-&ii74MPIs80WsOFZ(NX23oR-?As+*aq6b?~62@fSVmM-_*cb1RzZ)`5$agEiL`-E9s7{GM2?(KNPgK1(+c*|-FKoy}X(D_b#etO|YR z(BGZ)0Ntfv-7R4GHoXp?l5g#*={S1{u-QzxCGng*oWr~@X-5f~RA14b8~B+pLKvr4 zfgL|7I>jlak9>D4=(i(cqYf7#318!OSR=^`xxvI!bBlS??`xxWeg?+|>MxaIdH1U~#1tHu zB{QMR?EGRmQ_l4p6YXJ{o(hh-7Tdm>TAX380TZZZyVkqHNzjUn*_|cb?T? zt;d2s-?B#Mc>T-gvBmQZx(y_cfkXZO~{N zT6rP7SD6g~n9QJ)8F*8uHxTLCAZ{l1Y&?6v)BOJZ)=R-pY=Y=&1}jE7fQ>USS}xP#exo57uND0i*rEk@$;nLvRB@u~s^dwRf?G?_enN@$t* zbL%JO=rV(3Ju8#GqUpeE3l_Wu1lN9Y{D4uaUe`g>zlj$1ER$6S6@{m1!~V|bYkhZA z%CvrDRTkHuajMU8;&RZ&itnC~iYLW4DVkP<$}>#&(`UO>!n)Po;Mt(SY8Yb`AS9lt znbX^i?Oe9r_o=?})IHKHoQGKXsps_SE{hwrg?6dMI|^+$CeC&z@*LuF+P`7LfZ*yr+KN8B4{Nzv<`A(wyR@!|gw{zB6Ha ziwPAYh)oJ(nlqSknu(8g9N&1hu0$vFK$W#mp%>X~AU1ay+EKWcFdif{% z#4!4aoVVJ;ULmkQf!ke2}3hqxLK>eq|-d7Ly7-J9zMpT`?dxo6HdfJA|t)?qPEVBDv z{y_b?4^|YA4%WW0VZd8C(ZgQzRI5(I^)=Ub`Y#MHc@nv0w-DaJAqsbEHDWG8Ia6ju zo-iyr*sq((gEwCC&^TYBWt4_@|81?=B-?#P6NMff(*^re zYqvDuO`K@`mjm_Jd;mW_tP`3$cS?R$jR1ZN09$YO%_iBqh5ftzSpMQQtxKFU=FYmP zeY^jph+g<4>YO;U^O>-NFLn~-RqlHvnZl2yd2A{Yc1G@Ga$d+Q&(f^tnPf+Z7serIU};17+2DU_f4Z z@GaPFut27d?!YiD+QP@)T=77cR9~MK@bd~pY%X(h%L={{OIb8IQmf-!xmZkm8A0Ga zQSWONI17_ru5wpHg3jI@i9D+_Y|pCqVuHJNdHUauTD=R$JcD2K_liQisqG$(sm=k9;L* z!L?*4B~ql7uioSX$zWJ?;q-SWXRFhz2Jt4%fOHA=Bwf|RzhwqdXGr78y$J)LR7&3T zE1WWz*>GPWKZ0%|@%6=fyx)5rzUpI;bCj>3RKzNG_1w$fIFCZ&UR0(7S?g}`&Pg$M zf`SLsz8wK82Vyj7;RyKmY{a8G{2BHG%w!^T|Njr!h9TO2LaP^_f22Q1=l$QiU84ao zHe_#{S6;qrC6w~7{y(hs-?-j?lbOfgH^E=XcSgnwW*eEz{_Z<_xN#0001NP)t-s|Ns9~ z#rXRE|M&d=0au&!`~QyF`q}dRnBDt}*!qXo`c{v z{Djr|@Adh0(D_%#_&mM$D6{kE_x{oE{l@J5@%H*?%=t~i_`ufYOPkAEn!pfkr2$fs z652Tz0001XNklqeeKN4RM4i{jKqmiC$?+xN>3Apn^ z0QfuZLym_5b<*QdmkHjHlj811{If)dl(Z2K0A+ekGtrFJb?g|wt#k#pV-#A~bK=OT ts8>{%cPtyC${m|1#B1A6#u!Q;umknL1chzTM$P~L002ovPDHLkV1lTfnu!1a literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..797d452e458972bab9d994556c8305db4c827017 GIT binary patch literal 406 zcmV;H0crk;P))>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed2d933e1120817fe9182483a228007b18ab6ae GIT binary patch literal 450 zcmV;z0X_bSP)iGWQ_5NJQ_~rNh*z)}eT%KUb z`7gNk0#AwF^#0T0?hIa^`~Ck;!}#m+_uT050aTR(J!bU#|IzRL%^UsMS#KsYnTF*!YeDOytlP4VhV?b} z%rz_<=#CPc)tU1MZTq~*2=8~iZ!lSa<{9b@2Jl;?IEV8)=fG217*|@)CCYgFze-x? zIFODUIA>nWKpE+bn~n7;-89sa>#DR>TSlqWk*!2hSN6D~Qb#VqbP~4Fk&m`@1$JGr zXPIdeRE&b2Thd#{MtDK$px*d3-Wx``>!oimf%|A-&-q*6KAH)e$3|6JV%HX{Hig)k suLT-RhftRq8b9;(V=235Wa|I=027H2wCDra;{X5v07*qoM6N<$f;9x^2LJ#7 literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..4cd7b0099ca80c806f8fe495613e8d6c69460d76 GIT binary patch literal 282 zcmV+#0p(^bcu7P-R4C8Q z&e;xxFbF_Vrezo%_kH*OKhshZ6BFpG-Y1e10`QXJKbND7AMQ&cMj60B5TNObaZxYybcN07*qoM6N<$g3m;S%K!iX literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fe730945a01f64a61e2235dbe3f45b08f7729182 GIT binary patch literal 462 zcmV;<0WtoGP)-}iV`2<;=$?g5M=KQbZ{F&YRNy7Nn@%_*5{gvDM0aKI4?ESmw z{NnZg)A0R`+4?NF_RZexyVB&^^ZvN!{I28tr{Vje;QNTz`dG&Jz0~Ek&f2;*Z7>B|cg}xYpxEFY+0YrKLF;^Q+-HreN0P{&i zK~zY`?b7ECf-n?@;d<&orQ*Q7KoR%4|C>{W^h6@&01>0SKS`dn{Q}GT%Qj_{PLZ_& zs`MFI#j-(>?bvdZ!8^xTwlY{qA)T4QLbY@j(!YJ7aXJervHy6HaG_2SB`6CC{He}f zHVw(fJWApwPq!6VY7r1w-Fs)@ox~N+q|w~e;JI~C4Vf^@d>Wvj=fl`^u9x9wd9 zR%3*Q+)t%S!MU_`id^@&Y{y7-r98lZX0?YrHlfmwb?#}^1b{8g&KzmkE(L>Z&)179 zp<)v6Y}pRl100G2FL_t(o!|l{-Q-VMg#&MKg7c{O0 z2wJImOS3Gy*Z2Qifdv~JYOp;v+U)a|nLoc7hNH;I$;lzDt$}rkaFw1mYK5_0Q(Sut zvbEloxON7$+HSOgC9Z8ltuC&0OSF!-mXv5caV>#bc3@hBPX@I$58-z}(ZZE!t-aOG zpjNkbau@>yEzH(5Yj4kZiMH32XI!4~gVXNnjAvRx;Sdg^`>2DpUEwoMhTs_st8pKG z(%SHyHdU&v%f36~uERh!bd`!T2dw;z6PrOTQ7Vt*#9F2uHlUVnb#ev_o^fh}Dzmq} zWtlk35}k=?xj28uO|5>>$yXadTUE@@IPpgH`gJ~Ro4>jd1IF|(+IX>8M4Ps{PNvmI zNj4D+XgN83gPt_Gm}`Ybv{;+&yu-C(Grdiahmo~BjG-l&mWM+{e5M1sm&=xduwgM9 z`8OEh`=F3r`^E{n_;%9weN{cf2%7=VzC@cYj+lg>+3|D|_1C@{hcU(DyQG_BvBWe? zvTv``=%b1zrol#=R`JB)>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..502f463a9bc882b461c96aadf492d1729e49e725 GIT binary patch literal 586 zcmV-Q0=4~#P)+}#`wDE{8-2Mebf5<{{PqV{TgVcv*r8?UZ3{-|G?_}T*&y;@cqf{ z{Q*~+qr%%p!1pS*_Uicl#q9lc(D`!D`LN62sNwq{oYw(Wmhk)k<@f$!$@ng~_5)Ru z0Z)trIA5^j{DIW^c+vT2%lW+2<(RtE2wR;4O@)Tm`Xr*?A(qYoM}7i5Yxw>D(&6ou zxz!_Xr~yNF+waPe00049Nkl*;a!v6h%{rlvIH#gW3s8p;bFr=l}mRqpW2h zw=OA%hdyL~z+UHOzl0eKhEr$YYOL-c-%Y<)=j?(bzDweB7{b+%_ypvm_cG{SvM=DK zhv{K@m>#Bw>2W$eUI#iU)Wdgs8Y3U+A$Gd&{+j)d)BmGKx+43U_!tik_YlN)>$7G! zhkE!s;%oku3;IwG3U^2kw?z+HM)jB{@zFhK8P#KMSytSthr+4!c(5c%+^UBn`0X*2 zy3(k600_CSZj?O$Qu%&$;|TGUJrptR(HzyIx>5E(2r{eA(<6t3e3I0B)7d6s7?Z5J zZ!rtKvA{MiEBm&KFtoifx>5P^Z=vl)95XJn()aS5%ad(s?4-=Tkis9IGu{`Fy8r+H07*qoM6N<$f20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0ec303439225b78712f49115768196d8d76f6790 GIT binary patch literal 862 zcmV-k1EKthP)20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..e9f5fea27c705180eb716271f41b582e76dcbd90 GIT binary patch literal 1674 zcmV;526g#~P){YQnis^a@{&-nmRmq)<&%Mztj67_#M}W?l>kYSliK<%xAp;0j{!}J0!o7b zE>q9${Lb$D&h7k=+4=!ek^n+`0zq>LL1O?lVyea53S5x`Nqqo2YyeuIrQrJj9XjOp z{;T5qbj3}&1vg1VK~#9!?b~^C5-}JC@Pyrv-6dSEqJqT}#j9#dJ@GzT@B8}x zU&J@bBI>f6w6en+CeI)3^kC*U?}X%OD8$Fd$H&LV$H&LV$H&LV#|K5~mLYf|VqzOc zkc7qL~0sOYuM{tG`rYEDV{DWY`Z8&)kW*hc2VkBuY+^Yx&92j&StN}Wp=LD zxoGxXw6f&8sB^u})h@b@z0RBeD`K7RMR9deyL(ZJu#39Z>rT)^>v}Khq8U-IbIvT> z?4pV9qGj=2)TNH3d)=De<+^w;>S7m_eFKTvzeaBeir45xY!^m!FmxnljbSS_3o=g( z->^wC9%qkR{kbGnW8MfFew_o9h3(r55Is`L$8KI@d+*%{=Nx+FXJ98L0PjFIu;rGnnfY zn1R5Qnp<{Jq0M1vX=X&F8gtLmcWv$1*M@4ZfF^9``()#hGTeKeP`1!iED ztNE(TN}M5}3Bbc*d=FIv`DNv&@|C6yYj{sSqUj5oo$#*0$7pu|Dd2TLI>t5%I zIa4Dvr(iayb+5x=j*Vum9&irk)xV1`t509lnPO0%skL8_1c#Xbamh(2@f?4yUI zhhuT5<#8RJhGz4%b$`PJwKPAudsm|at?u;*hGgnA zU1;9gnxVBC)wA(BsB`AW54N{|qmikJR*%x0c`{LGsSfa|NK61pYH(r-UQ4_JXd!Rsz)=k zL{GMc5{h138)fF5CzHEDM>+FqY)$pdN3}Ml+riTgJOLN0F*Vh?{9ESR{SVVg>*>=# zix;VJHPtvFFCRY$Ks*F;VX~%*r9F)W`PmPE9F!(&s#x07n2<}?S{(ygpXgX-&B&OM zONY&BRQ(#%0%jeQs?oJ4P!p*R98>qCy5p8w>_gpuh39NcOlp)(wOoz0sY-Qz55eB~ z7OC-fKBaD1sE3$l-6QgBJO!n?QOTza`!S_YK z_v-lm^7{VO^8Q@M_^8F)09Ki6%=s?2_5eupee(w1FB%aqSweusQ-T+CH0Xt{` zFjMvW{@C&TB)k25()nh~_yJ9coBRL(0oO@HK~z}7?bm5j;y@69;bvlHb2tf!$ReA~x{22wTq550 z?f?Hnw(;m3ip30;QzdV~7pi!wyMYhDtXW#cO7T>|f=bdFhu+F!zMZ2UFj;GUKX7tI z;hv3{q~!*pMj75WP_c}>6)IWvg5_yyg<9Op()eD1hWC19M@?_9_MHec{Z8n3FaF{8 z;u`Mw0ly(uE>*CgQYv{be6ab2LWhlaH1^iLIM{olnag$78^Fd}%dR7;JECQ+hmk|o z!u2&!3MqPfP5ChDSkFSH8F2WVOEf0(E_M(JL17G}Y+fg0_IuW%WQ zG(mG&u?|->YSdk0;8rc{yw2@2Z&GA}z{Wb91Ooz9VhA{b2DYE7RmG zjL}?eq#iX%3#k;JWMx_{^2nNax`xPhByFiDX+a7uTGU|otOvIAUy|dEKkXOm-`aWS z27pUzD{a)Ct<6p{{3)+lq@i`t@%>-wT4r?*S}k)58e09WZYP0{{R3FC5Sl00039P)t-s|Ns9~ z#rP?<_5oL$Q^olD{r_0T`27C={r>*`|Nj71npVa5OTzc(_WfbW_({R{p56NV{r*M2 z_xt?)2V0#0NsfV0u>{42ctGP(8vQj-Btk1n|O0ZD=YLwd&R{Ko41Gr9H= zY@z@@bOAMB5Ltl$E>bJJ{>JP30ZxkmI%?eW{k`b?Wy<&gOo;dS`~CR$Vwb@XWtR|N zi~t=w02?-0&j0TD{>bb6sNwsK*!p?V`RMQUl(*DVjk-9Cx+-z1KXab|Ka2oXhX5f% z`$|e!000AhNklrxs)5QTeTVRiEmz~MKK1WAjCw(c-JK6eox;2O)?`? zTG`AHia671e^vgmp!llKp|=5sVHk#C7=~epA~VAf-~%aPC=%Qw01h8mnSZ|p?hz91 z7p83F3%LVu9;S$tSI$C^%^yud1dfTM_6p2|+5Ejp$bd`GDvbR|xit>i!ZD&F>@CJrPmu*UjD&?DfZs=$@e3FQA(vNiU+$A*%a} z?`XcG2jDxJ_ZQ#Md`H{4Lpf6QBDp81_KWZ6Tk#yCy1)32zO#3<7>b`eT7UyYH1eGz z;O(rH$=QR*L%%ZcBpc=eGua?N55nD^K(8<#gl2+pN_j~b2MHs4#mcLmv%DkspS-3< zpI1F=^9siI0s-;IN_IrA;5xm~3?3!StX}pUv0vkxMaqm+zxrg7X7(I&*N~&dEd0kD z-FRV|g=|QuUsuh>-xCI}vD2imzYIOIdcCVV=$Bz@*u0+Bs<|L^)32nN*=wu3n%Ynw z@1|eLG>!8ruU1pFXUfb`j>(=Gy~?Rn4QJ-c3%3T|(Frd!bI`9u&zAnyFYTqlG#&J7 zAkD(jpw|oZLNiA>;>hgp1KX7-wxC~31II47gc zHcehD6Uxlf%+M^^uN5Wc*G%^;>D5qT{>=uxUhX%WJu^Z*(_Wq9y}npFO{Hhb>s6<9 zNi0pHXWFaVZnb)1+RS&F)xOv6&aeILcI)`k#0YE+?e)5&#r7J#c`3Z7x!LpTc01dx zrdC3{Z;joZ^KN&))zB_i)I9fWedoN>Zl-6_Iz+^G&*ak2jpF07*qoM6N<$f;w%0(f|Me literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0467bf12aa4d28f374bb26596605a46dcbb3e7c8 GIT binary patch literal 1418 zcmV;51$Fv~P)q zKfU)WzW*n(@|xWGCA9ScMt*e9`2kdxPQ&&>|-UCa7_51w+ zLUsW@ZzZSW0y$)Hp~e9%PvP|a03ks1`~K?q{u;6NC8*{AOqIUq{CL&;p56Lf$oQGq z^={4hPQv)y=I|4n+?>7Fim=dxt1 z2H+Dm+1+fh+IF>G0SjJMkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJT zkdTm&kdTm&kdTm&kdP`esgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>Tmf` zm1{eLgw!b{bXkjWbF%dTkTZEJWyWOb##Lfw4EK2}<0d6%>AGS{po>WCOy&f$Tay_> z?NBlkpo@s-O;0V%Y_Xa-G#_O08q5LR*~F%&)}{}r&L%Sbs8AS4t7Y0NEx*{soY=0MZExqA5XHQkqi#4gW3 zqODM^iyZl;dvf)-bOXtOru(s)Uc7~BFx{w-FK;2{`VA?(g&@3z&bfLFyctOH!cVsF z7IL=fo-qBndRUm;kAdXR4e6>k-z|21AaN%ubeVrHl*<|s&Ax@W-t?LR(P-24A5=>a z*R9#QvjzF8n%@1Nw@?CG@6(%>+-0ASK~jEmCV|&a*7-GKT72W<(TbSjf)&Eme6nGE z>Gkj4Sq&2e+-G%|+NM8OOm5zVl9{Z8Dd8A5z3y8mZ=4Bv4%>as_{9cN#bm~;h>62( zdqY93Zy}v&c4n($Vv!UybR8ocs7#zbfX1IY-*w~)p}XyZ-SFC~4w>BvMVr`dFbelV{lLL0bx7@*ZZdebr3`sP;? zVImji)kG)(6Juv0lz@q`F!k1FE;CQ(D0iG$wchPbKZQELlsZ#~rt8#90Y_Xh&3U-< z{s<&cCV_1`^TD^ia9!*mQDq& zn2{r`j};V|uV%_wsP!zB?m%;FeaRe+X47K0e+KE!8C{gAWF8)lCd1u1%~|M!XNRvw zvtqy3iz0WSpWdhn6$hP8PaRBmp)q`#PCA`Vd#Tc$@f1tAcM>f_I@bC)hkI9|o(Iqv zo}Piadq!j76}004RBio<`)70k^`K1NK)q>w?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF z34$0Z;QO!JOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUC YUoZo%k(ykuW&i*H07*qoM6N<$f+CH{y8r+H literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 000000000000..0bedcf2fd467 --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/interactive_media_ads/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000000..f2e259c7c939 --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/interactive_media_ads/example/ios/Runner/Base.lproj/Main.storyboard b/packages/interactive_media_ads/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000000..f3c28516fb38 --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/interactive_media_ads/example/ios/Runner/Info.plist b/packages/interactive_media_ads/example/ios/Runner/Info.plist new file mode 100644 index 000000000000..5394f403367c --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Interactive Media Ads + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + interactive_media_ads_example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + + diff --git a/packages/interactive_media_ads/example/ios/Runner/Runner-Bridging-Header.h b/packages/interactive_media_ads/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 000000000000..eb7e8ba8052f --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1,5 @@ +// 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 "GeneratedPluginRegistrant.h" diff --git a/packages/interactive_media_ads/example/ios/RunnerTests/RunnerTests.swift b/packages/interactive_media_ads/example/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 000000000000..cea3cbd3a1fb --- /dev/null +++ b/packages/interactive_media_ads/example/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,30 @@ +// 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 Flutter +import UIKit +import XCTest + +@testable import interactive_media_ads + +// This demonstrates a simple unit test of the Swift portion of this plugin's implementation. +// +// See https://developer.apple.com/documentation/xctest for more information about using XCTest. + +class RunnerTests: XCTestCase { + + func testGetPlatformVersion() { + let plugin = InteractiveMediaAdsPlugin() + + let call = FlutterMethodCall(methodName: "getPlatformVersion", arguments: []) + + let resultExpectation = expectation(description: "result block must be called.") + plugin.handle(call) { result in + XCTAssertEqual(result as! String, "iOS " + UIDevice.current.systemVersion) + resultExpectation.fulfill() + } + waitForExpectations(timeout: 1) + } + +} diff --git a/packages/interactive_media_ads/example/lib/main.dart b/packages/interactive_media_ads/example/lib/main.dart new file mode 100644 index 000000000000..fd385e0a60c3 --- /dev/null +++ b/packages/interactive_media_ads/example/lib/main.dart @@ -0,0 +1,44 @@ +// 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/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_driver/driver_extension.dart'; + +/// Entry point for integration tests that require espresso. +@pragma('vm:entry-point') +void integrationTestMain() { + enableFlutterDriverExtension(); + main(); +} + +void main() { + runApp(const MyApp()); +} + +/// Home widget of the example app. +class MyApp extends StatefulWidget { + /// Constructs [MyApp]. + const MyApp({super.key}); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + @override + Widget build(BuildContext context) { + debugPrint('THEAPP'); + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Plugin example app'), + ), + body: Center( + child: Text('Running on: $defaultTargetPlatform'), + ), + ), + ); + } +} diff --git a/packages/interactive_media_ads/example/pubspec.yaml b/packages/interactive_media_ads/example/pubspec.yaml new file mode 100644 index 000000000000..fde3e7a8b2ec --- /dev/null +++ b/packages/interactive_media_ads/example/pubspec.yaml @@ -0,0 +1,25 @@ +name: interactive_media_ads_example +description: "Demonstrates how to use the interactive_media_ads plugin." +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: ^3.2.3 + flutter: ">=3.16.6" + +dependencies: + flutter: + sdk: flutter + flutter_driver: + sdk: flutter + interactive_media_ads: + path: ../ + +dev_dependencies: + espresso: ^0.2.0 + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/interactive_media_ads/example/test_driver/integration_test.dart b/packages/interactive_media_ads/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/interactive_media_ads/example/test_driver/integration_test.dart @@ -0,0 +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. + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/packages/interactive_media_ads/ios/Assets/.gitkeep b/packages/interactive_media_ads/ios/Assets/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/interactive_media_ads/ios/Classes/InteractiveMediaAdsPlugin.swift b/packages/interactive_media_ads/ios/Classes/InteractiveMediaAdsPlugin.swift new file mode 100644 index 000000000000..5b94fd6a24aa --- /dev/null +++ b/packages/interactive_media_ads/ios/Classes/InteractiveMediaAdsPlugin.swift @@ -0,0 +1,24 @@ +// 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 Flutter +import UIKit + +public class InteractiveMediaAdsPlugin: NSObject, FlutterPlugin { + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel( + name: "interactive_media_ads", binaryMessenger: registrar.messenger()) + let instance = InteractiveMediaAdsPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "getPlatformVersion": + result("iOS " + UIDevice.current.systemVersion) + default: + result(FlutterMethodNotImplemented) + } + } +} diff --git a/packages/interactive_media_ads/ios/Resources/PrivacyInfo.xcprivacy b/packages/interactive_media_ads/ios/Resources/PrivacyInfo.xcprivacy new file mode 100644 index 000000000000..5516ebf30d18 --- /dev/null +++ b/packages/interactive_media_ads/ios/Resources/PrivacyInfo.xcprivacy @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/packages/interactive_media_ads/ios/interactive_media_ads.podspec b/packages/interactive_media_ads/ios/interactive_media_ads.podspec new file mode 100644 index 000000000000..8980d486c212 --- /dev/null +++ b/packages/interactive_media_ads/ios/interactive_media_ads.podspec @@ -0,0 +1,29 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint interactive_media_ads.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'interactive_media_ads' + s.version = '0.0.1' + s.summary = 'A plugin for Interactive Media Ads SDKs.' + s.description = <<-DESC +A Flutter plugin for using the Interactive Media Ads SDKs. +Downloaded by pub (not CocoaPods). + DESC + s.homepage = 'https://github.com/flutter/packages' + s.license = { :type => 'BSD', :file => '../LICENSE' } + s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } + s.source = { :http => 'https://github.com/flutter/packages/tree/main/packages/interactive_media_ads/interactive_media_ads' } + s.source_files = 'Classes/**/*' + s.dependency 'Flutter' + s.platform = :ios, '12.0' + + # Flutter.framework does not contain a i386 slice. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } + s.xcconfig = { + 'LIBRARY_SEARCH_PATHS' => '$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)/ $(SDKROOT)/usr/lib/swift', + 'LD_RUNPATH_SEARCH_PATHS' => '/usr/lib/swift', + } + s.swift_version = '5.0' + s.resource_bundles = {'interactive_media_ads_privacy' => ['Resources/PrivacyInfo.xcprivacy']} +end diff --git a/packages/interactive_media_ads/lib/interactive_media_ads.dart b/packages/interactive_media_ads/lib/interactive_media_ads.dart new file mode 100644 index 000000000000..185d304cf5bc --- /dev/null +++ b/packages/interactive_media_ads/lib/interactive_media_ads.dart @@ -0,0 +1,10 @@ +// 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. + +export 'src/ad_display_container.dart'; +export 'src/ads_loader.dart'; +export 'src/ads_manager_delegate.dart'; +export 'src/platform_interface/ad_error.dart'; +export 'src/platform_interface/ad_event.dart'; +export 'src/platform_interface/ads_request.dart'; diff --git a/packages/interactive_media_ads/lib/src/ad_display_container.dart b/packages/interactive_media_ads/lib/src/ad_display_container.dart new file mode 100644 index 000000000000..99da19682b8e --- /dev/null +++ b/packages/interactive_media_ads/lib/src/ad_display_container.dart @@ -0,0 +1,102 @@ +// 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/cupertino.dart'; + +import 'platform_interface/platform_ad_display_container.dart'; +import 'platform_interface/platform_interface.dart'; + +/// Handles playing ads after they've been received from the server. +/// +/// ## Platform-Specific Features +/// This class contains an underlying implementation provided by the current +/// platform. Once a platform implementation is imported, the examples below +/// can be followed to use features provided by a platform's implementation. +/// +/// {@macro interactive_media_ads.AdDisplayContainer.fromPlatformCreationParams} +/// +/// Below is an example of accessing the platform-specific implementation for +/// iOS and Android: +/// +/// ```dart +/// final AdDisplayContainer container = AdDisplayContainer(); +/// +/// if (InteractiveMediaAdsPlatform.instance is IOSInteractiveMediaAdsPlatform) { +/// final IOSAdDisplayContainer iosContainer = container.platform as IOSAdDisplayContainer; +/// } else if (InteractiveMediaAdsPlatform.instance is AndroidInteractiveMediaAdsPlatform) { +/// final AndroidAdDisplayContainer androidContainer = +/// container.platform as AndroidAdDisplayContainer; +/// } +/// ``` +class AdDisplayContainer extends StatelessWidget { + /// Constructs an [AdDisplayContainer]. + /// + /// See [AdDisplayContainer.fromPlatformCreationParams] for setting parameters for a + /// specific platform. + AdDisplayContainer({ + Key? key, + required void Function(AdDisplayContainer container) onContainerAdded, + }) : this.fromPlatformCreationParams( + key: key, + params: PlatformAdDisplayContainerCreationParams( + onContainerAdded: (PlatformAdDisplayContainer container) { + onContainerAdded(AdDisplayContainer.fromPlatform( + platform: container, + )); + }, + ), + ); + + /// Constructs an [AdDisplayContainer] from creation params for a specific platform. + /// + /// {@template interactive_media_ads.AdDisplayContainer.fromPlatformCreationParams} + /// Below is an example of setting platform-specific creation parameters for + /// iOS and Android: + /// + /// ```dart + /// PlatformAdDisplayContainerCreationParams params = + /// const PlatformAdDisplayContainerCreationParams(); + /// + /// if (InteractiveMediaAdsPlatform.instance is IOSInteractiveMediaAdsPlatform) { + /// params = IOSAdDisplayContainerCreationParams + /// .fromPlatformAdDisplayContainerCreationParams( + /// params, + /// ); + /// } else if (InteractiveMediaAdsPlatform.instance is AndroidInteractiveMediaAdsPlatform) { + /// params = AndroidAdDisplayContainerCreationParams + /// .fromPlatformAdDisplayContainerCreationParams( + /// params, + /// ); + /// } + /// + /// final AdDisplayContainer container = AdDisplayContainer.fromPlatformCreationParams( + /// params, + /// ); + /// ``` + /// {@endtemplate} + AdDisplayContainer.fromPlatformCreationParams({ + Key? key, + required PlatformAdDisplayContainerCreationParams params, + }) : this.fromPlatform( + key: key, + platform: PlatformAdDisplayContainer(params), + ); + + /// Constructs an [AdDisplayContainer] from a specific platform + /// implementation. + const AdDisplayContainer.fromPlatform({super.key, required this.platform}); + + /// Implementation of [PlatformAdDisplayContainer] for the current platform. + final PlatformAdDisplayContainer platform; + + /// Invoked when the native view that contains the ad has been added to the + /// platform view hierarchy. + void Function(PlatformAdDisplayContainer container) get onContainerAdded => + platform.params.onContainerAdded; + + @override + Widget build(BuildContext context) { + return platform.build(context); + } +} diff --git a/packages/interactive_media_ads/lib/src/ads_loader.dart b/packages/interactive_media_ads/lib/src/ads_loader.dart new file mode 100644 index 000000000000..9370cc9fec35 --- /dev/null +++ b/packages/interactive_media_ads/lib/src/ads_loader.dart @@ -0,0 +1,162 @@ +// 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/foundation.dart'; + +import 'ad_display_container.dart'; +import 'ads_manager_delegate.dart'; +import 'platform_interface/platform_interface.dart'; + +/// Handles playing ads after they've been received from the server. +/// +/// ## Platform-Specific Features +/// This class contains an underlying implementation provided by the current +/// platform. Once a platform implementation is imported, the examples below +/// can be followed to use features provided by a platform's implementation. +/// +/// {@macro interactive_media_ads.AdsLoader.fromPlatformCreationParams} +/// +/// Below is an example of accessing the platform-specific implementation for +/// iOS and Android: +/// +/// ```dart +/// final AdsLoader loader = AdsLoader(); +/// +/// if (InteractiveMediaAdsPlatform.instance is IOSInteractiveMediaAdsPlatform) { +/// final IOSAdsLoader iosLoader = loader.platform as IOSAdsLoader; +/// } else if (InteractiveMediaAdsPlatform.instance is AndroidInteractiveMediaAdsPlatform) { +/// final AndroidAdsLoader androidLoader = +/// loader.platform as AndroidAdsLoader; +/// } +/// ``` +class AdsLoader { + /// Constructs an [AdsLoader]. + /// + /// See [AdsLoader.fromPlatformCreationParams] for setting parameters for a + /// specific platform. + AdsLoader({ + required AdDisplayContainer container, + required void Function(OnAdsLoadedData data) onAdsLoaded, + required void Function(AdsLoadErrorData data) onAdsLoadError, + }) : this.fromPlatformCreationParams( + PlatformAdsLoaderCreationParams( + container: container.platform, + onAdsLoaded: (PlatformOnAdsLoadedData data) { + onAdsLoaded(OnAdsLoadedData._(platform: data)); + }, + onAdsLoadError: onAdsLoadError, + ), + ); + + /// Constructs an [AdsLoader] from creation params for a specific platform. + /// + /// {@template interactive_media_ads.AdsLoader.fromPlatformCreationParams} + /// Below is an example of setting platform-specific creation parameters for + /// iOS and Android: + /// + /// ```dart + /// PlatformAdsLoaderCreationParams params = + /// const PlatformAdsLoaderCreationParams(); + /// + /// if (InteractiveMediaAdsPlatform.instance is IOSInteractiveMediaAdsPlatform) { + /// params = IOSAdsLoaderCreationParams + /// .fromPlatformAdsLoaderCreationParams( + /// params, + /// ); + /// } else if (InteractiveMediaAdsPlatform.instance is AndroidInteractiveMediaAdsPlatform) { + /// params = AndroidAdsLoaderCreationParams + /// .fromPlatformAdsLoaderCreationParams( + /// params, + /// ); + /// } + /// + /// final AdsLoader loader = AdsLoader.fromPlatformCreationParams( + /// params, + /// ); + /// ``` + /// {@endtemplate} + AdsLoader.fromPlatformCreationParams( + PlatformAdsLoaderCreationParams params, + ) : this.fromPlatform(PlatformAdsLoader(params)); + + /// Constructs a [AdsLoader] from a specific platform implementation. + AdsLoader.fromPlatform(this.platform); + + /// Implementation of [PlatformAdsLoader] for the current platform. + final PlatformAdsLoader platform; + + /// Signals to the SDK that the content has completed. + Future contentComplete() { + return platform.contentComplete(); + } + + /// Requests ads from a server. + Future requestAds(AdsRequest request) { + return platform.requestAds(request); + } +} + +/// Data when ads are successfully loaded from the ad server through an +/// [AdsLoader]. +@immutable +class OnAdsLoadedData { + OnAdsLoadedData._({required this.platform}); + + /// Implementation of [PlatformOnAdsLoadedData] for the current platform. + final PlatformOnAdsLoadedData platform; + + /// The ads manager instance created by the ads loader. + late final AdsManager manager = AdsManager._fromPlatform(platform.manager); +} + +/// Handles playing ads after they've been received from the server. +/// +/// ## Platform-Specific Features +/// This class contains an underlying implementation provided by the current +/// platform. Once a platform implementation is imported, the examples below +/// can be followed to use features provided by a platform's implementation. +/// +/// {@macro interactive_media_ads.AdsManager.fromPlatformCreationParams} +/// +/// Below is an example of accessing the platform-specific implementation for +/// iOS and Android: +/// +/// ```dart +/// final AdsManager manager = AdsManager(); +/// +/// if (InteractiveMediaAdsPlatform.instance is IOSInteractiveMediaAdsPlatform) { +/// final IOSAdsManager iosManager = manager.platform as IOSAdsManager; +/// } else if (InteractiveMediaAdsPlatform.instance is AndroidInteractiveMediaAdsPlatform) { +/// final AndroidAdsManager androidManager = +/// manager.platform as AndroidAdsManager; +/// } +/// ``` +class AdsManager { + /// Constructs a [AdsManager] from a specific platform implementation. + AdsManager._fromPlatform(this.platform); + + /// Implementation of [PlatformAdsManager] for the current platform. + final PlatformAdsManager platform; + + /// Initializes the ad experience using default rendering settings. + Future init() { + return platform.init(AdsManagerInitParams()); + } + + /// Starts playing the ads. + Future start() { + return platform.start(AdsManagerStartParams()); + } + + /// The [AdsManagerDelegate] to notify with events during ad playback. + Future setAdsManagerDelegate(AdsManagerDelegate delegate) { + return platform.setAdsManagerDelegate(delegate.platform); + } + + /// Stops the ad and all tracking, then releases all assets that were loaded + /// to play the ad. + Future destroy() { + return platform.destroy(); + } +} diff --git a/packages/interactive_media_ads/lib/src/ads_manager_delegate.dart b/packages/interactive_media_ads/lib/src/ads_manager_delegate.dart new file mode 100644 index 000000000000..4a3813c719af --- /dev/null +++ b/packages/interactive_media_ads/lib/src/ads_manager_delegate.dart @@ -0,0 +1,88 @@ +// 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 'platform_interface/platform_interface.dart'; + +/// Handles playing ads after they've been received from the server. +/// +/// ## Platform-Specific Features +/// This class contains an underlying implementation provided by the current +/// platform. Once a platform implementation is imported, the examples below +/// can be followed to use features provided by a platform's implementation. +/// +/// {@macro interactive_media_ads.AdsManagerDelegate.fromPlatformCreationParams} +/// +/// Below is an example of accessing the platform-specific implementation for +/// iOS and Android: +/// +/// ```dart +/// final AdsManagerDelegate delegate = AdsManagerDelegate(); +/// +/// if (InteractiveMediaAdsPlatform.instance is IOSInteractiveMediaAdsPlatform) { +/// final IOSAdsManagerDelegate iosDelegate = delegate.platform as IOSAdsManagerDelegate; +/// } else if (InteractiveMediaAdsPlatform.instance is AndroidInteractiveMediaAdsPlatform) { +/// final AndroidAdsManagerDelegate androidDelegate = +/// delegate.platform as AndroidAdsManagerDelegate; +/// } +/// ``` +class AdsManagerDelegate { + /// Constructs an [AdsManagerDelegate]. + /// + /// See [AdsManagerDelegate.fromPlatformCreationParams] for setting parameters for a + /// specific platform. + AdsManagerDelegate({ + void Function(AdEvent event)? onAdEvent, + void Function(AdErrorEvent event)? onAdErrorEvent, + }) : this.fromPlatformCreationParams( + PlatformAdsManagerDelegateCreationParams( + onAdEvent: onAdEvent, + onAdErrorEvent: onAdErrorEvent, + ), + ); + + /// Constructs an [AdsManagerDelegate] from creation params for a specific platform. + /// + /// {@template interactive_media_ads.AdsManagerDelegate.fromPlatformCreationParams} + /// Below is an example of setting platform-specific creation parameters for + /// iOS and Android: + /// + /// ```dart + /// PlatformAdsManagerDelegateCreationParams params = + /// const PlatformAdsManagerDelegateCreationParams(); + /// + /// if (InteractiveMediaAdsPlatform.instance is IOSInteractiveMediaAdsPlatform) { + /// params = IOSAdsManagerDelegateCreationParams + /// .fromPlatformAdsManagerDelegateCreationParams( + /// params, + /// ); + /// } else if (InteractiveMediaAdsPlatform.instance is AndroidInteractiveMediaAdsPlatform) { + /// params = AndroidAdsManagerDelegateCreationParams + /// .fromPlatformAdsManagerDelegateCreationParams( + /// params, + /// ); + /// } + /// + /// final AdsManagerDelegate delegate = AdsManagerDelegate.fromPlatformCreationParams( + /// params, + /// ); + /// ``` + /// {@endtemplate} + AdsManagerDelegate.fromPlatformCreationParams( + PlatformAdsManagerDelegateCreationParams params, + ) : this.fromPlatform(PlatformAdsManagerDelegate(params)); + + /// Constructs a [AdsManagerDelegate] from a specific platform implementation. + AdsManagerDelegate.fromPlatform(this.platform); + + /// Implementation of [PlatformAdsManagerDelegate] for the current platform. + final PlatformAdsManagerDelegate platform; + + /// Invoked when there is an [AdEvent]. + void Function(AdEvent event)? get onAdEvent => platform.params.onAdEvent; + + /// Invoked when there was an error playing the ad. Log the error and resume + /// playing content. + void Function(AdErrorEvent event)? get onAdErrorEvent => + platform.params.onAdErrorEvent; +} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/ad_error.dart b/packages/interactive_media_ads/lib/src/platform_interface/ad_error.dart new file mode 100644 index 000000000000..ca89e967307b --- /dev/null +++ b/packages/interactive_media_ads/lib/src/platform_interface/ad_error.dart @@ -0,0 +1,156 @@ +// 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/foundation.dart'; + +/// The types of errors that can be encountered when loading an ad. +enum AdErrorCode { + /// Generic invalid usage of the API. + apiError, + + /// Ads player was not provided. + adsPlayerNotProvided, + + /// There was a problem requesting ads from the server. + adsRequestNetworkError, + + /// The ad slot is not visible on the page. + adslotNotVisible, + + /// There was a problem requesting ads from the server. + companionAdLoadingFailed, + + /// Content playhead was not passed in, but list of ads has been returned from + /// the server. + contentPlayheadMissing, + + /// There was a problem requesting ads from the server. + failedToRequestAds, + + /// There was an error loading the ad. + failedLoadingAd, + + /// An error internal to the SDK occurred. + /// + /// More information may be available in the details. + internalError, + + /// Invalid arguments were provided to SDK methods. + invalidArguments, + + /// The version of the runtime is too old. + osRuntimeTooOld, + + /// An overlay ad failed to load. + overlayAdLoadingFailed, + + /// An overlay ad failed to render. + overlayAdPlayingFailed, + + /// Ads list was returned but ContentProgressProvider was not configured. + playlistNoContentTracking, + + /// Ads list response was malformed. + playlistMalformedResponse, + + /// Listener for at least one of the required vast events was not added. + requiredListenersNotAdded, + + /// There was an error initializing the stream. + streamInitializationFailed, + + /// Ads loader sent ads loaded event when it was not expected. + unexpectedAdsLoadedEvent, + + /// The ad response was not understood and cannot be parsed. + unknownAdResponse, + + /// An unexpected error occurred and the cause is not known. + /// + /// Refer to the inner error for more information. + unknownError, + + /// No assets were found in the VAST ad response. + vastAssetNotFound, + + /// A VAST response containing a single tag with no child tags. + vastEmptyResponse, + + /// Assets were found in the VAST ad response for a linear ad, but none of + /// them matched the video player's capabilities. + vastLinearAssetMismatch, + + /// At least one VAST wrapper ad loaded successfully and a subsequent wrapper + /// or inline ad load has timed out. + vastLoadTimeout, + + /// The ad response was not recognized as a valid VAST ad. + vastMalformedResponse, + + /// Failed to load media assets from a VAST response. + /// + /// The default timeout for media loading is 8 seconds. + vastMediaLoadTimeout, + + /// Assets were found in the VAST ad response for a nonlinear ad, but none of + /// them matched the video player's capabilities. + vastNonlinearAssetMismatch, + + /// No Ads VAST response after one or more wrappers. + vastNoAdsAfterWrapper, + + /// The maximum number of VAST wrapper redirects has been reached. + vastTooManyRedirects, + + /// Trafficking error. + /// + /// Video player received an ad type that it was not expecting and/or cannot + /// display. + vastTraffickingError, + + /// At least one VAST wrapper loaded and a subsequent wrapper or inline ad + /// load has resulted in a 404 response code. + vastInvalidUrl, + + /// There was an error playing the video ad. + videoPlayError, + + /// Another VideoAdsManager is still using the video. + /// + /// It must be unloaded before another ad can play on the same element. + videoElementUsed, + + /// A video element was not specified where it was required. + videoElementRequired, +} + +/// Possible error types while loading or playing ads. +enum AdErrorType { + /// Indicates an error occurred while loading the ads. + loading, + + /// Indicates an error occurred while playing the ads. + playing, + + /// An unexpected error occurred while loading or playing the ads. + /// + /// This may mean that the SDK wasn’t loaded properly. + unknown, +} + +/// Surfaces an error that occurred during ad loading or playing. +@immutable +class AdError { + /// Creates a [AdError]. + const AdError({required this.type, required this.code, this.message}); + + /// Specifies the source of the error. + final AdErrorType type; + + /// The error code for obtaining more specific information about the error. + final AdErrorCode code; + + /// A brief description about the error. + final String? message; +} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/ad_event.dart b/packages/interactive_media_ads/lib/src/platform_interface/ad_event.dart new file mode 100644 index 000000000000..9824d9915fb3 --- /dev/null +++ b/packages/interactive_media_ads/lib/src/platform_interface/ad_event.dart @@ -0,0 +1,53 @@ +// 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/foundation.dart'; + +import 'ad_error.dart'; + +/// Types of events that can occur during ad playback. +enum AdEventType { + /// Fired when the ads manager is done playing all the valid ads in the ads + /// response, or when the response doesn't return any valid ads. + allAdsCompleted, + + /// Fired when an ad is clicked. + clicked, + + /// Fired when an ad completes playing. + complete, + + /// Fired when content should be paused. + /// + /// This usually happens right before an ad is about to hide the content. + contentPauseRequested, + + /// Fired when content should be resumed. + /// + /// This usually happens when an ad finishes or collapses. + contentResumeRequested, + + /// Fired when the VAST response has been received. + loaded, +} + +/// Simple data class used to transport ad playback information. +@immutable +class AdEvent { + /// Creates an [AdEvent]. + const AdEvent({required this.type}); + + /// The type of event that occurred. + final AdEventType type; +} + +/// An event raised when there is an error loading or playing ads. +@immutable +class AdErrorEvent { + /// Creates an [AdErrorEvent]. + const AdErrorEvent({required this.error}); + + /// The error that caused this event. + final AdError error; +} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/ads_request.dart b/packages/interactive_media_ads/lib/src/platform_interface/ads_request.dart new file mode 100644 index 000000000000..72e4dfc69b63 --- /dev/null +++ b/packages/interactive_media_ads/lib/src/platform_interface/ads_request.dart @@ -0,0 +1,12 @@ +// 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. + +/// An object containing the data used to request ads from the server. +class AdsRequest { + /// Creates an [AdsRequest]. + AdsRequest({required this.adTagUrl}); + + /// The URL from which ads will be requested. + final String adTagUrl; +} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/interactive_media_ads_platform.dart b/packages/interactive_media_ads/lib/src/platform_interface/interactive_media_ads_platform.dart new file mode 100644 index 000000000000..f1dac2db7d1d --- /dev/null +++ b/packages/interactive_media_ads/lib/src/platform_interface/interactive_media_ads_platform.dart @@ -0,0 +1,32 @@ +// 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 'platform_ad_display_container.dart'; +import 'platform_ads_loader.dart'; +import 'platform_ads_manager_delegate.dart'; + +/// Interface for a platform implementation of the Interactive Media Ads SDKs. +abstract base class InteractiveMediaAdsPlatform { + /// The instance of [InteractiveMediaAdsPlatform] to use. + /// + /// Platform-specific plugins should set this with their own platform-specific + /// class that extends [InteractiveMediaAdsPlatform] when they register + /// themselves. + static InteractiveMediaAdsPlatform? instance; + + /// Creates a new [PlatformAdsLoader]. + PlatformAdsLoader createPlatformAdsLoader( + PlatformAdsLoaderCreationParams params, + ); + + /// Creates a new [PlatformAdsManagerDelegate]. + PlatformAdsManagerDelegate createPlatformAdsManagerDelegate( + PlatformAdsManagerDelegateCreationParams params, + ); + + /// Creates a new [PlatformAdDisplayContainer]. + PlatformAdDisplayContainer createPlatformAdDisplayContainer( + PlatformAdDisplayContainerCreationParams params, + ); +} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/platform_ad_display_container.dart b/packages/interactive_media_ads/lib/src/platform_interface/platform_ad_display_container.dart new file mode 100644 index 000000000000..075706f39307 --- /dev/null +++ b/packages/interactive_media_ads/lib/src/platform_interface/platform_ad_display_container.dart @@ -0,0 +1,95 @@ +// 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/cupertino.dart'; + +import 'interactive_media_ads_platform.dart'; + +/// Object specifying creation parameters for creating a +/// [PlatformAdDisplayContainer]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +/// +/// This example demonstrates how to extend the +/// [PlatformAdDisplayContainerCreationParams] to provide additional platform +/// specific parameters. +/// +/// When extending [PlatformAdDisplayContainerCreationParams] additional +/// parameters should always accept `null` or have a default value to prevent +/// breaking changes. +/// +/// ```dart +/// class AndroidPlatformAdDisplayContainerCreationParams +/// extends PlatformAdDisplayContainerCreationParams { +/// AndroidPlatformAdDisplayContainerCreationParams._( +/// PlatformAdDisplayContainerCreationParams params, { +/// this.uri, +/// }) : super(); +/// +/// factory AndroidAdDisplayContainerCreationParams.fromPlatformAdDisplayContainerCreationParams( +/// PlatformAdDisplayContainerCreationParams params, { +/// Uri? uri, +/// }) { +/// return AndroidAdDisplayContainerCreationParams._(params, uri: uri); +/// } +/// +/// final Uri? uri; +/// } +/// ``` +@immutable +base class PlatformAdDisplayContainerCreationParams { + /// Used by the platform implementation to create a new + /// [PlatformAdDisplayContainer]. + const PlatformAdDisplayContainerCreationParams({ + this.key, + required this.onContainerAdded, + }); + + /// Controls how one widget replaces another widget in the tree. + /// + /// See also: + /// * The discussions at [Key] and [GlobalKey]. + final Key? key; + + /// Invoked when the View that contains the ad has been added to the platform + /// view hierarchy. + final void Function(PlatformAdDisplayContainer container) onContainerAdded; +} + +/// The interface for a platform implementation for a container in which to +/// display ads. +abstract base class PlatformAdDisplayContainer { + /// Creates a new [PlatformAdDisplayContainer] + factory PlatformAdDisplayContainer( + PlatformAdDisplayContainerCreationParams params, + ) { + assert( + InteractiveMediaAdsPlatform.instance != null, + 'A platform implementation for `interactive_media_ads` has not been set. ' + 'Please ensure that an implementation of `InteractiveMediaAdsPlatform` ' + 'has been set to `InteractiveMediaAdsPlatform.instance` before use. For ' + 'unit testing, `InteractiveMediaAdsPlatform.instance` can be set with ' + 'your own test implementation.', + ); + final PlatformAdDisplayContainer implementation = + InteractiveMediaAdsPlatform.instance! + .createPlatformAdDisplayContainer(params); + return implementation; + } + + /// Used by the platform implementation to create a new + /// [PlatformAdDisplayContainer]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + @protected + PlatformAdDisplayContainer.implementation(this.params); + + /// The parameters used to initialize the [PlatformAdDisplayContainer]. + final PlatformAdDisplayContainerCreationParams params; + + /// Builds the Widget that contains the native View. + Widget build(BuildContext context); +} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_loader.dart b/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_loader.dart new file mode 100644 index 000000000000..7dc6a57a1d22 --- /dev/null +++ b/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_loader.dart @@ -0,0 +1,119 @@ +// 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/foundation.dart'; + +import 'ad_error.dart'; +import 'ads_request.dart'; +import 'interactive_media_ads_platform.dart'; +import 'platform_ad_display_container.dart'; +import 'platform_ads_manager.dart'; + +/// Object specifying creation parameters for creating a [PlatformAdsLoader]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +/// +/// This example demonstrates how to extend the +/// [PlatformAdsLoaderCreationParams] to provide additional platform specific +/// parameters. +/// +/// When extending [PlatformAdsLoaderCreationParams] additional parameters +/// should always accept `null` or have a default value to prevent breaking +/// changes. +/// +/// ```dart +/// class AndroidPlatformAdsLoaderCreationParams +/// extends PlatformAdsLoaderCreationParams { +/// AndroidPlatformAdsLoaderCreationParams._( +/// PlatformAdsLoaderCreationParams params, { +/// this.uri, +/// }) : super(); +/// +/// factory AndroidAdsLoaderCreationParams.fromPlatformAdsLoaderCreationParams( +/// PlatformAdsLoaderCreationParams params, { +/// Uri? uri, +/// }) { +/// return AndroidAdsLoaderCreationParams._(params, uri: uri); +/// } +/// +/// final Uri? uri; +/// } +/// ``` +@immutable +base class PlatformAdsLoaderCreationParams { + /// Used by the platform implementation to create a new [PlatformAdsLoader]. + const PlatformAdsLoaderCreationParams({ + required this.container, + required this.onAdsLoaded, + required this.onAdsLoadError, + }); + + /// A container object where ads are rendered. + final PlatformAdDisplayContainer container; + + /// Callback for the ads manager loaded event. + final void Function(PlatformOnAdsLoadedData data) onAdsLoaded; + + /// Callback for errors that occur during the ads request. + final void Function(AdsLoadErrorData data) onAdsLoadError; +} + +/// Interface for a platform implementation of an object that requests ads and +/// handles events from ads request responses. +abstract base class PlatformAdsLoader { + /// Creates a new [PlatformAdsLoader] + factory PlatformAdsLoader( + PlatformAdsLoaderCreationParams params, + ) { + assert( + InteractiveMediaAdsPlatform.instance != null, + 'A platform implementation for `interactive_media_ads` has not been set. ' + 'Please ensure that an implementation of `InteractiveMediaAdsPlatform` ' + 'has been set to `InteractiveMediaAdsPlatform.instance` before use. For ' + 'unit testing, `InteractiveMediaAdsPlatform.instance` can be set with ' + 'your own test implementation.', + ); + final PlatformAdsLoader implementation = + InteractiveMediaAdsPlatform.instance!.createPlatformAdsLoader(params); + return implementation; + } + + /// Used by the platform implementation to create a new [PlatformAdsLoader]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + @protected + PlatformAdsLoader.implementation(this.params); + + /// The parameters used to initialize the [PlatformAdsLoader]. + final PlatformAdsLoaderCreationParams params; + + /// Signal to the SDK that the content has completed. + Future contentComplete(); + + /// Requests ads from a server. + Future requestAds(AdsRequest request); +} + +/// Data when ads are successfully loaded from the ad server through an +/// [PlatformAdsLoader]. +@immutable +class PlatformOnAdsLoadedData { + /// Creates a [PlatformOnAdsLoadedData]. + const PlatformOnAdsLoadedData({required this.manager}); + + /// The ads manager instance created by the ads loader. + final PlatformAdsManager manager; +} + +/// Ad error data that is returned when the ads loader fails to load the ad. +@immutable +class AdsLoadErrorData { + /// Creates a [AdsLoadErrorData]. + const AdsLoadErrorData({required this.error}); + + /// The ad error that occurred while loading the ad. + final AdError error; +} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager.dart b/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager.dart new file mode 100644 index 000000000000..d80a17a6a730 --- /dev/null +++ b/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager.dart @@ -0,0 +1,34 @@ +// 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/foundation.dart'; + +import 'platform_ads_manager_delegate.dart'; + +/// Additional parameter passed to an [PlatformAdsManager] on initialization. +base class AdsManagerInitParams {} + +/// Additional parameter passed to an [PlatformAdsManager] when starting to play +/// ads. +base class AdsManagerStartParams {} + +/// Interface for a platform implementation of a `AdsManager`. +abstract class PlatformAdsManager { + /// Creates a [PlatformAdsManager]. + @protected + PlatformAdsManager(); + + /// Initializes the ad experience using default rendering settings. + Future init(AdsManagerInitParams params); + + /// Starts playing the ads. + Future start(AdsManagerStartParams params); + + /// /// The [AdsManagerDelegate] to notify with events during ad playback. + Future setAdsManagerDelegate(PlatformAdsManagerDelegate delegate); + + /// Stops the ad and all tracking, then releases all assets that were loaded + /// to play the ad. + Future destroy(); +} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager_delegate.dart b/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager_delegate.dart new file mode 100644 index 000000000000..146e5d1914e6 --- /dev/null +++ b/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager_delegate.dart @@ -0,0 +1,88 @@ +// 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/foundation.dart'; + +import 'ad_event.dart'; +import 'interactive_media_ads_platform.dart'; + +/// Object specifying creation parameters for creating a +/// [PlatformAdsManagerDelegate]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +/// +/// This example demonstrates how to extend the +/// [PlatformAdsManagerDelegateCreationParams] to provide additional platform +/// specific parameters. +/// +/// When extending [PlatformAdsManagerDelegateCreationParams] additional +/// parameters should always accept `null` or have a default value to prevent +/// breaking changes. +/// +/// ```dart +/// class AndroidPlatformAdsManagerDelegateCreationParams +/// extends PlatformAdsManagerDelegateCreationParams { +/// AndroidPlatformAdsManagerDelegateCreationParams._( +/// PlatformAdsManagerDelegateCreationParams params, { +/// this.uri, +/// }) : super(); +/// +/// factory AndroidAdsManagerDelegateCreationParams.fromPlatformAdsManagerDelegateCreationParams( +/// PlatformAdsManagerDelegateCreationParams params, { +/// Uri? uri, +/// }) { +/// return AndroidAdsManagerDelegateCreationParams._(params, uri: uri); +/// } +/// +/// final Uri? uri; +/// } +/// ``` +@immutable +base class PlatformAdsManagerDelegateCreationParams { + /// Used by the platform implementation to create a new [PlatformAdsManagerDelegate]. + const PlatformAdsManagerDelegateCreationParams({ + this.onAdEvent, + this.onAdErrorEvent, + }); + + /// Invoked when there is an [AdEvent]. + final void Function(AdEvent event)? onAdEvent; + + /// Invoked when there was an error playing the ad. Log the error and resume + /// playing content. + final void Function(AdErrorEvent event)? onAdErrorEvent; +} + +/// Interface for a platform implementation of a `AdsManagerDelegate`. +abstract base class PlatformAdsManagerDelegate { + /// Creates a new [PlatformAdsManagerDelegate] + factory PlatformAdsManagerDelegate( + PlatformAdsManagerDelegateCreationParams params, + ) { + assert( + InteractiveMediaAdsPlatform.instance != null, + 'A platform implementation for `interactive_media_ads` has not been set. ' + 'Please ensure that an implementation of `InteractiveMediaAdsPlatform` ' + 'has been set to `InteractiveMediaAdsPlatform.instance` before use. For ' + 'unit testing, `InteractiveMediaAdsPlatform.instance` can be set with ' + 'your own test implementation.', + ); + final PlatformAdsManagerDelegate implementation = + InteractiveMediaAdsPlatform.instance! + .createPlatformAdsManagerDelegate(params); + return implementation; + } + + /// Used by the platform implementation to create a new + /// [PlatformAdsManagerDelegate]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + @protected + PlatformAdsManagerDelegate.implementation(this.params); + + /// The parameters used to initialize the [PlatformAdsManagerDelegate]. + final PlatformAdsManagerDelegateCreationParams params; +} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/platform_interface.dart b/packages/interactive_media_ads/lib/src/platform_interface/platform_interface.dart new file mode 100644 index 000000000000..ad8896480443 --- /dev/null +++ b/packages/interactive_media_ads/lib/src/platform_interface/platform_interface.dart @@ -0,0 +1,12 @@ +// 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. + +export 'ad_error.dart'; +export 'ad_event.dart'; +export 'ads_request.dart'; +export 'interactive_media_ads_platform.dart'; +export 'platform_ad_display_container.dart'; +export 'platform_ads_loader.dart'; +export 'platform_ads_manager.dart'; +export 'platform_ads_manager_delegate.dart'; diff --git a/packages/interactive_media_ads/pubspec.yaml b/packages/interactive_media_ads/pubspec.yaml new file mode 100644 index 000000000000..a6b7b04a2148 --- /dev/null +++ b/packages/interactive_media_ads/pubspec.yaml @@ -0,0 +1,31 @@ +name: interactive_media_ads +description: A Flutter plugin for using the Interactive Media Ads SDKs on Android and iOS. +repository: https://github.com/flutter/packages/tree/main/packages/interactive_media_ads +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+interactive_media_ads%22 +version: 0.0.1 + +environment: + sdk: ^3.2.3 + flutter: ">=3.16.6" + +flutter: + plugin: + platforms: + android: + package: dev.flutter.packages.interactive_media_ads + pluginClass: InteractiveMediaAdsPlugin + ios: + pluginClass: InteractiveMediaAdsPlugin + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + build_runner: ^2.1.4 + flutter_test: + sdk: flutter + mockito: 5.4.4 + +topics: + - ads diff --git a/packages/interactive_media_ads/test/ad_display_container_test.dart b/packages/interactive_media_ads/test/ad_display_container_test.dart new file mode 100644 index 000000000000..a82a16e8c4c2 --- /dev/null +++ b/packages/interactive_media_ads/test/ad_display_container_test.dart @@ -0,0 +1,59 @@ +// 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:interactive_media_ads/interactive_media_ads.dart'; +import 'package:interactive_media_ads/src/ad_display_container.dart'; +import 'package:interactive_media_ads/src/platform_interface/platform_interface.dart'; + +import 'test_stubs.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('build', (WidgetTester tester) async { + final TestPlatformAdDisplayContainer adDisplayContainer = + TestPlatformAdDisplayContainer( + PlatformAdDisplayContainerCreationParams( + onContainerAdded: (_) {}, + ), + onBuild: (_) => Container(), + ); + + await tester.pumpWidget(AdDisplayContainer.fromPlatform( + platform: adDisplayContainer, + )); + + expect(find.byType(Container), findsOneWidget); + }); + + testWidgets('constructor parameters are correctly passed to creation params', + (WidgetTester tester) async { + InteractiveMediaAdsPlatform.instance = + TestInteractiveMediaAdsPlatform(onCreatePlatformAdDisplayContainer: ( + PlatformAdDisplayContainerCreationParams params, + ) { + return TestPlatformAdDisplayContainer( + params, + onBuild: (_) => Container(), + ); + }, onCreatePlatformAdsLoader: (PlatformAdsLoaderCreationParams params) { + throw UnimplementedError(); + }, onCreatePlatformAdsManagerDelegate: ( + PlatformAdsManagerDelegateCreationParams params, + ) { + throw UnimplementedError(); + }); + + final AdDisplayContainer adDisplayContainer = AdDisplayContainer( + key: GlobalKey(), + onContainerAdded: (_) {}, + ); + + // The key passed to the default constructor is used by the super class + // and not passed to the platform implementation. + expect(adDisplayContainer.platform.params.key, isNull); + }); +} diff --git a/packages/interactive_media_ads/test/ads_loader_test.dart b/packages/interactive_media_ads/test/ads_loader_test.dart new file mode 100644 index 000000000000..424c86ccc1d5 --- /dev/null +++ b/packages/interactive_media_ads/test/ads_loader_test.dart @@ -0,0 +1,51 @@ +// 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/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:interactive_media_ads/interactive_media_ads.dart'; +import 'package:interactive_media_ads/src/platform_interface/platform_interface.dart'; + +import 'test_stubs.dart'; + +void main() { + test('contentComplete', () async { + final TestPlatformAdsLoader adsLoader = TestPlatformAdsLoader( + PlatformAdsLoaderCreationParams( + container: createTestAdDisplayContainer(), + onAdsLoaded: (PlatformOnAdsLoadedData data) {}, + onAdsLoadError: (AdsLoadErrorData data) {}, + ), + onContentComplete: expectAsync0(() async {}), + onRequestAds: (AdsRequest request) async {}, + ); + + final AdsLoader loader = AdsLoader.fromPlatform(adsLoader); + await loader.contentComplete(); + }); + + test('requestAds', () async { + final TestPlatformAdsLoader adsLoader = TestPlatformAdsLoader( + PlatformAdsLoaderCreationParams( + container: createTestAdDisplayContainer(), + onAdsLoaded: (PlatformOnAdsLoadedData data) {}, + onAdsLoadError: (AdsLoadErrorData data) {}, + ), + onRequestAds: expectAsync1((AdsRequest request) async {}), + onContentComplete: () async {}, + ); + + final AdsLoader loader = AdsLoader.fromPlatform(adsLoader); + await loader.requestAds(AdsRequest(adTagUrl: '')); + }); +} + +TestPlatformAdDisplayContainer createTestAdDisplayContainer() { + return TestPlatformAdDisplayContainer( + PlatformAdDisplayContainerCreationParams( + onContainerAdded: (_) {}, + ), + onBuild: (_) => Container(), + ); +} diff --git a/packages/interactive_media_ads/test/ads_manager_delegate_test.dart b/packages/interactive_media_ads/test/ads_manager_delegate_test.dart new file mode 100644 index 000000000000..ba6801dc41f3 --- /dev/null +++ b/packages/interactive_media_ads/test/ads_manager_delegate_test.dart @@ -0,0 +1,38 @@ +// 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_test/flutter_test.dart'; +import 'package:interactive_media_ads/interactive_media_ads.dart'; +import 'package:interactive_media_ads/src/platform_interface/platform_interface.dart'; + +import 'test_stubs.dart'; + +void main() { + test('passes params to platform instance', () async { + InteractiveMediaAdsPlatform.instance = TestInteractiveMediaAdsPlatform( + onCreatePlatformAdsManagerDelegate: + (PlatformAdsManagerDelegateCreationParams params) { + return TestPlatformAdsManagerDelegate(params); + }, + onCreatePlatformAdsLoader: (PlatformAdsLoaderCreationParams params) { + throw UnimplementedError(); + }, + onCreatePlatformAdDisplayContainer: + (PlatformAdDisplayContainerCreationParams params) { + throw UnimplementedError(); + }, + ); + + void onAdEvent(AdEvent event) {} + void onAdErrorEvent(AdErrorEvent event) {} + + final AdsManagerDelegate delegate = AdsManagerDelegate( + onAdEvent: onAdEvent, + onAdErrorEvent: onAdErrorEvent, + ); + + expect(delegate.platform.params.onAdEvent, onAdEvent); + expect(delegate.platform.params.onAdErrorEvent, onAdErrorEvent); + }); +} diff --git a/packages/interactive_media_ads/test/ads_manager_test.dart b/packages/interactive_media_ads/test/ads_manager_test.dart new file mode 100644 index 000000000000..cb5f42ccb3f6 --- /dev/null +++ b/packages/interactive_media_ads/test/ads_manager_test.dart @@ -0,0 +1,93 @@ +// 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/cupertino.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:interactive_media_ads/interactive_media_ads.dart'; +import 'package:interactive_media_ads/src/platform_interface/platform_interface.dart'; + +import 'test_stubs.dart'; + +void main() { + test('init', () async { + final TestAdsManager platformManager = TestAdsManager( + onInit: expectAsync1((_) async {}), + ); + + final AdsManager manager = createAdsManager(platformManager); + await manager.init(); + }); + + test('start', () async { + final TestAdsManager platformManager = TestAdsManager( + onStart: expectAsync1((_) async {}), + ); + + final AdsManager manager = createAdsManager(platformManager); + await manager.start(); + }); + + test('setAdsManagerDelegate', () async { + final TestAdsManager platformManager = TestAdsManager( + onSetAdsManagerDelegate: expectAsync1((_) async {}), + ); + + final AdsManager manager = createAdsManager(platformManager); + await manager.setAdsManagerDelegate(AdsManagerDelegate.fromPlatform( + TestPlatformAdsManagerDelegate( + const PlatformAdsManagerDelegateCreationParams(), + ), + )); + }); + + test('destroy', () async { + final TestAdsManager platformManager = TestAdsManager( + onDestroy: expectAsync0(() async {}), + ); + + final AdsManager manager = createAdsManager(platformManager); + await manager.destroy(); + }); +} + +AdsManager createAdsManager(PlatformAdsManager platformManager) { + InteractiveMediaAdsPlatform.instance = TestInteractiveMediaAdsPlatform( + onCreatePlatformAdsLoader: (PlatformAdsLoaderCreationParams params) { + return TestPlatformAdsLoader(params, + onContentComplete: () async {}, + onRequestAds: (AdsRequest request) async {}); + }, + onCreatePlatformAdsManagerDelegate: + (PlatformAdsManagerDelegateCreationParams params) { + throw UnimplementedError(); + }, + onCreatePlatformAdDisplayContainer: + (PlatformAdDisplayContainerCreationParams params) { + throw UnimplementedError(); + }, + ); + + late final AdsManager manager; + + final AdsLoader loader = AdsLoader( + container: AdDisplayContainer.fromPlatform( + platform: TestPlatformAdDisplayContainer( + PlatformAdDisplayContainerCreationParams( + onContainerAdded: (_) {}, + ), + onBuild: (_) => Container(), + ), + ), + onAdsLoaded: (OnAdsLoadedData data) { + manager = data.manager; + }, + onAdsLoadError: (_) {}, + ); + + loader.platform.params.onAdsLoaded(PlatformOnAdsLoadedData( + manager: platformManager, + )); + + return manager; +} diff --git a/packages/interactive_media_ads/test/test_stubs.dart b/packages/interactive_media_ads/test/test_stubs.dart new file mode 100644 index 000000000000..fd59fb3073f1 --- /dev/null +++ b/packages/interactive_media_ads/test/test_stubs.dart @@ -0,0 +1,127 @@ +// 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:interactive_media_ads/src/platform_interface/platform_interface.dart'; + +final class TestInteractiveMediaAdsPlatform + extends InteractiveMediaAdsPlatform { + TestInteractiveMediaAdsPlatform({ + required this.onCreatePlatformAdsLoader, + required this.onCreatePlatformAdsManagerDelegate, + required this.onCreatePlatformAdDisplayContainer, + }); + + PlatformAdsLoader Function(PlatformAdsLoaderCreationParams params) + onCreatePlatformAdsLoader; + + PlatformAdsManagerDelegate Function( + PlatformAdsManagerDelegateCreationParams params, + ) onCreatePlatformAdsManagerDelegate; + + PlatformAdDisplayContainer Function( + PlatformAdDisplayContainerCreationParams params, + ) onCreatePlatformAdDisplayContainer; + + @override + PlatformAdsLoader createPlatformAdsLoader( + PlatformAdsLoaderCreationParams params, + ) { + return onCreatePlatformAdsLoader(params); + } + + @override + PlatformAdsManagerDelegate createPlatformAdsManagerDelegate( + PlatformAdsManagerDelegateCreationParams params, + ) { + return onCreatePlatformAdsManagerDelegate(params); + } + + @override + PlatformAdDisplayContainer createPlatformAdDisplayContainer( + PlatformAdDisplayContainerCreationParams params, + ) { + return onCreatePlatformAdDisplayContainer(params); + } +} + +final class TestPlatformAdDisplayContainer extends PlatformAdDisplayContainer { + TestPlatformAdDisplayContainer( + super.params, { + required this.onBuild, + }) : super.implementation(); + + Widget Function(BuildContext context) onBuild; + + @override + Widget build(BuildContext context) { + return onBuild.call(context); + } +} + +final class TestPlatformAdsLoader extends PlatformAdsLoader { + TestPlatformAdsLoader( + super.params, { + required this.onContentComplete, + required this.onRequestAds, + }) : super.implementation(); + + Future Function() onContentComplete; + + Future Function(AdsRequest request) onRequestAds; + + @override + Future contentComplete() async { + return onContentComplete(); + } + + @override + Future requestAds(AdsRequest request) async { + return onRequestAds(request); + } +} + +final class TestPlatformAdsManagerDelegate extends PlatformAdsManagerDelegate { + TestPlatformAdsManagerDelegate(super.params) : super.implementation(); +} + +class TestAdsManager extends PlatformAdsManager { + TestAdsManager({ + this.onInit, + this.onSetAdsManagerDelegate, + this.onStart, + this.onDestroy, + }); + + Future Function(AdsManagerInitParams params)? onInit; + + Future Function(PlatformAdsManagerDelegate delegate)? + onSetAdsManagerDelegate; + + Future Function(AdsManagerStartParams params)? onStart; + + Future Function()? onDestroy; + + @override + Future init(AdsManagerInitParams params) async { + return onInit?.call(params); + } + + @override + Future setAdsManagerDelegate( + PlatformAdsManagerDelegate delegate, + ) async { + return onSetAdsManagerDelegate?.call(delegate); + } + + @override + Future start(AdsManagerStartParams params) async { + return onStart?.call(params); + } + + @override + Future destroy() async { + return onDestroy?.call(); + } +} From 974f2845c7aae730bbab26786278af84771e3c36 Mon Sep 17 00:00:00 2001 From: "auto-submit[bot]" <98614782+auto-submit[bot]@users.noreply.github.com> Date: Thu, 28 Mar 2024 02:08:26 +0000 Subject: [PATCH 112/126] Reverts "[interactive_media_ads] Creates and adds the `interactive_media_ads` plugin (#6060)" (#6417) Reverts: flutter/packages#6060 Initiated by: ditman Reason for reverting: This is consistently breaking in `main` with: ``` BUILD FAILED in 1m 2s 01:03 +0: loading /b/s/w/ir/x/w/packages/packages/interactive_media_ads/example/integration_test/interactive_media_ads_test.dart Running Gradle task 'assembleDebug'... 63.0s [!] App requires Multidex support ``` It probably needs something [like this](https://github.com/flutter/packag Original PR Author: bparrishMines Reviewed By: {stuartmorgan} This change reverts the following previous change: Part of https://github.com/flutter/flutter/issues/134228 Initial implementation of the plugin. * Adds minimum changes to pass CI (licenses, gradle, podpec changes, etc..). * Adds platform interface and tests for it. --- .github/dependabot.yml | 28 - CODEOWNERS | 1 - README.md | 1 - packages/interactive_media_ads/AUTHORS | 6 - packages/interactive_media_ads/CHANGELOG.md | 3 - packages/interactive_media_ads/LICENSE | 25 - packages/interactive_media_ads/README.md | 15 - .../android/build.gradle | 78 --- .../android/settings.gradle | 1 - .../android/src/main/AndroidManifest.xml | 3 - .../InteractiveMediaAdsPlugin.kt | 37 -- .../InteractiveMediaAdsPluginTest.kt | 31 - .../interactive_media_ads/example/README.md | 9 - .../example/android/app/build.gradle | 68 -- .../MainActivityTest.kt | 17 - .../android/app/src/debug/AndroidManifest.xml | 20 - .../android/app/src/main/AndroidManifest.xml | 44 -- .../DriverExtensionActivity.kt | 10 - .../MainActivity.kt | 9 - .../io/flutter/plugins/DartIntegrationTest.kt | 16 - .../res/drawable-v21/launch_background.xml | 12 - .../main/res/drawable/launch_background.xml | 12 - .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 544 -> 0 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 442 -> 0 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 721 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 1031 -> 0 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 1443 -> 0 bytes .../app/src/main/res/values-night/styles.xml | 18 - .../app/src/main/res/values/styles.xml | 18 - .../example/android/build.gradle | 39 -- .../example/android/gradle.properties | 3 - .../gradle/wrapper/gradle-wrapper.properties | 5 - .../example/android/settings.gradle | 39 -- .../interactive_media_ads_test.dart | 23 - .../ios/Flutter/AppFrameworkInfo.plist | 26 - .../example/ios/Flutter/Debug.xcconfig | 2 - .../example/ios/Flutter/Release.xcconfig | 2 - .../interactive_media_ads/example/ios/Podfile | 44 -- .../ios/Runner.xcodeproj/project.pbxproj | 619 ------------------ .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/WorkspaceSettings.xcsettings | 8 - .../xcshareddata/xcschemes/Runner.xcscheme | 98 --- .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/WorkspaceSettings.xcsettings | 8 - .../example/ios/Runner/AppDelegate.swift | 17 - .../AppIcon.appiconset/Contents.json | 122 ---- .../Icon-App-1024x1024@1x.png | Bin 10932 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 295 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 406 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 450 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 282 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 462 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 704 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 406 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 586 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 862 -> 0 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 862 -> 0 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 1674 -> 0 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 762 -> 0 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 1226 -> 0 bytes .../Icon-App-83.5x83.5@2x.png | Bin 1418 -> 0 bytes .../LaunchImage.imageset/Contents.json | 23 - .../LaunchImage.imageset/LaunchImage.png | Bin 68 -> 0 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 68 -> 0 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 68 -> 0 bytes .../Runner/Base.lproj/LaunchScreen.storyboard | 37 -- .../ios/Runner/Base.lproj/Main.storyboard | 26 - .../example/ios/Runner/Info.plist | 49 -- .../ios/Runner/Runner-Bridging-Header.h | 5 - .../example/ios/RunnerTests/RunnerTests.swift | 30 - .../example/lib/main.dart | 44 -- .../example/pubspec.yaml | 25 - .../example/test_driver/integration_test.dart | 7 - .../interactive_media_ads/ios/Assets/.gitkeep | 0 .../Classes/InteractiveMediaAdsPlugin.swift | 24 - .../ios/Resources/PrivacyInfo.xcprivacy | 6 - .../ios/interactive_media_ads.podspec | 29 - .../lib/interactive_media_ads.dart | 10 - .../lib/src/ad_display_container.dart | 102 --- .../lib/src/ads_loader.dart | 162 ----- .../lib/src/ads_manager_delegate.dart | 88 --- .../lib/src/platform_interface/ad_error.dart | 156 ----- .../lib/src/platform_interface/ad_event.dart | 53 -- .../src/platform_interface/ads_request.dart | 12 - .../interactive_media_ads_platform.dart | 32 - .../platform_ad_display_container.dart | 95 --- .../platform_ads_loader.dart | 119 ---- .../platform_ads_manager.dart | 34 - .../platform_ads_manager_delegate.dart | 88 --- .../platform_interface.dart | 12 - packages/interactive_media_ads/pubspec.yaml | 31 - .../test/ad_display_container_test.dart | 59 -- .../test/ads_loader_test.dart | 51 -- .../test/ads_manager_delegate_test.dart | 38 -- .../test/ads_manager_test.dart | 93 --- .../test/test_stubs.dart | 127 ---- 98 files changed, 3234 deletions(-) delete mode 100644 packages/interactive_media_ads/AUTHORS delete mode 100644 packages/interactive_media_ads/CHANGELOG.md delete mode 100644 packages/interactive_media_ads/LICENSE delete mode 100644 packages/interactive_media_ads/README.md delete mode 100644 packages/interactive_media_ads/android/build.gradle delete mode 100644 packages/interactive_media_ads/android/settings.gradle delete mode 100644 packages/interactive_media_ads/android/src/main/AndroidManifest.xml delete mode 100644 packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsPlugin.kt delete mode 100644 packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsPluginTest.kt delete mode 100644 packages/interactive_media_ads/example/README.md delete mode 100644 packages/interactive_media_ads/example/android/app/build.gradle delete mode 100644 packages/interactive_media_ads/example/android/app/src/androidTest/kotlin/dev/flutter/packages/interactive_media_ads_example/MainActivityTest.kt delete mode 100644 packages/interactive_media_ads/example/android/app/src/debug/AndroidManifest.xml delete mode 100644 packages/interactive_media_ads/example/android/app/src/main/AndroidManifest.xml delete mode 100644 packages/interactive_media_ads/example/android/app/src/main/kotlin/dev/flutter/packages/interactive_media_ads_example/DriverExtensionActivity.kt delete mode 100644 packages/interactive_media_ads/example/android/app/src/main/kotlin/dev/flutter/packages/interactive_media_ads_example/MainActivity.kt delete mode 100644 packages/interactive_media_ads/example/android/app/src/main/kotlin/io/flutter/plugins/DartIntegrationTest.kt delete mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/drawable-v21/launch_background.xml delete mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/drawable/launch_background.xml delete mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png delete mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png delete mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png delete mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png delete mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/values-night/styles.xml delete mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/values/styles.xml delete mode 100644 packages/interactive_media_ads/example/android/build.gradle delete mode 100644 packages/interactive_media_ads/example/android/gradle.properties delete mode 100644 packages/interactive_media_ads/example/android/gradle/wrapper/gradle-wrapper.properties delete mode 100644 packages/interactive_media_ads/example/android/settings.gradle delete mode 100644 packages/interactive_media_ads/example/integration_test/interactive_media_ads_test.dart delete mode 100644 packages/interactive_media_ads/example/ios/Flutter/AppFrameworkInfo.plist delete mode 100644 packages/interactive_media_ads/example/ios/Flutter/Debug.xcconfig delete mode 100644 packages/interactive_media_ads/example/ios/Flutter/Release.xcconfig delete mode 100644 packages/interactive_media_ads/example/ios/Podfile delete mode 100644 packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.pbxproj delete mode 100644 packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 packages/interactive_media_ads/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme delete mode 100644 packages/interactive_media_ads/example/ios/Runner.xcworkspace/contents.xcworkspacedata delete mode 100644 packages/interactive_media_ads/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 packages/interactive_media_ads/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 packages/interactive_media_ads/example/ios/Runner/AppDelegate.swift delete mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png delete mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png delete mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png delete mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png delete mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png delete mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png delete mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png delete mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png delete mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png delete mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png delete mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png delete mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png delete mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png delete mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png delete mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png delete mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json delete mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png delete mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png delete mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png delete mode 100644 packages/interactive_media_ads/example/ios/Runner/Base.lproj/LaunchScreen.storyboard delete mode 100644 packages/interactive_media_ads/example/ios/Runner/Base.lproj/Main.storyboard delete mode 100644 packages/interactive_media_ads/example/ios/Runner/Info.plist delete mode 100644 packages/interactive_media_ads/example/ios/Runner/Runner-Bridging-Header.h delete mode 100644 packages/interactive_media_ads/example/ios/RunnerTests/RunnerTests.swift delete mode 100644 packages/interactive_media_ads/example/lib/main.dart delete mode 100644 packages/interactive_media_ads/example/pubspec.yaml delete mode 100644 packages/interactive_media_ads/example/test_driver/integration_test.dart delete mode 100644 packages/interactive_media_ads/ios/Assets/.gitkeep delete mode 100644 packages/interactive_media_ads/ios/Classes/InteractiveMediaAdsPlugin.swift delete mode 100644 packages/interactive_media_ads/ios/Resources/PrivacyInfo.xcprivacy delete mode 100644 packages/interactive_media_ads/ios/interactive_media_ads.podspec delete mode 100644 packages/interactive_media_ads/lib/interactive_media_ads.dart delete mode 100644 packages/interactive_media_ads/lib/src/ad_display_container.dart delete mode 100644 packages/interactive_media_ads/lib/src/ads_loader.dart delete mode 100644 packages/interactive_media_ads/lib/src/ads_manager_delegate.dart delete mode 100644 packages/interactive_media_ads/lib/src/platform_interface/ad_error.dart delete mode 100644 packages/interactive_media_ads/lib/src/platform_interface/ad_event.dart delete mode 100644 packages/interactive_media_ads/lib/src/platform_interface/ads_request.dart delete mode 100644 packages/interactive_media_ads/lib/src/platform_interface/interactive_media_ads_platform.dart delete mode 100644 packages/interactive_media_ads/lib/src/platform_interface/platform_ad_display_container.dart delete mode 100644 packages/interactive_media_ads/lib/src/platform_interface/platform_ads_loader.dart delete mode 100644 packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager.dart delete mode 100644 packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager_delegate.dart delete mode 100644 packages/interactive_media_ads/lib/src/platform_interface/platform_interface.dart delete mode 100644 packages/interactive_media_ads/pubspec.yaml delete mode 100644 packages/interactive_media_ads/test/ad_display_container_test.dart delete mode 100644 packages/interactive_media_ads/test/ads_loader_test.dart delete mode 100644 packages/interactive_media_ads/test/ads_manager_delegate_test.dart delete mode 100644 packages/interactive_media_ads/test/ads_manager_test.dart delete mode 100644 packages/interactive_media_ads/test/test_stubs.dart diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d1f55572e699..d9157022847f 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -395,34 +395,6 @@ updates: - dependency-name: "*" update-types: ["version-update:semver-minor", "version-update:semver-patch"] - - package-ecosystem: "gradle" - directory: "/packages/interactive_media_ads/android" - commit-message: - prefix: "[interactive_media_ads]" - schedule: - interval: "weekly" - open-pull-requests-limit: 10 - ignore: - - dependency-name: "com.android.tools.build:gradle" - update-types: ["version-update:semver-minor", "version-update:semver-patch"] - - dependency-name: "junit:junit" - update-types: ["version-update:semver-minor", "version-update:semver-patch"] - - dependency-name: "org.mockito:*" - update-types: ["version-update:semver-minor", "version-update:semver-patch"] - - dependency-name: "androidx.test:*" - update-types: ["version-update:semver-minor", "version-update:semver-patch"] - - - package-ecosystem: "gradle" - directory: "/packages/interactive_media_ads/example/android/app" - commit-message: - prefix: "[interactive_media_ads]" - schedule: - interval: "weekly" - open-pull-requests-limit: 10 - ignore: - - dependency-name: "*" - update-types: ["version-update:semver-minor", "version-update:semver-patch"] - - package-ecosystem: "gradle" directory: "/packages/image_picker/image_picker/example/android/app" commit-message: diff --git a/CODEOWNERS b/CODEOWNERS index 77f7adb749f2..4d7c8b95b3f7 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -23,7 +23,6 @@ packages/google_identity_services_web/** @ditman packages/google_maps_flutter/** @stuartmorgan packages/google_sign_in/** @stuartmorgan packages/image_picker/** @tarrinneal -packages/interactive_media_ads/** @bparrishMines packages/in_app_purchase/** @bparrishMines packages/local_auth/** @stuartmorgan packages/metrics_center/** @keyonghan diff --git a/README.md b/README.md index 1996143171fb..ac0078ff5f72 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,6 @@ These are the packages hosted in this repository: | [google\_maps\_flutter](./packages/google_maps_flutter/) | [![pub package](https://img.shields.io/pub/v/google_maps_flutter.svg)](https://pub.dev/packages/google_maps_flutter) | [![pub points](https://img.shields.io/pub/points/google_maps_flutter)](https://pub.dev/packages/google_maps_flutter/score) | [![popularity](https://img.shields.io/pub/popularity/google_maps_flutter)](https://pub.dev/packages/google_maps_flutter/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20maps?label=)](https://github.com/flutter/flutter/labels/p%3A%20maps) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20google_maps_flutter?label=)](https://github.com/flutter/packages/labels/p%3A%20google_maps_flutter) | | [google\_sign\_in](./packages/google_sign_in/) | [![pub package](https://img.shields.io/pub/v/google_sign_in.svg)](https://pub.dev/packages/google_sign_in) | [![pub points](https://img.shields.io/pub/points/google_sign_in)](https://pub.dev/packages/google_sign_in/score) | [![popularity](https://img.shields.io/pub/popularity/google_sign_in)](https://pub.dev/packages/google_sign_in/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20google_sign_in?label=)](https://github.com/flutter/flutter/labels/p%3A%20google_sign_in) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20google_sign_in?label=)](https://github.com/flutter/packages/labels/p%3A%20google_sign_in) | | [image\_picker](./packages/image_picker/) | [![pub package](https://img.shields.io/pub/v/image_picker.svg)](https://pub.dev/packages/image_picker) | [![pub points](https://img.shields.io/pub/points/image_picker)](https://pub.dev/packages/image_picker/score) | [![popularity](https://img.shields.io/pub/popularity/image_picker)](https://pub.dev/packages/image_picker/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20image_picker?label=)](https://github.com/flutter/flutter/labels/p%3A%20image_picker) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20image_picker?label=)](https://github.com/flutter/packages/labels/p%3A%20image_picker) | -| [interactive\_media\_ads](./packages/interactive_media_ads/) | [![pub package](https://img.shields.io/pub/v/interactive_media_ads.svg)](https://pub.dev/packages/interactive_media_ads) | [![pub points](https://img.shields.io/pub/points/interactive_media_ads)](https://pub.dev/packages/interactive_media_ads/score) | [![popularity](https://img.shields.io/pub/popularity/interactive_media_ads)](https://pub.dev/packages/interactive_media_ads/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20interactive_media_ads?label=)](https://github.com/flutter/flutter/labels/p%3A%20interactive_media_ads) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20interactive_media_ads?label=)](https://github.com/flutter/packages/labels/p%3A%20interactive_media_ads) | | [in\_app\_purchase](./packages/in_app_purchase/) | [![pub package](https://img.shields.io/pub/v/in_app_purchase.svg)](https://pub.dev/packages/in_app_purchase) | [![pub points](https://img.shields.io/pub/points/in_app_purchase)](https://pub.dev/packages/in_app_purchase/score) | [![popularity](https://img.shields.io/pub/popularity/in_app_purchase)](https://pub.dev/packages/in_app_purchase/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20in_app_purchase?label=)](https://github.com/flutter/flutter/labels/p%3A%20in_app_purchase) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20in_app_purchase?label=)](https://github.com/flutter/packages/labels/p%3A%20in_app_purchase) | | [ios\_platform\_images](./packages/ios_platform_images/) | [![pub package](https://img.shields.io/pub/v/ios_platform_images.svg)](https://pub.dev/packages/ios_platform_images) | [![pub points](https://img.shields.io/pub/points/ios_platform_images)](https://pub.dev/packages/ios_platform_images/score) | [![popularity](https://img.shields.io/pub/popularity/ios_platform_images)](https://pub.dev/packages/ios_platform_images/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20ios_platform_images?label=)](https://github.com/flutter/flutter/labels/p%3A%20ios_platform_images) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20ios_platform_images?label=)](https://github.com/flutter/packages/labels/p%3A%20ios_platform_images) | | [local\_auth](./packages/local_auth/) | [![pub package](https://img.shields.io/pub/v/local_auth.svg)](https://pub.dev/packages/local_auth) | [![pub points](https://img.shields.io/pub/points/local_auth)](https://pub.dev/packages/local_auth/score) | [![popularity](https://img.shields.io/pub/popularity/local_auth)](https://pub.dev/packages/local_auth/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20local_auth?label=)](https://github.com/flutter/flutter/labels/p%3A%20local_auth) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20local_auth?label=)](https://github.com/flutter/packages/labels/p%3A%20local_auth) | diff --git a/packages/interactive_media_ads/AUTHORS b/packages/interactive_media_ads/AUTHORS deleted file mode 100644 index 557dff97933b..000000000000 --- a/packages/interactive_media_ads/AUTHORS +++ /dev/null @@ -1,6 +0,0 @@ -# Below is a list of people and organizations that have contributed -# to the Flutter project. Names should be added to the list like so: -# -# Name/Organization - -Google Inc. diff --git a/packages/interactive_media_ads/CHANGELOG.md b/packages/interactive_media_ads/CHANGELOG.md deleted file mode 100644 index 477158a8871e..000000000000 --- a/packages/interactive_media_ads/CHANGELOG.md +++ /dev/null @@ -1,3 +0,0 @@ -## 0.0.1 - -* Adds platform interface for Android and iOS. diff --git a/packages/interactive_media_ads/LICENSE b/packages/interactive_media_ads/LICENSE deleted file mode 100644 index c6823b81eb84..000000000000 --- a/packages/interactive_media_ads/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright 2013 The Flutter Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/interactive_media_ads/README.md b/packages/interactive_media_ads/README.md deleted file mode 100644 index d1d17151f02b..000000000000 --- a/packages/interactive_media_ads/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# interactive\_media\_ads - -Flutter plugin for the [Interactive Media Ads SDKs][1]. - -[![pub package](https://img.shields.io/pub/v/webview_flutter.svg)](https://pub.dev/packages/interactive_media_ads) - -A Flutter plugin for using the Interactive Media Ads SDKs on Android and iOS. - -| | Android | iOS | -|-------------|---------|-------| -| **Support** | SDK 19+ | 12.0+ | - -**This package is still in development.** - -[1]: https://developers.google.com/interactive-media-ads diff --git a/packages/interactive_media_ads/android/build.gradle b/packages/interactive_media_ads/android/build.gradle deleted file mode 100644 index 3490e07ba38e..000000000000 --- a/packages/interactive_media_ads/android/build.gradle +++ /dev/null @@ -1,78 +0,0 @@ -group 'dev.flutter.packages.interactive_media_ads' -version '1.0-SNAPSHOT' - -buildscript { - ext.kotlin_version = '1.7.10' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:8.0.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' - -android { - if (project.android.hasProperty("namespace")) { - namespace 'dev.flutter.packages.interactive_media_ads' - } - - compileSdk 34 - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = '1.8' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - test.java.srcDirs += 'src/test/kotlin' - } - - defaultConfig { - minSdk 19 - } - - dependencies { - implementation 'androidx.annotation:annotation:1.5.0' - testImplementation 'junit:junit:4.13.2' - testImplementation 'org.jetbrains.kotlin:kotlin-test' - testImplementation 'org.mockito:mockito-inline:5.1.0' - testImplementation 'androidx.test:core:1.3.0' - } - - lintOptions { - checkAllWarnings true - warningsAsErrors true - disable 'AndroidGradlePluginVersion', 'InvalidPackage', 'GradleDependency' - } - - testOptions { - unitTests.includeAndroidResources = true - unitTests.returnDefaultValues = true - unitTests.all { - useJUnitPlatform() - testLogging { - events "passed", "skipped", "failed", "standardOut", "standardError" - outputs.upToDateWhen {false} - showStandardStreams = true - } - } - } -} diff --git a/packages/interactive_media_ads/android/settings.gradle b/packages/interactive_media_ads/android/settings.gradle deleted file mode 100644 index 388e84d5a359..000000000000 --- a/packages/interactive_media_ads/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'interactive_media_ads' diff --git a/packages/interactive_media_ads/android/src/main/AndroidManifest.xml b/packages/interactive_media_ads/android/src/main/AndroidManifest.xml deleted file mode 100644 index cde3df78e2ba..000000000000 --- a/packages/interactive_media_ads/android/src/main/AndroidManifest.xml +++ /dev/null @@ -1,3 +0,0 @@ - - diff --git a/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsPlugin.kt b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsPlugin.kt deleted file mode 100644 index 50595236588a..000000000000 --- a/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsPlugin.kt +++ /dev/null @@ -1,37 +0,0 @@ -// 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. - -package dev.flutter.packages.interactive_media_ads - -import io.flutter.embedding.engine.plugins.FlutterPlugin -import io.flutter.plugin.common.MethodCall -import io.flutter.plugin.common.MethodChannel -import io.flutter.plugin.common.MethodChannel.MethodCallHandler -import io.flutter.plugin.common.MethodChannel.Result - -/** InteractiveMediaAdsPlugin */ -class InteractiveMediaAdsPlugin : FlutterPlugin, MethodCallHandler { - /// The MethodChannel that will the communication between Flutter and native Android - /// - /// This local reference serves to register the plugin with the Flutter Engine and unregister it - /// when the Flutter Engine is detached from the Activity - private lateinit var channel: MethodChannel - - override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { - channel = MethodChannel(flutterPluginBinding.binaryMessenger, "interactive_media_ads") - channel.setMethodCallHandler(this) - } - - override fun onMethodCall(call: MethodCall, result: Result) { - if (call.method == "getPlatformVersion") { - result.success("Android ${android.os.Build.VERSION.RELEASE}") - } else { - result.notImplemented() - } - } - - override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { - channel.setMethodCallHandler(null) - } -} diff --git a/packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsPluginTest.kt b/packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsPluginTest.kt deleted file mode 100644 index 3adc0d0a56b4..000000000000 --- a/packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsPluginTest.kt +++ /dev/null @@ -1,31 +0,0 @@ -// 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. - -package dev.flutter.packages.interactive_media_ads - -import io.flutter.plugin.common.MethodCall -import io.flutter.plugin.common.MethodChannel -import kotlin.test.Test -import org.mockito.Mockito - -/* - * This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation. - * - * Once you have built the plugin's example app, you can run these tests from the command - * line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or - * you can run them directly from IDEs that support JUnit such as Android Studio. - */ - -internal class InteractiveMediaAdsPluginTest { - @Test - fun onMethodCall_getPlatformVersion_returnsExpectedValue() { - val plugin = InteractiveMediaAdsPlugin() - - val call = MethodCall("getPlatformVersion", null) - val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java) - plugin.onMethodCall(call, mockResult) - - Mockito.verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE) - } -} diff --git a/packages/interactive_media_ads/example/README.md b/packages/interactive_media_ads/example/README.md deleted file mode 100644 index 96b8bb17dbff..000000000000 --- a/packages/interactive_media_ads/example/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Platform Implementation Test App - -This is a test app for manual testing and automated integration testing -of this platform implementation. It is not intended to demonstrate actual use of -this package, since the intent is that plugin clients use the app-facing -package. - -Unless you are making changes to this implementation package, this example is -very unlikely to be relevant. diff --git a/packages/interactive_media_ads/example/android/app/build.gradle b/packages/interactive_media_ads/example/android/app/build.gradle deleted file mode 100644 index 326ce9b6246f..000000000000 --- a/packages/interactive_media_ads/example/android/app/build.gradle +++ /dev/null @@ -1,68 +0,0 @@ -plugins { - id "com.android.application" - id "kotlin-android" - id "dev.flutter.flutter-gradle-plugin" -} - -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -android { - namespace "dev.flutter.packages.interactive_media_ads_example" - compileSdk flutter.compileSdkVersion - ndkVersion flutter.ndkVersion - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = '1.8' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - defaultConfig { - applicationId "dev.flutter.packages.interactive_media_ads_example" - minSdk flutter.minSdkVersion - targetSdk flutter.targetSdkVersion - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - signingConfig signingConfigs.debug - } - } -} - -flutter { - source '../..' -} - -dependencies { - testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test:runner:1.2.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' - api 'androidx.test:core:1.4.0' -} diff --git a/packages/interactive_media_ads/example/android/app/src/androidTest/kotlin/dev/flutter/packages/interactive_media_ads_example/MainActivityTest.kt b/packages/interactive_media_ads/example/android/app/src/androidTest/kotlin/dev/flutter/packages/interactive_media_ads_example/MainActivityTest.kt deleted file mode 100644 index 7f51c3fccf13..000000000000 --- a/packages/interactive_media_ads/example/android/app/src/androidTest/kotlin/dev/flutter/packages/interactive_media_ads_example/MainActivityTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -// 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. - -package dev.flutter.packages.interactive_media_ads_example - -import androidx.test.rule.ActivityTestRule -import dev.flutter.plugins.integration_test.FlutterTestRunner -import io.flutter.plugins.DartIntegrationTest -import org.junit.Rule -import org.junit.runner.RunWith - -@DartIntegrationTest -@RunWith(FlutterTestRunner::class) -class MainActivityTest { - @JvmField @Rule var rule = ActivityTestRule(MainActivity::class.java) -} diff --git a/packages/interactive_media_ads/example/android/app/src/debug/AndroidManifest.xml b/packages/interactive_media_ads/example/android/app/src/debug/AndroidManifest.xml deleted file mode 100644 index 4665eaa3ae96..000000000000 --- a/packages/interactive_media_ads/example/android/app/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - diff --git a/packages/interactive_media_ads/example/android/app/src/main/AndroidManifest.xml b/packages/interactive_media_ads/example/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index 8733f2b862ab..000000000000 --- a/packages/interactive_media_ads/example/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/interactive_media_ads/example/android/app/src/main/kotlin/dev/flutter/packages/interactive_media_ads_example/DriverExtensionActivity.kt b/packages/interactive_media_ads/example/android/app/src/main/kotlin/dev/flutter/packages/interactive_media_ads_example/DriverExtensionActivity.kt deleted file mode 100644 index d43924d0054f..000000000000 --- a/packages/interactive_media_ads/example/android/app/src/main/kotlin/dev/flutter/packages/interactive_media_ads_example/DriverExtensionActivity.kt +++ /dev/null @@ -1,10 +0,0 @@ -// 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. - -package dev.flutter.packages.interactive_media_ads_example - -import io.flutter.embedding.android.FlutterActivity - -/** Test Activity that sets the name of the Dart method entrypoint in the manifest. */ -class DriverExtensionActivity : FlutterActivity() diff --git a/packages/interactive_media_ads/example/android/app/src/main/kotlin/dev/flutter/packages/interactive_media_ads_example/MainActivity.kt b/packages/interactive_media_ads/example/android/app/src/main/kotlin/dev/flutter/packages/interactive_media_ads_example/MainActivity.kt deleted file mode 100644 index 3392748b8720..000000000000 --- a/packages/interactive_media_ads/example/android/app/src/main/kotlin/dev/flutter/packages/interactive_media_ads_example/MainActivity.kt +++ /dev/null @@ -1,9 +0,0 @@ -// 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. - -package dev.flutter.packages.interactive_media_ads_example - -import io.flutter.embedding.android.FlutterActivity - -class MainActivity : FlutterActivity() diff --git a/packages/interactive_media_ads/example/android/app/src/main/kotlin/io/flutter/plugins/DartIntegrationTest.kt b/packages/interactive_media_ads/example/android/app/src/main/kotlin/io/flutter/plugins/DartIntegrationTest.kt deleted file mode 100644 index 099fb761cc9d..000000000000 --- a/packages/interactive_media_ads/example/android/app/src/main/kotlin/io/flutter/plugins/DartIntegrationTest.kt +++ /dev/null @@ -1,16 +0,0 @@ -// 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. - -package io.flutter.plugins - -/* - * Annotation to aid repository tooling in determining if a test is - * a native java unit test or a java class with a dart integration. - * - * See: https://github.com/flutter/flutter/wiki/Plugin-Tests#enabling-android-ui-tests - * for more infomation. - */ -@Retention(AnnotationRetention.RUNTIME) -@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) -annotation class DartIntegrationTest diff --git a/packages/interactive_media_ads/example/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/interactive_media_ads/example/android/app/src/main/res/drawable-v21/launch_background.xml deleted file mode 100644 index f74085f3f6a2..000000000000 --- a/packages/interactive_media_ads/example/android/app/src/main/res/drawable-v21/launch_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/packages/interactive_media_ads/example/android/app/src/main/res/drawable/launch_background.xml b/packages/interactive_media_ads/example/android/app/src/main/res/drawable/launch_background.xml deleted file mode 100644 index 304732f88420..000000000000 --- a/packages/interactive_media_ads/example/android/app/src/main/res/drawable/launch_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/packages/interactive_media_ads/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/interactive_media_ads/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index db77bb4b7b0906d62b1847e87f15cdcacf6a4f29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ diff --git a/packages/interactive_media_ads/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/interactive_media_ads/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 17987b79bb8a35cc66c3c1fd44f5a5526c1b78be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ diff --git a/packages/interactive_media_ads/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/interactive_media_ads/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index d5f1c8d34e7a88e3f88bea192c3a370d44689c3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof diff --git a/packages/interactive_media_ads/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/interactive_media_ads/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 4d6372eebdb28e45604e46eeda8dd24651419bc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` diff --git a/packages/interactive_media_ads/example/android/app/src/main/res/values-night/styles.xml b/packages/interactive_media_ads/example/android/app/src/main/res/values-night/styles.xml deleted file mode 100644 index 06952be745f9..000000000000 --- a/packages/interactive_media_ads/example/android/app/src/main/res/values-night/styles.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/packages/interactive_media_ads/example/android/app/src/main/res/values/styles.xml b/packages/interactive_media_ads/example/android/app/src/main/res/values/styles.xml deleted file mode 100644 index cb1ef88056ed..000000000000 --- a/packages/interactive_media_ads/example/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/packages/interactive_media_ads/example/android/build.gradle b/packages/interactive_media_ads/example/android/build.gradle deleted file mode 100644 index 29a592fd9d6d..000000000000 --- a/packages/interactive_media_ads/example/android/build.gradle +++ /dev/null @@ -1,39 +0,0 @@ -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -tasks.register("clean", Delete) { - delete rootProject.buildDir -} - -allprojects { - repositories { - // See https://github.com/flutter/flutter/wiki/Plugins-and-Packages-repository-structure#gradle-structure for more info. - def artifactRepoKey = 'ARTIFACT_HUB_REPOSITORY' - if (System.getenv().containsKey(artifactRepoKey)) { - println "Using artifact hub" - maven { url System.getenv(artifactRepoKey) } - } - google() - mavenCentral() - } -} - -gradle.projectsEvaluated { - project(":interactive_media_ads") { - tasks.withType(JavaCompile) { - options.compilerArgs << "-Xlint:all" << "-Werror" - } - } -} diff --git a/packages/interactive_media_ads/example/android/gradle.properties b/packages/interactive_media_ads/example/android/gradle.properties deleted file mode 100644 index 3b5b324f6e3f..000000000000 --- a/packages/interactive_media_ads/example/android/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError -android.useAndroidX=true -android.enableJetifier=true diff --git a/packages/interactive_media_ads/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/interactive_media_ads/example/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index e1ca574ef017..000000000000 --- a/packages/interactive_media_ads/example/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip diff --git a/packages/interactive_media_ads/example/android/settings.gradle b/packages/interactive_media_ads/example/android/settings.gradle deleted file mode 100644 index 12cfac56b468..000000000000 --- a/packages/interactive_media_ads/example/android/settings.gradle +++ /dev/null @@ -1,39 +0,0 @@ -pluginManagement { - def flutterSdkPath = { - def properties = new Properties() - file("local.properties").withInputStream { properties.load(it) } - def flutterSdkPath = properties.getProperty("flutter.sdk") - assert flutterSdkPath != null, "flutter.sdk not set in local.properties" - return flutterSdkPath - }() - - includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") - - repositories { - google() - mavenCentral() - gradlePluginPortal() - } -} - -// See https://github.com/flutter/flutter/wiki/Plugins-and-Packages-repository-structure#gradle-structure for more info. -buildscript { - repositories { - maven { - url "https://plugins.gradle.org/m2/" - } - } - dependencies { - classpath "gradle.plugin.com.google.cloud.artifactregistry:artifactregistry-gradle-plugin:2.2.1" - } -} - -plugins { - id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "7.3.0" apply false - id "org.jetbrains.kotlin.android" version "1.7.10" apply false -} - -include ":app" - -apply plugin: "com.google.cloud.artifactregistry.gradle-plugin" diff --git a/packages/interactive_media_ads/example/integration_test/interactive_media_ads_test.dart b/packages/interactive_media_ads/example/integration_test/interactive_media_ads_test.dart deleted file mode 100644 index d79165b3788b..000000000000 --- a/packages/interactive_media_ads/example/integration_test/interactive_media_ads_test.dart +++ /dev/null @@ -1,23 +0,0 @@ -// 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_driver/driver_extension.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:integration_test/integration_test.dart'; -import 'package:interactive_media_ads_example/main.dart' as app; - -/// Entry point for integration tests that require espresso. -@pragma('vm:entry-point') -void integrationTestMain() { - enableFlutterDriverExtension(); - app.main(); -} - -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - // Since this test is lacking integration tests, this test ensures the example - // app can be launched on an emulator/device. - testWidgets('Launch Test', (WidgetTester tester) async {}); -} diff --git a/packages/interactive_media_ads/example/ios/Flutter/AppFrameworkInfo.plist b/packages/interactive_media_ads/example/ios/Flutter/AppFrameworkInfo.plist deleted file mode 100644 index 7c5696400627..000000000000 --- a/packages/interactive_media_ads/example/ios/Flutter/AppFrameworkInfo.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - MinimumOSVersion - 12.0 - - diff --git a/packages/interactive_media_ads/example/ios/Flutter/Debug.xcconfig b/packages/interactive_media_ads/example/ios/Flutter/Debug.xcconfig deleted file mode 100644 index ec97fc6f3021..000000000000 --- a/packages/interactive_media_ads/example/ios/Flutter/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "Generated.xcconfig" diff --git a/packages/interactive_media_ads/example/ios/Flutter/Release.xcconfig b/packages/interactive_media_ads/example/ios/Flutter/Release.xcconfig deleted file mode 100644 index c4855bfe2000..000000000000 --- a/packages/interactive_media_ads/example/ios/Flutter/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "Generated.xcconfig" diff --git a/packages/interactive_media_ads/example/ios/Podfile b/packages/interactive_media_ads/example/ios/Podfile deleted file mode 100644 index d97f17e223fb..000000000000 --- a/packages/interactive_media_ads/example/ios/Podfile +++ /dev/null @@ -1,44 +0,0 @@ -# Uncomment this line to define a global platform for your project -# platform :ios, '12.0' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_ios_podfile_setup - -target 'Runner' do - use_frameworks! - use_modular_headers! - - flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) - target 'RunnerTests' do - inherit! :search_paths - end -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_ios_build_settings(target) - end -end diff --git a/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.pbxproj b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index 966e3a40fea7..000000000000 --- a/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,619 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 54; - objects = { - -/* Begin PBXBuildFile section */ - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 97C146E61CF9000F007C117D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 97C146ED1CF9000F007C117D; - remoteInfo = Runner; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 9705A1C41CF9048500538489 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; - 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; - 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 97C146EB1CF9000F007C117D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 331C8082294A63A400263BE5 /* RunnerTests */ = { - isa = PBXGroup; - children = ( - 331C807B294A618700263BE5 /* RunnerTests.swift */, - ); - path = RunnerTests; - sourceTree = ""; - }; - 9740EEB11CF90186004384FC /* Flutter */ = { - isa = PBXGroup; - children = ( - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 9740EEB31CF90195004384FC /* Generated.xcconfig */, - ); - name = Flutter; - sourceTree = ""; - }; - 97C146E51CF9000F007C117D = { - isa = PBXGroup; - children = ( - 9740EEB11CF90186004384FC /* Flutter */, - 97C146F01CF9000F007C117D /* Runner */, - 97C146EF1CF9000F007C117D /* Products */, - 331C8082294A63A400263BE5 /* RunnerTests */, - ); - sourceTree = ""; - }; - 97C146EF1CF9000F007C117D /* Products */ = { - isa = PBXGroup; - children = ( - 97C146EE1CF9000F007C117D /* Runner.app */, - 331C8081294A63A400263BE5 /* RunnerTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 97C146F01CF9000F007C117D /* Runner */ = { - isa = PBXGroup; - children = ( - 97C146FA1CF9000F007C117D /* Main.storyboard */, - 97C146FD1CF9000F007C117D /* Assets.xcassets */, - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, - 97C147021CF9000F007C117D /* Info.plist */, - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, - ); - path = Runner; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 331C8080294A63A400263BE5 /* RunnerTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; - buildPhases = ( - 331C807D294A63A400263BE5 /* Sources */, - 331C807F294A63A400263BE5 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 331C8086294A63A400263BE5 /* PBXTargetDependency */, - ); - name = RunnerTests; - productName = RunnerTests; - productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 97C146ED1CF9000F007C117D /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 9740EEB61CF901F6004384FC /* Run Script */, - 97C146EA1CF9000F007C117D /* Sources */, - 97C146EB1CF9000F007C117D /* Frameworks */, - 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, - 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Runner; - productName = Runner; - productReference = 97C146EE1CF9000F007C117D /* Runner.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 97C146E61CF9000F007C117D /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 1510; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 331C8080294A63A400263BE5 = { - CreatedOnToolsVersion = 14.0; - TestTargetID = 97C146ED1CF9000F007C117D; - }; - 97C146ED1CF9000F007C117D = { - CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 1100; - }; - }; - }; - buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 97C146E51CF9000F007C117D; - productRefGroup = 97C146EF1CF9000F007C117D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 97C146ED1CF9000F007C117D /* Runner */, - 331C8080294A63A400263BE5 /* RunnerTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 331C807F294A63A400263BE5 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 97C146EC1CF9000F007C117D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", - ); - name = "Thin Binary"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; - }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 331C807D294A63A400263BE5 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 97C146EA1CF9000F007C117D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 97C146FA1CF9000F007C117D /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C146FB1CF9000F007C117D /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C147001CF9000F007C117D /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 249021D3217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Profile; - }; - 249021D4217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = S8QB4VV633; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.packages.interactiveMediaAdsExample; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Profile; - }; - 331C8088294A63A400263BE5 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.packages.interactiveMediaAdsExample.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; - }; - name = Debug; - }; - 331C8089294A63A400263BE5 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.packages.interactiveMediaAdsExample.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; - }; - name = Release; - }; - 331C808A294A63A400263BE5 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.packages.interactiveMediaAdsExample.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; - }; - name = Profile; - }; - 97C147031CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 97C147041CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 97C147061CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = S8QB4VV633; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.packages.interactiveMediaAdsExample; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Debug; - }; - 97C147071CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = S8QB4VV633; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.packages.interactiveMediaAdsExample; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 331C8088294A63A400263BE5 /* Debug */, - 331C8089294A63A400263BE5 /* Release */, - 331C808A294A63A400263BE5 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147031CF9000F007C117D /* Debug */, - 97C147041CF9000F007C117D /* Release */, - 249021D3217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147061CF9000F007C117D /* Debug */, - 97C147071CF9000F007C117D /* Release */, - 249021D4217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 97C146E61CF9000F007C117D /* Project object */; -} diff --git a/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a6254f..000000000000 --- a/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d68..000000000000 --- a/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5ea15..000000000000 --- a/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/packages/interactive_media_ads/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index 8e3ca5dfe193..000000000000 --- a/packages/interactive_media_ads/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/interactive_media_ads/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/interactive_media_ads/example/ios/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 1d526a16ed0f..000000000000 --- a/packages/interactive_media_ads/example/ios/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/packages/interactive_media_ads/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/interactive_media_ads/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d68..000000000000 --- a/packages/interactive_media_ads/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/packages/interactive_media_ads/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/interactive_media_ads/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5ea15..000000000000 --- a/packages/interactive_media_ads/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/packages/interactive_media_ads/example/ios/Runner/AppDelegate.swift b/packages/interactive_media_ads/example/ios/Runner/AppDelegate.swift deleted file mode 100644 index d83c0ff0beea..000000000000 --- a/packages/interactive_media_ads/example/ios/Runner/AppDelegate.swift +++ /dev/null @@ -1,17 +0,0 @@ -// 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 Flutter -import UIKit - -@UIApplicationMain -@objc class AppDelegate: FlutterAppDelegate { - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - GeneratedPluginRegistrant.register(with: self) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } -} diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d36b1fab2d9d..000000000000 --- a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - }, - { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "Icon-App-1024x1024@1x.png", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png deleted file mode 100644 index dc9ada4725e9b0ddb1deab583e5b5102493aa332..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10932 zcmeHN2~<R zh`|8`A_PQ1nSu(UMFx?8j8PC!!VDphaL#`F42fd#7Vlc`zIE4n%Y~eiz4y1j|NDpi z?<@|pSJ-HM`qifhf@m%MamgwK83`XpBA<+azdF#2QsT{X@z0A9Bq>~TVErigKH1~P zRX-!h-f0NJ4Mh++{D}J+K>~~rq}d%o%+4dogzXp7RxX4C>Km5XEI|PAFDmo;DFm6G zzjVoB`@qW98Yl0Kvc-9w09^PrsobmG*Eju^=3f?0o-t$U)TL1B3;sZ^!++3&bGZ!o-*6w?;oOhf z=A+Qb$scV5!RbG+&2S}BQ6YH!FKb0``VVX~T$dzzeSZ$&9=X$3)_7Z{SspSYJ!lGE z7yig_41zpQ)%5dr4ff0rh$@ky3-JLRk&DK)NEIHecf9c*?Z1bUB4%pZjQ7hD!A0r-@NF(^WKdr(LXj|=UE7?gBYGgGQV zidf2`ZT@pzXf7}!NH4q(0IMcxsUGDih(0{kRSez&z?CFA0RVXsVFw3^u=^KMtt95q z43q$b*6#uQDLoiCAF_{RFc{!H^moH_cmll#Fc^KXi{9GDl{>%+3qyfOE5;Zq|6#Hb zp^#1G+z^AXfRKaa9HK;%b3Ux~U@q?xg<2DXP%6k!3E)PA<#4$ui8eDy5|9hA5&{?v z(-;*1%(1~-NTQ`Is1_MGdQ{+i*ccd96ab$R$T3=% zw_KuNF@vI!A>>Y_2pl9L{9h1-C6H8<)J4gKI6{WzGBi<@u3P6hNsXG=bRq5c+z;Gc3VUCe;LIIFDmQAGy+=mRyF++u=drBWV8-^>0yE9N&*05XHZpPlE zxu@?8(ZNy7rm?|<+UNe0Vs6&o?l`Pt>P&WaL~M&#Eh%`rg@Mbb)J&@DA-wheQ>hRV z<(XhigZAT z>=M;URcdCaiO3d^?H<^EiEMDV+7HsTiOhoaMX%P65E<(5xMPJKxf!0u>U~uVqnPN7T!X!o@_gs3Ct1 zlZ_$5QXP4{Aj645wG_SNT&6m|O6~Tsl$q?nK*)(`{J4b=(yb^nOATtF1_aS978$x3 zx>Q@s4i3~IT*+l{@dx~Hst21fR*+5}S1@cf>&8*uLw-0^zK(+OpW?cS-YG1QBZ5q! zgTAgivzoF#`cSz&HL>Ti!!v#?36I1*l^mkrx7Y|K6L#n!-~5=d3;K<;Zqi|gpNUn_ z_^GaQDEQ*jfzh;`j&KXb66fWEk1K7vxQIMQ_#Wu_%3 z4Oeb7FJ`8I>Px;^S?)}2+4D_83gHEq>8qSQY0PVP?o)zAv3K~;R$fnwTmI-=ZLK`= zTm+0h*e+Yfr(IlH3i7gUclNH^!MU>id$Jw>O?2i0Cila#v|twub21@e{S2v}8Z13( zNDrTXZVgris|qYm<0NU(tAPouG!QF4ZNpZPkX~{tVf8xY690JqY1NVdiTtW+NqyRP zZ&;T0ikb8V{wxmFhlLTQ&?OP7 z;(z*<+?J2~z*6asSe7h`$8~Se(@t(#%?BGLVs$p``;CyvcT?7Y!{tIPva$LxCQ&4W z6v#F*);|RXvI%qnoOY&i4S*EL&h%hP3O zLsrFZhv&Hu5tF$Lx!8(hs&?!Kx5&L(fdu}UI5d*wn~A`nPUhG&Rv z2#ixiJdhSF-K2tpVL=)5UkXRuPAFrEW}7mW=uAmtVQ&pGE-&az6@#-(Te^n*lrH^m@X-ftVcwO_#7{WI)5v(?>uC9GG{lcGXYJ~Q8q zbMFl7;t+kV;|;KkBW2!P_o%Czhw&Q(nXlxK9ak&6r5t_KH8#1Mr-*0}2h8R9XNkr zto5-b7P_auqTJb(TJlmJ9xreA=6d=d)CVbYP-r4$hDn5|TIhB>SReMfh&OVLkMk-T zYf%$taLF0OqYF?V{+6Xkn>iX@TuqQ?&cN6UjC9YF&%q{Ut3zv{U2)~$>-3;Dp)*(? zg*$mu8^i=-e#acaj*T$pNowo{xiGEk$%DusaQiS!KjJH96XZ-hXv+jk%ard#fu=@Q z$AM)YWvE^{%tDfK%nD49=PI|wYu}lYVbB#a7wtN^Nml@CE@{Gv7+jo{_V?I*jkdLD zJE|jfdrmVbkfS>rN*+`#l%ZUi5_bMS<>=MBDNlpiSb_tAF|Zy`K7kcp@|d?yaTmB^ zo?(vg;B$vxS|SszusORgDg-*Uitzdi{dUV+glA~R8V(?`3GZIl^egW{a919!j#>f` znL1o_^-b`}xnU0+~KIFLQ)$Q6#ym%)(GYC`^XM*{g zv3AM5$+TtDRs%`2TyR^$(hqE7Y1b&`Jd6dS6B#hDVbJlUXcG3y*439D8MrK!2D~6gn>UD4Imctb z+IvAt0iaW73Iq$K?4}H`7wq6YkTMm`tcktXgK0lKPmh=>h+l}Y+pDtvHnG>uqBA)l zAH6BV4F}v$(o$8Gfo*PB>IuaY1*^*`OTx4|hM8jZ?B6HY;F6p4{`OcZZ(us-RVwDx zUzJrCQlp@mz1ZFiSZ*$yX3c_#h9J;yBE$2g%xjmGF4ca z&yL`nGVs!Zxsh^j6i%$a*I3ZD2SoNT`{D%mU=LKaEwbN(_J5%i-6Va?@*>=3(dQy` zOv%$_9lcy9+(t>qohkuU4r_P=R^6ME+wFu&LA9tw9RA?azGhjrVJKy&8=*qZT5Dr8g--d+S8zAyJ$1HlW3Olryt`yE zFIph~Z6oF&o64rw{>lgZISC6p^CBer9C5G6yq%?8tC+)7*d+ib^?fU!JRFxynRLEZ zj;?PwtS}Ao#9whV@KEmwQgM0TVP{hs>dg(1*DiMUOKHdQGIqa0`yZnHk9mtbPfoLx zo;^V6pKUJ!5#n`w2D&381#5#_t}AlTGEgDz$^;u;-vxDN?^#5!zN9ngytY@oTv!nc zp1Xn8uR$1Z;7vY`-<*?DfPHB;x|GUi_fI9@I9SVRv1)qETbNU_8{5U|(>Du84qP#7 z*l9Y$SgA&wGbj>R1YeT9vYjZuC@|{rajTL0f%N@>3$DFU=`lSPl=Iv;EjuGjBa$Gw zHD-;%YOE@<-!7-Mn`0WuO3oWuL6tB2cpPw~Nvuj|KM@))ixuDK`9;jGMe2d)7gHin zS<>k@!x;!TJEc#HdL#RF(`|4W+H88d4V%zlh(7#{q2d0OQX9*FW^`^_<3r$kabWAB z$9BONo5}*(%kx zOXi-yM_cmB3>inPpI~)duvZykJ@^^aWzQ=eQ&STUa}2uT@lV&WoRzkUoE`rR0)`=l zFT%f|LA9fCw>`enm$p7W^E@U7RNBtsh{_-7vVz3DtB*y#*~(L9+x9*wn8VjWw|Q~q zKFsj1Yl>;}%MG3=PY`$g$_mnyhuV&~O~u~)968$0b2!Jkd;2MtAP#ZDYw9hmK_+M$ zb3pxyYC&|CuAbtiG8HZjj?MZJBFbt`ryf+c1dXFuC z0*ZQhBzNBd*}s6K_G}(|Z_9NDV162#y%WSNe|FTDDhx)K!c(mMJh@h87@8(^YdK$&d*^WQe8Z53 z(|@MRJ$Lk-&ii74MPIs80WsOFZ(NX23oR-?As+*aq6b?~62@fSVmM-_*cb1RzZ)`5$agEiL`-E9s7{GM2?(KNPgK1(+c*|-FKoy}X(D_b#etO|YR z(BGZ)0Ntfv-7R4GHoXp?l5g#*={S1{u-QzxCGng*oWr~@X-5f~RA14b8~B+pLKvr4 zfgL|7I>jlak9>D4=(i(cqYf7#318!OSR=^`xxvI!bBlS??`xxWeg?+|>MxaIdH1U~#1tHu zB{QMR?EGRmQ_l4p6YXJ{o(hh-7Tdm>TAX380TZZZyVkqHNzjUn*_|cb?T? zt;d2s-?B#Mc>T-gvBmQZx(y_cfkXZO~{N zT6rP7SD6g~n9QJ)8F*8uHxTLCAZ{l1Y&?6v)BOJZ)=R-pY=Y=&1}jE7fQ>USS}xP#exo57uND0i*rEk@$;nLvRB@u~s^dwRf?G?_enN@$t* zbL%JO=rV(3Ju8#GqUpeE3l_Wu1lN9Y{D4uaUe`g>zlj$1ER$6S6@{m1!~V|bYkhZA z%CvrDRTkHuajMU8;&RZ&itnC~iYLW4DVkP<$}>#&(`UO>!n)Po;Mt(SY8Yb`AS9lt znbX^i?Oe9r_o=?})IHKHoQGKXsps_SE{hwrg?6dMI|^+$CeC&z@*LuF+P`7LfZ*yr+KN8B4{Nzv<`A(wyR@!|gw{zB6Ha ziwPAYh)oJ(nlqSknu(8g9N&1hu0$vFK$W#mp%>X~AU1ay+EKWcFdif{% z#4!4aoVVJ;ULmkQf!ke2}3hqxLK>eq|-d7Ly7-J9zMpT`?dxo6HdfJA|t)?qPEVBDv z{y_b?4^|YA4%WW0VZd8C(ZgQzRI5(I^)=Ub`Y#MHc@nv0w-DaJAqsbEHDWG8Ia6ju zo-iyr*sq((gEwCC&^TYBWt4_@|81?=B-?#P6NMff(*^re zYqvDuO`K@`mjm_Jd;mW_tP`3$cS?R$jR1ZN09$YO%_iBqh5ftzSpMQQtxKFU=FYmP zeY^jph+g<4>YO;U^O>-NFLn~-RqlHvnZl2yd2A{Yc1G@Ga$d+Q&(f^tnPf+Z7serIU};17+2DU_f4Z z@GaPFut27d?!YiD+QP@)T=77cR9~MK@bd~pY%X(h%L={{OIb8IQmf-!xmZkm8A0Ga zQSWONI17_ru5wpHg3jI@i9D+_Y|pCqVuHJNdHUauTD=R$JcD2K_liQisqG$(sm=k9;L* z!L?*4B~ql7uioSX$zWJ?;q-SWXRFhz2Jt4%fOHA=Bwf|RzhwqdXGr78y$J)LR7&3T zE1WWz*>GPWKZ0%|@%6=fyx)5rzUpI;bCj>3RKzNG_1w$fIFCZ&UR0(7S?g}`&Pg$M zf`SLsz8wK82Vyj7;RyKmY{a8G{2BHG%w!^T|Njr!h9TO2LaP^_f22Q1=l$QiU84ao zHe_#{S6;qrC6w~7{y(hs-?-j?lbOfgH^E=XcSgnwW*eEz{_Z<_xN#0001NP)t-s|Ns9~ z#rXRE|M&d=0au&!`~QyF`q}dRnBDt}*!qXo`c{v z{Djr|@Adh0(D_%#_&mM$D6{kE_x{oE{l@J5@%H*?%=t~i_`ufYOPkAEn!pfkr2$fs z652Tz0001XNklqeeKN4RM4i{jKqmiC$?+xN>3Apn^ z0QfuZLym_5b<*QdmkHjHlj811{If)dl(Z2K0A+ekGtrFJb?g|wt#k#pV-#A~bK=OT ts8>{%cPtyC${m|1#B1A6#u!Q;umknL1chzTM$P~L002ovPDHLkV1lTfnu!1a diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png deleted file mode 100644 index 797d452e458972bab9d994556c8305db4c827017..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 406 zcmV;H0crk;P))>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index 6ed2d933e1120817fe9182483a228007b18ab6ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 450 zcmV;z0X_bSP)iGWQ_5NJQ_~rNh*z)}eT%KUb z`7gNk0#AwF^#0T0?hIa^`~Ck;!}#m+_uT050aTR(J!bU#|IzRL%^UsMS#KsYnTF*!YeDOytlP4VhV?b} z%rz_<=#CPc)tU1MZTq~*2=8~iZ!lSa<{9b@2Jl;?IEV8)=fG217*|@)CCYgFze-x? zIFODUIA>nWKpE+bn~n7;-89sa>#DR>TSlqWk*!2hSN6D~Qb#VqbP~4Fk&m`@1$JGr zXPIdeRE&b2Thd#{MtDK$px*d3-Wx``>!oimf%|A-&-q*6KAH)e$3|6JV%HX{Hig)k suLT-RhftRq8b9;(V=235Wa|I=027H2wCDra;{X5v07*qoM6N<$f;9x^2LJ#7 diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png deleted file mode 100644 index 4cd7b0099ca80c806f8fe495613e8d6c69460d76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 282 zcmV+#0p(^bcu7P-R4C8Q z&e;xxFbF_Vrezo%_kH*OKhshZ6BFpG-Y1e10`QXJKbND7AMQ&cMj60B5TNObaZxYybcN07*qoM6N<$g3m;S%K!iX diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png deleted file mode 100644 index fe730945a01f64a61e2235dbe3f45b08f7729182..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 462 zcmV;<0WtoGP)-}iV`2<;=$?g5M=KQbZ{F&YRNy7Nn@%_*5{gvDM0aKI4?ESmw z{NnZg)A0R`+4?NF_RZexyVB&^^ZvN!{I28tr{Vje;QNTz`dG&Jz0~Ek&f2;*Z7>B|cg}xYpxEFY+0YrKLF;^Q+-HreN0P{&i zK~zY`?b7ECf-n?@;d<&orQ*Q7KoR%4|C>{W^h6@&01>0SKS`dn{Q}GT%Qj_{PLZ_& zs`MFI#j-(>?bvdZ!8^xTwlY{qA)T4QLbY@j(!YJ7aXJervHy6HaG_2SB`6CC{He}f zHVw(fJWApwPq!6VY7r1w-Fs)@ox~N+q|w~e;JI~C4Vf^@d>Wvj=fl`^u9x9wd9 zR%3*Q+)t%S!MU_`id^@&Y{y7-r98lZX0?YrHlfmwb?#}^1b{8g&KzmkE(L>Z&)179 zp<)v6Y}pRl100G2FL_t(o!|l{-Q-VMg#&MKg7c{O0 z2wJImOS3Gy*Z2Qifdv~JYOp;v+U)a|nLoc7hNH;I$;lzDt$}rkaFw1mYK5_0Q(Sut zvbEloxON7$+HSOgC9Z8ltuC&0OSF!-mXv5caV>#bc3@hBPX@I$58-z}(ZZE!t-aOG zpjNkbau@>yEzH(5Yj4kZiMH32XI!4~gVXNnjAvRx;Sdg^`>2DpUEwoMhTs_st8pKG z(%SHyHdU&v%f36~uERh!bd`!T2dw;z6PrOTQ7Vt*#9F2uHlUVnb#ev_o^fh}Dzmq} zWtlk35}k=?xj28uO|5>>$yXadTUE@@IPpgH`gJ~Ro4>jd1IF|(+IX>8M4Ps{PNvmI zNj4D+XgN83gPt_Gm}`Ybv{;+&yu-C(Grdiahmo~BjG-l&mWM+{e5M1sm&=xduwgM9 z`8OEh`=F3r`^E{n_;%9weN{cf2%7=VzC@cYj+lg>+3|D|_1C@{hcU(DyQG_BvBWe? zvTv``=%b1zrol#=R`JB)>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png deleted file mode 100644 index 502f463a9bc882b461c96aadf492d1729e49e725..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 586 zcmV-Q0=4~#P)+}#`wDE{8-2Mebf5<{{PqV{TgVcv*r8?UZ3{-|G?_}T*&y;@cqf{ z{Q*~+qr%%p!1pS*_Uicl#q9lc(D`!D`LN62sNwq{oYw(Wmhk)k<@f$!$@ng~_5)Ru z0Z)trIA5^j{DIW^c+vT2%lW+2<(RtE2wR;4O@)Tm`Xr*?A(qYoM}7i5Yxw>D(&6ou zxz!_Xr~yNF+waPe00049Nkl*;a!v6h%{rlvIH#gW3s8p;bFr=l}mRqpW2h zw=OA%hdyL~z+UHOzl0eKhEr$YYOL-c-%Y<)=j?(bzDweB7{b+%_ypvm_cG{SvM=DK zhv{K@m>#Bw>2W$eUI#iU)Wdgs8Y3U+A$Gd&{+j)d)BmGKx+43U_!tik_YlN)>$7G! zhkE!s;%oku3;IwG3U^2kw?z+HM)jB{@zFhK8P#KMSytSthr+4!c(5c%+^UBn`0X*2 zy3(k600_CSZj?O$Qu%&$;|TGUJrptR(HzyIx>5E(2r{eA(<6t3e3I0B)7d6s7?Z5J zZ!rtKvA{MiEBm&KFtoifx>5P^Z=vl)95XJn()aS5%ad(s?4-=Tkis9IGu{`Fy8r+H07*qoM6N<$f20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png deleted file mode 100644 index 0ec303439225b78712f49115768196d8d76f6790..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 862 zcmV-k1EKthP)20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png deleted file mode 100644 index e9f5fea27c705180eb716271f41b582e76dcbd90..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1674 zcmV;526g#~P){YQnis^a@{&-nmRmq)<&%Mztj67_#M}W?l>kYSliK<%xAp;0j{!}J0!o7b zE>q9${Lb$D&h7k=+4=!ek^n+`0zq>LL1O?lVyea53S5x`Nqqo2YyeuIrQrJj9XjOp z{;T5qbj3}&1vg1VK~#9!?b~^C5-}JC@Pyrv-6dSEqJqT}#j9#dJ@GzT@B8}x zU&J@bBI>f6w6en+CeI)3^kC*U?}X%OD8$Fd$H&LV$H&LV$H&LV#|K5~mLYf|VqzOc zkc7qL~0sOYuM{tG`rYEDV{DWY`Z8&)kW*hc2VkBuY+^Yx&92j&StN}Wp=LD zxoGxXw6f&8sB^u})h@b@z0RBeD`K7RMR9deyL(ZJu#39Z>rT)^>v}Khq8U-IbIvT> z?4pV9qGj=2)TNH3d)=De<+^w;>S7m_eFKTvzeaBeir45xY!^m!FmxnljbSS_3o=g( z->^wC9%qkR{kbGnW8MfFew_o9h3(r55Is`L$8KI@d+*%{=Nx+FXJ98L0PjFIu;rGnnfY zn1R5Qnp<{Jq0M1vX=X&F8gtLmcWv$1*M@4ZfF^9``()#hGTeKeP`1!iED ztNE(TN}M5}3Bbc*d=FIv`DNv&@|C6yYj{sSqUj5oo$#*0$7pu|Dd2TLI>t5%I zIa4Dvr(iayb+5x=j*Vum9&irk)xV1`t509lnPO0%skL8_1c#Xbamh(2@f?4yUI zhhuT5<#8RJhGz4%b$`PJwKPAudsm|at?u;*hGgnA zU1;9gnxVBC)wA(BsB`AW54N{|qmikJR*%x0c`{LGsSfa|NK61pYH(r-UQ4_JXd!Rsz)=k zL{GMc5{h138)fF5CzHEDM>+FqY)$pdN3}Ml+riTgJOLN0F*Vh?{9ESR{SVVg>*>=# zix;VJHPtvFFCRY$Ks*F;VX~%*r9F)W`PmPE9F!(&s#x07n2<}?S{(ygpXgX-&B&OM zONY&BRQ(#%0%jeQs?oJ4P!p*R98>qCy5p8w>_gpuh39NcOlp)(wOoz0sY-Qz55eB~ z7OC-fKBaD1sE3$l-6QgBJO!n?QOTza`!S_YK z_v-lm^7{VO^8Q@M_^8F)09Ki6%=s?2_5eupee(w1FB%aqSweusQ-T+CH0Xt{` zFjMvW{@C&TB)k25()nh~_yJ9coBRL(0oO@HK~z}7?bm5j;y@69;bvlHb2tf!$ReA~x{22wTq550 z?f?Hnw(;m3ip30;QzdV~7pi!wyMYhDtXW#cO7T>|f=bdFhu+F!zMZ2UFj;GUKX7tI z;hv3{q~!*pMj75WP_c}>6)IWvg5_yyg<9Op()eD1hWC19M@?_9_MHec{Z8n3FaF{8 z;u`Mw0ly(uE>*CgQYv{be6ab2LWhlaH1^iLIM{olnag$78^Fd}%dR7;JECQ+hmk|o z!u2&!3MqPfP5ChDSkFSH8F2WVOEf0(E_M(JL17G}Y+fg0_IuW%WQ zG(mG&u?|->YSdk0;8rc{yw2@2Z&GA}z{Wb91Ooz9VhA{b2DYE7RmG zjL}?eq#iX%3#k;JWMx_{^2nNax`xPhByFiDX+a7uTGU|otOvIAUy|dEKkXOm-`aWS z27pUzD{a)Ct<6p{{3)+lq@i`t@%>-wT4r?*S}k)58e09WZYP0{{R3FC5Sl00039P)t-s|Ns9~ z#rP?<_5oL$Q^olD{r_0T`27C={r>*`|Nj71npVa5OTzc(_WfbW_({R{p56NV{r*M2 z_xt?)2V0#0NsfV0u>{42ctGP(8vQj-Btk1n|O0ZD=YLwd&R{Ko41Gr9H= zY@z@@bOAMB5Ltl$E>bJJ{>JP30ZxkmI%?eW{k`b?Wy<&gOo;dS`~CR$Vwb@XWtR|N zi~t=w02?-0&j0TD{>bb6sNwsK*!p?V`RMQUl(*DVjk-9Cx+-z1KXab|Ka2oXhX5f% z`$|e!000AhNklrxs)5QTeTVRiEmz~MKK1WAjCw(c-JK6eox;2O)?`? zTG`AHia671e^vgmp!llKp|=5sVHk#C7=~epA~VAf-~%aPC=%Qw01h8mnSZ|p?hz91 z7p83F3%LVu9;S$tSI$C^%^yud1dfTM_6p2|+5Ejp$bd`GDvbR|xit>i!ZD&F>@CJrPmu*UjD&?DfZs=$@e3FQA(vNiU+$A*%a} z?`XcG2jDxJ_ZQ#Md`H{4Lpf6QBDp81_KWZ6Tk#yCy1)32zO#3<7>b`eT7UyYH1eGz z;O(rH$=QR*L%%ZcBpc=eGua?N55nD^K(8<#gl2+pN_j~b2MHs4#mcLmv%DkspS-3< zpI1F=^9siI0s-;IN_IrA;5xm~3?3!StX}pUv0vkxMaqm+zxrg7X7(I&*N~&dEd0kD z-FRV|g=|QuUsuh>-xCI}vD2imzYIOIdcCVV=$Bz@*u0+Bs<|L^)32nN*=wu3n%Ynw z@1|eLG>!8ruU1pFXUfb`j>(=Gy~?Rn4QJ-c3%3T|(Frd!bI`9u&zAnyFYTqlG#&J7 zAkD(jpw|oZLNiA>;>hgp1KX7-wxC~31II47gc zHcehD6Uxlf%+M^^uN5Wc*G%^;>D5qT{>=uxUhX%WJu^Z*(_Wq9y}npFO{Hhb>s6<9 zNi0pHXWFaVZnb)1+RS&F)xOv6&aeILcI)`k#0YE+?e)5&#r7J#c`3Z7x!LpTc01dx zrdC3{Z;joZ^KN&))zB_i)I9fWedoN>Zl-6_Iz+^G&*ak2jpF07*qoM6N<$f;w%0(f|Me diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png deleted file mode 100644 index 0467bf12aa4d28f374bb26596605a46dcbb3e7c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1418 zcmV;51$Fv~P)q zKfU)WzW*n(@|xWGCA9ScMt*e9`2kdxPQ&&>|-UCa7_51w+ zLUsW@ZzZSW0y$)Hp~e9%PvP|a03ks1`~K?q{u;6NC8*{AOqIUq{CL&;p56Lf$oQGq z^={4hPQv)y=I|4n+?>7Fim=dxt1 z2H+Dm+1+fh+IF>G0SjJMkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJT zkdTm&kdTm&kdTm&kdP`esgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>Tmf` zm1{eLgw!b{bXkjWbF%dTkTZEJWyWOb##Lfw4EK2}<0d6%>AGS{po>WCOy&f$Tay_> z?NBlkpo@s-O;0V%Y_Xa-G#_O08q5LR*~F%&)}{}r&L%Sbs8AS4t7Y0NEx*{soY=0MZExqA5XHQkqi#4gW3 zqODM^iyZl;dvf)-bOXtOru(s)Uc7~BFx{w-FK;2{`VA?(g&@3z&bfLFyctOH!cVsF z7IL=fo-qBndRUm;kAdXR4e6>k-z|21AaN%ubeVrHl*<|s&Ax@W-t?LR(P-24A5=>a z*R9#QvjzF8n%@1Nw@?CG@6(%>+-0ASK~jEmCV|&a*7-GKT72W<(TbSjf)&Eme6nGE z>Gkj4Sq&2e+-G%|+NM8OOm5zVl9{Z8Dd8A5z3y8mZ=4Bv4%>as_{9cN#bm~;h>62( zdqY93Zy}v&c4n($Vv!UybR8ocs7#zbfX1IY-*w~)p}XyZ-SFC~4w>BvMVr`dFbelV{lLL0bx7@*ZZdebr3`sP;? zVImji)kG)(6Juv0lz@q`F!k1FE;CQ(D0iG$wchPbKZQELlsZ#~rt8#90Y_Xh&3U-< z{s<&cCV_1`^TD^ia9!*mQDq& zn2{r`j};V|uV%_wsP!zB?m%;FeaRe+X47K0e+KE!8C{gAWF8)lCd1u1%~|M!XNRvw zvtqy3iz0WSpWdhn6$hP8PaRBmp)q`#PCA`Vd#Tc$@f1tAcM>f_I@bC)hkI9|o(Iqv zo}Piadq!j76}004RBio<`)70k^`K1NK)q>w?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF z34$0Z;QO!JOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUC YUoZo%k(ykuW&i*H07*qoM6N<$f+CH{y8r+H diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json deleted file mode 100644 index 0bedcf2fd467..000000000000 --- a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "LaunchImage.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png deleted file mode 100644 index 9da19eacad3b03bb08bbddbbf4ac48dd78b3d838..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png deleted file mode 100644 index 9da19eacad3b03bb08bbddbbf4ac48dd78b3d838..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png deleted file mode 100644 index 9da19eacad3b03bb08bbddbbf4ac48dd78b3d838..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v diff --git a/packages/interactive_media_ads/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/interactive_media_ads/example/ios/Runner/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index f2e259c7c939..000000000000 --- a/packages/interactive_media_ads/example/ios/Runner/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/interactive_media_ads/example/ios/Runner/Base.lproj/Main.storyboard b/packages/interactive_media_ads/example/ios/Runner/Base.lproj/Main.storyboard deleted file mode 100644 index f3c28516fb38..000000000000 --- a/packages/interactive_media_ads/example/ios/Runner/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/interactive_media_ads/example/ios/Runner/Info.plist b/packages/interactive_media_ads/example/ios/Runner/Info.plist deleted file mode 100644 index 5394f403367c..000000000000 --- a/packages/interactive_media_ads/example/ios/Runner/Info.plist +++ /dev/null @@ -1,49 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - Interactive Media Ads - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - interactive_media_ads_example - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - CADisableMinimumFrameDurationOnPhone - - UIApplicationSupportsIndirectInputEvents - - - diff --git a/packages/interactive_media_ads/example/ios/Runner/Runner-Bridging-Header.h b/packages/interactive_media_ads/example/ios/Runner/Runner-Bridging-Header.h deleted file mode 100644 index eb7e8ba8052f..000000000000 --- a/packages/interactive_media_ads/example/ios/Runner/Runner-Bridging-Header.h +++ /dev/null @@ -1,5 +0,0 @@ -// 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 "GeneratedPluginRegistrant.h" diff --git a/packages/interactive_media_ads/example/ios/RunnerTests/RunnerTests.swift b/packages/interactive_media_ads/example/ios/RunnerTests/RunnerTests.swift deleted file mode 100644 index cea3cbd3a1fb..000000000000 --- a/packages/interactive_media_ads/example/ios/RunnerTests/RunnerTests.swift +++ /dev/null @@ -1,30 +0,0 @@ -// 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 Flutter -import UIKit -import XCTest - -@testable import interactive_media_ads - -// This demonstrates a simple unit test of the Swift portion of this plugin's implementation. -// -// See https://developer.apple.com/documentation/xctest for more information about using XCTest. - -class RunnerTests: XCTestCase { - - func testGetPlatformVersion() { - let plugin = InteractiveMediaAdsPlugin() - - let call = FlutterMethodCall(methodName: "getPlatformVersion", arguments: []) - - let resultExpectation = expectation(description: "result block must be called.") - plugin.handle(call) { result in - XCTAssertEqual(result as! String, "iOS " + UIDevice.current.systemVersion) - resultExpectation.fulfill() - } - waitForExpectations(timeout: 1) - } - -} diff --git a/packages/interactive_media_ads/example/lib/main.dart b/packages/interactive_media_ads/example/lib/main.dart deleted file mode 100644 index fd385e0a60c3..000000000000 --- a/packages/interactive_media_ads/example/lib/main.dart +++ /dev/null @@ -1,44 +0,0 @@ -// 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/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_driver/driver_extension.dart'; - -/// Entry point for integration tests that require espresso. -@pragma('vm:entry-point') -void integrationTestMain() { - enableFlutterDriverExtension(); - main(); -} - -void main() { - runApp(const MyApp()); -} - -/// Home widget of the example app. -class MyApp extends StatefulWidget { - /// Constructs [MyApp]. - const MyApp({super.key}); - - @override - State createState() => _MyAppState(); -} - -class _MyAppState extends State { - @override - Widget build(BuildContext context) { - debugPrint('THEAPP'); - return MaterialApp( - home: Scaffold( - appBar: AppBar( - title: const Text('Plugin example app'), - ), - body: Center( - child: Text('Running on: $defaultTargetPlatform'), - ), - ), - ); - } -} diff --git a/packages/interactive_media_ads/example/pubspec.yaml b/packages/interactive_media_ads/example/pubspec.yaml deleted file mode 100644 index fde3e7a8b2ec..000000000000 --- a/packages/interactive_media_ads/example/pubspec.yaml +++ /dev/null @@ -1,25 +0,0 @@ -name: interactive_media_ads_example -description: "Demonstrates how to use the interactive_media_ads plugin." -publish_to: 'none' # Remove this line if you wish to publish to pub.dev - -environment: - sdk: ^3.2.3 - flutter: ">=3.16.6" - -dependencies: - flutter: - sdk: flutter - flutter_driver: - sdk: flutter - interactive_media_ads: - path: ../ - -dev_dependencies: - espresso: ^0.2.0 - flutter_test: - sdk: flutter - integration_test: - sdk: flutter - -flutter: - uses-material-design: true diff --git a/packages/interactive_media_ads/example/test_driver/integration_test.dart b/packages/interactive_media_ads/example/test_driver/integration_test.dart deleted file mode 100644 index 4f10f2a522f3..000000000000 --- a/packages/interactive_media_ads/example/test_driver/integration_test.dart +++ /dev/null @@ -1,7 +0,0 @@ -// 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:integration_test/integration_test_driver.dart'; - -Future main() => integrationDriver(); diff --git a/packages/interactive_media_ads/ios/Assets/.gitkeep b/packages/interactive_media_ads/ios/Assets/.gitkeep deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/interactive_media_ads/ios/Classes/InteractiveMediaAdsPlugin.swift b/packages/interactive_media_ads/ios/Classes/InteractiveMediaAdsPlugin.swift deleted file mode 100644 index 5b94fd6a24aa..000000000000 --- a/packages/interactive_media_ads/ios/Classes/InteractiveMediaAdsPlugin.swift +++ /dev/null @@ -1,24 +0,0 @@ -// 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 Flutter -import UIKit - -public class InteractiveMediaAdsPlugin: NSObject, FlutterPlugin { - public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel( - name: "interactive_media_ads", binaryMessenger: registrar.messenger()) - let instance = InteractiveMediaAdsPlugin() - registrar.addMethodCallDelegate(instance, channel: channel) - } - - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - switch call.method { - case "getPlatformVersion": - result("iOS " + UIDevice.current.systemVersion) - default: - result(FlutterMethodNotImplemented) - } - } -} diff --git a/packages/interactive_media_ads/ios/Resources/PrivacyInfo.xcprivacy b/packages/interactive_media_ads/ios/Resources/PrivacyInfo.xcprivacy deleted file mode 100644 index 5516ebf30d18..000000000000 --- a/packages/interactive_media_ads/ios/Resources/PrivacyInfo.xcprivacy +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/packages/interactive_media_ads/ios/interactive_media_ads.podspec b/packages/interactive_media_ads/ios/interactive_media_ads.podspec deleted file mode 100644 index 8980d486c212..000000000000 --- a/packages/interactive_media_ads/ios/interactive_media_ads.podspec +++ /dev/null @@ -1,29 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. -# Run `pod lib lint interactive_media_ads.podspec` to validate before publishing. -# -Pod::Spec.new do |s| - s.name = 'interactive_media_ads' - s.version = '0.0.1' - s.summary = 'A plugin for Interactive Media Ads SDKs.' - s.description = <<-DESC -A Flutter plugin for using the Interactive Media Ads SDKs. -Downloaded by pub (not CocoaPods). - DESC - s.homepage = 'https://github.com/flutter/packages' - s.license = { :type => 'BSD', :file => '../LICENSE' } - s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/packages/tree/main/packages/interactive_media_ads/interactive_media_ads' } - s.source_files = 'Classes/**/*' - s.dependency 'Flutter' - s.platform = :ios, '12.0' - - # Flutter.framework does not contain a i386 slice. - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } - s.xcconfig = { - 'LIBRARY_SEARCH_PATHS' => '$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)/ $(SDKROOT)/usr/lib/swift', - 'LD_RUNPATH_SEARCH_PATHS' => '/usr/lib/swift', - } - s.swift_version = '5.0' - s.resource_bundles = {'interactive_media_ads_privacy' => ['Resources/PrivacyInfo.xcprivacy']} -end diff --git a/packages/interactive_media_ads/lib/interactive_media_ads.dart b/packages/interactive_media_ads/lib/interactive_media_ads.dart deleted file mode 100644 index 185d304cf5bc..000000000000 --- a/packages/interactive_media_ads/lib/interactive_media_ads.dart +++ /dev/null @@ -1,10 +0,0 @@ -// 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. - -export 'src/ad_display_container.dart'; -export 'src/ads_loader.dart'; -export 'src/ads_manager_delegate.dart'; -export 'src/platform_interface/ad_error.dart'; -export 'src/platform_interface/ad_event.dart'; -export 'src/platform_interface/ads_request.dart'; diff --git a/packages/interactive_media_ads/lib/src/ad_display_container.dart b/packages/interactive_media_ads/lib/src/ad_display_container.dart deleted file mode 100644 index 99da19682b8e..000000000000 --- a/packages/interactive_media_ads/lib/src/ad_display_container.dart +++ /dev/null @@ -1,102 +0,0 @@ -// 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/cupertino.dart'; - -import 'platform_interface/platform_ad_display_container.dart'; -import 'platform_interface/platform_interface.dart'; - -/// Handles playing ads after they've been received from the server. -/// -/// ## Platform-Specific Features -/// This class contains an underlying implementation provided by the current -/// platform. Once a platform implementation is imported, the examples below -/// can be followed to use features provided by a platform's implementation. -/// -/// {@macro interactive_media_ads.AdDisplayContainer.fromPlatformCreationParams} -/// -/// Below is an example of accessing the platform-specific implementation for -/// iOS and Android: -/// -/// ```dart -/// final AdDisplayContainer container = AdDisplayContainer(); -/// -/// if (InteractiveMediaAdsPlatform.instance is IOSInteractiveMediaAdsPlatform) { -/// final IOSAdDisplayContainer iosContainer = container.platform as IOSAdDisplayContainer; -/// } else if (InteractiveMediaAdsPlatform.instance is AndroidInteractiveMediaAdsPlatform) { -/// final AndroidAdDisplayContainer androidContainer = -/// container.platform as AndroidAdDisplayContainer; -/// } -/// ``` -class AdDisplayContainer extends StatelessWidget { - /// Constructs an [AdDisplayContainer]. - /// - /// See [AdDisplayContainer.fromPlatformCreationParams] for setting parameters for a - /// specific platform. - AdDisplayContainer({ - Key? key, - required void Function(AdDisplayContainer container) onContainerAdded, - }) : this.fromPlatformCreationParams( - key: key, - params: PlatformAdDisplayContainerCreationParams( - onContainerAdded: (PlatformAdDisplayContainer container) { - onContainerAdded(AdDisplayContainer.fromPlatform( - platform: container, - )); - }, - ), - ); - - /// Constructs an [AdDisplayContainer] from creation params for a specific platform. - /// - /// {@template interactive_media_ads.AdDisplayContainer.fromPlatformCreationParams} - /// Below is an example of setting platform-specific creation parameters for - /// iOS and Android: - /// - /// ```dart - /// PlatformAdDisplayContainerCreationParams params = - /// const PlatformAdDisplayContainerCreationParams(); - /// - /// if (InteractiveMediaAdsPlatform.instance is IOSInteractiveMediaAdsPlatform) { - /// params = IOSAdDisplayContainerCreationParams - /// .fromPlatformAdDisplayContainerCreationParams( - /// params, - /// ); - /// } else if (InteractiveMediaAdsPlatform.instance is AndroidInteractiveMediaAdsPlatform) { - /// params = AndroidAdDisplayContainerCreationParams - /// .fromPlatformAdDisplayContainerCreationParams( - /// params, - /// ); - /// } - /// - /// final AdDisplayContainer container = AdDisplayContainer.fromPlatformCreationParams( - /// params, - /// ); - /// ``` - /// {@endtemplate} - AdDisplayContainer.fromPlatformCreationParams({ - Key? key, - required PlatformAdDisplayContainerCreationParams params, - }) : this.fromPlatform( - key: key, - platform: PlatformAdDisplayContainer(params), - ); - - /// Constructs an [AdDisplayContainer] from a specific platform - /// implementation. - const AdDisplayContainer.fromPlatform({super.key, required this.platform}); - - /// Implementation of [PlatformAdDisplayContainer] for the current platform. - final PlatformAdDisplayContainer platform; - - /// Invoked when the native view that contains the ad has been added to the - /// platform view hierarchy. - void Function(PlatformAdDisplayContainer container) get onContainerAdded => - platform.params.onContainerAdded; - - @override - Widget build(BuildContext context) { - return platform.build(context); - } -} diff --git a/packages/interactive_media_ads/lib/src/ads_loader.dart b/packages/interactive_media_ads/lib/src/ads_loader.dart deleted file mode 100644 index 9370cc9fec35..000000000000 --- a/packages/interactive_media_ads/lib/src/ads_loader.dart +++ /dev/null @@ -1,162 +0,0 @@ -// 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/foundation.dart'; - -import 'ad_display_container.dart'; -import 'ads_manager_delegate.dart'; -import 'platform_interface/platform_interface.dart'; - -/// Handles playing ads after they've been received from the server. -/// -/// ## Platform-Specific Features -/// This class contains an underlying implementation provided by the current -/// platform. Once a platform implementation is imported, the examples below -/// can be followed to use features provided by a platform's implementation. -/// -/// {@macro interactive_media_ads.AdsLoader.fromPlatformCreationParams} -/// -/// Below is an example of accessing the platform-specific implementation for -/// iOS and Android: -/// -/// ```dart -/// final AdsLoader loader = AdsLoader(); -/// -/// if (InteractiveMediaAdsPlatform.instance is IOSInteractiveMediaAdsPlatform) { -/// final IOSAdsLoader iosLoader = loader.platform as IOSAdsLoader; -/// } else if (InteractiveMediaAdsPlatform.instance is AndroidInteractiveMediaAdsPlatform) { -/// final AndroidAdsLoader androidLoader = -/// loader.platform as AndroidAdsLoader; -/// } -/// ``` -class AdsLoader { - /// Constructs an [AdsLoader]. - /// - /// See [AdsLoader.fromPlatformCreationParams] for setting parameters for a - /// specific platform. - AdsLoader({ - required AdDisplayContainer container, - required void Function(OnAdsLoadedData data) onAdsLoaded, - required void Function(AdsLoadErrorData data) onAdsLoadError, - }) : this.fromPlatformCreationParams( - PlatformAdsLoaderCreationParams( - container: container.platform, - onAdsLoaded: (PlatformOnAdsLoadedData data) { - onAdsLoaded(OnAdsLoadedData._(platform: data)); - }, - onAdsLoadError: onAdsLoadError, - ), - ); - - /// Constructs an [AdsLoader] from creation params for a specific platform. - /// - /// {@template interactive_media_ads.AdsLoader.fromPlatformCreationParams} - /// Below is an example of setting platform-specific creation parameters for - /// iOS and Android: - /// - /// ```dart - /// PlatformAdsLoaderCreationParams params = - /// const PlatformAdsLoaderCreationParams(); - /// - /// if (InteractiveMediaAdsPlatform.instance is IOSInteractiveMediaAdsPlatform) { - /// params = IOSAdsLoaderCreationParams - /// .fromPlatformAdsLoaderCreationParams( - /// params, - /// ); - /// } else if (InteractiveMediaAdsPlatform.instance is AndroidInteractiveMediaAdsPlatform) { - /// params = AndroidAdsLoaderCreationParams - /// .fromPlatformAdsLoaderCreationParams( - /// params, - /// ); - /// } - /// - /// final AdsLoader loader = AdsLoader.fromPlatformCreationParams( - /// params, - /// ); - /// ``` - /// {@endtemplate} - AdsLoader.fromPlatformCreationParams( - PlatformAdsLoaderCreationParams params, - ) : this.fromPlatform(PlatformAdsLoader(params)); - - /// Constructs a [AdsLoader] from a specific platform implementation. - AdsLoader.fromPlatform(this.platform); - - /// Implementation of [PlatformAdsLoader] for the current platform. - final PlatformAdsLoader platform; - - /// Signals to the SDK that the content has completed. - Future contentComplete() { - return platform.contentComplete(); - } - - /// Requests ads from a server. - Future requestAds(AdsRequest request) { - return platform.requestAds(request); - } -} - -/// Data when ads are successfully loaded from the ad server through an -/// [AdsLoader]. -@immutable -class OnAdsLoadedData { - OnAdsLoadedData._({required this.platform}); - - /// Implementation of [PlatformOnAdsLoadedData] for the current platform. - final PlatformOnAdsLoadedData platform; - - /// The ads manager instance created by the ads loader. - late final AdsManager manager = AdsManager._fromPlatform(platform.manager); -} - -/// Handles playing ads after they've been received from the server. -/// -/// ## Platform-Specific Features -/// This class contains an underlying implementation provided by the current -/// platform. Once a platform implementation is imported, the examples below -/// can be followed to use features provided by a platform's implementation. -/// -/// {@macro interactive_media_ads.AdsManager.fromPlatformCreationParams} -/// -/// Below is an example of accessing the platform-specific implementation for -/// iOS and Android: -/// -/// ```dart -/// final AdsManager manager = AdsManager(); -/// -/// if (InteractiveMediaAdsPlatform.instance is IOSInteractiveMediaAdsPlatform) { -/// final IOSAdsManager iosManager = manager.platform as IOSAdsManager; -/// } else if (InteractiveMediaAdsPlatform.instance is AndroidInteractiveMediaAdsPlatform) { -/// final AndroidAdsManager androidManager = -/// manager.platform as AndroidAdsManager; -/// } -/// ``` -class AdsManager { - /// Constructs a [AdsManager] from a specific platform implementation. - AdsManager._fromPlatform(this.platform); - - /// Implementation of [PlatformAdsManager] for the current platform. - final PlatformAdsManager platform; - - /// Initializes the ad experience using default rendering settings. - Future init() { - return platform.init(AdsManagerInitParams()); - } - - /// Starts playing the ads. - Future start() { - return platform.start(AdsManagerStartParams()); - } - - /// The [AdsManagerDelegate] to notify with events during ad playback. - Future setAdsManagerDelegate(AdsManagerDelegate delegate) { - return platform.setAdsManagerDelegate(delegate.platform); - } - - /// Stops the ad and all tracking, then releases all assets that were loaded - /// to play the ad. - Future destroy() { - return platform.destroy(); - } -} diff --git a/packages/interactive_media_ads/lib/src/ads_manager_delegate.dart b/packages/interactive_media_ads/lib/src/ads_manager_delegate.dart deleted file mode 100644 index 4a3813c719af..000000000000 --- a/packages/interactive_media_ads/lib/src/ads_manager_delegate.dart +++ /dev/null @@ -1,88 +0,0 @@ -// 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 'platform_interface/platform_interface.dart'; - -/// Handles playing ads after they've been received from the server. -/// -/// ## Platform-Specific Features -/// This class contains an underlying implementation provided by the current -/// platform. Once a platform implementation is imported, the examples below -/// can be followed to use features provided by a platform's implementation. -/// -/// {@macro interactive_media_ads.AdsManagerDelegate.fromPlatformCreationParams} -/// -/// Below is an example of accessing the platform-specific implementation for -/// iOS and Android: -/// -/// ```dart -/// final AdsManagerDelegate delegate = AdsManagerDelegate(); -/// -/// if (InteractiveMediaAdsPlatform.instance is IOSInteractiveMediaAdsPlatform) { -/// final IOSAdsManagerDelegate iosDelegate = delegate.platform as IOSAdsManagerDelegate; -/// } else if (InteractiveMediaAdsPlatform.instance is AndroidInteractiveMediaAdsPlatform) { -/// final AndroidAdsManagerDelegate androidDelegate = -/// delegate.platform as AndroidAdsManagerDelegate; -/// } -/// ``` -class AdsManagerDelegate { - /// Constructs an [AdsManagerDelegate]. - /// - /// See [AdsManagerDelegate.fromPlatformCreationParams] for setting parameters for a - /// specific platform. - AdsManagerDelegate({ - void Function(AdEvent event)? onAdEvent, - void Function(AdErrorEvent event)? onAdErrorEvent, - }) : this.fromPlatformCreationParams( - PlatformAdsManagerDelegateCreationParams( - onAdEvent: onAdEvent, - onAdErrorEvent: onAdErrorEvent, - ), - ); - - /// Constructs an [AdsManagerDelegate] from creation params for a specific platform. - /// - /// {@template interactive_media_ads.AdsManagerDelegate.fromPlatformCreationParams} - /// Below is an example of setting platform-specific creation parameters for - /// iOS and Android: - /// - /// ```dart - /// PlatformAdsManagerDelegateCreationParams params = - /// const PlatformAdsManagerDelegateCreationParams(); - /// - /// if (InteractiveMediaAdsPlatform.instance is IOSInteractiveMediaAdsPlatform) { - /// params = IOSAdsManagerDelegateCreationParams - /// .fromPlatformAdsManagerDelegateCreationParams( - /// params, - /// ); - /// } else if (InteractiveMediaAdsPlatform.instance is AndroidInteractiveMediaAdsPlatform) { - /// params = AndroidAdsManagerDelegateCreationParams - /// .fromPlatformAdsManagerDelegateCreationParams( - /// params, - /// ); - /// } - /// - /// final AdsManagerDelegate delegate = AdsManagerDelegate.fromPlatformCreationParams( - /// params, - /// ); - /// ``` - /// {@endtemplate} - AdsManagerDelegate.fromPlatformCreationParams( - PlatformAdsManagerDelegateCreationParams params, - ) : this.fromPlatform(PlatformAdsManagerDelegate(params)); - - /// Constructs a [AdsManagerDelegate] from a specific platform implementation. - AdsManagerDelegate.fromPlatform(this.platform); - - /// Implementation of [PlatformAdsManagerDelegate] for the current platform. - final PlatformAdsManagerDelegate platform; - - /// Invoked when there is an [AdEvent]. - void Function(AdEvent event)? get onAdEvent => platform.params.onAdEvent; - - /// Invoked when there was an error playing the ad. Log the error and resume - /// playing content. - void Function(AdErrorEvent event)? get onAdErrorEvent => - platform.params.onAdErrorEvent; -} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/ad_error.dart b/packages/interactive_media_ads/lib/src/platform_interface/ad_error.dart deleted file mode 100644 index ca89e967307b..000000000000 --- a/packages/interactive_media_ads/lib/src/platform_interface/ad_error.dart +++ /dev/null @@ -1,156 +0,0 @@ -// 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/foundation.dart'; - -/// The types of errors that can be encountered when loading an ad. -enum AdErrorCode { - /// Generic invalid usage of the API. - apiError, - - /// Ads player was not provided. - adsPlayerNotProvided, - - /// There was a problem requesting ads from the server. - adsRequestNetworkError, - - /// The ad slot is not visible on the page. - adslotNotVisible, - - /// There was a problem requesting ads from the server. - companionAdLoadingFailed, - - /// Content playhead was not passed in, but list of ads has been returned from - /// the server. - contentPlayheadMissing, - - /// There was a problem requesting ads from the server. - failedToRequestAds, - - /// There was an error loading the ad. - failedLoadingAd, - - /// An error internal to the SDK occurred. - /// - /// More information may be available in the details. - internalError, - - /// Invalid arguments were provided to SDK methods. - invalidArguments, - - /// The version of the runtime is too old. - osRuntimeTooOld, - - /// An overlay ad failed to load. - overlayAdLoadingFailed, - - /// An overlay ad failed to render. - overlayAdPlayingFailed, - - /// Ads list was returned but ContentProgressProvider was not configured. - playlistNoContentTracking, - - /// Ads list response was malformed. - playlistMalformedResponse, - - /// Listener for at least one of the required vast events was not added. - requiredListenersNotAdded, - - /// There was an error initializing the stream. - streamInitializationFailed, - - /// Ads loader sent ads loaded event when it was not expected. - unexpectedAdsLoadedEvent, - - /// The ad response was not understood and cannot be parsed. - unknownAdResponse, - - /// An unexpected error occurred and the cause is not known. - /// - /// Refer to the inner error for more information. - unknownError, - - /// No assets were found in the VAST ad response. - vastAssetNotFound, - - /// A VAST response containing a single tag with no child tags. - vastEmptyResponse, - - /// Assets were found in the VAST ad response for a linear ad, but none of - /// them matched the video player's capabilities. - vastLinearAssetMismatch, - - /// At least one VAST wrapper ad loaded successfully and a subsequent wrapper - /// or inline ad load has timed out. - vastLoadTimeout, - - /// The ad response was not recognized as a valid VAST ad. - vastMalformedResponse, - - /// Failed to load media assets from a VAST response. - /// - /// The default timeout for media loading is 8 seconds. - vastMediaLoadTimeout, - - /// Assets were found in the VAST ad response for a nonlinear ad, but none of - /// them matched the video player's capabilities. - vastNonlinearAssetMismatch, - - /// No Ads VAST response after one or more wrappers. - vastNoAdsAfterWrapper, - - /// The maximum number of VAST wrapper redirects has been reached. - vastTooManyRedirects, - - /// Trafficking error. - /// - /// Video player received an ad type that it was not expecting and/or cannot - /// display. - vastTraffickingError, - - /// At least one VAST wrapper loaded and a subsequent wrapper or inline ad - /// load has resulted in a 404 response code. - vastInvalidUrl, - - /// There was an error playing the video ad. - videoPlayError, - - /// Another VideoAdsManager is still using the video. - /// - /// It must be unloaded before another ad can play on the same element. - videoElementUsed, - - /// A video element was not specified where it was required. - videoElementRequired, -} - -/// Possible error types while loading or playing ads. -enum AdErrorType { - /// Indicates an error occurred while loading the ads. - loading, - - /// Indicates an error occurred while playing the ads. - playing, - - /// An unexpected error occurred while loading or playing the ads. - /// - /// This may mean that the SDK wasn’t loaded properly. - unknown, -} - -/// Surfaces an error that occurred during ad loading or playing. -@immutable -class AdError { - /// Creates a [AdError]. - const AdError({required this.type, required this.code, this.message}); - - /// Specifies the source of the error. - final AdErrorType type; - - /// The error code for obtaining more specific information about the error. - final AdErrorCode code; - - /// A brief description about the error. - final String? message; -} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/ad_event.dart b/packages/interactive_media_ads/lib/src/platform_interface/ad_event.dart deleted file mode 100644 index 9824d9915fb3..000000000000 --- a/packages/interactive_media_ads/lib/src/platform_interface/ad_event.dart +++ /dev/null @@ -1,53 +0,0 @@ -// 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/foundation.dart'; - -import 'ad_error.dart'; - -/// Types of events that can occur during ad playback. -enum AdEventType { - /// Fired when the ads manager is done playing all the valid ads in the ads - /// response, or when the response doesn't return any valid ads. - allAdsCompleted, - - /// Fired when an ad is clicked. - clicked, - - /// Fired when an ad completes playing. - complete, - - /// Fired when content should be paused. - /// - /// This usually happens right before an ad is about to hide the content. - contentPauseRequested, - - /// Fired when content should be resumed. - /// - /// This usually happens when an ad finishes or collapses. - contentResumeRequested, - - /// Fired when the VAST response has been received. - loaded, -} - -/// Simple data class used to transport ad playback information. -@immutable -class AdEvent { - /// Creates an [AdEvent]. - const AdEvent({required this.type}); - - /// The type of event that occurred. - final AdEventType type; -} - -/// An event raised when there is an error loading or playing ads. -@immutable -class AdErrorEvent { - /// Creates an [AdErrorEvent]. - const AdErrorEvent({required this.error}); - - /// The error that caused this event. - final AdError error; -} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/ads_request.dart b/packages/interactive_media_ads/lib/src/platform_interface/ads_request.dart deleted file mode 100644 index 72e4dfc69b63..000000000000 --- a/packages/interactive_media_ads/lib/src/platform_interface/ads_request.dart +++ /dev/null @@ -1,12 +0,0 @@ -// 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. - -/// An object containing the data used to request ads from the server. -class AdsRequest { - /// Creates an [AdsRequest]. - AdsRequest({required this.adTagUrl}); - - /// The URL from which ads will be requested. - final String adTagUrl; -} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/interactive_media_ads_platform.dart b/packages/interactive_media_ads/lib/src/platform_interface/interactive_media_ads_platform.dart deleted file mode 100644 index f1dac2db7d1d..000000000000 --- a/packages/interactive_media_ads/lib/src/platform_interface/interactive_media_ads_platform.dart +++ /dev/null @@ -1,32 +0,0 @@ -// 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 'platform_ad_display_container.dart'; -import 'platform_ads_loader.dart'; -import 'platform_ads_manager_delegate.dart'; - -/// Interface for a platform implementation of the Interactive Media Ads SDKs. -abstract base class InteractiveMediaAdsPlatform { - /// The instance of [InteractiveMediaAdsPlatform] to use. - /// - /// Platform-specific plugins should set this with their own platform-specific - /// class that extends [InteractiveMediaAdsPlatform] when they register - /// themselves. - static InteractiveMediaAdsPlatform? instance; - - /// Creates a new [PlatformAdsLoader]. - PlatformAdsLoader createPlatformAdsLoader( - PlatformAdsLoaderCreationParams params, - ); - - /// Creates a new [PlatformAdsManagerDelegate]. - PlatformAdsManagerDelegate createPlatformAdsManagerDelegate( - PlatformAdsManagerDelegateCreationParams params, - ); - - /// Creates a new [PlatformAdDisplayContainer]. - PlatformAdDisplayContainer createPlatformAdDisplayContainer( - PlatformAdDisplayContainerCreationParams params, - ); -} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/platform_ad_display_container.dart b/packages/interactive_media_ads/lib/src/platform_interface/platform_ad_display_container.dart deleted file mode 100644 index 075706f39307..000000000000 --- a/packages/interactive_media_ads/lib/src/platform_interface/platform_ad_display_container.dart +++ /dev/null @@ -1,95 +0,0 @@ -// 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/cupertino.dart'; - -import 'interactive_media_ads_platform.dart'; - -/// Object specifying creation parameters for creating a -/// [PlatformAdDisplayContainer]. -/// -/// Platform specific implementations can add additional fields by extending -/// this class. -/// -/// This example demonstrates how to extend the -/// [PlatformAdDisplayContainerCreationParams] to provide additional platform -/// specific parameters. -/// -/// When extending [PlatformAdDisplayContainerCreationParams] additional -/// parameters should always accept `null` or have a default value to prevent -/// breaking changes. -/// -/// ```dart -/// class AndroidPlatformAdDisplayContainerCreationParams -/// extends PlatformAdDisplayContainerCreationParams { -/// AndroidPlatformAdDisplayContainerCreationParams._( -/// PlatformAdDisplayContainerCreationParams params, { -/// this.uri, -/// }) : super(); -/// -/// factory AndroidAdDisplayContainerCreationParams.fromPlatformAdDisplayContainerCreationParams( -/// PlatformAdDisplayContainerCreationParams params, { -/// Uri? uri, -/// }) { -/// return AndroidAdDisplayContainerCreationParams._(params, uri: uri); -/// } -/// -/// final Uri? uri; -/// } -/// ``` -@immutable -base class PlatformAdDisplayContainerCreationParams { - /// Used by the platform implementation to create a new - /// [PlatformAdDisplayContainer]. - const PlatformAdDisplayContainerCreationParams({ - this.key, - required this.onContainerAdded, - }); - - /// Controls how one widget replaces another widget in the tree. - /// - /// See also: - /// * The discussions at [Key] and [GlobalKey]. - final Key? key; - - /// Invoked when the View that contains the ad has been added to the platform - /// view hierarchy. - final void Function(PlatformAdDisplayContainer container) onContainerAdded; -} - -/// The interface for a platform implementation for a container in which to -/// display ads. -abstract base class PlatformAdDisplayContainer { - /// Creates a new [PlatformAdDisplayContainer] - factory PlatformAdDisplayContainer( - PlatformAdDisplayContainerCreationParams params, - ) { - assert( - InteractiveMediaAdsPlatform.instance != null, - 'A platform implementation for `interactive_media_ads` has not been set. ' - 'Please ensure that an implementation of `InteractiveMediaAdsPlatform` ' - 'has been set to `InteractiveMediaAdsPlatform.instance` before use. For ' - 'unit testing, `InteractiveMediaAdsPlatform.instance` can be set with ' - 'your own test implementation.', - ); - final PlatformAdDisplayContainer implementation = - InteractiveMediaAdsPlatform.instance! - .createPlatformAdDisplayContainer(params); - return implementation; - } - - /// Used by the platform implementation to create a new - /// [PlatformAdDisplayContainer]. - /// - /// Should only be used by platform implementations because they can't extend - /// a class that only contains a factory constructor. - @protected - PlatformAdDisplayContainer.implementation(this.params); - - /// The parameters used to initialize the [PlatformAdDisplayContainer]. - final PlatformAdDisplayContainerCreationParams params; - - /// Builds the Widget that contains the native View. - Widget build(BuildContext context); -} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_loader.dart b/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_loader.dart deleted file mode 100644 index 7dc6a57a1d22..000000000000 --- a/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_loader.dart +++ /dev/null @@ -1,119 +0,0 @@ -// 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/foundation.dart'; - -import 'ad_error.dart'; -import 'ads_request.dart'; -import 'interactive_media_ads_platform.dart'; -import 'platform_ad_display_container.dart'; -import 'platform_ads_manager.dart'; - -/// Object specifying creation parameters for creating a [PlatformAdsLoader]. -/// -/// Platform specific implementations can add additional fields by extending -/// this class. -/// -/// This example demonstrates how to extend the -/// [PlatformAdsLoaderCreationParams] to provide additional platform specific -/// parameters. -/// -/// When extending [PlatformAdsLoaderCreationParams] additional parameters -/// should always accept `null` or have a default value to prevent breaking -/// changes. -/// -/// ```dart -/// class AndroidPlatformAdsLoaderCreationParams -/// extends PlatformAdsLoaderCreationParams { -/// AndroidPlatformAdsLoaderCreationParams._( -/// PlatformAdsLoaderCreationParams params, { -/// this.uri, -/// }) : super(); -/// -/// factory AndroidAdsLoaderCreationParams.fromPlatformAdsLoaderCreationParams( -/// PlatformAdsLoaderCreationParams params, { -/// Uri? uri, -/// }) { -/// return AndroidAdsLoaderCreationParams._(params, uri: uri); -/// } -/// -/// final Uri? uri; -/// } -/// ``` -@immutable -base class PlatformAdsLoaderCreationParams { - /// Used by the platform implementation to create a new [PlatformAdsLoader]. - const PlatformAdsLoaderCreationParams({ - required this.container, - required this.onAdsLoaded, - required this.onAdsLoadError, - }); - - /// A container object where ads are rendered. - final PlatformAdDisplayContainer container; - - /// Callback for the ads manager loaded event. - final void Function(PlatformOnAdsLoadedData data) onAdsLoaded; - - /// Callback for errors that occur during the ads request. - final void Function(AdsLoadErrorData data) onAdsLoadError; -} - -/// Interface for a platform implementation of an object that requests ads and -/// handles events from ads request responses. -abstract base class PlatformAdsLoader { - /// Creates a new [PlatformAdsLoader] - factory PlatformAdsLoader( - PlatformAdsLoaderCreationParams params, - ) { - assert( - InteractiveMediaAdsPlatform.instance != null, - 'A platform implementation for `interactive_media_ads` has not been set. ' - 'Please ensure that an implementation of `InteractiveMediaAdsPlatform` ' - 'has been set to `InteractiveMediaAdsPlatform.instance` before use. For ' - 'unit testing, `InteractiveMediaAdsPlatform.instance` can be set with ' - 'your own test implementation.', - ); - final PlatformAdsLoader implementation = - InteractiveMediaAdsPlatform.instance!.createPlatformAdsLoader(params); - return implementation; - } - - /// Used by the platform implementation to create a new [PlatformAdsLoader]. - /// - /// Should only be used by platform implementations because they can't extend - /// a class that only contains a factory constructor. - @protected - PlatformAdsLoader.implementation(this.params); - - /// The parameters used to initialize the [PlatformAdsLoader]. - final PlatformAdsLoaderCreationParams params; - - /// Signal to the SDK that the content has completed. - Future contentComplete(); - - /// Requests ads from a server. - Future requestAds(AdsRequest request); -} - -/// Data when ads are successfully loaded from the ad server through an -/// [PlatformAdsLoader]. -@immutable -class PlatformOnAdsLoadedData { - /// Creates a [PlatformOnAdsLoadedData]. - const PlatformOnAdsLoadedData({required this.manager}); - - /// The ads manager instance created by the ads loader. - final PlatformAdsManager manager; -} - -/// Ad error data that is returned when the ads loader fails to load the ad. -@immutable -class AdsLoadErrorData { - /// Creates a [AdsLoadErrorData]. - const AdsLoadErrorData({required this.error}); - - /// The ad error that occurred while loading the ad. - final AdError error; -} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager.dart b/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager.dart deleted file mode 100644 index d80a17a6a730..000000000000 --- a/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager.dart +++ /dev/null @@ -1,34 +0,0 @@ -// 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/foundation.dart'; - -import 'platform_ads_manager_delegate.dart'; - -/// Additional parameter passed to an [PlatformAdsManager] on initialization. -base class AdsManagerInitParams {} - -/// Additional parameter passed to an [PlatformAdsManager] when starting to play -/// ads. -base class AdsManagerStartParams {} - -/// Interface for a platform implementation of a `AdsManager`. -abstract class PlatformAdsManager { - /// Creates a [PlatformAdsManager]. - @protected - PlatformAdsManager(); - - /// Initializes the ad experience using default rendering settings. - Future init(AdsManagerInitParams params); - - /// Starts playing the ads. - Future start(AdsManagerStartParams params); - - /// /// The [AdsManagerDelegate] to notify with events during ad playback. - Future setAdsManagerDelegate(PlatformAdsManagerDelegate delegate); - - /// Stops the ad and all tracking, then releases all assets that were loaded - /// to play the ad. - Future destroy(); -} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager_delegate.dart b/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager_delegate.dart deleted file mode 100644 index 146e5d1914e6..000000000000 --- a/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager_delegate.dart +++ /dev/null @@ -1,88 +0,0 @@ -// 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/foundation.dart'; - -import 'ad_event.dart'; -import 'interactive_media_ads_platform.dart'; - -/// Object specifying creation parameters for creating a -/// [PlatformAdsManagerDelegate]. -/// -/// Platform specific implementations can add additional fields by extending -/// this class. -/// -/// This example demonstrates how to extend the -/// [PlatformAdsManagerDelegateCreationParams] to provide additional platform -/// specific parameters. -/// -/// When extending [PlatformAdsManagerDelegateCreationParams] additional -/// parameters should always accept `null` or have a default value to prevent -/// breaking changes. -/// -/// ```dart -/// class AndroidPlatformAdsManagerDelegateCreationParams -/// extends PlatformAdsManagerDelegateCreationParams { -/// AndroidPlatformAdsManagerDelegateCreationParams._( -/// PlatformAdsManagerDelegateCreationParams params, { -/// this.uri, -/// }) : super(); -/// -/// factory AndroidAdsManagerDelegateCreationParams.fromPlatformAdsManagerDelegateCreationParams( -/// PlatformAdsManagerDelegateCreationParams params, { -/// Uri? uri, -/// }) { -/// return AndroidAdsManagerDelegateCreationParams._(params, uri: uri); -/// } -/// -/// final Uri? uri; -/// } -/// ``` -@immutable -base class PlatformAdsManagerDelegateCreationParams { - /// Used by the platform implementation to create a new [PlatformAdsManagerDelegate]. - const PlatformAdsManagerDelegateCreationParams({ - this.onAdEvent, - this.onAdErrorEvent, - }); - - /// Invoked when there is an [AdEvent]. - final void Function(AdEvent event)? onAdEvent; - - /// Invoked when there was an error playing the ad. Log the error and resume - /// playing content. - final void Function(AdErrorEvent event)? onAdErrorEvent; -} - -/// Interface for a platform implementation of a `AdsManagerDelegate`. -abstract base class PlatformAdsManagerDelegate { - /// Creates a new [PlatformAdsManagerDelegate] - factory PlatformAdsManagerDelegate( - PlatformAdsManagerDelegateCreationParams params, - ) { - assert( - InteractiveMediaAdsPlatform.instance != null, - 'A platform implementation for `interactive_media_ads` has not been set. ' - 'Please ensure that an implementation of `InteractiveMediaAdsPlatform` ' - 'has been set to `InteractiveMediaAdsPlatform.instance` before use. For ' - 'unit testing, `InteractiveMediaAdsPlatform.instance` can be set with ' - 'your own test implementation.', - ); - final PlatformAdsManagerDelegate implementation = - InteractiveMediaAdsPlatform.instance! - .createPlatformAdsManagerDelegate(params); - return implementation; - } - - /// Used by the platform implementation to create a new - /// [PlatformAdsManagerDelegate]. - /// - /// Should only be used by platform implementations because they can't extend - /// a class that only contains a factory constructor. - @protected - PlatformAdsManagerDelegate.implementation(this.params); - - /// The parameters used to initialize the [PlatformAdsManagerDelegate]. - final PlatformAdsManagerDelegateCreationParams params; -} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/platform_interface.dart b/packages/interactive_media_ads/lib/src/platform_interface/platform_interface.dart deleted file mode 100644 index ad8896480443..000000000000 --- a/packages/interactive_media_ads/lib/src/platform_interface/platform_interface.dart +++ /dev/null @@ -1,12 +0,0 @@ -// 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. - -export 'ad_error.dart'; -export 'ad_event.dart'; -export 'ads_request.dart'; -export 'interactive_media_ads_platform.dart'; -export 'platform_ad_display_container.dart'; -export 'platform_ads_loader.dart'; -export 'platform_ads_manager.dart'; -export 'platform_ads_manager_delegate.dart'; diff --git a/packages/interactive_media_ads/pubspec.yaml b/packages/interactive_media_ads/pubspec.yaml deleted file mode 100644 index a6b7b04a2148..000000000000 --- a/packages/interactive_media_ads/pubspec.yaml +++ /dev/null @@ -1,31 +0,0 @@ -name: interactive_media_ads -description: A Flutter plugin for using the Interactive Media Ads SDKs on Android and iOS. -repository: https://github.com/flutter/packages/tree/main/packages/interactive_media_ads -issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+interactive_media_ads%22 -version: 0.0.1 - -environment: - sdk: ^3.2.3 - flutter: ">=3.16.6" - -flutter: - plugin: - platforms: - android: - package: dev.flutter.packages.interactive_media_ads - pluginClass: InteractiveMediaAdsPlugin - ios: - pluginClass: InteractiveMediaAdsPlugin - -dependencies: - flutter: - sdk: flutter - -dev_dependencies: - build_runner: ^2.1.4 - flutter_test: - sdk: flutter - mockito: 5.4.4 - -topics: - - ads diff --git a/packages/interactive_media_ads/test/ad_display_container_test.dart b/packages/interactive_media_ads/test/ad_display_container_test.dart deleted file mode 100644 index a82a16e8c4c2..000000000000 --- a/packages/interactive_media_ads/test/ad_display_container_test.dart +++ /dev/null @@ -1,59 +0,0 @@ -// 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:interactive_media_ads/interactive_media_ads.dart'; -import 'package:interactive_media_ads/src/ad_display_container.dart'; -import 'package:interactive_media_ads/src/platform_interface/platform_interface.dart'; - -import 'test_stubs.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - testWidgets('build', (WidgetTester tester) async { - final TestPlatformAdDisplayContainer adDisplayContainer = - TestPlatformAdDisplayContainer( - PlatformAdDisplayContainerCreationParams( - onContainerAdded: (_) {}, - ), - onBuild: (_) => Container(), - ); - - await tester.pumpWidget(AdDisplayContainer.fromPlatform( - platform: adDisplayContainer, - )); - - expect(find.byType(Container), findsOneWidget); - }); - - testWidgets('constructor parameters are correctly passed to creation params', - (WidgetTester tester) async { - InteractiveMediaAdsPlatform.instance = - TestInteractiveMediaAdsPlatform(onCreatePlatformAdDisplayContainer: ( - PlatformAdDisplayContainerCreationParams params, - ) { - return TestPlatformAdDisplayContainer( - params, - onBuild: (_) => Container(), - ); - }, onCreatePlatformAdsLoader: (PlatformAdsLoaderCreationParams params) { - throw UnimplementedError(); - }, onCreatePlatformAdsManagerDelegate: ( - PlatformAdsManagerDelegateCreationParams params, - ) { - throw UnimplementedError(); - }); - - final AdDisplayContainer adDisplayContainer = AdDisplayContainer( - key: GlobalKey(), - onContainerAdded: (_) {}, - ); - - // The key passed to the default constructor is used by the super class - // and not passed to the platform implementation. - expect(adDisplayContainer.platform.params.key, isNull); - }); -} diff --git a/packages/interactive_media_ads/test/ads_loader_test.dart b/packages/interactive_media_ads/test/ads_loader_test.dart deleted file mode 100644 index 424c86ccc1d5..000000000000 --- a/packages/interactive_media_ads/test/ads_loader_test.dart +++ /dev/null @@ -1,51 +0,0 @@ -// 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/widgets.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:interactive_media_ads/interactive_media_ads.dart'; -import 'package:interactive_media_ads/src/platform_interface/platform_interface.dart'; - -import 'test_stubs.dart'; - -void main() { - test('contentComplete', () async { - final TestPlatformAdsLoader adsLoader = TestPlatformAdsLoader( - PlatformAdsLoaderCreationParams( - container: createTestAdDisplayContainer(), - onAdsLoaded: (PlatformOnAdsLoadedData data) {}, - onAdsLoadError: (AdsLoadErrorData data) {}, - ), - onContentComplete: expectAsync0(() async {}), - onRequestAds: (AdsRequest request) async {}, - ); - - final AdsLoader loader = AdsLoader.fromPlatform(adsLoader); - await loader.contentComplete(); - }); - - test('requestAds', () async { - final TestPlatformAdsLoader adsLoader = TestPlatformAdsLoader( - PlatformAdsLoaderCreationParams( - container: createTestAdDisplayContainer(), - onAdsLoaded: (PlatformOnAdsLoadedData data) {}, - onAdsLoadError: (AdsLoadErrorData data) {}, - ), - onRequestAds: expectAsync1((AdsRequest request) async {}), - onContentComplete: () async {}, - ); - - final AdsLoader loader = AdsLoader.fromPlatform(adsLoader); - await loader.requestAds(AdsRequest(adTagUrl: '')); - }); -} - -TestPlatformAdDisplayContainer createTestAdDisplayContainer() { - return TestPlatformAdDisplayContainer( - PlatformAdDisplayContainerCreationParams( - onContainerAdded: (_) {}, - ), - onBuild: (_) => Container(), - ); -} diff --git a/packages/interactive_media_ads/test/ads_manager_delegate_test.dart b/packages/interactive_media_ads/test/ads_manager_delegate_test.dart deleted file mode 100644 index ba6801dc41f3..000000000000 --- a/packages/interactive_media_ads/test/ads_manager_delegate_test.dart +++ /dev/null @@ -1,38 +0,0 @@ -// 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_test/flutter_test.dart'; -import 'package:interactive_media_ads/interactive_media_ads.dart'; -import 'package:interactive_media_ads/src/platform_interface/platform_interface.dart'; - -import 'test_stubs.dart'; - -void main() { - test('passes params to platform instance', () async { - InteractiveMediaAdsPlatform.instance = TestInteractiveMediaAdsPlatform( - onCreatePlatformAdsManagerDelegate: - (PlatformAdsManagerDelegateCreationParams params) { - return TestPlatformAdsManagerDelegate(params); - }, - onCreatePlatformAdsLoader: (PlatformAdsLoaderCreationParams params) { - throw UnimplementedError(); - }, - onCreatePlatformAdDisplayContainer: - (PlatformAdDisplayContainerCreationParams params) { - throw UnimplementedError(); - }, - ); - - void onAdEvent(AdEvent event) {} - void onAdErrorEvent(AdErrorEvent event) {} - - final AdsManagerDelegate delegate = AdsManagerDelegate( - onAdEvent: onAdEvent, - onAdErrorEvent: onAdErrorEvent, - ); - - expect(delegate.platform.params.onAdEvent, onAdEvent); - expect(delegate.platform.params.onAdErrorEvent, onAdErrorEvent); - }); -} diff --git a/packages/interactive_media_ads/test/ads_manager_test.dart b/packages/interactive_media_ads/test/ads_manager_test.dart deleted file mode 100644 index cb5f42ccb3f6..000000000000 --- a/packages/interactive_media_ads/test/ads_manager_test.dart +++ /dev/null @@ -1,93 +0,0 @@ -// 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/cupertino.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:interactive_media_ads/interactive_media_ads.dart'; -import 'package:interactive_media_ads/src/platform_interface/platform_interface.dart'; - -import 'test_stubs.dart'; - -void main() { - test('init', () async { - final TestAdsManager platformManager = TestAdsManager( - onInit: expectAsync1((_) async {}), - ); - - final AdsManager manager = createAdsManager(platformManager); - await manager.init(); - }); - - test('start', () async { - final TestAdsManager platformManager = TestAdsManager( - onStart: expectAsync1((_) async {}), - ); - - final AdsManager manager = createAdsManager(platformManager); - await manager.start(); - }); - - test('setAdsManagerDelegate', () async { - final TestAdsManager platformManager = TestAdsManager( - onSetAdsManagerDelegate: expectAsync1((_) async {}), - ); - - final AdsManager manager = createAdsManager(platformManager); - await manager.setAdsManagerDelegate(AdsManagerDelegate.fromPlatform( - TestPlatformAdsManagerDelegate( - const PlatformAdsManagerDelegateCreationParams(), - ), - )); - }); - - test('destroy', () async { - final TestAdsManager platformManager = TestAdsManager( - onDestroy: expectAsync0(() async {}), - ); - - final AdsManager manager = createAdsManager(platformManager); - await manager.destroy(); - }); -} - -AdsManager createAdsManager(PlatformAdsManager platformManager) { - InteractiveMediaAdsPlatform.instance = TestInteractiveMediaAdsPlatform( - onCreatePlatformAdsLoader: (PlatformAdsLoaderCreationParams params) { - return TestPlatformAdsLoader(params, - onContentComplete: () async {}, - onRequestAds: (AdsRequest request) async {}); - }, - onCreatePlatformAdsManagerDelegate: - (PlatformAdsManagerDelegateCreationParams params) { - throw UnimplementedError(); - }, - onCreatePlatformAdDisplayContainer: - (PlatformAdDisplayContainerCreationParams params) { - throw UnimplementedError(); - }, - ); - - late final AdsManager manager; - - final AdsLoader loader = AdsLoader( - container: AdDisplayContainer.fromPlatform( - platform: TestPlatformAdDisplayContainer( - PlatformAdDisplayContainerCreationParams( - onContainerAdded: (_) {}, - ), - onBuild: (_) => Container(), - ), - ), - onAdsLoaded: (OnAdsLoadedData data) { - manager = data.manager; - }, - onAdsLoadError: (_) {}, - ); - - loader.platform.params.onAdsLoaded(PlatformOnAdsLoadedData( - manager: platformManager, - )); - - return manager; -} diff --git a/packages/interactive_media_ads/test/test_stubs.dart b/packages/interactive_media_ads/test/test_stubs.dart deleted file mode 100644 index fd59fb3073f1..000000000000 --- a/packages/interactive_media_ads/test/test_stubs.dart +++ /dev/null @@ -1,127 +0,0 @@ -// 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:interactive_media_ads/src/platform_interface/platform_interface.dart'; - -final class TestInteractiveMediaAdsPlatform - extends InteractiveMediaAdsPlatform { - TestInteractiveMediaAdsPlatform({ - required this.onCreatePlatformAdsLoader, - required this.onCreatePlatformAdsManagerDelegate, - required this.onCreatePlatformAdDisplayContainer, - }); - - PlatformAdsLoader Function(PlatformAdsLoaderCreationParams params) - onCreatePlatformAdsLoader; - - PlatformAdsManagerDelegate Function( - PlatformAdsManagerDelegateCreationParams params, - ) onCreatePlatformAdsManagerDelegate; - - PlatformAdDisplayContainer Function( - PlatformAdDisplayContainerCreationParams params, - ) onCreatePlatformAdDisplayContainer; - - @override - PlatformAdsLoader createPlatformAdsLoader( - PlatformAdsLoaderCreationParams params, - ) { - return onCreatePlatformAdsLoader(params); - } - - @override - PlatformAdsManagerDelegate createPlatformAdsManagerDelegate( - PlatformAdsManagerDelegateCreationParams params, - ) { - return onCreatePlatformAdsManagerDelegate(params); - } - - @override - PlatformAdDisplayContainer createPlatformAdDisplayContainer( - PlatformAdDisplayContainerCreationParams params, - ) { - return onCreatePlatformAdDisplayContainer(params); - } -} - -final class TestPlatformAdDisplayContainer extends PlatformAdDisplayContainer { - TestPlatformAdDisplayContainer( - super.params, { - required this.onBuild, - }) : super.implementation(); - - Widget Function(BuildContext context) onBuild; - - @override - Widget build(BuildContext context) { - return onBuild.call(context); - } -} - -final class TestPlatformAdsLoader extends PlatformAdsLoader { - TestPlatformAdsLoader( - super.params, { - required this.onContentComplete, - required this.onRequestAds, - }) : super.implementation(); - - Future Function() onContentComplete; - - Future Function(AdsRequest request) onRequestAds; - - @override - Future contentComplete() async { - return onContentComplete(); - } - - @override - Future requestAds(AdsRequest request) async { - return onRequestAds(request); - } -} - -final class TestPlatformAdsManagerDelegate extends PlatformAdsManagerDelegate { - TestPlatformAdsManagerDelegate(super.params) : super.implementation(); -} - -class TestAdsManager extends PlatformAdsManager { - TestAdsManager({ - this.onInit, - this.onSetAdsManagerDelegate, - this.onStart, - this.onDestroy, - }); - - Future Function(AdsManagerInitParams params)? onInit; - - Future Function(PlatformAdsManagerDelegate delegate)? - onSetAdsManagerDelegate; - - Future Function(AdsManagerStartParams params)? onStart; - - Future Function()? onDestroy; - - @override - Future init(AdsManagerInitParams params) async { - return onInit?.call(params); - } - - @override - Future setAdsManagerDelegate( - PlatformAdsManagerDelegate delegate, - ) async { - return onSetAdsManagerDelegate?.call(delegate); - } - - @override - Future start(AdsManagerStartParams params) async { - return onStart?.call(params); - } - - @override - Future destroy() async { - return onDestroy?.call(); - } -} From e23442506eea027e94f2c93d44ceb7a38e0fa463 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Wed, 27 Mar 2024 19:18:23 -0700 Subject: [PATCH 113/126] [ci] Temporarily allow-warnings in podspec_check_command.dart (#6416) Adds `--allow-warnings` to podspec_check_command.dart so stuff can keep rolling. **To be removed soon!** --- script/tool/lib/src/podspec_check_command.dart | 1 + script/tool/test/podspec_check_command_test.dart | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/script/tool/lib/src/podspec_check_command.dart b/script/tool/lib/src/podspec_check_command.dart index b0982b7ee69e..d437e2188c1b 100644 --- a/script/tool/lib/src/podspec_check_command.dart +++ b/script/tool/lib/src/podspec_check_command.dart @@ -151,6 +151,7 @@ class PodspecCheckCommand extends PackageLoopingCommand { podspecPath, '--configuration=Debug', // Release targets unsupported arm64 simulators. Use Debug to only build against targeted x86_64 simulator devices. '--skip-tests', + '--allow-warnings', '--use-modular-headers', // Flutter sets use_modular_headers! in its templates. if (libraryLint) '--use-libraries' ]; diff --git a/script/tool/test/podspec_check_command_test.dart b/script/tool/test/podspec_check_command_test.dart index 6745f18b6d4f..3bbe60b506c7 100644 --- a/script/tool/test/podspec_check_command_test.dart +++ b/script/tool/test/podspec_check_command_test.dart @@ -156,6 +156,7 @@ void main() { .path, '--configuration=Debug', '--skip-tests', + '--allow-warnings', '--use-modular-headers', '--use-libraries' ], @@ -171,6 +172,7 @@ void main() { .path, '--configuration=Debug', '--skip-tests', + '--allow-warnings', '--use-modular-headers', ], packagesDir.path), @@ -212,6 +214,7 @@ void main() { .path, '--configuration=Debug', '--skip-tests', + '--allow-warnings', '--use-modular-headers', '--use-libraries' ], @@ -227,6 +230,7 @@ void main() { .path, '--configuration=Debug', '--skip-tests', + '--allow-warnings', '--use-modular-headers', ], packagesDir.path), From 924c7e6e899d7c8c0a2f5ed5244b685b0a1c8aa8 Mon Sep 17 00:00:00 2001 From: "auto-submit[bot]" <98614782+auto-submit[bot]@users.noreply.github.com> Date: Thu, 28 Mar 2024 02:55:25 +0000 Subject: [PATCH 114/126] Reverts "[ci] Temporarily allow-warnings in podspec_check_command.dart (#6416)" (#6419) Reverts: flutter/packages#6416 Initiated by: ditman Reason for reverting: Failures happen (later) in other plugins (more updates in the related issue) https://github.com/flutter/flutter/issues/145866 Original PR Author: ditman Reviewed By: {jmagman} This change reverts the following previous change: Adds `--allow-warnings` to podspec_check_command.dart so stuff can keep rolling. **To be removed soon!** --- script/tool/lib/src/podspec_check_command.dart | 1 - script/tool/test/podspec_check_command_test.dart | 4 ---- 2 files changed, 5 deletions(-) diff --git a/script/tool/lib/src/podspec_check_command.dart b/script/tool/lib/src/podspec_check_command.dart index d437e2188c1b..b0982b7ee69e 100644 --- a/script/tool/lib/src/podspec_check_command.dart +++ b/script/tool/lib/src/podspec_check_command.dart @@ -151,7 +151,6 @@ class PodspecCheckCommand extends PackageLoopingCommand { podspecPath, '--configuration=Debug', // Release targets unsupported arm64 simulators. Use Debug to only build against targeted x86_64 simulator devices. '--skip-tests', - '--allow-warnings', '--use-modular-headers', // Flutter sets use_modular_headers! in its templates. if (libraryLint) '--use-libraries' ]; diff --git a/script/tool/test/podspec_check_command_test.dart b/script/tool/test/podspec_check_command_test.dart index 3bbe60b506c7..6745f18b6d4f 100644 --- a/script/tool/test/podspec_check_command_test.dart +++ b/script/tool/test/podspec_check_command_test.dart @@ -156,7 +156,6 @@ void main() { .path, '--configuration=Debug', '--skip-tests', - '--allow-warnings', '--use-modular-headers', '--use-libraries' ], @@ -172,7 +171,6 @@ void main() { .path, '--configuration=Debug', '--skip-tests', - '--allow-warnings', '--use-modular-headers', ], packagesDir.path), @@ -214,7 +212,6 @@ void main() { .path, '--configuration=Debug', '--skip-tests', - '--allow-warnings', '--use-modular-headers', '--use-libraries' ], @@ -230,7 +227,6 @@ void main() { .path, '--configuration=Debug', '--skip-tests', - '--allow-warnings', '--use-modular-headers', ], packagesDir.path), From 88a02f0c657fe2210aaf984b0aea9c49c3adae11 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Thu, 28 Mar 2024 14:24:05 -0700 Subject: [PATCH 115/126] [google_sign_in_ios] Pins GoogleSignIn to 7.0.0 in podspec. (#6430) This PR temporarily pins the GoogleSignIn iOS SDK to version 7.0.0 in the darwin podspec configuration. This'll enable our CI to keep going, until a more proper fix to `google_sign_in_ios` lands, with an update to the latest [7.1 version](https://github.com/google/GoogleSignIn-iOS/releases). See: * https://github.com/flutter/flutter/issues/145866 For more information about the current CI failures. ## Testing This PR should help with the tests that are currently broken in CI. --- packages/google_sign_in/google_sign_in_ios/CHANGELOG.md | 4 ++++ .../google_sign_in_ios/darwin/google_sign_in_ios.podspec | 2 +- packages/google_sign_in/google_sign_in_ios/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md index 5d5716e1715d..dd958dfb6d36 100644 --- a/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.7.5 + +* Pins GoogleSignIn to iOS SDK "7.0.0" while preparing the update to 7.1. + ## 5.7.4 * Improves type handling in Objective-C code. diff --git a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios.podspec b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios.podspec index 8e5524343b64..10c0b64a69b8 100644 --- a/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios.podspec +++ b/packages/google_sign_in/google_sign_in_ios/darwin/google_sign_in_ios.podspec @@ -15,7 +15,7 @@ Enables Google Sign-In in Flutter apps. s.source_files = 'Classes/**/*.{h,m}' s.public_header_files = 'Classes/**/*.h' s.module_map = 'Classes/FLTGoogleSignInPlugin.modulemap' - s.dependency 'GoogleSignIn', '~> 7.0' + s.dependency 'GoogleSignIn', '~> 7.0.0' s.static_framework = true s.ios.dependency 'Flutter' s.osx.dependency 'FlutterMacOS' diff --git a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml index 22c153723756..81592f091696 100644 --- a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: google_sign_in_ios description: iOS implementation of the google_sign_in plugin. repository: https://github.com/flutter/packages/tree/main/packages/google_sign_in/google_sign_in_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.7.4 +version: 5.7.5 environment: sdk: ^3.2.3 From ead738f4e7f1e501cae01ebffb008ea733815f99 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 28 Mar 2024 17:35:23 -0400 Subject: [PATCH 116/126] [interactive_media_ads] Reland "Creates and adds the interactive_media_ads plugin #6060" (#6425) Relands https://github.com/flutter/packages/pull/6060 with the addition of the `multidexEnabled` as mentioned in https://github.com/flutter/packages/pull/6417. See https://github.com/flutter/packages/pull/6417 for revert Part of https://github.com/flutter/flutter/issues/134228 --- .github/dependabot.yml | 28 + CODEOWNERS | 1 + README.md | 1 + packages/interactive_media_ads/AUTHORS | 6 + packages/interactive_media_ads/CHANGELOG.md | 3 + packages/interactive_media_ads/LICENSE | 25 + packages/interactive_media_ads/README.md | 15 + .../android/build.gradle | 78 +++ .../android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 3 + .../InteractiveMediaAdsPlugin.kt | 37 ++ .../InteractiveMediaAdsPluginTest.kt | 31 + .../interactive_media_ads/example/README.md | 9 + .../example/android/app/build.gradle | 69 ++ .../MainActivityTest.kt | 17 + .../android/app/src/debug/AndroidManifest.xml | 20 + .../android/app/src/main/AndroidManifest.xml | 44 ++ .../DriverExtensionActivity.kt | 10 + .../MainActivity.kt | 9 + .../io/flutter/plugins/DartIntegrationTest.kt | 16 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + .../app/src/main/res/values/styles.xml | 18 + .../example/android/build.gradle | 39 ++ .../example/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + .../example/android/settings.gradle | 39 ++ .../interactive_media_ads_test.dart | 23 + .../ios/Flutter/AppFrameworkInfo.plist | 26 + .../example/ios/Flutter/Debug.xcconfig | 2 + .../example/ios/Flutter/Release.xcconfig | 2 + .../interactive_media_ads/example/ios/Podfile | 44 ++ .../ios/Runner.xcodeproj/project.pbxproj | 619 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 98 +++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../example/ios/Runner/AppDelegate.swift | 17 + .../AppIcon.appiconset/Contents.json | 122 ++++ .../Icon-App-1024x1024@1x.png | Bin 0 -> 10932 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 295 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 450 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 282 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 462 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 704 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 586 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 1674 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 762 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 1226 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 1418 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../Runner/Base.lproj/LaunchScreen.storyboard | 37 ++ .../ios/Runner/Base.lproj/Main.storyboard | 26 + .../example/ios/Runner/Info.plist | 49 ++ .../ios/Runner/Runner-Bridging-Header.h | 5 + .../example/ios/RunnerTests/RunnerTests.swift | 30 + .../example/lib/main.dart | 44 ++ .../example/pubspec.yaml | 25 + .../example/test_driver/integration_test.dart | 7 + .../interactive_media_ads/ios/Assets/.gitkeep | 0 .../Classes/InteractiveMediaAdsPlugin.swift | 24 + .../ios/Resources/PrivacyInfo.xcprivacy | 6 + .../ios/interactive_media_ads.podspec | 29 + .../lib/interactive_media_ads.dart | 10 + .../lib/src/ad_display_container.dart | 102 +++ .../lib/src/ads_loader.dart | 162 +++++ .../lib/src/ads_manager_delegate.dart | 88 +++ .../lib/src/platform_interface/ad_error.dart | 156 +++++ .../lib/src/platform_interface/ad_event.dart | 53 ++ .../src/platform_interface/ads_request.dart | 12 + .../interactive_media_ads_platform.dart | 32 + .../platform_ad_display_container.dart | 95 +++ .../platform_ads_loader.dart | 119 ++++ .../platform_ads_manager.dart | 34 + .../platform_ads_manager_delegate.dart | 88 +++ .../platform_interface.dart | 12 + packages/interactive_media_ads/pubspec.yaml | 31 + .../test/ad_display_container_test.dart | 59 ++ .../test/ads_loader_test.dart | 51 ++ .../test/ads_manager_delegate_test.dart | 38 ++ .../test/ads_manager_test.dart | 93 +++ .../test/test_stubs.dart | 127 ++++ 98 files changed, 3235 insertions(+) create mode 100644 packages/interactive_media_ads/AUTHORS create mode 100644 packages/interactive_media_ads/CHANGELOG.md create mode 100644 packages/interactive_media_ads/LICENSE create mode 100644 packages/interactive_media_ads/README.md create mode 100644 packages/interactive_media_ads/android/build.gradle create mode 100644 packages/interactive_media_ads/android/settings.gradle create mode 100644 packages/interactive_media_ads/android/src/main/AndroidManifest.xml create mode 100644 packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsPlugin.kt create mode 100644 packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsPluginTest.kt create mode 100644 packages/interactive_media_ads/example/README.md create mode 100644 packages/interactive_media_ads/example/android/app/build.gradle create mode 100644 packages/interactive_media_ads/example/android/app/src/androidTest/kotlin/dev/flutter/packages/interactive_media_ads_example/MainActivityTest.kt create mode 100644 packages/interactive_media_ads/example/android/app/src/debug/AndroidManifest.xml create mode 100644 packages/interactive_media_ads/example/android/app/src/main/AndroidManifest.xml create mode 100644 packages/interactive_media_ads/example/android/app/src/main/kotlin/dev/flutter/packages/interactive_media_ads_example/DriverExtensionActivity.kt create mode 100644 packages/interactive_media_ads/example/android/app/src/main/kotlin/dev/flutter/packages/interactive_media_ads_example/MainActivity.kt create mode 100644 packages/interactive_media_ads/example/android/app/src/main/kotlin/io/flutter/plugins/DartIntegrationTest.kt create mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/drawable/launch_background.xml create mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/values-night/styles.xml create mode 100644 packages/interactive_media_ads/example/android/app/src/main/res/values/styles.xml create mode 100644 packages/interactive_media_ads/example/android/build.gradle create mode 100644 packages/interactive_media_ads/example/android/gradle.properties create mode 100644 packages/interactive_media_ads/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/interactive_media_ads/example/android/settings.gradle create mode 100644 packages/interactive_media_ads/example/integration_test/interactive_media_ads_test.dart create mode 100644 packages/interactive_media_ads/example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 packages/interactive_media_ads/example/ios/Flutter/Debug.xcconfig create mode 100644 packages/interactive_media_ads/example/ios/Flutter/Release.xcconfig create mode 100644 packages/interactive_media_ads/example/ios/Podfile create mode 100644 packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 packages/interactive_media_ads/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/interactive_media_ads/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/interactive_media_ads/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/interactive_media_ads/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 packages/interactive_media_ads/example/ios/Runner/AppDelegate.swift create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 packages/interactive_media_ads/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 packages/interactive_media_ads/example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 packages/interactive_media_ads/example/ios/Runner/Info.plist create mode 100644 packages/interactive_media_ads/example/ios/Runner/Runner-Bridging-Header.h create mode 100644 packages/interactive_media_ads/example/ios/RunnerTests/RunnerTests.swift create mode 100644 packages/interactive_media_ads/example/lib/main.dart create mode 100644 packages/interactive_media_ads/example/pubspec.yaml create mode 100644 packages/interactive_media_ads/example/test_driver/integration_test.dart create mode 100644 packages/interactive_media_ads/ios/Assets/.gitkeep create mode 100644 packages/interactive_media_ads/ios/Classes/InteractiveMediaAdsPlugin.swift create mode 100644 packages/interactive_media_ads/ios/Resources/PrivacyInfo.xcprivacy create mode 100644 packages/interactive_media_ads/ios/interactive_media_ads.podspec create mode 100644 packages/interactive_media_ads/lib/interactive_media_ads.dart create mode 100644 packages/interactive_media_ads/lib/src/ad_display_container.dart create mode 100644 packages/interactive_media_ads/lib/src/ads_loader.dart create mode 100644 packages/interactive_media_ads/lib/src/ads_manager_delegate.dart create mode 100644 packages/interactive_media_ads/lib/src/platform_interface/ad_error.dart create mode 100644 packages/interactive_media_ads/lib/src/platform_interface/ad_event.dart create mode 100644 packages/interactive_media_ads/lib/src/platform_interface/ads_request.dart create mode 100644 packages/interactive_media_ads/lib/src/platform_interface/interactive_media_ads_platform.dart create mode 100644 packages/interactive_media_ads/lib/src/platform_interface/platform_ad_display_container.dart create mode 100644 packages/interactive_media_ads/lib/src/platform_interface/platform_ads_loader.dart create mode 100644 packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager.dart create mode 100644 packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager_delegate.dart create mode 100644 packages/interactive_media_ads/lib/src/platform_interface/platform_interface.dart create mode 100644 packages/interactive_media_ads/pubspec.yaml create mode 100644 packages/interactive_media_ads/test/ad_display_container_test.dart create mode 100644 packages/interactive_media_ads/test/ads_loader_test.dart create mode 100644 packages/interactive_media_ads/test/ads_manager_delegate_test.dart create mode 100644 packages/interactive_media_ads/test/ads_manager_test.dart create mode 100644 packages/interactive_media_ads/test/test_stubs.dart diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d9157022847f..d1f55572e699 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -395,6 +395,34 @@ updates: - dependency-name: "*" update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - package-ecosystem: "gradle" + directory: "/packages/interactive_media_ads/android" + commit-message: + prefix: "[interactive_media_ads]" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + ignore: + - dependency-name: "com.android.tools.build:gradle" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "junit:junit" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "org.mockito:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - dependency-name: "androidx.test:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + + - package-ecosystem: "gradle" + directory: "/packages/interactive_media_ads/example/android/app" + commit-message: + prefix: "[interactive_media_ads]" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] + - package-ecosystem: "gradle" directory: "/packages/image_picker/image_picker/example/android/app" commit-message: diff --git a/CODEOWNERS b/CODEOWNERS index 4d7c8b95b3f7..77f7adb749f2 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -23,6 +23,7 @@ packages/google_identity_services_web/** @ditman packages/google_maps_flutter/** @stuartmorgan packages/google_sign_in/** @stuartmorgan packages/image_picker/** @tarrinneal +packages/interactive_media_ads/** @bparrishMines packages/in_app_purchase/** @bparrishMines packages/local_auth/** @stuartmorgan packages/metrics_center/** @keyonghan diff --git a/README.md b/README.md index ac0078ff5f72..1996143171fb 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ These are the packages hosted in this repository: | [google\_maps\_flutter](./packages/google_maps_flutter/) | [![pub package](https://img.shields.io/pub/v/google_maps_flutter.svg)](https://pub.dev/packages/google_maps_flutter) | [![pub points](https://img.shields.io/pub/points/google_maps_flutter)](https://pub.dev/packages/google_maps_flutter/score) | [![popularity](https://img.shields.io/pub/popularity/google_maps_flutter)](https://pub.dev/packages/google_maps_flutter/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20maps?label=)](https://github.com/flutter/flutter/labels/p%3A%20maps) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20google_maps_flutter?label=)](https://github.com/flutter/packages/labels/p%3A%20google_maps_flutter) | | [google\_sign\_in](./packages/google_sign_in/) | [![pub package](https://img.shields.io/pub/v/google_sign_in.svg)](https://pub.dev/packages/google_sign_in) | [![pub points](https://img.shields.io/pub/points/google_sign_in)](https://pub.dev/packages/google_sign_in/score) | [![popularity](https://img.shields.io/pub/popularity/google_sign_in)](https://pub.dev/packages/google_sign_in/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20google_sign_in?label=)](https://github.com/flutter/flutter/labels/p%3A%20google_sign_in) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20google_sign_in?label=)](https://github.com/flutter/packages/labels/p%3A%20google_sign_in) | | [image\_picker](./packages/image_picker/) | [![pub package](https://img.shields.io/pub/v/image_picker.svg)](https://pub.dev/packages/image_picker) | [![pub points](https://img.shields.io/pub/points/image_picker)](https://pub.dev/packages/image_picker/score) | [![popularity](https://img.shields.io/pub/popularity/image_picker)](https://pub.dev/packages/image_picker/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20image_picker?label=)](https://github.com/flutter/flutter/labels/p%3A%20image_picker) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20image_picker?label=)](https://github.com/flutter/packages/labels/p%3A%20image_picker) | +| [interactive\_media\_ads](./packages/interactive_media_ads/) | [![pub package](https://img.shields.io/pub/v/interactive_media_ads.svg)](https://pub.dev/packages/interactive_media_ads) | [![pub points](https://img.shields.io/pub/points/interactive_media_ads)](https://pub.dev/packages/interactive_media_ads/score) | [![popularity](https://img.shields.io/pub/popularity/interactive_media_ads)](https://pub.dev/packages/interactive_media_ads/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20interactive_media_ads?label=)](https://github.com/flutter/flutter/labels/p%3A%20interactive_media_ads) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20interactive_media_ads?label=)](https://github.com/flutter/packages/labels/p%3A%20interactive_media_ads) | | [in\_app\_purchase](./packages/in_app_purchase/) | [![pub package](https://img.shields.io/pub/v/in_app_purchase.svg)](https://pub.dev/packages/in_app_purchase) | [![pub points](https://img.shields.io/pub/points/in_app_purchase)](https://pub.dev/packages/in_app_purchase/score) | [![popularity](https://img.shields.io/pub/popularity/in_app_purchase)](https://pub.dev/packages/in_app_purchase/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20in_app_purchase?label=)](https://github.com/flutter/flutter/labels/p%3A%20in_app_purchase) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20in_app_purchase?label=)](https://github.com/flutter/packages/labels/p%3A%20in_app_purchase) | | [ios\_platform\_images](./packages/ios_platform_images/) | [![pub package](https://img.shields.io/pub/v/ios_platform_images.svg)](https://pub.dev/packages/ios_platform_images) | [![pub points](https://img.shields.io/pub/points/ios_platform_images)](https://pub.dev/packages/ios_platform_images/score) | [![popularity](https://img.shields.io/pub/popularity/ios_platform_images)](https://pub.dev/packages/ios_platform_images/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20ios_platform_images?label=)](https://github.com/flutter/flutter/labels/p%3A%20ios_platform_images) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20ios_platform_images?label=)](https://github.com/flutter/packages/labels/p%3A%20ios_platform_images) | | [local\_auth](./packages/local_auth/) | [![pub package](https://img.shields.io/pub/v/local_auth.svg)](https://pub.dev/packages/local_auth) | [![pub points](https://img.shields.io/pub/points/local_auth)](https://pub.dev/packages/local_auth/score) | [![popularity](https://img.shields.io/pub/popularity/local_auth)](https://pub.dev/packages/local_auth/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20local_auth?label=)](https://github.com/flutter/flutter/labels/p%3A%20local_auth) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20local_auth?label=)](https://github.com/flutter/packages/labels/p%3A%20local_auth) | diff --git a/packages/interactive_media_ads/AUTHORS b/packages/interactive_media_ads/AUTHORS new file mode 100644 index 000000000000..557dff97933b --- /dev/null +++ b/packages/interactive_media_ads/AUTHORS @@ -0,0 +1,6 @@ +# Below is a list of people and organizations that have contributed +# to the Flutter project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. diff --git a/packages/interactive_media_ads/CHANGELOG.md b/packages/interactive_media_ads/CHANGELOG.md new file mode 100644 index 000000000000..477158a8871e --- /dev/null +++ b/packages/interactive_media_ads/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* Adds platform interface for Android and iOS. diff --git a/packages/interactive_media_ads/LICENSE b/packages/interactive_media_ads/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/interactive_media_ads/LICENSE @@ -0,0 +1,25 @@ +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/interactive_media_ads/README.md b/packages/interactive_media_ads/README.md new file mode 100644 index 000000000000..d1d17151f02b --- /dev/null +++ b/packages/interactive_media_ads/README.md @@ -0,0 +1,15 @@ +# interactive\_media\_ads + +Flutter plugin for the [Interactive Media Ads SDKs][1]. + +[![pub package](https://img.shields.io/pub/v/webview_flutter.svg)](https://pub.dev/packages/interactive_media_ads) + +A Flutter plugin for using the Interactive Media Ads SDKs on Android and iOS. + +| | Android | iOS | +|-------------|---------|-------| +| **Support** | SDK 19+ | 12.0+ | + +**This package is still in development.** + +[1]: https://developers.google.com/interactive-media-ads diff --git a/packages/interactive_media_ads/android/build.gradle b/packages/interactive_media_ads/android/build.gradle new file mode 100644 index 000000000000..3490e07ba38e --- /dev/null +++ b/packages/interactive_media_ads/android/build.gradle @@ -0,0 +1,78 @@ +group 'dev.flutter.packages.interactive_media_ads' +version '1.0-SNAPSHOT' + +buildscript { + ext.kotlin_version = '1.7.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:8.0.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + if (project.android.hasProperty("namespace")) { + namespace 'dev.flutter.packages.interactive_media_ads' + } + + compileSdk 34 + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + test.java.srcDirs += 'src/test/kotlin' + } + + defaultConfig { + minSdk 19 + } + + dependencies { + implementation 'androidx.annotation:annotation:1.5.0' + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.jetbrains.kotlin:kotlin-test' + testImplementation 'org.mockito:mockito-inline:5.1.0' + testImplementation 'androidx.test:core:1.3.0' + } + + lintOptions { + checkAllWarnings true + warningsAsErrors true + disable 'AndroidGradlePluginVersion', 'InvalidPackage', 'GradleDependency' + } + + testOptions { + unitTests.includeAndroidResources = true + unitTests.returnDefaultValues = true + unitTests.all { + useJUnitPlatform() + testLogging { + events "passed", "skipped", "failed", "standardOut", "standardError" + outputs.upToDateWhen {false} + showStandardStreams = true + } + } + } +} diff --git a/packages/interactive_media_ads/android/settings.gradle b/packages/interactive_media_ads/android/settings.gradle new file mode 100644 index 000000000000..388e84d5a359 --- /dev/null +++ b/packages/interactive_media_ads/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'interactive_media_ads' diff --git a/packages/interactive_media_ads/android/src/main/AndroidManifest.xml b/packages/interactive_media_ads/android/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..cde3df78e2ba --- /dev/null +++ b/packages/interactive_media_ads/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsPlugin.kt b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsPlugin.kt new file mode 100644 index 000000000000..50595236588a --- /dev/null +++ b/packages/interactive_media_ads/android/src/main/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsPlugin.kt @@ -0,0 +1,37 @@ +// 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. + +package dev.flutter.packages.interactive_media_ads + +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import io.flutter.plugin.common.MethodChannel.MethodCallHandler +import io.flutter.plugin.common.MethodChannel.Result + +/** InteractiveMediaAdsPlugin */ +class InteractiveMediaAdsPlugin : FlutterPlugin, MethodCallHandler { + /// The MethodChannel that will the communication between Flutter and native Android + /// + /// This local reference serves to register the plugin with the Flutter Engine and unregister it + /// when the Flutter Engine is detached from the Activity + private lateinit var channel: MethodChannel + + override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + channel = MethodChannel(flutterPluginBinding.binaryMessenger, "interactive_media_ads") + channel.setMethodCallHandler(this) + } + + override fun onMethodCall(call: MethodCall, result: Result) { + if (call.method == "getPlatformVersion") { + result.success("Android ${android.os.Build.VERSION.RELEASE}") + } else { + result.notImplemented() + } + } + + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + channel.setMethodCallHandler(null) + } +} diff --git a/packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsPluginTest.kt b/packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsPluginTest.kt new file mode 100644 index 000000000000..3adc0d0a56b4 --- /dev/null +++ b/packages/interactive_media_ads/android/src/test/kotlin/dev/flutter/packages/interactive_media_ads/InteractiveMediaAdsPluginTest.kt @@ -0,0 +1,31 @@ +// 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. + +package dev.flutter.packages.interactive_media_ads + +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import kotlin.test.Test +import org.mockito.Mockito + +/* + * This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation. + * + * Once you have built the plugin's example app, you can run these tests from the command + * line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or + * you can run them directly from IDEs that support JUnit such as Android Studio. + */ + +internal class InteractiveMediaAdsPluginTest { + @Test + fun onMethodCall_getPlatformVersion_returnsExpectedValue() { + val plugin = InteractiveMediaAdsPlugin() + + val call = MethodCall("getPlatformVersion", null) + val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java) + plugin.onMethodCall(call, mockResult) + + Mockito.verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE) + } +} diff --git a/packages/interactive_media_ads/example/README.md b/packages/interactive_media_ads/example/README.md new file mode 100644 index 000000000000..96b8bb17dbff --- /dev/null +++ b/packages/interactive_media_ads/example/README.md @@ -0,0 +1,9 @@ +# Platform Implementation Test App + +This is a test app for manual testing and automated integration testing +of this platform implementation. It is not intended to demonstrate actual use of +this package, since the intent is that plugin clients use the app-facing +package. + +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/interactive_media_ads/example/android/app/build.gradle b/packages/interactive_media_ads/example/android/app/build.gradle new file mode 100644 index 000000000000..f3018aafa7a5 --- /dev/null +++ b/packages/interactive_media_ads/example/android/app/build.gradle @@ -0,0 +1,69 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} + +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +android { + namespace "dev.flutter.packages.interactive_media_ads_example" + compileSdk flutter.compileSdkVersion + ndkVersion flutter.ndkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + applicationId "dev.flutter.packages.interactive_media_ads_example" + minSdk flutter.minSdkVersion + targetSdk flutter.targetSdkVersion + multiDexEnabled true + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + api 'androidx.test:core:1.4.0' +} diff --git a/packages/interactive_media_ads/example/android/app/src/androidTest/kotlin/dev/flutter/packages/interactive_media_ads_example/MainActivityTest.kt b/packages/interactive_media_ads/example/android/app/src/androidTest/kotlin/dev/flutter/packages/interactive_media_ads_example/MainActivityTest.kt new file mode 100644 index 000000000000..7f51c3fccf13 --- /dev/null +++ b/packages/interactive_media_ads/example/android/app/src/androidTest/kotlin/dev/flutter/packages/interactive_media_ads_example/MainActivityTest.kt @@ -0,0 +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. + +package dev.flutter.packages.interactive_media_ads_example + +import androidx.test.rule.ActivityTestRule +import dev.flutter.plugins.integration_test.FlutterTestRunner +import io.flutter.plugins.DartIntegrationTest +import org.junit.Rule +import org.junit.runner.RunWith + +@DartIntegrationTest +@RunWith(FlutterTestRunner::class) +class MainActivityTest { + @JvmField @Rule var rule = ActivityTestRule(MainActivity::class.java) +} diff --git a/packages/interactive_media_ads/example/android/app/src/debug/AndroidManifest.xml b/packages/interactive_media_ads/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000000..4665eaa3ae96 --- /dev/null +++ b/packages/interactive_media_ads/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/packages/interactive_media_ads/example/android/app/src/main/AndroidManifest.xml b/packages/interactive_media_ads/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..8733f2b862ab --- /dev/null +++ b/packages/interactive_media_ads/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/interactive_media_ads/example/android/app/src/main/kotlin/dev/flutter/packages/interactive_media_ads_example/DriverExtensionActivity.kt b/packages/interactive_media_ads/example/android/app/src/main/kotlin/dev/flutter/packages/interactive_media_ads_example/DriverExtensionActivity.kt new file mode 100644 index 000000000000..d43924d0054f --- /dev/null +++ b/packages/interactive_media_ads/example/android/app/src/main/kotlin/dev/flutter/packages/interactive_media_ads_example/DriverExtensionActivity.kt @@ -0,0 +1,10 @@ +// 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. + +package dev.flutter.packages.interactive_media_ads_example + +import io.flutter.embedding.android.FlutterActivity + +/** Test Activity that sets the name of the Dart method entrypoint in the manifest. */ +class DriverExtensionActivity : FlutterActivity() diff --git a/packages/interactive_media_ads/example/android/app/src/main/kotlin/dev/flutter/packages/interactive_media_ads_example/MainActivity.kt b/packages/interactive_media_ads/example/android/app/src/main/kotlin/dev/flutter/packages/interactive_media_ads_example/MainActivity.kt new file mode 100644 index 000000000000..3392748b8720 --- /dev/null +++ b/packages/interactive_media_ads/example/android/app/src/main/kotlin/dev/flutter/packages/interactive_media_ads_example/MainActivity.kt @@ -0,0 +1,9 @@ +// 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. + +package dev.flutter.packages.interactive_media_ads_example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity : FlutterActivity() diff --git a/packages/interactive_media_ads/example/android/app/src/main/kotlin/io/flutter/plugins/DartIntegrationTest.kt b/packages/interactive_media_ads/example/android/app/src/main/kotlin/io/flutter/plugins/DartIntegrationTest.kt new file mode 100644 index 000000000000..099fb761cc9d --- /dev/null +++ b/packages/interactive_media_ads/example/android/app/src/main/kotlin/io/flutter/plugins/DartIntegrationTest.kt @@ -0,0 +1,16 @@ +// 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. + +package io.flutter.plugins + +/* + * Annotation to aid repository tooling in determining if a test is + * a native java unit test or a java class with a dart integration. + * + * See: https://github.com/flutter/flutter/wiki/Plugin-Tests#enabling-android-ui-tests + * for more infomation. + */ +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) +annotation class DartIntegrationTest diff --git a/packages/interactive_media_ads/example/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/interactive_media_ads/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 000000000000..f74085f3f6a2 --- /dev/null +++ b/packages/interactive_media_ads/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/packages/interactive_media_ads/example/android/app/src/main/res/drawable/launch_background.xml b/packages/interactive_media_ads/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 000000000000..304732f88420 --- /dev/null +++ b/packages/interactive_media_ads/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/packages/interactive_media_ads/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/interactive_media_ads/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/interactive_media_ads/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/interactive_media_ads/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/interactive_media_ads/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/android/app/src/main/res/values-night/styles.xml b/packages/interactive_media_ads/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 000000000000..06952be745f9 --- /dev/null +++ b/packages/interactive_media_ads/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/packages/interactive_media_ads/example/android/app/src/main/res/values/styles.xml b/packages/interactive_media_ads/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000000..cb1ef88056ed --- /dev/null +++ b/packages/interactive_media_ads/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/packages/interactive_media_ads/example/android/build.gradle b/packages/interactive_media_ads/example/android/build.gradle new file mode 100644 index 000000000000..29a592fd9d6d --- /dev/null +++ b/packages/interactive_media_ads/example/android/build.gradle @@ -0,0 +1,39 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} + +allprojects { + repositories { + // See https://github.com/flutter/flutter/wiki/Plugins-and-Packages-repository-structure#gradle-structure for more info. + def artifactRepoKey = 'ARTIFACT_HUB_REPOSITORY' + if (System.getenv().containsKey(artifactRepoKey)) { + println "Using artifact hub" + maven { url System.getenv(artifactRepoKey) } + } + google() + mavenCentral() + } +} + +gradle.projectsEvaluated { + project(":interactive_media_ads") { + tasks.withType(JavaCompile) { + options.compilerArgs << "-Xlint:all" << "-Werror" + } + } +} diff --git a/packages/interactive_media_ads/example/android/gradle.properties b/packages/interactive_media_ads/example/android/gradle.properties new file mode 100644 index 000000000000..3b5b324f6e3f --- /dev/null +++ b/packages/interactive_media_ads/example/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true +android.enableJetifier=true diff --git a/packages/interactive_media_ads/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/interactive_media_ads/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..e1ca574ef017 --- /dev/null +++ b/packages/interactive_media_ads/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip diff --git a/packages/interactive_media_ads/example/android/settings.gradle b/packages/interactive_media_ads/example/android/settings.gradle new file mode 100644 index 000000000000..12cfac56b468 --- /dev/null +++ b/packages/interactive_media_ads/example/android/settings.gradle @@ -0,0 +1,39 @@ +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +// See https://github.com/flutter/flutter/wiki/Plugins-and-Packages-repository-structure#gradle-structure for more info. +buildscript { + repositories { + maven { + url "https://plugins.gradle.org/m2/" + } + } + dependencies { + classpath "gradle.plugin.com.google.cloud.artifactregistry:artifactregistry-gradle-plugin:2.2.1" + } +} + +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.3.0" apply false + id "org.jetbrains.kotlin.android" version "1.7.10" apply false +} + +include ":app" + +apply plugin: "com.google.cloud.artifactregistry.gradle-plugin" diff --git a/packages/interactive_media_ads/example/integration_test/interactive_media_ads_test.dart b/packages/interactive_media_ads/example/integration_test/interactive_media_ads_test.dart new file mode 100644 index 000000000000..d79165b3788b --- /dev/null +++ b/packages/interactive_media_ads/example/integration_test/interactive_media_ads_test.dart @@ -0,0 +1,23 @@ +// 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_driver/driver_extension.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:interactive_media_ads_example/main.dart' as app; + +/// Entry point for integration tests that require espresso. +@pragma('vm:entry-point') +void integrationTestMain() { + enableFlutterDriverExtension(); + app.main(); +} + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + // Since this test is lacking integration tests, this test ensures the example + // app can be launched on an emulator/device. + testWidgets('Launch Test', (WidgetTester tester) async {}); +} diff --git a/packages/interactive_media_ads/example/ios/Flutter/AppFrameworkInfo.plist b/packages/interactive_media_ads/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 000000000000..7c5696400627 --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 12.0 + + diff --git a/packages/interactive_media_ads/example/ios/Flutter/Debug.xcconfig b/packages/interactive_media_ads/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000000..ec97fc6f3021 --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/interactive_media_ads/example/ios/Flutter/Release.xcconfig b/packages/interactive_media_ads/example/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000000..c4855bfe2000 --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/interactive_media_ads/example/ios/Podfile b/packages/interactive_media_ads/example/ios/Podfile new file mode 100644 index 000000000000..d97f17e223fb --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Podfile @@ -0,0 +1,44 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '12.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.pbxproj b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..966e3a40fea7 --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,619 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = S8QB4VV633; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.packages.interactiveMediaAdsExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.packages.interactiveMediaAdsExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.packages.interactiveMediaAdsExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.packages.interactiveMediaAdsExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = S8QB4VV633; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.packages.interactiveMediaAdsExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = S8QB4VV633; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.packages.interactiveMediaAdsExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..919434a6254f --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000000..f9b0d7c5ea15 --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/interactive_media_ads/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000000..8e3ca5dfe193 --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/interactive_media_ads/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/interactive_media_ads/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..1d526a16ed0f --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/interactive_media_ads/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/interactive_media_ads/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/interactive_media_ads/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/interactive_media_ads/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000000..f9b0d7c5ea15 --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/interactive_media_ads/example/ios/Runner/AppDelegate.swift b/packages/interactive_media_ads/example/ios/Runner/AppDelegate.swift new file mode 100644 index 000000000000..d83c0ff0beea --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner/AppDelegate.swift @@ -0,0 +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. + +import Flutter +import UIKit + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000000..d36b1fab2d9d --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..dc9ada4725e9b0ddb1deab583e5b5102493aa332 GIT binary patch literal 10932 zcmeHN2~<R zh`|8`A_PQ1nSu(UMFx?8j8PC!!VDphaL#`F42fd#7Vlc`zIE4n%Y~eiz4y1j|NDpi z?<@|pSJ-HM`qifhf@m%MamgwK83`XpBA<+azdF#2QsT{X@z0A9Bq>~TVErigKH1~P zRX-!h-f0NJ4Mh++{D}J+K>~~rq}d%o%+4dogzXp7RxX4C>Km5XEI|PAFDmo;DFm6G zzjVoB`@qW98Yl0Kvc-9w09^PrsobmG*Eju^=3f?0o-t$U)TL1B3;sZ^!++3&bGZ!o-*6w?;oOhf z=A+Qb$scV5!RbG+&2S}BQ6YH!FKb0``VVX~T$dzzeSZ$&9=X$3)_7Z{SspSYJ!lGE z7yig_41zpQ)%5dr4ff0rh$@ky3-JLRk&DK)NEIHecf9c*?Z1bUB4%pZjQ7hD!A0r-@NF(^WKdr(LXj|=UE7?gBYGgGQV zidf2`ZT@pzXf7}!NH4q(0IMcxsUGDih(0{kRSez&z?CFA0RVXsVFw3^u=^KMtt95q z43q$b*6#uQDLoiCAF_{RFc{!H^moH_cmll#Fc^KXi{9GDl{>%+3qyfOE5;Zq|6#Hb zp^#1G+z^AXfRKaa9HK;%b3Ux~U@q?xg<2DXP%6k!3E)PA<#4$ui8eDy5|9hA5&{?v z(-;*1%(1~-NTQ`Is1_MGdQ{+i*ccd96ab$R$T3=% zw_KuNF@vI!A>>Y_2pl9L{9h1-C6H8<)J4gKI6{WzGBi<@u3P6hNsXG=bRq5c+z;Gc3VUCe;LIIFDmQAGy+=mRyF++u=drBWV8-^>0yE9N&*05XHZpPlE zxu@?8(ZNy7rm?|<+UNe0Vs6&o?l`Pt>P&WaL~M&#Eh%`rg@Mbb)J&@DA-wheQ>hRV z<(XhigZAT z>=M;URcdCaiO3d^?H<^EiEMDV+7HsTiOhoaMX%P65E<(5xMPJKxf!0u>U~uVqnPN7T!X!o@_gs3Ct1 zlZ_$5QXP4{Aj645wG_SNT&6m|O6~Tsl$q?nK*)(`{J4b=(yb^nOATtF1_aS978$x3 zx>Q@s4i3~IT*+l{@dx~Hst21fR*+5}S1@cf>&8*uLw-0^zK(+OpW?cS-YG1QBZ5q! zgTAgivzoF#`cSz&HL>Ti!!v#?36I1*l^mkrx7Y|K6L#n!-~5=d3;K<;Zqi|gpNUn_ z_^GaQDEQ*jfzh;`j&KXb66fWEk1K7vxQIMQ_#Wu_%3 z4Oeb7FJ`8I>Px;^S?)}2+4D_83gHEq>8qSQY0PVP?o)zAv3K~;R$fnwTmI-=ZLK`= zTm+0h*e+Yfr(IlH3i7gUclNH^!MU>id$Jw>O?2i0Cila#v|twub21@e{S2v}8Z13( zNDrTXZVgris|qYm<0NU(tAPouG!QF4ZNpZPkX~{tVf8xY690JqY1NVdiTtW+NqyRP zZ&;T0ikb8V{wxmFhlLTQ&?OP7 z;(z*<+?J2~z*6asSe7h`$8~Se(@t(#%?BGLVs$p``;CyvcT?7Y!{tIPva$LxCQ&4W z6v#F*);|RXvI%qnoOY&i4S*EL&h%hP3O zLsrFZhv&Hu5tF$Lx!8(hs&?!Kx5&L(fdu}UI5d*wn~A`nPUhG&Rv z2#ixiJdhSF-K2tpVL=)5UkXRuPAFrEW}7mW=uAmtVQ&pGE-&az6@#-(Te^n*lrH^m@X-ftVcwO_#7{WI)5v(?>uC9GG{lcGXYJ~Q8q zbMFl7;t+kV;|;KkBW2!P_o%Czhw&Q(nXlxK9ak&6r5t_KH8#1Mr-*0}2h8R9XNkr zto5-b7P_auqTJb(TJlmJ9xreA=6d=d)CVbYP-r4$hDn5|TIhB>SReMfh&OVLkMk-T zYf%$taLF0OqYF?V{+6Xkn>iX@TuqQ?&cN6UjC9YF&%q{Ut3zv{U2)~$>-3;Dp)*(? zg*$mu8^i=-e#acaj*T$pNowo{xiGEk$%DusaQiS!KjJH96XZ-hXv+jk%ard#fu=@Q z$AM)YWvE^{%tDfK%nD49=PI|wYu}lYVbB#a7wtN^Nml@CE@{Gv7+jo{_V?I*jkdLD zJE|jfdrmVbkfS>rN*+`#l%ZUi5_bMS<>=MBDNlpiSb_tAF|Zy`K7kcp@|d?yaTmB^ zo?(vg;B$vxS|SszusORgDg-*Uitzdi{dUV+glA~R8V(?`3GZIl^egW{a919!j#>f` znL1o_^-b`}xnU0+~KIFLQ)$Q6#ym%)(GYC`^XM*{g zv3AM5$+TtDRs%`2TyR^$(hqE7Y1b&`Jd6dS6B#hDVbJlUXcG3y*439D8MrK!2D~6gn>UD4Imctb z+IvAt0iaW73Iq$K?4}H`7wq6YkTMm`tcktXgK0lKPmh=>h+l}Y+pDtvHnG>uqBA)l zAH6BV4F}v$(o$8Gfo*PB>IuaY1*^*`OTx4|hM8jZ?B6HY;F6p4{`OcZZ(us-RVwDx zUzJrCQlp@mz1ZFiSZ*$yX3c_#h9J;yBE$2g%xjmGF4ca z&yL`nGVs!Zxsh^j6i%$a*I3ZD2SoNT`{D%mU=LKaEwbN(_J5%i-6Va?@*>=3(dQy` zOv%$_9lcy9+(t>qohkuU4r_P=R^6ME+wFu&LA9tw9RA?azGhjrVJKy&8=*qZT5Dr8g--d+S8zAyJ$1HlW3Olryt`yE zFIph~Z6oF&o64rw{>lgZISC6p^CBer9C5G6yq%?8tC+)7*d+ib^?fU!JRFxynRLEZ zj;?PwtS}Ao#9whV@KEmwQgM0TVP{hs>dg(1*DiMUOKHdQGIqa0`yZnHk9mtbPfoLx zo;^V6pKUJ!5#n`w2D&381#5#_t}AlTGEgDz$^;u;-vxDN?^#5!zN9ngytY@oTv!nc zp1Xn8uR$1Z;7vY`-<*?DfPHB;x|GUi_fI9@I9SVRv1)qETbNU_8{5U|(>Du84qP#7 z*l9Y$SgA&wGbj>R1YeT9vYjZuC@|{rajTL0f%N@>3$DFU=`lSPl=Iv;EjuGjBa$Gw zHD-;%YOE@<-!7-Mn`0WuO3oWuL6tB2cpPw~Nvuj|KM@))ixuDK`9;jGMe2d)7gHin zS<>k@!x;!TJEc#HdL#RF(`|4W+H88d4V%zlh(7#{q2d0OQX9*FW^`^_<3r$kabWAB z$9BONo5}*(%kx zOXi-yM_cmB3>inPpI~)duvZykJ@^^aWzQ=eQ&STUa}2uT@lV&WoRzkUoE`rR0)`=l zFT%f|LA9fCw>`enm$p7W^E@U7RNBtsh{_-7vVz3DtB*y#*~(L9+x9*wn8VjWw|Q~q zKFsj1Yl>;}%MG3=PY`$g$_mnyhuV&~O~u~)968$0b2!Jkd;2MtAP#ZDYw9hmK_+M$ zb3pxyYC&|CuAbtiG8HZjj?MZJBFbt`ryf+c1dXFuC z0*ZQhBzNBd*}s6K_G}(|Z_9NDV162#y%WSNe|FTDDhx)K!c(mMJh@h87@8(^YdK$&d*^WQe8Z53 z(|@MRJ$Lk-&ii74MPIs80WsOFZ(NX23oR-?As+*aq6b?~62@fSVmM-_*cb1RzZ)`5$agEiL`-E9s7{GM2?(KNPgK1(+c*|-FKoy}X(D_b#etO|YR z(BGZ)0Ntfv-7R4GHoXp?l5g#*={S1{u-QzxCGng*oWr~@X-5f~RA14b8~B+pLKvr4 zfgL|7I>jlak9>D4=(i(cqYf7#318!OSR=^`xxvI!bBlS??`xxWeg?+|>MxaIdH1U~#1tHu zB{QMR?EGRmQ_l4p6YXJ{o(hh-7Tdm>TAX380TZZZyVkqHNzjUn*_|cb?T? zt;d2s-?B#Mc>T-gvBmQZx(y_cfkXZO~{N zT6rP7SD6g~n9QJ)8F*8uHxTLCAZ{l1Y&?6v)BOJZ)=R-pY=Y=&1}jE7fQ>USS}xP#exo57uND0i*rEk@$;nLvRB@u~s^dwRf?G?_enN@$t* zbL%JO=rV(3Ju8#GqUpeE3l_Wu1lN9Y{D4uaUe`g>zlj$1ER$6S6@{m1!~V|bYkhZA z%CvrDRTkHuajMU8;&RZ&itnC~iYLW4DVkP<$}>#&(`UO>!n)Po;Mt(SY8Yb`AS9lt znbX^i?Oe9r_o=?})IHKHoQGKXsps_SE{hwrg?6dMI|^+$CeC&z@*LuF+P`7LfZ*yr+KN8B4{Nzv<`A(wyR@!|gw{zB6Ha ziwPAYh)oJ(nlqSknu(8g9N&1hu0$vFK$W#mp%>X~AU1ay+EKWcFdif{% z#4!4aoVVJ;ULmkQf!ke2}3hqxLK>eq|-d7Ly7-J9zMpT`?dxo6HdfJA|t)?qPEVBDv z{y_b?4^|YA4%WW0VZd8C(ZgQzRI5(I^)=Ub`Y#MHc@nv0w-DaJAqsbEHDWG8Ia6ju zo-iyr*sq((gEwCC&^TYBWt4_@|81?=B-?#P6NMff(*^re zYqvDuO`K@`mjm_Jd;mW_tP`3$cS?R$jR1ZN09$YO%_iBqh5ftzSpMQQtxKFU=FYmP zeY^jph+g<4>YO;U^O>-NFLn~-RqlHvnZl2yd2A{Yc1G@Ga$d+Q&(f^tnPf+Z7serIU};17+2DU_f4Z z@GaPFut27d?!YiD+QP@)T=77cR9~MK@bd~pY%X(h%L={{OIb8IQmf-!xmZkm8A0Ga zQSWONI17_ru5wpHg3jI@i9D+_Y|pCqVuHJNdHUauTD=R$JcD2K_liQisqG$(sm=k9;L* z!L?*4B~ql7uioSX$zWJ?;q-SWXRFhz2Jt4%fOHA=Bwf|RzhwqdXGr78y$J)LR7&3T zE1WWz*>GPWKZ0%|@%6=fyx)5rzUpI;bCj>3RKzNG_1w$fIFCZ&UR0(7S?g}`&Pg$M zf`SLsz8wK82Vyj7;RyKmY{a8G{2BHG%w!^T|Njr!h9TO2LaP^_f22Q1=l$QiU84ao zHe_#{S6;qrC6w~7{y(hs-?-j?lbOfgH^E=XcSgnwW*eEz{_Z<_xN#0001NP)t-s|Ns9~ z#rXRE|M&d=0au&!`~QyF`q}dRnBDt}*!qXo`c{v z{Djr|@Adh0(D_%#_&mM$D6{kE_x{oE{l@J5@%H*?%=t~i_`ufYOPkAEn!pfkr2$fs z652Tz0001XNklqeeKN4RM4i{jKqmiC$?+xN>3Apn^ z0QfuZLym_5b<*QdmkHjHlj811{If)dl(Z2K0A+ekGtrFJb?g|wt#k#pV-#A~bK=OT ts8>{%cPtyC${m|1#B1A6#u!Q;umknL1chzTM$P~L002ovPDHLkV1lTfnu!1a literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..797d452e458972bab9d994556c8305db4c827017 GIT binary patch literal 406 zcmV;H0crk;P))>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed2d933e1120817fe9182483a228007b18ab6ae GIT binary patch literal 450 zcmV;z0X_bSP)iGWQ_5NJQ_~rNh*z)}eT%KUb z`7gNk0#AwF^#0T0?hIa^`~Ck;!}#m+_uT050aTR(J!bU#|IzRL%^UsMS#KsYnTF*!YeDOytlP4VhV?b} z%rz_<=#CPc)tU1MZTq~*2=8~iZ!lSa<{9b@2Jl;?IEV8)=fG217*|@)CCYgFze-x? zIFODUIA>nWKpE+bn~n7;-89sa>#DR>TSlqWk*!2hSN6D~Qb#VqbP~4Fk&m`@1$JGr zXPIdeRE&b2Thd#{MtDK$px*d3-Wx``>!oimf%|A-&-q*6KAH)e$3|6JV%HX{Hig)k suLT-RhftRq8b9;(V=235Wa|I=027H2wCDra;{X5v07*qoM6N<$f;9x^2LJ#7 literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..4cd7b0099ca80c806f8fe495613e8d6c69460d76 GIT binary patch literal 282 zcmV+#0p(^bcu7P-R4C8Q z&e;xxFbF_Vrezo%_kH*OKhshZ6BFpG-Y1e10`QXJKbND7AMQ&cMj60B5TNObaZxYybcN07*qoM6N<$g3m;S%K!iX literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fe730945a01f64a61e2235dbe3f45b08f7729182 GIT binary patch literal 462 zcmV;<0WtoGP)-}iV`2<;=$?g5M=KQbZ{F&YRNy7Nn@%_*5{gvDM0aKI4?ESmw z{NnZg)A0R`+4?NF_RZexyVB&^^ZvN!{I28tr{Vje;QNTz`dG&Jz0~Ek&f2;*Z7>B|cg}xYpxEFY+0YrKLF;^Q+-HreN0P{&i zK~zY`?b7ECf-n?@;d<&orQ*Q7KoR%4|C>{W^h6@&01>0SKS`dn{Q}GT%Qj_{PLZ_& zs`MFI#j-(>?bvdZ!8^xTwlY{qA)T4QLbY@j(!YJ7aXJervHy6HaG_2SB`6CC{He}f zHVw(fJWApwPq!6VY7r1w-Fs)@ox~N+q|w~e;JI~C4Vf^@d>Wvj=fl`^u9x9wd9 zR%3*Q+)t%S!MU_`id^@&Y{y7-r98lZX0?YrHlfmwb?#}^1b{8g&KzmkE(L>Z&)179 zp<)v6Y}pRl100G2FL_t(o!|l{-Q-VMg#&MKg7c{O0 z2wJImOS3Gy*Z2Qifdv~JYOp;v+U)a|nLoc7hNH;I$;lzDt$}rkaFw1mYK5_0Q(Sut zvbEloxON7$+HSOgC9Z8ltuC&0OSF!-mXv5caV>#bc3@hBPX@I$58-z}(ZZE!t-aOG zpjNkbau@>yEzH(5Yj4kZiMH32XI!4~gVXNnjAvRx;Sdg^`>2DpUEwoMhTs_st8pKG z(%SHyHdU&v%f36~uERh!bd`!T2dw;z6PrOTQ7Vt*#9F2uHlUVnb#ev_o^fh}Dzmq} zWtlk35}k=?xj28uO|5>>$yXadTUE@@IPpgH`gJ~Ro4>jd1IF|(+IX>8M4Ps{PNvmI zNj4D+XgN83gPt_Gm}`Ybv{;+&yu-C(Grdiahmo~BjG-l&mWM+{e5M1sm&=xduwgM9 z`8OEh`=F3r`^E{n_;%9weN{cf2%7=VzC@cYj+lg>+3|D|_1C@{hcU(DyQG_BvBWe? zvTv``=%b1zrol#=R`JB)>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..502f463a9bc882b461c96aadf492d1729e49e725 GIT binary patch literal 586 zcmV-Q0=4~#P)+}#`wDE{8-2Mebf5<{{PqV{TgVcv*r8?UZ3{-|G?_}T*&y;@cqf{ z{Q*~+qr%%p!1pS*_Uicl#q9lc(D`!D`LN62sNwq{oYw(Wmhk)k<@f$!$@ng~_5)Ru z0Z)trIA5^j{DIW^c+vT2%lW+2<(RtE2wR;4O@)Tm`Xr*?A(qYoM}7i5Yxw>D(&6ou zxz!_Xr~yNF+waPe00049Nkl*;a!v6h%{rlvIH#gW3s8p;bFr=l}mRqpW2h zw=OA%hdyL~z+UHOzl0eKhEr$YYOL-c-%Y<)=j?(bzDweB7{b+%_ypvm_cG{SvM=DK zhv{K@m>#Bw>2W$eUI#iU)Wdgs8Y3U+A$Gd&{+j)d)BmGKx+43U_!tik_YlN)>$7G! zhkE!s;%oku3;IwG3U^2kw?z+HM)jB{@zFhK8P#KMSytSthr+4!c(5c%+^UBn`0X*2 zy3(k600_CSZj?O$Qu%&$;|TGUJrptR(HzyIx>5E(2r{eA(<6t3e3I0B)7d6s7?Z5J zZ!rtKvA{MiEBm&KFtoifx>5P^Z=vl)95XJn()aS5%ad(s?4-=Tkis9IGu{`Fy8r+H07*qoM6N<$f20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0ec303439225b78712f49115768196d8d76f6790 GIT binary patch literal 862 zcmV-k1EKthP)20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..e9f5fea27c705180eb716271f41b582e76dcbd90 GIT binary patch literal 1674 zcmV;526g#~P){YQnis^a@{&-nmRmq)<&%Mztj67_#M}W?l>kYSliK<%xAp;0j{!}J0!o7b zE>q9${Lb$D&h7k=+4=!ek^n+`0zq>LL1O?lVyea53S5x`Nqqo2YyeuIrQrJj9XjOp z{;T5qbj3}&1vg1VK~#9!?b~^C5-}JC@Pyrv-6dSEqJqT}#j9#dJ@GzT@B8}x zU&J@bBI>f6w6en+CeI)3^kC*U?}X%OD8$Fd$H&LV$H&LV$H&LV#|K5~mLYf|VqzOc zkc7qL~0sOYuM{tG`rYEDV{DWY`Z8&)kW*hc2VkBuY+^Yx&92j&StN}Wp=LD zxoGxXw6f&8sB^u})h@b@z0RBeD`K7RMR9deyL(ZJu#39Z>rT)^>v}Khq8U-IbIvT> z?4pV9qGj=2)TNH3d)=De<+^w;>S7m_eFKTvzeaBeir45xY!^m!FmxnljbSS_3o=g( z->^wC9%qkR{kbGnW8MfFew_o9h3(r55Is`L$8KI@d+*%{=Nx+FXJ98L0PjFIu;rGnnfY zn1R5Qnp<{Jq0M1vX=X&F8gtLmcWv$1*M@4ZfF^9``()#hGTeKeP`1!iED ztNE(TN}M5}3Bbc*d=FIv`DNv&@|C6yYj{sSqUj5oo$#*0$7pu|Dd2TLI>t5%I zIa4Dvr(iayb+5x=j*Vum9&irk)xV1`t509lnPO0%skL8_1c#Xbamh(2@f?4yUI zhhuT5<#8RJhGz4%b$`PJwKPAudsm|at?u;*hGgnA zU1;9gnxVBC)wA(BsB`AW54N{|qmikJR*%x0c`{LGsSfa|NK61pYH(r-UQ4_JXd!Rsz)=k zL{GMc5{h138)fF5CzHEDM>+FqY)$pdN3}Ml+riTgJOLN0F*Vh?{9ESR{SVVg>*>=# zix;VJHPtvFFCRY$Ks*F;VX~%*r9F)W`PmPE9F!(&s#x07n2<}?S{(ygpXgX-&B&OM zONY&BRQ(#%0%jeQs?oJ4P!p*R98>qCy5p8w>_gpuh39NcOlp)(wOoz0sY-Qz55eB~ z7OC-fKBaD1sE3$l-6QgBJO!n?QOTza`!S_YK z_v-lm^7{VO^8Q@M_^8F)09Ki6%=s?2_5eupee(w1FB%aqSweusQ-T+CH0Xt{` zFjMvW{@C&TB)k25()nh~_yJ9coBRL(0oO@HK~z}7?bm5j;y@69;bvlHb2tf!$ReA~x{22wTq550 z?f?Hnw(;m3ip30;QzdV~7pi!wyMYhDtXW#cO7T>|f=bdFhu+F!zMZ2UFj;GUKX7tI z;hv3{q~!*pMj75WP_c}>6)IWvg5_yyg<9Op()eD1hWC19M@?_9_MHec{Z8n3FaF{8 z;u`Mw0ly(uE>*CgQYv{be6ab2LWhlaH1^iLIM{olnag$78^Fd}%dR7;JECQ+hmk|o z!u2&!3MqPfP5ChDSkFSH8F2WVOEf0(E_M(JL17G}Y+fg0_IuW%WQ zG(mG&u?|->YSdk0;8rc{yw2@2Z&GA}z{Wb91Ooz9VhA{b2DYE7RmG zjL}?eq#iX%3#k;JWMx_{^2nNax`xPhByFiDX+a7uTGU|otOvIAUy|dEKkXOm-`aWS z27pUzD{a)Ct<6p{{3)+lq@i`t@%>-wT4r?*S}k)58e09WZYP0{{R3FC5Sl00039P)t-s|Ns9~ z#rP?<_5oL$Q^olD{r_0T`27C={r>*`|Nj71npVa5OTzc(_WfbW_({R{p56NV{r*M2 z_xt?)2V0#0NsfV0u>{42ctGP(8vQj-Btk1n|O0ZD=YLwd&R{Ko41Gr9H= zY@z@@bOAMB5Ltl$E>bJJ{>JP30ZxkmI%?eW{k`b?Wy<&gOo;dS`~CR$Vwb@XWtR|N zi~t=w02?-0&j0TD{>bb6sNwsK*!p?V`RMQUl(*DVjk-9Cx+-z1KXab|Ka2oXhX5f% z`$|e!000AhNklrxs)5QTeTVRiEmz~MKK1WAjCw(c-JK6eox;2O)?`? zTG`AHia671e^vgmp!llKp|=5sVHk#C7=~epA~VAf-~%aPC=%Qw01h8mnSZ|p?hz91 z7p83F3%LVu9;S$tSI$C^%^yud1dfTM_6p2|+5Ejp$bd`GDvbR|xit>i!ZD&F>@CJrPmu*UjD&?DfZs=$@e3FQA(vNiU+$A*%a} z?`XcG2jDxJ_ZQ#Md`H{4Lpf6QBDp81_KWZ6Tk#yCy1)32zO#3<7>b`eT7UyYH1eGz z;O(rH$=QR*L%%ZcBpc=eGua?N55nD^K(8<#gl2+pN_j~b2MHs4#mcLmv%DkspS-3< zpI1F=^9siI0s-;IN_IrA;5xm~3?3!StX}pUv0vkxMaqm+zxrg7X7(I&*N~&dEd0kD z-FRV|g=|QuUsuh>-xCI}vD2imzYIOIdcCVV=$Bz@*u0+Bs<|L^)32nN*=wu3n%Ynw z@1|eLG>!8ruU1pFXUfb`j>(=Gy~?Rn4QJ-c3%3T|(Frd!bI`9u&zAnyFYTqlG#&J7 zAkD(jpw|oZLNiA>;>hgp1KX7-wxC~31II47gc zHcehD6Uxlf%+M^^uN5Wc*G%^;>D5qT{>=uxUhX%WJu^Z*(_Wq9y}npFO{Hhb>s6<9 zNi0pHXWFaVZnb)1+RS&F)xOv6&aeILcI)`k#0YE+?e)5&#r7J#c`3Z7x!LpTc01dx zrdC3{Z;joZ^KN&))zB_i)I9fWedoN>Zl-6_Iz+^G&*ak2jpF07*qoM6N<$f;w%0(f|Me literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0467bf12aa4d28f374bb26596605a46dcbb3e7c8 GIT binary patch literal 1418 zcmV;51$Fv~P)q zKfU)WzW*n(@|xWGCA9ScMt*e9`2kdxPQ&&>|-UCa7_51w+ zLUsW@ZzZSW0y$)Hp~e9%PvP|a03ks1`~K?q{u;6NC8*{AOqIUq{CL&;p56Lf$oQGq z^={4hPQv)y=I|4n+?>7Fim=dxt1 z2H+Dm+1+fh+IF>G0SjJMkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJT zkdTm&kdTm&kdTm&kdP`esgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>Tmf` zm1{eLgw!b{bXkjWbF%dTkTZEJWyWOb##Lfw4EK2}<0d6%>AGS{po>WCOy&f$Tay_> z?NBlkpo@s-O;0V%Y_Xa-G#_O08q5LR*~F%&)}{}r&L%Sbs8AS4t7Y0NEx*{soY=0MZExqA5XHQkqi#4gW3 zqODM^iyZl;dvf)-bOXtOru(s)Uc7~BFx{w-FK;2{`VA?(g&@3z&bfLFyctOH!cVsF z7IL=fo-qBndRUm;kAdXR4e6>k-z|21AaN%ubeVrHl*<|s&Ax@W-t?LR(P-24A5=>a z*R9#QvjzF8n%@1Nw@?CG@6(%>+-0ASK~jEmCV|&a*7-GKT72W<(TbSjf)&Eme6nGE z>Gkj4Sq&2e+-G%|+NM8OOm5zVl9{Z8Dd8A5z3y8mZ=4Bv4%>as_{9cN#bm~;h>62( zdqY93Zy}v&c4n($Vv!UybR8ocs7#zbfX1IY-*w~)p}XyZ-SFC~4w>BvMVr`dFbelV{lLL0bx7@*ZZdebr3`sP;? zVImji)kG)(6Juv0lz@q`F!k1FE;CQ(D0iG$wchPbKZQELlsZ#~rt8#90Y_Xh&3U-< z{s<&cCV_1`^TD^ia9!*mQDq& zn2{r`j};V|uV%_wsP!zB?m%;FeaRe+X47K0e+KE!8C{gAWF8)lCd1u1%~|M!XNRvw zvtqy3iz0WSpWdhn6$hP8PaRBmp)q`#PCA`Vd#Tc$@f1tAcM>f_I@bC)hkI9|o(Iqv zo}Piadq!j76}004RBio<`)70k^`K1NK)q>w?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF z34$0Z;QO!JOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUC YUoZo%k(ykuW&i*H07*qoM6N<$f+CH{y8r+H literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 000000000000..0bedcf2fd467 --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/interactive_media_ads/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/packages/interactive_media_ads/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/interactive_media_ads/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000000..f2e259c7c939 --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/interactive_media_ads/example/ios/Runner/Base.lproj/Main.storyboard b/packages/interactive_media_ads/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000000..f3c28516fb38 --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/interactive_media_ads/example/ios/Runner/Info.plist b/packages/interactive_media_ads/example/ios/Runner/Info.plist new file mode 100644 index 000000000000..5394f403367c --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Interactive Media Ads + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + interactive_media_ads_example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + + diff --git a/packages/interactive_media_ads/example/ios/Runner/Runner-Bridging-Header.h b/packages/interactive_media_ads/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 000000000000..eb7e8ba8052f --- /dev/null +++ b/packages/interactive_media_ads/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1,5 @@ +// 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 "GeneratedPluginRegistrant.h" diff --git a/packages/interactive_media_ads/example/ios/RunnerTests/RunnerTests.swift b/packages/interactive_media_ads/example/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 000000000000..cea3cbd3a1fb --- /dev/null +++ b/packages/interactive_media_ads/example/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,30 @@ +// 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 Flutter +import UIKit +import XCTest + +@testable import interactive_media_ads + +// This demonstrates a simple unit test of the Swift portion of this plugin's implementation. +// +// See https://developer.apple.com/documentation/xctest for more information about using XCTest. + +class RunnerTests: XCTestCase { + + func testGetPlatformVersion() { + let plugin = InteractiveMediaAdsPlugin() + + let call = FlutterMethodCall(methodName: "getPlatformVersion", arguments: []) + + let resultExpectation = expectation(description: "result block must be called.") + plugin.handle(call) { result in + XCTAssertEqual(result as! String, "iOS " + UIDevice.current.systemVersion) + resultExpectation.fulfill() + } + waitForExpectations(timeout: 1) + } + +} diff --git a/packages/interactive_media_ads/example/lib/main.dart b/packages/interactive_media_ads/example/lib/main.dart new file mode 100644 index 000000000000..fd385e0a60c3 --- /dev/null +++ b/packages/interactive_media_ads/example/lib/main.dart @@ -0,0 +1,44 @@ +// 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/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_driver/driver_extension.dart'; + +/// Entry point for integration tests that require espresso. +@pragma('vm:entry-point') +void integrationTestMain() { + enableFlutterDriverExtension(); + main(); +} + +void main() { + runApp(const MyApp()); +} + +/// Home widget of the example app. +class MyApp extends StatefulWidget { + /// Constructs [MyApp]. + const MyApp({super.key}); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + @override + Widget build(BuildContext context) { + debugPrint('THEAPP'); + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Plugin example app'), + ), + body: Center( + child: Text('Running on: $defaultTargetPlatform'), + ), + ), + ); + } +} diff --git a/packages/interactive_media_ads/example/pubspec.yaml b/packages/interactive_media_ads/example/pubspec.yaml new file mode 100644 index 000000000000..fde3e7a8b2ec --- /dev/null +++ b/packages/interactive_media_ads/example/pubspec.yaml @@ -0,0 +1,25 @@ +name: interactive_media_ads_example +description: "Demonstrates how to use the interactive_media_ads plugin." +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: ^3.2.3 + flutter: ">=3.16.6" + +dependencies: + flutter: + sdk: flutter + flutter_driver: + sdk: flutter + interactive_media_ads: + path: ../ + +dev_dependencies: + espresso: ^0.2.0 + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/interactive_media_ads/example/test_driver/integration_test.dart b/packages/interactive_media_ads/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/interactive_media_ads/example/test_driver/integration_test.dart @@ -0,0 +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. + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/packages/interactive_media_ads/ios/Assets/.gitkeep b/packages/interactive_media_ads/ios/Assets/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/interactive_media_ads/ios/Classes/InteractiveMediaAdsPlugin.swift b/packages/interactive_media_ads/ios/Classes/InteractiveMediaAdsPlugin.swift new file mode 100644 index 000000000000..5b94fd6a24aa --- /dev/null +++ b/packages/interactive_media_ads/ios/Classes/InteractiveMediaAdsPlugin.swift @@ -0,0 +1,24 @@ +// 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 Flutter +import UIKit + +public class InteractiveMediaAdsPlugin: NSObject, FlutterPlugin { + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel( + name: "interactive_media_ads", binaryMessenger: registrar.messenger()) + let instance = InteractiveMediaAdsPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "getPlatformVersion": + result("iOS " + UIDevice.current.systemVersion) + default: + result(FlutterMethodNotImplemented) + } + } +} diff --git a/packages/interactive_media_ads/ios/Resources/PrivacyInfo.xcprivacy b/packages/interactive_media_ads/ios/Resources/PrivacyInfo.xcprivacy new file mode 100644 index 000000000000..5516ebf30d18 --- /dev/null +++ b/packages/interactive_media_ads/ios/Resources/PrivacyInfo.xcprivacy @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/packages/interactive_media_ads/ios/interactive_media_ads.podspec b/packages/interactive_media_ads/ios/interactive_media_ads.podspec new file mode 100644 index 000000000000..8980d486c212 --- /dev/null +++ b/packages/interactive_media_ads/ios/interactive_media_ads.podspec @@ -0,0 +1,29 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint interactive_media_ads.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'interactive_media_ads' + s.version = '0.0.1' + s.summary = 'A plugin for Interactive Media Ads SDKs.' + s.description = <<-DESC +A Flutter plugin for using the Interactive Media Ads SDKs. +Downloaded by pub (not CocoaPods). + DESC + s.homepage = 'https://github.com/flutter/packages' + s.license = { :type => 'BSD', :file => '../LICENSE' } + s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } + s.source = { :http => 'https://github.com/flutter/packages/tree/main/packages/interactive_media_ads/interactive_media_ads' } + s.source_files = 'Classes/**/*' + s.dependency 'Flutter' + s.platform = :ios, '12.0' + + # Flutter.framework does not contain a i386 slice. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } + s.xcconfig = { + 'LIBRARY_SEARCH_PATHS' => '$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)/ $(SDKROOT)/usr/lib/swift', + 'LD_RUNPATH_SEARCH_PATHS' => '/usr/lib/swift', + } + s.swift_version = '5.0' + s.resource_bundles = {'interactive_media_ads_privacy' => ['Resources/PrivacyInfo.xcprivacy']} +end diff --git a/packages/interactive_media_ads/lib/interactive_media_ads.dart b/packages/interactive_media_ads/lib/interactive_media_ads.dart new file mode 100644 index 000000000000..185d304cf5bc --- /dev/null +++ b/packages/interactive_media_ads/lib/interactive_media_ads.dart @@ -0,0 +1,10 @@ +// 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. + +export 'src/ad_display_container.dart'; +export 'src/ads_loader.dart'; +export 'src/ads_manager_delegate.dart'; +export 'src/platform_interface/ad_error.dart'; +export 'src/platform_interface/ad_event.dart'; +export 'src/platform_interface/ads_request.dart'; diff --git a/packages/interactive_media_ads/lib/src/ad_display_container.dart b/packages/interactive_media_ads/lib/src/ad_display_container.dart new file mode 100644 index 000000000000..99da19682b8e --- /dev/null +++ b/packages/interactive_media_ads/lib/src/ad_display_container.dart @@ -0,0 +1,102 @@ +// 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/cupertino.dart'; + +import 'platform_interface/platform_ad_display_container.dart'; +import 'platform_interface/platform_interface.dart'; + +/// Handles playing ads after they've been received from the server. +/// +/// ## Platform-Specific Features +/// This class contains an underlying implementation provided by the current +/// platform. Once a platform implementation is imported, the examples below +/// can be followed to use features provided by a platform's implementation. +/// +/// {@macro interactive_media_ads.AdDisplayContainer.fromPlatformCreationParams} +/// +/// Below is an example of accessing the platform-specific implementation for +/// iOS and Android: +/// +/// ```dart +/// final AdDisplayContainer container = AdDisplayContainer(); +/// +/// if (InteractiveMediaAdsPlatform.instance is IOSInteractiveMediaAdsPlatform) { +/// final IOSAdDisplayContainer iosContainer = container.platform as IOSAdDisplayContainer; +/// } else if (InteractiveMediaAdsPlatform.instance is AndroidInteractiveMediaAdsPlatform) { +/// final AndroidAdDisplayContainer androidContainer = +/// container.platform as AndroidAdDisplayContainer; +/// } +/// ``` +class AdDisplayContainer extends StatelessWidget { + /// Constructs an [AdDisplayContainer]. + /// + /// See [AdDisplayContainer.fromPlatformCreationParams] for setting parameters for a + /// specific platform. + AdDisplayContainer({ + Key? key, + required void Function(AdDisplayContainer container) onContainerAdded, + }) : this.fromPlatformCreationParams( + key: key, + params: PlatformAdDisplayContainerCreationParams( + onContainerAdded: (PlatformAdDisplayContainer container) { + onContainerAdded(AdDisplayContainer.fromPlatform( + platform: container, + )); + }, + ), + ); + + /// Constructs an [AdDisplayContainer] from creation params for a specific platform. + /// + /// {@template interactive_media_ads.AdDisplayContainer.fromPlatformCreationParams} + /// Below is an example of setting platform-specific creation parameters for + /// iOS and Android: + /// + /// ```dart + /// PlatformAdDisplayContainerCreationParams params = + /// const PlatformAdDisplayContainerCreationParams(); + /// + /// if (InteractiveMediaAdsPlatform.instance is IOSInteractiveMediaAdsPlatform) { + /// params = IOSAdDisplayContainerCreationParams + /// .fromPlatformAdDisplayContainerCreationParams( + /// params, + /// ); + /// } else if (InteractiveMediaAdsPlatform.instance is AndroidInteractiveMediaAdsPlatform) { + /// params = AndroidAdDisplayContainerCreationParams + /// .fromPlatformAdDisplayContainerCreationParams( + /// params, + /// ); + /// } + /// + /// final AdDisplayContainer container = AdDisplayContainer.fromPlatformCreationParams( + /// params, + /// ); + /// ``` + /// {@endtemplate} + AdDisplayContainer.fromPlatformCreationParams({ + Key? key, + required PlatformAdDisplayContainerCreationParams params, + }) : this.fromPlatform( + key: key, + platform: PlatformAdDisplayContainer(params), + ); + + /// Constructs an [AdDisplayContainer] from a specific platform + /// implementation. + const AdDisplayContainer.fromPlatform({super.key, required this.platform}); + + /// Implementation of [PlatformAdDisplayContainer] for the current platform. + final PlatformAdDisplayContainer platform; + + /// Invoked when the native view that contains the ad has been added to the + /// platform view hierarchy. + void Function(PlatformAdDisplayContainer container) get onContainerAdded => + platform.params.onContainerAdded; + + @override + Widget build(BuildContext context) { + return platform.build(context); + } +} diff --git a/packages/interactive_media_ads/lib/src/ads_loader.dart b/packages/interactive_media_ads/lib/src/ads_loader.dart new file mode 100644 index 000000000000..9370cc9fec35 --- /dev/null +++ b/packages/interactive_media_ads/lib/src/ads_loader.dart @@ -0,0 +1,162 @@ +// 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/foundation.dart'; + +import 'ad_display_container.dart'; +import 'ads_manager_delegate.dart'; +import 'platform_interface/platform_interface.dart'; + +/// Handles playing ads after they've been received from the server. +/// +/// ## Platform-Specific Features +/// This class contains an underlying implementation provided by the current +/// platform. Once a platform implementation is imported, the examples below +/// can be followed to use features provided by a platform's implementation. +/// +/// {@macro interactive_media_ads.AdsLoader.fromPlatformCreationParams} +/// +/// Below is an example of accessing the platform-specific implementation for +/// iOS and Android: +/// +/// ```dart +/// final AdsLoader loader = AdsLoader(); +/// +/// if (InteractiveMediaAdsPlatform.instance is IOSInteractiveMediaAdsPlatform) { +/// final IOSAdsLoader iosLoader = loader.platform as IOSAdsLoader; +/// } else if (InteractiveMediaAdsPlatform.instance is AndroidInteractiveMediaAdsPlatform) { +/// final AndroidAdsLoader androidLoader = +/// loader.platform as AndroidAdsLoader; +/// } +/// ``` +class AdsLoader { + /// Constructs an [AdsLoader]. + /// + /// See [AdsLoader.fromPlatformCreationParams] for setting parameters for a + /// specific platform. + AdsLoader({ + required AdDisplayContainer container, + required void Function(OnAdsLoadedData data) onAdsLoaded, + required void Function(AdsLoadErrorData data) onAdsLoadError, + }) : this.fromPlatformCreationParams( + PlatformAdsLoaderCreationParams( + container: container.platform, + onAdsLoaded: (PlatformOnAdsLoadedData data) { + onAdsLoaded(OnAdsLoadedData._(platform: data)); + }, + onAdsLoadError: onAdsLoadError, + ), + ); + + /// Constructs an [AdsLoader] from creation params for a specific platform. + /// + /// {@template interactive_media_ads.AdsLoader.fromPlatformCreationParams} + /// Below is an example of setting platform-specific creation parameters for + /// iOS and Android: + /// + /// ```dart + /// PlatformAdsLoaderCreationParams params = + /// const PlatformAdsLoaderCreationParams(); + /// + /// if (InteractiveMediaAdsPlatform.instance is IOSInteractiveMediaAdsPlatform) { + /// params = IOSAdsLoaderCreationParams + /// .fromPlatformAdsLoaderCreationParams( + /// params, + /// ); + /// } else if (InteractiveMediaAdsPlatform.instance is AndroidInteractiveMediaAdsPlatform) { + /// params = AndroidAdsLoaderCreationParams + /// .fromPlatformAdsLoaderCreationParams( + /// params, + /// ); + /// } + /// + /// final AdsLoader loader = AdsLoader.fromPlatformCreationParams( + /// params, + /// ); + /// ``` + /// {@endtemplate} + AdsLoader.fromPlatformCreationParams( + PlatformAdsLoaderCreationParams params, + ) : this.fromPlatform(PlatformAdsLoader(params)); + + /// Constructs a [AdsLoader] from a specific platform implementation. + AdsLoader.fromPlatform(this.platform); + + /// Implementation of [PlatformAdsLoader] for the current platform. + final PlatformAdsLoader platform; + + /// Signals to the SDK that the content has completed. + Future contentComplete() { + return platform.contentComplete(); + } + + /// Requests ads from a server. + Future requestAds(AdsRequest request) { + return platform.requestAds(request); + } +} + +/// Data when ads are successfully loaded from the ad server through an +/// [AdsLoader]. +@immutable +class OnAdsLoadedData { + OnAdsLoadedData._({required this.platform}); + + /// Implementation of [PlatformOnAdsLoadedData] for the current platform. + final PlatformOnAdsLoadedData platform; + + /// The ads manager instance created by the ads loader. + late final AdsManager manager = AdsManager._fromPlatform(platform.manager); +} + +/// Handles playing ads after they've been received from the server. +/// +/// ## Platform-Specific Features +/// This class contains an underlying implementation provided by the current +/// platform. Once a platform implementation is imported, the examples below +/// can be followed to use features provided by a platform's implementation. +/// +/// {@macro interactive_media_ads.AdsManager.fromPlatformCreationParams} +/// +/// Below is an example of accessing the platform-specific implementation for +/// iOS and Android: +/// +/// ```dart +/// final AdsManager manager = AdsManager(); +/// +/// if (InteractiveMediaAdsPlatform.instance is IOSInteractiveMediaAdsPlatform) { +/// final IOSAdsManager iosManager = manager.platform as IOSAdsManager; +/// } else if (InteractiveMediaAdsPlatform.instance is AndroidInteractiveMediaAdsPlatform) { +/// final AndroidAdsManager androidManager = +/// manager.platform as AndroidAdsManager; +/// } +/// ``` +class AdsManager { + /// Constructs a [AdsManager] from a specific platform implementation. + AdsManager._fromPlatform(this.platform); + + /// Implementation of [PlatformAdsManager] for the current platform. + final PlatformAdsManager platform; + + /// Initializes the ad experience using default rendering settings. + Future init() { + return platform.init(AdsManagerInitParams()); + } + + /// Starts playing the ads. + Future start() { + return platform.start(AdsManagerStartParams()); + } + + /// The [AdsManagerDelegate] to notify with events during ad playback. + Future setAdsManagerDelegate(AdsManagerDelegate delegate) { + return platform.setAdsManagerDelegate(delegate.platform); + } + + /// Stops the ad and all tracking, then releases all assets that were loaded + /// to play the ad. + Future destroy() { + return platform.destroy(); + } +} diff --git a/packages/interactive_media_ads/lib/src/ads_manager_delegate.dart b/packages/interactive_media_ads/lib/src/ads_manager_delegate.dart new file mode 100644 index 000000000000..4a3813c719af --- /dev/null +++ b/packages/interactive_media_ads/lib/src/ads_manager_delegate.dart @@ -0,0 +1,88 @@ +// 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 'platform_interface/platform_interface.dart'; + +/// Handles playing ads after they've been received from the server. +/// +/// ## Platform-Specific Features +/// This class contains an underlying implementation provided by the current +/// platform. Once a platform implementation is imported, the examples below +/// can be followed to use features provided by a platform's implementation. +/// +/// {@macro interactive_media_ads.AdsManagerDelegate.fromPlatformCreationParams} +/// +/// Below is an example of accessing the platform-specific implementation for +/// iOS and Android: +/// +/// ```dart +/// final AdsManagerDelegate delegate = AdsManagerDelegate(); +/// +/// if (InteractiveMediaAdsPlatform.instance is IOSInteractiveMediaAdsPlatform) { +/// final IOSAdsManagerDelegate iosDelegate = delegate.platform as IOSAdsManagerDelegate; +/// } else if (InteractiveMediaAdsPlatform.instance is AndroidInteractiveMediaAdsPlatform) { +/// final AndroidAdsManagerDelegate androidDelegate = +/// delegate.platform as AndroidAdsManagerDelegate; +/// } +/// ``` +class AdsManagerDelegate { + /// Constructs an [AdsManagerDelegate]. + /// + /// See [AdsManagerDelegate.fromPlatformCreationParams] for setting parameters for a + /// specific platform. + AdsManagerDelegate({ + void Function(AdEvent event)? onAdEvent, + void Function(AdErrorEvent event)? onAdErrorEvent, + }) : this.fromPlatformCreationParams( + PlatformAdsManagerDelegateCreationParams( + onAdEvent: onAdEvent, + onAdErrorEvent: onAdErrorEvent, + ), + ); + + /// Constructs an [AdsManagerDelegate] from creation params for a specific platform. + /// + /// {@template interactive_media_ads.AdsManagerDelegate.fromPlatformCreationParams} + /// Below is an example of setting platform-specific creation parameters for + /// iOS and Android: + /// + /// ```dart + /// PlatformAdsManagerDelegateCreationParams params = + /// const PlatformAdsManagerDelegateCreationParams(); + /// + /// if (InteractiveMediaAdsPlatform.instance is IOSInteractiveMediaAdsPlatform) { + /// params = IOSAdsManagerDelegateCreationParams + /// .fromPlatformAdsManagerDelegateCreationParams( + /// params, + /// ); + /// } else if (InteractiveMediaAdsPlatform.instance is AndroidInteractiveMediaAdsPlatform) { + /// params = AndroidAdsManagerDelegateCreationParams + /// .fromPlatformAdsManagerDelegateCreationParams( + /// params, + /// ); + /// } + /// + /// final AdsManagerDelegate delegate = AdsManagerDelegate.fromPlatformCreationParams( + /// params, + /// ); + /// ``` + /// {@endtemplate} + AdsManagerDelegate.fromPlatformCreationParams( + PlatformAdsManagerDelegateCreationParams params, + ) : this.fromPlatform(PlatformAdsManagerDelegate(params)); + + /// Constructs a [AdsManagerDelegate] from a specific platform implementation. + AdsManagerDelegate.fromPlatform(this.platform); + + /// Implementation of [PlatformAdsManagerDelegate] for the current platform. + final PlatformAdsManagerDelegate platform; + + /// Invoked when there is an [AdEvent]. + void Function(AdEvent event)? get onAdEvent => platform.params.onAdEvent; + + /// Invoked when there was an error playing the ad. Log the error and resume + /// playing content. + void Function(AdErrorEvent event)? get onAdErrorEvent => + platform.params.onAdErrorEvent; +} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/ad_error.dart b/packages/interactive_media_ads/lib/src/platform_interface/ad_error.dart new file mode 100644 index 000000000000..ca89e967307b --- /dev/null +++ b/packages/interactive_media_ads/lib/src/platform_interface/ad_error.dart @@ -0,0 +1,156 @@ +// 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/foundation.dart'; + +/// The types of errors that can be encountered when loading an ad. +enum AdErrorCode { + /// Generic invalid usage of the API. + apiError, + + /// Ads player was not provided. + adsPlayerNotProvided, + + /// There was a problem requesting ads from the server. + adsRequestNetworkError, + + /// The ad slot is not visible on the page. + adslotNotVisible, + + /// There was a problem requesting ads from the server. + companionAdLoadingFailed, + + /// Content playhead was not passed in, but list of ads has been returned from + /// the server. + contentPlayheadMissing, + + /// There was a problem requesting ads from the server. + failedToRequestAds, + + /// There was an error loading the ad. + failedLoadingAd, + + /// An error internal to the SDK occurred. + /// + /// More information may be available in the details. + internalError, + + /// Invalid arguments were provided to SDK methods. + invalidArguments, + + /// The version of the runtime is too old. + osRuntimeTooOld, + + /// An overlay ad failed to load. + overlayAdLoadingFailed, + + /// An overlay ad failed to render. + overlayAdPlayingFailed, + + /// Ads list was returned but ContentProgressProvider was not configured. + playlistNoContentTracking, + + /// Ads list response was malformed. + playlistMalformedResponse, + + /// Listener for at least one of the required vast events was not added. + requiredListenersNotAdded, + + /// There was an error initializing the stream. + streamInitializationFailed, + + /// Ads loader sent ads loaded event when it was not expected. + unexpectedAdsLoadedEvent, + + /// The ad response was not understood and cannot be parsed. + unknownAdResponse, + + /// An unexpected error occurred and the cause is not known. + /// + /// Refer to the inner error for more information. + unknownError, + + /// No assets were found in the VAST ad response. + vastAssetNotFound, + + /// A VAST response containing a single tag with no child tags. + vastEmptyResponse, + + /// Assets were found in the VAST ad response for a linear ad, but none of + /// them matched the video player's capabilities. + vastLinearAssetMismatch, + + /// At least one VAST wrapper ad loaded successfully and a subsequent wrapper + /// or inline ad load has timed out. + vastLoadTimeout, + + /// The ad response was not recognized as a valid VAST ad. + vastMalformedResponse, + + /// Failed to load media assets from a VAST response. + /// + /// The default timeout for media loading is 8 seconds. + vastMediaLoadTimeout, + + /// Assets were found in the VAST ad response for a nonlinear ad, but none of + /// them matched the video player's capabilities. + vastNonlinearAssetMismatch, + + /// No Ads VAST response after one or more wrappers. + vastNoAdsAfterWrapper, + + /// The maximum number of VAST wrapper redirects has been reached. + vastTooManyRedirects, + + /// Trafficking error. + /// + /// Video player received an ad type that it was not expecting and/or cannot + /// display. + vastTraffickingError, + + /// At least one VAST wrapper loaded and a subsequent wrapper or inline ad + /// load has resulted in a 404 response code. + vastInvalidUrl, + + /// There was an error playing the video ad. + videoPlayError, + + /// Another VideoAdsManager is still using the video. + /// + /// It must be unloaded before another ad can play on the same element. + videoElementUsed, + + /// A video element was not specified where it was required. + videoElementRequired, +} + +/// Possible error types while loading or playing ads. +enum AdErrorType { + /// Indicates an error occurred while loading the ads. + loading, + + /// Indicates an error occurred while playing the ads. + playing, + + /// An unexpected error occurred while loading or playing the ads. + /// + /// This may mean that the SDK wasn’t loaded properly. + unknown, +} + +/// Surfaces an error that occurred during ad loading or playing. +@immutable +class AdError { + /// Creates a [AdError]. + const AdError({required this.type, required this.code, this.message}); + + /// Specifies the source of the error. + final AdErrorType type; + + /// The error code for obtaining more specific information about the error. + final AdErrorCode code; + + /// A brief description about the error. + final String? message; +} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/ad_event.dart b/packages/interactive_media_ads/lib/src/platform_interface/ad_event.dart new file mode 100644 index 000000000000..9824d9915fb3 --- /dev/null +++ b/packages/interactive_media_ads/lib/src/platform_interface/ad_event.dart @@ -0,0 +1,53 @@ +// 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/foundation.dart'; + +import 'ad_error.dart'; + +/// Types of events that can occur during ad playback. +enum AdEventType { + /// Fired when the ads manager is done playing all the valid ads in the ads + /// response, or when the response doesn't return any valid ads. + allAdsCompleted, + + /// Fired when an ad is clicked. + clicked, + + /// Fired when an ad completes playing. + complete, + + /// Fired when content should be paused. + /// + /// This usually happens right before an ad is about to hide the content. + contentPauseRequested, + + /// Fired when content should be resumed. + /// + /// This usually happens when an ad finishes or collapses. + contentResumeRequested, + + /// Fired when the VAST response has been received. + loaded, +} + +/// Simple data class used to transport ad playback information. +@immutable +class AdEvent { + /// Creates an [AdEvent]. + const AdEvent({required this.type}); + + /// The type of event that occurred. + final AdEventType type; +} + +/// An event raised when there is an error loading or playing ads. +@immutable +class AdErrorEvent { + /// Creates an [AdErrorEvent]. + const AdErrorEvent({required this.error}); + + /// The error that caused this event. + final AdError error; +} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/ads_request.dart b/packages/interactive_media_ads/lib/src/platform_interface/ads_request.dart new file mode 100644 index 000000000000..72e4dfc69b63 --- /dev/null +++ b/packages/interactive_media_ads/lib/src/platform_interface/ads_request.dart @@ -0,0 +1,12 @@ +// 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. + +/// An object containing the data used to request ads from the server. +class AdsRequest { + /// Creates an [AdsRequest]. + AdsRequest({required this.adTagUrl}); + + /// The URL from which ads will be requested. + final String adTagUrl; +} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/interactive_media_ads_platform.dart b/packages/interactive_media_ads/lib/src/platform_interface/interactive_media_ads_platform.dart new file mode 100644 index 000000000000..f1dac2db7d1d --- /dev/null +++ b/packages/interactive_media_ads/lib/src/platform_interface/interactive_media_ads_platform.dart @@ -0,0 +1,32 @@ +// 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 'platform_ad_display_container.dart'; +import 'platform_ads_loader.dart'; +import 'platform_ads_manager_delegate.dart'; + +/// Interface for a platform implementation of the Interactive Media Ads SDKs. +abstract base class InteractiveMediaAdsPlatform { + /// The instance of [InteractiveMediaAdsPlatform] to use. + /// + /// Platform-specific plugins should set this with their own platform-specific + /// class that extends [InteractiveMediaAdsPlatform] when they register + /// themselves. + static InteractiveMediaAdsPlatform? instance; + + /// Creates a new [PlatformAdsLoader]. + PlatformAdsLoader createPlatformAdsLoader( + PlatformAdsLoaderCreationParams params, + ); + + /// Creates a new [PlatformAdsManagerDelegate]. + PlatformAdsManagerDelegate createPlatformAdsManagerDelegate( + PlatformAdsManagerDelegateCreationParams params, + ); + + /// Creates a new [PlatformAdDisplayContainer]. + PlatformAdDisplayContainer createPlatformAdDisplayContainer( + PlatformAdDisplayContainerCreationParams params, + ); +} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/platform_ad_display_container.dart b/packages/interactive_media_ads/lib/src/platform_interface/platform_ad_display_container.dart new file mode 100644 index 000000000000..075706f39307 --- /dev/null +++ b/packages/interactive_media_ads/lib/src/platform_interface/platform_ad_display_container.dart @@ -0,0 +1,95 @@ +// 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/cupertino.dart'; + +import 'interactive_media_ads_platform.dart'; + +/// Object specifying creation parameters for creating a +/// [PlatformAdDisplayContainer]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +/// +/// This example demonstrates how to extend the +/// [PlatformAdDisplayContainerCreationParams] to provide additional platform +/// specific parameters. +/// +/// When extending [PlatformAdDisplayContainerCreationParams] additional +/// parameters should always accept `null` or have a default value to prevent +/// breaking changes. +/// +/// ```dart +/// class AndroidPlatformAdDisplayContainerCreationParams +/// extends PlatformAdDisplayContainerCreationParams { +/// AndroidPlatformAdDisplayContainerCreationParams._( +/// PlatformAdDisplayContainerCreationParams params, { +/// this.uri, +/// }) : super(); +/// +/// factory AndroidAdDisplayContainerCreationParams.fromPlatformAdDisplayContainerCreationParams( +/// PlatformAdDisplayContainerCreationParams params, { +/// Uri? uri, +/// }) { +/// return AndroidAdDisplayContainerCreationParams._(params, uri: uri); +/// } +/// +/// final Uri? uri; +/// } +/// ``` +@immutable +base class PlatformAdDisplayContainerCreationParams { + /// Used by the platform implementation to create a new + /// [PlatformAdDisplayContainer]. + const PlatformAdDisplayContainerCreationParams({ + this.key, + required this.onContainerAdded, + }); + + /// Controls how one widget replaces another widget in the tree. + /// + /// See also: + /// * The discussions at [Key] and [GlobalKey]. + final Key? key; + + /// Invoked when the View that contains the ad has been added to the platform + /// view hierarchy. + final void Function(PlatformAdDisplayContainer container) onContainerAdded; +} + +/// The interface for a platform implementation for a container in which to +/// display ads. +abstract base class PlatformAdDisplayContainer { + /// Creates a new [PlatformAdDisplayContainer] + factory PlatformAdDisplayContainer( + PlatformAdDisplayContainerCreationParams params, + ) { + assert( + InteractiveMediaAdsPlatform.instance != null, + 'A platform implementation for `interactive_media_ads` has not been set. ' + 'Please ensure that an implementation of `InteractiveMediaAdsPlatform` ' + 'has been set to `InteractiveMediaAdsPlatform.instance` before use. For ' + 'unit testing, `InteractiveMediaAdsPlatform.instance` can be set with ' + 'your own test implementation.', + ); + final PlatformAdDisplayContainer implementation = + InteractiveMediaAdsPlatform.instance! + .createPlatformAdDisplayContainer(params); + return implementation; + } + + /// Used by the platform implementation to create a new + /// [PlatformAdDisplayContainer]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + @protected + PlatformAdDisplayContainer.implementation(this.params); + + /// The parameters used to initialize the [PlatformAdDisplayContainer]. + final PlatformAdDisplayContainerCreationParams params; + + /// Builds the Widget that contains the native View. + Widget build(BuildContext context); +} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_loader.dart b/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_loader.dart new file mode 100644 index 000000000000..7dc6a57a1d22 --- /dev/null +++ b/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_loader.dart @@ -0,0 +1,119 @@ +// 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/foundation.dart'; + +import 'ad_error.dart'; +import 'ads_request.dart'; +import 'interactive_media_ads_platform.dart'; +import 'platform_ad_display_container.dart'; +import 'platform_ads_manager.dart'; + +/// Object specifying creation parameters for creating a [PlatformAdsLoader]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +/// +/// This example demonstrates how to extend the +/// [PlatformAdsLoaderCreationParams] to provide additional platform specific +/// parameters. +/// +/// When extending [PlatformAdsLoaderCreationParams] additional parameters +/// should always accept `null` or have a default value to prevent breaking +/// changes. +/// +/// ```dart +/// class AndroidPlatformAdsLoaderCreationParams +/// extends PlatformAdsLoaderCreationParams { +/// AndroidPlatformAdsLoaderCreationParams._( +/// PlatformAdsLoaderCreationParams params, { +/// this.uri, +/// }) : super(); +/// +/// factory AndroidAdsLoaderCreationParams.fromPlatformAdsLoaderCreationParams( +/// PlatformAdsLoaderCreationParams params, { +/// Uri? uri, +/// }) { +/// return AndroidAdsLoaderCreationParams._(params, uri: uri); +/// } +/// +/// final Uri? uri; +/// } +/// ``` +@immutable +base class PlatformAdsLoaderCreationParams { + /// Used by the platform implementation to create a new [PlatformAdsLoader]. + const PlatformAdsLoaderCreationParams({ + required this.container, + required this.onAdsLoaded, + required this.onAdsLoadError, + }); + + /// A container object where ads are rendered. + final PlatformAdDisplayContainer container; + + /// Callback for the ads manager loaded event. + final void Function(PlatformOnAdsLoadedData data) onAdsLoaded; + + /// Callback for errors that occur during the ads request. + final void Function(AdsLoadErrorData data) onAdsLoadError; +} + +/// Interface for a platform implementation of an object that requests ads and +/// handles events from ads request responses. +abstract base class PlatformAdsLoader { + /// Creates a new [PlatformAdsLoader] + factory PlatformAdsLoader( + PlatformAdsLoaderCreationParams params, + ) { + assert( + InteractiveMediaAdsPlatform.instance != null, + 'A platform implementation for `interactive_media_ads` has not been set. ' + 'Please ensure that an implementation of `InteractiveMediaAdsPlatform` ' + 'has been set to `InteractiveMediaAdsPlatform.instance` before use. For ' + 'unit testing, `InteractiveMediaAdsPlatform.instance` can be set with ' + 'your own test implementation.', + ); + final PlatformAdsLoader implementation = + InteractiveMediaAdsPlatform.instance!.createPlatformAdsLoader(params); + return implementation; + } + + /// Used by the platform implementation to create a new [PlatformAdsLoader]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + @protected + PlatformAdsLoader.implementation(this.params); + + /// The parameters used to initialize the [PlatformAdsLoader]. + final PlatformAdsLoaderCreationParams params; + + /// Signal to the SDK that the content has completed. + Future contentComplete(); + + /// Requests ads from a server. + Future requestAds(AdsRequest request); +} + +/// Data when ads are successfully loaded from the ad server through an +/// [PlatformAdsLoader]. +@immutable +class PlatformOnAdsLoadedData { + /// Creates a [PlatformOnAdsLoadedData]. + const PlatformOnAdsLoadedData({required this.manager}); + + /// The ads manager instance created by the ads loader. + final PlatformAdsManager manager; +} + +/// Ad error data that is returned when the ads loader fails to load the ad. +@immutable +class AdsLoadErrorData { + /// Creates a [AdsLoadErrorData]. + const AdsLoadErrorData({required this.error}); + + /// The ad error that occurred while loading the ad. + final AdError error; +} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager.dart b/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager.dart new file mode 100644 index 000000000000..d80a17a6a730 --- /dev/null +++ b/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager.dart @@ -0,0 +1,34 @@ +// 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/foundation.dart'; + +import 'platform_ads_manager_delegate.dart'; + +/// Additional parameter passed to an [PlatformAdsManager] on initialization. +base class AdsManagerInitParams {} + +/// Additional parameter passed to an [PlatformAdsManager] when starting to play +/// ads. +base class AdsManagerStartParams {} + +/// Interface for a platform implementation of a `AdsManager`. +abstract class PlatformAdsManager { + /// Creates a [PlatformAdsManager]. + @protected + PlatformAdsManager(); + + /// Initializes the ad experience using default rendering settings. + Future init(AdsManagerInitParams params); + + /// Starts playing the ads. + Future start(AdsManagerStartParams params); + + /// /// The [AdsManagerDelegate] to notify with events during ad playback. + Future setAdsManagerDelegate(PlatformAdsManagerDelegate delegate); + + /// Stops the ad and all tracking, then releases all assets that were loaded + /// to play the ad. + Future destroy(); +} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager_delegate.dart b/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager_delegate.dart new file mode 100644 index 000000000000..146e5d1914e6 --- /dev/null +++ b/packages/interactive_media_ads/lib/src/platform_interface/platform_ads_manager_delegate.dart @@ -0,0 +1,88 @@ +// 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/foundation.dart'; + +import 'ad_event.dart'; +import 'interactive_media_ads_platform.dart'; + +/// Object specifying creation parameters for creating a +/// [PlatformAdsManagerDelegate]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +/// +/// This example demonstrates how to extend the +/// [PlatformAdsManagerDelegateCreationParams] to provide additional platform +/// specific parameters. +/// +/// When extending [PlatformAdsManagerDelegateCreationParams] additional +/// parameters should always accept `null` or have a default value to prevent +/// breaking changes. +/// +/// ```dart +/// class AndroidPlatformAdsManagerDelegateCreationParams +/// extends PlatformAdsManagerDelegateCreationParams { +/// AndroidPlatformAdsManagerDelegateCreationParams._( +/// PlatformAdsManagerDelegateCreationParams params, { +/// this.uri, +/// }) : super(); +/// +/// factory AndroidAdsManagerDelegateCreationParams.fromPlatformAdsManagerDelegateCreationParams( +/// PlatformAdsManagerDelegateCreationParams params, { +/// Uri? uri, +/// }) { +/// return AndroidAdsManagerDelegateCreationParams._(params, uri: uri); +/// } +/// +/// final Uri? uri; +/// } +/// ``` +@immutable +base class PlatformAdsManagerDelegateCreationParams { + /// Used by the platform implementation to create a new [PlatformAdsManagerDelegate]. + const PlatformAdsManagerDelegateCreationParams({ + this.onAdEvent, + this.onAdErrorEvent, + }); + + /// Invoked when there is an [AdEvent]. + final void Function(AdEvent event)? onAdEvent; + + /// Invoked when there was an error playing the ad. Log the error and resume + /// playing content. + final void Function(AdErrorEvent event)? onAdErrorEvent; +} + +/// Interface for a platform implementation of a `AdsManagerDelegate`. +abstract base class PlatformAdsManagerDelegate { + /// Creates a new [PlatformAdsManagerDelegate] + factory PlatformAdsManagerDelegate( + PlatformAdsManagerDelegateCreationParams params, + ) { + assert( + InteractiveMediaAdsPlatform.instance != null, + 'A platform implementation for `interactive_media_ads` has not been set. ' + 'Please ensure that an implementation of `InteractiveMediaAdsPlatform` ' + 'has been set to `InteractiveMediaAdsPlatform.instance` before use. For ' + 'unit testing, `InteractiveMediaAdsPlatform.instance` can be set with ' + 'your own test implementation.', + ); + final PlatformAdsManagerDelegate implementation = + InteractiveMediaAdsPlatform.instance! + .createPlatformAdsManagerDelegate(params); + return implementation; + } + + /// Used by the platform implementation to create a new + /// [PlatformAdsManagerDelegate]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + @protected + PlatformAdsManagerDelegate.implementation(this.params); + + /// The parameters used to initialize the [PlatformAdsManagerDelegate]. + final PlatformAdsManagerDelegateCreationParams params; +} diff --git a/packages/interactive_media_ads/lib/src/platform_interface/platform_interface.dart b/packages/interactive_media_ads/lib/src/platform_interface/platform_interface.dart new file mode 100644 index 000000000000..ad8896480443 --- /dev/null +++ b/packages/interactive_media_ads/lib/src/platform_interface/platform_interface.dart @@ -0,0 +1,12 @@ +// 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. + +export 'ad_error.dart'; +export 'ad_event.dart'; +export 'ads_request.dart'; +export 'interactive_media_ads_platform.dart'; +export 'platform_ad_display_container.dart'; +export 'platform_ads_loader.dart'; +export 'platform_ads_manager.dart'; +export 'platform_ads_manager_delegate.dart'; diff --git a/packages/interactive_media_ads/pubspec.yaml b/packages/interactive_media_ads/pubspec.yaml new file mode 100644 index 000000000000..a6b7b04a2148 --- /dev/null +++ b/packages/interactive_media_ads/pubspec.yaml @@ -0,0 +1,31 @@ +name: interactive_media_ads +description: A Flutter plugin for using the Interactive Media Ads SDKs on Android and iOS. +repository: https://github.com/flutter/packages/tree/main/packages/interactive_media_ads +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+interactive_media_ads%22 +version: 0.0.1 + +environment: + sdk: ^3.2.3 + flutter: ">=3.16.6" + +flutter: + plugin: + platforms: + android: + package: dev.flutter.packages.interactive_media_ads + pluginClass: InteractiveMediaAdsPlugin + ios: + pluginClass: InteractiveMediaAdsPlugin + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + build_runner: ^2.1.4 + flutter_test: + sdk: flutter + mockito: 5.4.4 + +topics: + - ads diff --git a/packages/interactive_media_ads/test/ad_display_container_test.dart b/packages/interactive_media_ads/test/ad_display_container_test.dart new file mode 100644 index 000000000000..a82a16e8c4c2 --- /dev/null +++ b/packages/interactive_media_ads/test/ad_display_container_test.dart @@ -0,0 +1,59 @@ +// 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:interactive_media_ads/interactive_media_ads.dart'; +import 'package:interactive_media_ads/src/ad_display_container.dart'; +import 'package:interactive_media_ads/src/platform_interface/platform_interface.dart'; + +import 'test_stubs.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('build', (WidgetTester tester) async { + final TestPlatformAdDisplayContainer adDisplayContainer = + TestPlatformAdDisplayContainer( + PlatformAdDisplayContainerCreationParams( + onContainerAdded: (_) {}, + ), + onBuild: (_) => Container(), + ); + + await tester.pumpWidget(AdDisplayContainer.fromPlatform( + platform: adDisplayContainer, + )); + + expect(find.byType(Container), findsOneWidget); + }); + + testWidgets('constructor parameters are correctly passed to creation params', + (WidgetTester tester) async { + InteractiveMediaAdsPlatform.instance = + TestInteractiveMediaAdsPlatform(onCreatePlatformAdDisplayContainer: ( + PlatformAdDisplayContainerCreationParams params, + ) { + return TestPlatformAdDisplayContainer( + params, + onBuild: (_) => Container(), + ); + }, onCreatePlatformAdsLoader: (PlatformAdsLoaderCreationParams params) { + throw UnimplementedError(); + }, onCreatePlatformAdsManagerDelegate: ( + PlatformAdsManagerDelegateCreationParams params, + ) { + throw UnimplementedError(); + }); + + final AdDisplayContainer adDisplayContainer = AdDisplayContainer( + key: GlobalKey(), + onContainerAdded: (_) {}, + ); + + // The key passed to the default constructor is used by the super class + // and not passed to the platform implementation. + expect(adDisplayContainer.platform.params.key, isNull); + }); +} diff --git a/packages/interactive_media_ads/test/ads_loader_test.dart b/packages/interactive_media_ads/test/ads_loader_test.dart new file mode 100644 index 000000000000..424c86ccc1d5 --- /dev/null +++ b/packages/interactive_media_ads/test/ads_loader_test.dart @@ -0,0 +1,51 @@ +// 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/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:interactive_media_ads/interactive_media_ads.dart'; +import 'package:interactive_media_ads/src/platform_interface/platform_interface.dart'; + +import 'test_stubs.dart'; + +void main() { + test('contentComplete', () async { + final TestPlatformAdsLoader adsLoader = TestPlatformAdsLoader( + PlatformAdsLoaderCreationParams( + container: createTestAdDisplayContainer(), + onAdsLoaded: (PlatformOnAdsLoadedData data) {}, + onAdsLoadError: (AdsLoadErrorData data) {}, + ), + onContentComplete: expectAsync0(() async {}), + onRequestAds: (AdsRequest request) async {}, + ); + + final AdsLoader loader = AdsLoader.fromPlatform(adsLoader); + await loader.contentComplete(); + }); + + test('requestAds', () async { + final TestPlatformAdsLoader adsLoader = TestPlatformAdsLoader( + PlatformAdsLoaderCreationParams( + container: createTestAdDisplayContainer(), + onAdsLoaded: (PlatformOnAdsLoadedData data) {}, + onAdsLoadError: (AdsLoadErrorData data) {}, + ), + onRequestAds: expectAsync1((AdsRequest request) async {}), + onContentComplete: () async {}, + ); + + final AdsLoader loader = AdsLoader.fromPlatform(adsLoader); + await loader.requestAds(AdsRequest(adTagUrl: '')); + }); +} + +TestPlatformAdDisplayContainer createTestAdDisplayContainer() { + return TestPlatformAdDisplayContainer( + PlatformAdDisplayContainerCreationParams( + onContainerAdded: (_) {}, + ), + onBuild: (_) => Container(), + ); +} diff --git a/packages/interactive_media_ads/test/ads_manager_delegate_test.dart b/packages/interactive_media_ads/test/ads_manager_delegate_test.dart new file mode 100644 index 000000000000..ba6801dc41f3 --- /dev/null +++ b/packages/interactive_media_ads/test/ads_manager_delegate_test.dart @@ -0,0 +1,38 @@ +// 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_test/flutter_test.dart'; +import 'package:interactive_media_ads/interactive_media_ads.dart'; +import 'package:interactive_media_ads/src/platform_interface/platform_interface.dart'; + +import 'test_stubs.dart'; + +void main() { + test('passes params to platform instance', () async { + InteractiveMediaAdsPlatform.instance = TestInteractiveMediaAdsPlatform( + onCreatePlatformAdsManagerDelegate: + (PlatformAdsManagerDelegateCreationParams params) { + return TestPlatformAdsManagerDelegate(params); + }, + onCreatePlatformAdsLoader: (PlatformAdsLoaderCreationParams params) { + throw UnimplementedError(); + }, + onCreatePlatformAdDisplayContainer: + (PlatformAdDisplayContainerCreationParams params) { + throw UnimplementedError(); + }, + ); + + void onAdEvent(AdEvent event) {} + void onAdErrorEvent(AdErrorEvent event) {} + + final AdsManagerDelegate delegate = AdsManagerDelegate( + onAdEvent: onAdEvent, + onAdErrorEvent: onAdErrorEvent, + ); + + expect(delegate.platform.params.onAdEvent, onAdEvent); + expect(delegate.platform.params.onAdErrorEvent, onAdErrorEvent); + }); +} diff --git a/packages/interactive_media_ads/test/ads_manager_test.dart b/packages/interactive_media_ads/test/ads_manager_test.dart new file mode 100644 index 000000000000..cb5f42ccb3f6 --- /dev/null +++ b/packages/interactive_media_ads/test/ads_manager_test.dart @@ -0,0 +1,93 @@ +// 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/cupertino.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:interactive_media_ads/interactive_media_ads.dart'; +import 'package:interactive_media_ads/src/platform_interface/platform_interface.dart'; + +import 'test_stubs.dart'; + +void main() { + test('init', () async { + final TestAdsManager platformManager = TestAdsManager( + onInit: expectAsync1((_) async {}), + ); + + final AdsManager manager = createAdsManager(platformManager); + await manager.init(); + }); + + test('start', () async { + final TestAdsManager platformManager = TestAdsManager( + onStart: expectAsync1((_) async {}), + ); + + final AdsManager manager = createAdsManager(platformManager); + await manager.start(); + }); + + test('setAdsManagerDelegate', () async { + final TestAdsManager platformManager = TestAdsManager( + onSetAdsManagerDelegate: expectAsync1((_) async {}), + ); + + final AdsManager manager = createAdsManager(platformManager); + await manager.setAdsManagerDelegate(AdsManagerDelegate.fromPlatform( + TestPlatformAdsManagerDelegate( + const PlatformAdsManagerDelegateCreationParams(), + ), + )); + }); + + test('destroy', () async { + final TestAdsManager platformManager = TestAdsManager( + onDestroy: expectAsync0(() async {}), + ); + + final AdsManager manager = createAdsManager(platformManager); + await manager.destroy(); + }); +} + +AdsManager createAdsManager(PlatformAdsManager platformManager) { + InteractiveMediaAdsPlatform.instance = TestInteractiveMediaAdsPlatform( + onCreatePlatformAdsLoader: (PlatformAdsLoaderCreationParams params) { + return TestPlatformAdsLoader(params, + onContentComplete: () async {}, + onRequestAds: (AdsRequest request) async {}); + }, + onCreatePlatformAdsManagerDelegate: + (PlatformAdsManagerDelegateCreationParams params) { + throw UnimplementedError(); + }, + onCreatePlatformAdDisplayContainer: + (PlatformAdDisplayContainerCreationParams params) { + throw UnimplementedError(); + }, + ); + + late final AdsManager manager; + + final AdsLoader loader = AdsLoader( + container: AdDisplayContainer.fromPlatform( + platform: TestPlatformAdDisplayContainer( + PlatformAdDisplayContainerCreationParams( + onContainerAdded: (_) {}, + ), + onBuild: (_) => Container(), + ), + ), + onAdsLoaded: (OnAdsLoadedData data) { + manager = data.manager; + }, + onAdsLoadError: (_) {}, + ); + + loader.platform.params.onAdsLoaded(PlatformOnAdsLoadedData( + manager: platformManager, + )); + + return manager; +} diff --git a/packages/interactive_media_ads/test/test_stubs.dart b/packages/interactive_media_ads/test/test_stubs.dart new file mode 100644 index 000000000000..fd59fb3073f1 --- /dev/null +++ b/packages/interactive_media_ads/test/test_stubs.dart @@ -0,0 +1,127 @@ +// 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:interactive_media_ads/src/platform_interface/platform_interface.dart'; + +final class TestInteractiveMediaAdsPlatform + extends InteractiveMediaAdsPlatform { + TestInteractiveMediaAdsPlatform({ + required this.onCreatePlatformAdsLoader, + required this.onCreatePlatformAdsManagerDelegate, + required this.onCreatePlatformAdDisplayContainer, + }); + + PlatformAdsLoader Function(PlatformAdsLoaderCreationParams params) + onCreatePlatformAdsLoader; + + PlatformAdsManagerDelegate Function( + PlatformAdsManagerDelegateCreationParams params, + ) onCreatePlatformAdsManagerDelegate; + + PlatformAdDisplayContainer Function( + PlatformAdDisplayContainerCreationParams params, + ) onCreatePlatformAdDisplayContainer; + + @override + PlatformAdsLoader createPlatformAdsLoader( + PlatformAdsLoaderCreationParams params, + ) { + return onCreatePlatformAdsLoader(params); + } + + @override + PlatformAdsManagerDelegate createPlatformAdsManagerDelegate( + PlatformAdsManagerDelegateCreationParams params, + ) { + return onCreatePlatformAdsManagerDelegate(params); + } + + @override + PlatformAdDisplayContainer createPlatformAdDisplayContainer( + PlatformAdDisplayContainerCreationParams params, + ) { + return onCreatePlatformAdDisplayContainer(params); + } +} + +final class TestPlatformAdDisplayContainer extends PlatformAdDisplayContainer { + TestPlatformAdDisplayContainer( + super.params, { + required this.onBuild, + }) : super.implementation(); + + Widget Function(BuildContext context) onBuild; + + @override + Widget build(BuildContext context) { + return onBuild.call(context); + } +} + +final class TestPlatformAdsLoader extends PlatformAdsLoader { + TestPlatformAdsLoader( + super.params, { + required this.onContentComplete, + required this.onRequestAds, + }) : super.implementation(); + + Future Function() onContentComplete; + + Future Function(AdsRequest request) onRequestAds; + + @override + Future contentComplete() async { + return onContentComplete(); + } + + @override + Future requestAds(AdsRequest request) async { + return onRequestAds(request); + } +} + +final class TestPlatformAdsManagerDelegate extends PlatformAdsManagerDelegate { + TestPlatformAdsManagerDelegate(super.params) : super.implementation(); +} + +class TestAdsManager extends PlatformAdsManager { + TestAdsManager({ + this.onInit, + this.onSetAdsManagerDelegate, + this.onStart, + this.onDestroy, + }); + + Future Function(AdsManagerInitParams params)? onInit; + + Future Function(PlatformAdsManagerDelegate delegate)? + onSetAdsManagerDelegate; + + Future Function(AdsManagerStartParams params)? onStart; + + Future Function()? onDestroy; + + @override + Future init(AdsManagerInitParams params) async { + return onInit?.call(params); + } + + @override + Future setAdsManagerDelegate( + PlatformAdsManagerDelegate delegate, + ) async { + return onSetAdsManagerDelegate?.call(delegate); + } + + @override + Future start(AdsManagerStartParams params) async { + return onStart?.call(params); + } + + @override + Future destroy() async { + return onDestroy?.call(); + } +} From c7d30e251b7758f1e54e745228ed1ae5c1cae2a6 Mon Sep 17 00:00:00 2001 From: anisovdev <112062427+anisovdev@users.noreply.github.com> Date: Fri, 29 Mar 2024 02:38:39 +0500 Subject: [PATCH 117/126] [go_router]: fix GoRouter.optionURLReflectsImperativeAPIs flag works with new imperative APIs (#6236) After 13.0.0 release of go_router package `GoRouter.optionURLReflectsImperativeAPIs` is not working correct. Isn't correct = url in browser doesn't updates after push, example you can see in new test, or in linked issue [List which issues are fixed by this PR. You must list at least one issue.](https://github.com/flutter/flutter/issues/142053) --- packages/go_router/CHANGELOG.md | 4 ++ packages/go_router/lib/src/parser.dart | 27 +++++++++----- packages/go_router/pubspec.yaml | 2 +- packages/go_router/test/go_router_test.dart | 41 +++++++++++++++++++++ 4 files changed, 64 insertions(+), 10 deletions(-) diff --git a/packages/go_router/CHANGELOG.md b/packages/go_router/CHANGELOG.md index 73e6fe03ae84..d268648720c3 100644 --- a/packages/go_router/CHANGELOG.md +++ b/packages/go_router/CHANGELOG.md @@ -1,3 +1,7 @@ +## 13.2.2 + +- Fixes restoreRouteInformation issue when GoRouter.optionURLReflectsImperativeAPIs is true and the last match is ShellRouteMatch + ## 13.2.1 - Updates minimum supported SDK version to Flutter 3.16/Dart 3.2. diff --git a/packages/go_router/lib/src/parser.dart b/packages/go_router/lib/src/parser.dart index 4ac4f03423c7..9cd92c1848b6 100644 --- a/packages/go_router/lib/src/parser.dart +++ b/packages/go_router/lib/src/parser.dart @@ -125,18 +125,27 @@ class GoRouteInformationParser extends RouteInformationParser { if (configuration.isEmpty) { return null; } - final String location; + String? location; if (GoRouter.optionURLReflectsImperativeAPIs && - configuration.matches.last is ImperativeRouteMatch) { - location = (configuration.matches.last as ImperativeRouteMatch) - .matches - .uri - .toString(); - } else { - location = configuration.uri.toString(); + (configuration.matches.last is ImperativeRouteMatch || + configuration.matches.last is ShellRouteMatch)) { + RouteMatchBase route = configuration.matches.last; + + while (route is! ImperativeRouteMatch) { + if (route is ShellRouteMatch && route.matches.isNotEmpty) { + route = route.matches.last; + } else { + break; + } + } + + if (route case final ImperativeRouteMatch safeRoute) { + location = safeRoute.matches.uri.toString(); + } } + return RouteInformation( - uri: Uri.parse(location), + uri: Uri.parse(location ?? configuration.uri.toString()), state: _routeMatchListCodec.encode(configuration), ); } diff --git a/packages/go_router/pubspec.yaml b/packages/go_router/pubspec.yaml index 3974ba276066..ac7945e62ece 100644 --- a/packages/go_router/pubspec.yaml +++ b/packages/go_router/pubspec.yaml @@ -1,7 +1,7 @@ name: go_router description: A declarative router for Flutter based on Navigation 2 supporting deep linking, data-driven routes and more -version: 13.2.1 +version: 13.2.2 repository: https://github.com/flutter/packages/tree/main/packages/go_router issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+go_router%22 diff --git a/packages/go_router/test/go_router_test.dart b/packages/go_router/test/go_router_test.dart index 776e9a4d680e..57d8612c0767 100644 --- a/packages/go_router/test/go_router_test.dart +++ b/packages/go_router/test/go_router_test.dart @@ -1111,6 +1111,47 @@ void main() { log.clear(); }); + testWidgets( + 'on push shell route with optionURLReflectImperativeAPIs = true', + (WidgetTester tester) async { + GoRouter.optionURLReflectsImperativeAPIs = true; + final List routes = [ + GoRoute( + path: '/', + builder: (BuildContext context, GoRouterState state) => + const DummyScreen(), + routes: [ + ShellRoute( + builder: + (BuildContext context, GoRouterState state, Widget child) => + child, + routes: [ + GoRoute( + path: 'c', + builder: (BuildContext context, GoRouterState state) => + const DummyScreen(), + ) + ], + ), + ], + ), + ]; + + final GoRouter router = await createRouter(routes, tester); + + log.clear(); + router.push('/c?foo=bar'); + final RouteMatchListCodec codec = + RouteMatchListCodec(router.configuration); + await tester.pumpAndSettle(); + expect(log, [ + isMethodCall('selectMultiEntryHistory', arguments: null), + IsRouteUpdateCall('/c?foo=bar', false, + codec.encode(router.routerDelegate.currentConfiguration)), + ]); + GoRouter.optionURLReflectsImperativeAPIs = false; + }); + testWidgets('on push with optionURLReflectImperativeAPIs = true', (WidgetTester tester) async { GoRouter.optionURLReflectsImperativeAPIs = true; From 6d8680034cf2597a4ba324de76a689904d5fc0ca Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Thu, 28 Mar 2024 14:38:42 -0700 Subject: [PATCH 118/126] [ci] Adds sleep 60s to release action. (#6405) As suggested in flutter/flutter#137299: * Adds a 1 minute sleep to our `release.yml` workflow so LUCI checks have time to be populated through the GitHub Webhooks, before attempting to look at them. This will make the `release` action a little bit slower, but hopefully much more reliable. ### Issues * Fixes flutter/flutter#137299 --- .github/workflows/release.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 977f7f5f1525..777e15bcc6e9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,6 +43,13 @@ jobs: run: dart pub get working-directory: ${{ github.workspace }}/script/tool + # Give some time for LUCI checks to start becoming populated. + # Because of latency in Github Webhooks, we need to wait for a while + # before being able to look at checks scheduled by LUCI. + - name: Give webhooks a minute + run: sleep 60s + shell: bash + # The next step waits for all tests, but when there are issues with the # hooks it can take a long time for the tests to even be registered. If # "Wait on all tests" runs before that happens, it will pass immediately From fbf44e21e8b84400475a5d258742bb74ebbdeb1c Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 28 Mar 2024 19:13:46 -0400 Subject: [PATCH 119/126] Manual roll Flutter from dbdcead93225 to 89ea49204b37 (54 revisions) (#6431) Manual roll Flutter from dbdcead93225 to 89ea49204b37 (54 revisions) Manual roll requested by dit@google.com https://github.com/flutter/flutter/compare/dbdcead93225...89ea49204b37 2024-03-28 engine-flutter-autoroll@skia.org Roll Flutter Engine from f71e5ad8586b to a396dc1a03a9 (3 revisions) (flutter/flutter#145928) 2024-03-28 engine-flutter-autoroll@skia.org Roll Flutter Engine from 043af350ae85 to f71e5ad8586b (1 revision) (flutter/flutter#145919) 2024-03-28 103135467+sealesj@users.noreply.github.com Refactor skp_generator_tests (flutter/flutter#145871) 2024-03-28 engine-flutter-autoroll@skia.org Roll Flutter Engine from 7c9d5adb6ff8 to 043af350ae85 (2 revisions) (flutter/flutter#145917) 2024-03-28 tessertaha@gmail.com Update `TabBar` and `TabBar.secondary` to use indicator height/color M3 tokens (flutter/flutter#145753) 2024-03-28 engine-flutter-autoroll@skia.org Roll Packages from e6b3e11d9d5a to 924c7e6e899d (5 revisions) (flutter/flutter#145915) 2024-03-28 mdebbar@google.com Add `viewId` to `TextInputConfiguration` (flutter/flutter#145708) 2024-03-28 engine-flutter-autoroll@skia.org Roll Flutter Engine from 9df2d3a0778e to 7c9d5adb6ff8 (3 revisions) (flutter/flutter#145909) 2024-03-28 engine-flutter-autoroll@skia.org Manual roll Flutter Engine from c602abdbae16 to 9df2d3a0778e (10 revisions) (flutter/flutter#145903) 2024-03-28 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Roll Flutter Engine from c602abdbae16 to 922c7b133bc2 (7 revisions) (#145877)" (flutter/flutter#145901) 2024-03-28 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Roll Flutter Engine from 922c7b133bc2 to b3516c4c5683 (1 revision) (#145879)" (flutter/flutter#145900) 2024-03-28 engine-flutter-autoroll@skia.org Roll Flutter Engine from 922c7b133bc2 to b3516c4c5683 (1 revision) (flutter/flutter#145879) 2024-03-28 engine-flutter-autoroll@skia.org Roll Flutter Engine from c602abdbae16 to 922c7b133bc2 (7 revisions) (flutter/flutter#145877) 2024-03-28 rmolivares@renzo-olivares.dev Remove deprecated `TextTheme` members (flutter/flutter#139255) 2024-03-27 maRci002@users.noreply.github.com [WIP] Predictive back support for routes (flutter/flutter#141373) 2024-03-27 engine-flutter-autoroll@skia.org Roll Flutter Engine from 73c145c9ac3a to c602abdbae16 (1 revision) (flutter/flutter#145865) 2024-03-27 engine-flutter-autoroll@skia.org Roll Flutter Engine from d65662541682 to 73c145c9ac3a (8 revisions) (flutter/flutter#145862) 2024-03-27 engine-flutter-autoroll@skia.org Roll Flutter Engine from b7dddee939f2 to d65662541682 (2 revisions) (flutter/flutter#145851) 2024-03-27 engine-flutter-autoroll@skia.org Roll Flutter Engine from 00dab0d9d310 to b7dddee939f2 (2 revisions) (flutter/flutter#145841) 2024-03-27 engine-flutter-autoroll@skia.org Roll Packages from ab1630b9b9bd to e6b3e11d9d5a (6 revisions) (flutter/flutter#145833) 2024-03-27 103135467+sealesj@users.noreply.github.com Refactor web long running tests (flutter/flutter#145776) 2024-03-27 engine-flutter-autoroll@skia.org Roll Flutter Engine from da64c6bcbbb6 to 00dab0d9d310 (1 revision) (flutter/flutter#145830) 2024-03-27 engine-flutter-autoroll@skia.org Roll Flutter Engine from d6c6ba5aa157 to da64c6bcbbb6 (1 revision) (flutter/flutter#145811) 2024-03-27 engine-flutter-autoroll@skia.org Roll Flutter Engine from 064a4f5d9042 to d6c6ba5aa157 (1 revision) (flutter/flutter#145807) 2024-03-27 engine-flutter-autoroll@skia.org Roll Flutter Engine from cad8e7a9ad70 to 064a4f5d9042 (1 revision) (flutter/flutter#145805) 2024-03-27 engine-flutter-autoroll@skia.org Roll Flutter Engine from 441005698702 to cad8e7a9ad70 (1 revision) (flutter/flutter#145804) 2024-03-27 engine-flutter-autoroll@skia.org Roll Flutter Engine from d872d50e53f4 to 441005698702 (1 revision) (flutter/flutter#145803) 2024-03-27 engine-flutter-autoroll@skia.org Roll Flutter Engine from 92ebd47dd8a8 to d872d50e53f4 (6 revisions) (flutter/flutter#145801) 2024-03-27 godofredoc@google.com Update localization files. (flutter/flutter#145780) 2024-03-27 engine-flutter-autoroll@skia.org Roll Flutter Engine from 026d8902e3b5 to 92ebd47dd8a8 (1 revision) (flutter/flutter#145788) 2024-03-26 ditman@gmail.com [web] Add BackgroundIsolateBinaryMessenger.ensureInitialized to web. (flutter/flutter#145786) 2024-03-26 49699333+dependabot[bot]@users.noreply.github.com Bump codecov/codecov-action from 4.1.0 to 4.1.1 (flutter/flutter#145787) 2024-03-26 christopherfujino@gmail.com Roll pub packages and regenerate gradle lockfiles (flutter/flutter#145727) 2024-03-26 engine-flutter-autoroll@skia.org Roll Flutter Engine from 5c7aea6f20fc to 026d8902e3b5 (1 revision) (flutter/flutter#145785) 2024-03-26 engine-flutter-autoroll@skia.org Roll Flutter Engine from baede78d2352 to 5c7aea6f20fc (2 revisions) (flutter/flutter#145784) 2024-03-26 engine-flutter-autoroll@skia.org Roll Flutter Engine from cffd1dcfe6a5 to baede78d2352 (2 revisions) (flutter/flutter#145778) 2024-03-26 dwzrlp@163.com Correct typo: "Free" to "Three" in comments (flutter/flutter#145689) 2024-03-26 tessertaha@gmail.com Fix disabled `DropdownMenu` doesn't defer the mouse cursor (flutter/flutter#145686) 2024-03-26 engine-flutter-autoroll@skia.org Roll Flutter Engine from b2d93a64cbc7 to cffd1dcfe6a5 (9 revisions) (flutter/flutter#145773) 2024-03-26 engine-flutter-autoroll@skia.org Roll Packages from 28d126c54c63 to ab1630b9b9bd (1 revision) (flutter/flutter#145755) 2024-03-26 sokolovskyi.konstantin@gmail.com Memory leaks clean up 2 (flutter/flutter#145757) 2024-03-26 sokolovskyi.konstantin@gmail.com Fix memory leak in Overlay.wrap. (flutter/flutter#145744) 2024-03-26 jacksongardner@google.com Be tolerant of backticks around directory name in `pub` output. (flutter/flutter#145768) 2024-03-26 tessertaha@gmail.com Fix `ExpansionTile` Expanded/Collapsed announcement is interrupted by VoiceOver (flutter/flutter#143936) ... --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 460e1b5f8eb8..bdb8491481f6 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -dbdcead93225bf701bdf03d1acb1b98a98f59334 +89ea49204b37523a16daec53b5e6fae70995929d From 286c94ef68bf9c6252088ad854ad735022221898 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 Mar 2024 23:59:17 +0000 Subject: [PATCH 120/126] [image_picker]: Bump androidx.exifinterface:exifinterface from 1.3.6 to 1.3.7 in /packages/image_picker/image_picker_android/android (#5705) Bumps androidx.exifinterface:exifinterface from 1.3.6 to 1.3.7. [![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=androidx.exifinterface:exifinterface&package-manager=gradle&previous-version=1.3.6&new-version=1.3.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) You can trigger a rebase of this PR by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
> **Note** > Automatic rebases have been disabled on this pull request as it has been open for over 30 days. --- packages/image_picker/image_picker_android/CHANGELOG.md | 4 ++++ .../image_picker/image_picker_android/android/build.gradle | 2 +- packages/image_picker/image_picker_android/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md index 08b563a98856..09624c2ceb89 100644 --- a/packages/image_picker/image_picker_android/CHANGELOG.md +++ b/packages/image_picker/image_picker_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.9+5 + +* Bumps androidx.exifinterface:exifinterface from 1.3.6 to 1.3.7. + ## 0.8.9+4 * Minimizes scope of deprecation warning suppression to only the versions where it is required. diff --git a/packages/image_picker/image_picker_android/android/build.gradle b/packages/image_picker/image_picker_android/android/build.gradle index 9a1d1773fa2a..ef035edd41b1 100644 --- a/packages/image_picker/image_picker_android/android/build.gradle +++ b/packages/image_picker/image_picker_android/android/build.gradle @@ -40,7 +40,7 @@ android { dependencies { implementation 'androidx.core:core:1.10.1' implementation 'androidx.annotation:annotation:1.7.1' - implementation 'androidx.exifinterface:exifinterface:1.3.6' + implementation 'androidx.exifinterface:exifinterface:1.3.7' implementation 'androidx.activity:activity:1.7.2' // org.jetbrains.kotlin:kotlin-bom artifact purpose is to align kotlin stdlib and related code versions. // See: https://youtrack.jetbrains.com/issue/KT-55297/kotlin-stdlib-should-declare-constraints-on-kotlin-stdlib-jdk8-and-kotlin-stdlib-jdk7 diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index a2e8ac5eaefe..a567b6dbac51 100755 --- a/packages/image_picker/image_picker_android/pubspec.yaml +++ b/packages/image_picker/image_picker_android/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_android description: Android implementation of the image_picker plugin. repository: https://github.com/flutter/packages/tree/main/packages/image_picker/image_picker_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.9+4 +version: 0.8.9+5 environment: sdk: ^3.1.0 From ad0274ad12dd11ab3bc2da571c4af949b2396ef2 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 28 Mar 2024 23:47:20 -0400 Subject: [PATCH 121/126] Manual roll Flutter (stable) from 68bfaea22488 to 300451adae58 (2 revisions) (#6433) Manual roll requested by dit@google.com https://github.com/flutter/flutter/compare/68bfaea22488...300451adae58 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-stable-packages Please CC dit@google.com,rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter (stable): https://github.com/flutter/flutter/issues/new/choose To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_stable.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_stable.version b/.ci/flutter_stable.version index 57c34a63ace5..304f1de6baf4 100644 --- a/.ci/flutter_stable.version +++ b/.ci/flutter_stable.version @@ -1 +1 @@ -68bfaea224880b488c617afe30ab12091ea8fa4e +300451adae589accbece3490f4396f10bdf15e6e From 51faaa156b61f2a851bdd23d25fb1f8e938a8ee2 Mon Sep 17 00:00:00 2001 From: krupikivan Date: Fri, 29 Mar 2024 12:00:59 -0400 Subject: [PATCH 122/126] [shared_preferences] Update mockito to the new version available 5.2.0 (#6332) Basically I'm trying to fix this issue ![image](https://github.com/flutter/packages/assets/30050951/1da48bdd-679c-4278-9350-4a6dac169c88) *List which issues are fixed by this PR. You must list at least one issue.* fixes https://github.com/flutter/flutter/issues/145174 *If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].* ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [relevant style guides] and ran the auto-formatter. (Unlike the flutter/flutter repo, the flutter/packages repo does use `dart format`.) - [x] I signed the [CLA]. - [x] The title of the PR starts with the name of the package surrounded by square brackets, e.g. `[shared_preferences]` - [x] I [linked to at least one issue that this PR fixes] in the description above. - [x] I updated `pubspec.yaml` with an appropriate new version according to the [pub versioning philosophy], or this PR is [exempt from version changes]. - [x] I updated `CHANGELOG.md` to add a description of the change, [following repository CHANGELOG style]. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [Contributor Guide]: https://github.com/flutter/packages/blob/main/CONTRIBUTING.md [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [relevant style guides]: https://github.com/flutter/packages/blob/main/CONTRIBUTING.md#style [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat [linked to at least one issue that this PR fixes]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [pub versioning philosophy]: https://dart.dev/tools/pub/versioning [exempt from version changes]: https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#version-and-changelog-updates [following repository CHANGELOG style]: https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changelog-style [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests --------- Co-authored-by: Reid Baker --- .../shared_preferences/shared_preferences_android/CHANGELOG.md | 1 + .../shared_preferences_android/android/build.gradle | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md index b625a5cd67bc..dfd1a45f1af6 100644 --- a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md @@ -2,6 +2,7 @@ * Updates minimum supported SDK version to Flutter 3.13/Dart 3.1. * Updates compileSdk version to 34. +* Updates mockito to 5.2.0. ## 2.2.1 diff --git a/packages/shared_preferences/shared_preferences_android/android/build.gradle b/packages/shared_preferences/shared_preferences_android/android/build.gradle index ef0a6afef709..879464c4d9e4 100644 --- a/packages/shared_preferences/shared_preferences_android/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/android/build.gradle @@ -52,7 +52,7 @@ android { } dependencies { testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-inline:5.0.0' + testImplementation 'org.mockito:mockito-inline:5.2.0' } From 302e73c3f651c61d40388c18644b7d8e06d6808d Mon Sep 17 00:00:00 2001 From: Kate Lovett Date: Fri, 29 Mar 2024 11:40:21 -0500 Subject: [PATCH 123/126] [two_dimensional_scrollables] Infinite TableView (#6411) Fixes https://github.com/flutter/flutter/issues/131226 This adds support for infinite rows and columns in TableView. When using a TableCellBuilderDelegate or the TableView.builder constructor, the row and column counts are no longer required. Omitting them will result in an infinite number or rows and/or columns. When the row and column count are finite, the layout of the table is eagerly computed, while the children are actually laid out lazily. In the infinite TableView world, both the layout computation and the layout of children is lazy. The metrics for the table will only be computed for what is visible or in the cache extent, and will update the metrics as needed. Similar to widgets like ListView, there is an option to null terminate the number of rows or columns in the table. The table's rows and/or columns will be infinite in number unless the rowBuilder or columnBuilder returns null, signifying the end. The ScrollPosition will reflect a maxScrollExtent of double.infinity unless the null terminating row or column has been reached, similar to other infinite-until-null-terminated widgets like ListView. --- .../two_dimensional_scrollables/CHANGELOG.md | 4 + .../lib/src/table_view/table.dart | 422 +++- .../lib/src/table_view/table_delegate.dart | 110 +- .../two_dimensional_scrollables/pubspec.yaml | 2 +- .../test/table_view/table_test.dart | 1858 ++++++++++++++++- 5 files changed, 2259 insertions(+), 137 deletions(-) diff --git a/packages/two_dimensional_scrollables/CHANGELOG.md b/packages/two_dimensional_scrollables/CHANGELOG.md index 222468fa1776..e1541589d45f 100644 --- a/packages/two_dimensional_scrollables/CHANGELOG.md +++ b/packages/two_dimensional_scrollables/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.0 + +* Adds support for infinite rows and columns in TableView. + ## 0.1.2 * Fixes a layout issue for unpinned merged cells that follow pinned table spans. diff --git a/packages/two_dimensional_scrollables/lib/src/table_view/table.dart b/packages/two_dimensional_scrollables/lib/src/table_view/table.dart index 506204f917a7..9f2caa667d4a 100644 --- a/packages/two_dimensional_scrollables/lib/src/table_view/table.dart +++ b/packages/two_dimensional_scrollables/lib/src/table_view/table.dart @@ -41,6 +41,18 @@ import 'table_span.dart'; /// the [TableCellDelegateMixin]. The [TableView.builder] and [TableView.list] /// constructors create their own delegate. /// +/// A table with infinite rows and columns can be made by using a +/// [TableCellBuilderDelegate], or the [TableView.builder] constructor, and +/// omitting the row or column count. Returning null from the +/// [columnBuilder] or [rowBuilder] in this case will terminate +/// the row or column at that index, representing the end of the table in that +/// axis. In this scenario, until the potential end of the table in either +/// dimension is reached by returning null, the +/// [ScrollPosition.maxScrollExtent] will reflect [double.infinity]. This is +/// because as the table is built lazily, it will not know the end has been +/// reached until the [ScrollPosition] arrives there. This is similar to +/// returning null from [ListView.builder] to signify the end of the list. +/// /// This example shows a TableView of 100 children, all sized 100 by 100 /// pixels with a few [TableSpanDecoration]s like background colors and borders. /// The `builder` constructor is called on demand for the cells that are visible @@ -116,6 +128,16 @@ class TableView extends TwoDimensionalScrollView { /// This constructor generates a [TableCellBuilderDelegate] for building /// children on demand using the required [cellBuilder], /// [columnBuilder], and [rowBuilder]. + /// + /// For infinite rows and columns, omit providing [columnCount] or [rowCount]. + /// Returning null from the [columnBuilder] or [rowBuilder] will terminate + /// the row or column at that index, representing the end of the table in that + /// axis. In this scenario, until the potential end of the table in either + /// dimension is reached by returning null, the + /// [ScrollPosition.maxScrollExtent] will reflect [double.infinity]. This is + /// because as the table is built lazily, it will not know the end has been + /// reached until the [ScrollPosition] arrives there. This is similar to + /// returning null from [ListView.builder] to signify the end of the list. TableView.builder({ super.key, super.primary, @@ -129,17 +151,17 @@ class TableView extends TwoDimensionalScrollView { super.clipBehavior, int pinnedRowCount = 0, int pinnedColumnCount = 0, - required int columnCount, - required int rowCount, + int? columnCount, + int? rowCount, required TableSpanBuilder columnBuilder, required TableSpanBuilder rowBuilder, required TableViewCellBuilder cellBuilder, }) : assert(pinnedRowCount >= 0), - assert(rowCount >= 0), - assert(rowCount >= pinnedRowCount), - assert(columnCount >= 0), + assert(rowCount == null || rowCount >= 0), + assert(rowCount == null || rowCount >= pinnedRowCount), + assert(columnCount == null || columnCount >= 0), assert(pinnedColumnCount >= 0), - assert(columnCount >= pinnedColumnCount), + assert(columnCount == null || columnCount >= pinnedColumnCount), super( delegate: TableCellBuilderDelegate( columnCount: columnCount, @@ -302,13 +324,33 @@ class RenderTableViewport extends RenderTwoDimensionalViewport { final List _mergedColumns = []; // Cached Table metrics - Map _columnMetrics = {}; - Map _rowMetrics = {}; + final Map _columnMetrics = {}; + final Map _rowMetrics = {}; int? _firstNonPinnedRow; int? _firstNonPinnedColumn; int? _lastNonPinnedRow; int? _lastNonPinnedColumn; + int? _columnNullTerminatedIndex; + bool get _columnsAreInfinite => delegate.columnCount == null; + // How far columns should be laid out in a given frame. + double get _targetColumnPixel { + return cacheExtent + + horizontalOffset.pixels + + viewportDimension.width - + _pinnedColumnsExtent; + } + + int? _rowNullTerminatedIndex; + bool get _rowsAreInfinite => delegate.rowCount == null; + // How far rows should be laid out in a given frame. + double get _targetRowPixel { + return cacheExtent + + verticalOffset.pixels + + viewportDimension.height - + _pinnedRowsExtent; + } + TableVicinity? get _firstNonPinnedCell { if (_firstNonPinnedRow == null || _firstNonPinnedColumn == null) { return null; @@ -406,31 +448,79 @@ class RenderTableViewport extends RenderTwoDimensionalViewport { return false; } - // Updates the cached metrics for the table. - // - // Will iterate through all columns and rows to define the layout pattern of - // the cells of the table. + // Updates the cached column metrics for the table. // - // TODO(Piinks): Add back infinite separately for easier review, https://github.com/flutter/flutter/issues/131226 - // Only relevant when the number of rows and columns is finite - void _updateAllMetrics() { - assert(needsDelegateRebuild || didResize); - - _firstNonPinnedColumn = null; - _lastNonPinnedColumn = null; - double startOfRegularColumn = 0; - double startOfPinnedColumn = 0; + // By default, existing column metrics will be updated if they have changed. + // Setting `appendColumns` to false will preserve existing metrics, adding + // additional metrics up to the visible+cacheExtent or up to a provided index, + // `toColumnIndex`. Appending is only relevant when the number of columns is + // infinite. + void _updateColumnMetrics({bool appendColumns = false, int? toColumnIndex}) { + assert(() { + if (toColumnIndex != null) { + // If we are computing up to an index, we must be appending. + return appendColumns; + } + return true; + }()); + double startOfRegularColumn = 0.0; + double startOfPinnedColumn = 0.0; + if (appendColumns) { + // We are only adding to the metrics we already know, since we are lazily + // compiling metrics. This should only be the case when the + // number of columns is infinite, and saves us going through all the + // columns we already know about. + assert(_columnsAreInfinite); + assert(_columnMetrics.isNotEmpty); + startOfPinnedColumn = + _columnMetrics[_firstNonPinnedColumn]?.trailingOffset ?? 0.0; + startOfRegularColumn = + _columnMetrics[_lastNonPinnedColumn]?.trailingOffset ?? 0.0; + } + // If we are computing up to a specific index, we are getting info for a + // merged cell, do not change the visible cells. + _firstNonPinnedColumn = + toColumnIndex == null ? null : _firstNonPinnedColumn; + _lastNonPinnedColumn = toColumnIndex == null ? null : _lastNonPinnedColumn; + int column = appendColumns ? _columnMetrics.length : 0; + + bool reachedColumnEnd() { + if (_columnsAreInfinite) { + if (toColumnIndex != null) { + // Column metrics should be computed up to the provided index. + // Only relevant when we are filling in missing column metrics in an + // infinite context. + return _columnMetrics.length > toColumnIndex; + } + // There are infinite columns, and no target index, compute metrics + // up to what is visible and in the cache extent, or the index that null + // terminates. + return _lastNonPinnedColumn != null || + _columnNullTerminatedIndex != null; + } + // Compute all the metrics if the columns are finite. + return column == delegate.columnCount!; + } - final Map newColumnMetrics = {}; - for (int column = 0; column < delegate.columnCount; column++) { + while (!reachedColumnEnd()) { final bool isPinned = column < delegate.pinnedColumnCount; final double leadingOffset = isPinned ? startOfPinnedColumn : startOfRegularColumn; _Span? span = _columnMetrics.remove(column); - assert(needsDelegateRebuild || span != null); - final TableSpan configuration = needsDelegateRebuild - ? delegate.buildColumn(column) - : span!.configuration; + final TableSpan? configuration = + span?.configuration ?? delegate.buildColumn(column); + if (configuration == null) { + // We have reached the end of columns based on a null termination. This + // This happens when a column count has not been specified. + assert(_columnsAreInfinite); + _lastNonPinnedColumn ??= column - 1; + _columnNullTerminatedIndex = column; + final bool acceptedDimension = _updateHorizontalScrollBounds(); + if (!acceptedDimension) { + _updateFirstAndLastVisibleCell(); + } + break; + } span ??= _Span(); span.update( isPinned: isPinned, @@ -443,17 +533,13 @@ class RenderTableViewport extends RenderTwoDimensionalViewport { ), ), ); - newColumnMetrics[column] = span; + _columnMetrics[column] = span; if (!isPinned) { if (span.trailingOffset >= horizontalOffset.pixels && _firstNonPinnedColumn == null) { _firstNonPinnedColumn = column; } - final double targetColumnPixel = cacheExtent + - horizontalOffset.pixels + - viewportDimension.width - - startOfPinnedColumn; - if (span.trailingOffset >= targetColumnPixel && + if (span.trailingOffset >= _targetColumnPixel && _lastNonPinnedColumn == null) { _lastNonPinnedColumn = column; } @@ -461,27 +547,82 @@ class RenderTableViewport extends RenderTwoDimensionalViewport { } else { startOfPinnedColumn = span.trailingOffset; } + column++; } - assert(newColumnMetrics.length >= delegate.pinnedColumnCount); - for (final _Span span in _columnMetrics.values) { - span.dispose(); - } - _columnMetrics = newColumnMetrics; - _firstNonPinnedRow = null; - _lastNonPinnedRow = null; - double startOfRegularRow = 0; - double startOfPinnedRow = 0; + assert(_columnMetrics.length >= delegate.pinnedColumnCount); + } + + // Updates the cached row metrics for the table. + // + // By default, existing row metrics will be updated if they have changed. + // Setting `appendRows` to false will preserve existing metrics, adding + // additional metrics up to the visible+cacheExtent or up to a provided index, + // `toRowIndex`. Appending is only relevant when the number of rows is + // infinite. + void _updateRowMetrics({bool appendRows = false, int? toRowIndex}) { + assert(() { + if (toRowIndex != null) { + // If we are computing up to an index, we must be appending. + return appendRows; + } + return true; + }()); + double startOfRegularRow = 0.0; + double startOfPinnedRow = 0.0; + if (appendRows) { + // We are only adding to the metrics we already know, since we are lazily + // compiling metrics. This should only be the case when the + // number of rows is infinite, and saves us going through all the + // rows we already know about. + assert(_rowsAreInfinite); + assert(_rowMetrics.isNotEmpty); + startOfPinnedRow = _rowMetrics[_firstNonPinnedRow]?.trailingOffset ?? 0.0; + startOfRegularRow = _rowMetrics[_lastNonPinnedRow]?.trailingOffset ?? 0.0; + } + // If we are computing up to a specific index, we are getting info for a + // merged cell, do not change the visible cells. + _firstNonPinnedRow = toRowIndex == null ? null : _firstNonPinnedRow; + _lastNonPinnedRow = toRowIndex == null ? null : _lastNonPinnedRow; + int row = appendRows ? _rowMetrics.length : 0; + + bool reachedRowEnd() { + if (_rowsAreInfinite) { + if (toRowIndex != null) { + // Row metrics should be computed up to the provided index. + // Only relevant when we are filling in missing column metrics in an + // infinite context. + return _rowMetrics.length > toRowIndex; + } + // There are infinite row, and no target index, compute metrics + // up to what is visible and in the cache extent, or the index that null + // terminates. + return _lastNonPinnedRow != null || _rowNullTerminatedIndex != null; + } + // Compute all the metrics if the rows are finite. + return row == delegate.rowCount!; + } - final Map newRowMetrics = {}; - for (int row = 0; row < delegate.rowCount; row++) { + while (!reachedRowEnd()) { final bool isPinned = row < delegate.pinnedRowCount; final double leadingOffset = isPinned ? startOfPinnedRow : startOfRegularRow; _Span? span = _rowMetrics.remove(row); - assert(needsDelegateRebuild || span != null); - final TableSpan configuration = - needsDelegateRebuild ? delegate.buildRow(row) : span!.configuration; + final TableSpan? configuration = + span?.configuration ?? delegate.buildRow(row); + if (configuration == null) { + // We have reached the end of rows based on a null termination. This + // This happens when a row count has not been specified, but we have + // reached the end. + assert(_rowsAreInfinite); + _lastNonPinnedRow ??= row - 1; + _rowNullTerminatedIndex = row; + final bool acceptedDimension = _updateVerticalScrollBounds(); + if (!acceptedDimension) { + _updateFirstAndLastVisibleCell(); + } + break; + } span ??= _Span(); span.update( isPinned: isPinned, @@ -494,17 +635,13 @@ class RenderTableViewport extends RenderTwoDimensionalViewport { ), ), ); - newRowMetrics[row] = span; + _rowMetrics[row] = span; if (!isPinned) { if (span.trailingOffset >= verticalOffset.pixels && _firstNonPinnedRow == null) { _firstNonPinnedRow = row; } - final double targetRowPixel = cacheExtent + - verticalOffset.pixels + - viewportDimension.height - - startOfPinnedRow; - if (span.trailingOffset >= targetRowPixel && + if (span.trailingOffset > _targetRowPixel && _lastNonPinnedRow == null) { _lastNonPinnedRow = row; } @@ -512,32 +649,26 @@ class RenderTableViewport extends RenderTwoDimensionalViewport { } else { startOfPinnedRow = span.trailingOffset; } + row++; } - assert(newRowMetrics.length >= delegate.pinnedRowCount); - for (final _Span span in _rowMetrics.values) { - span.dispose(); - } - _rowMetrics = newRowMetrics; - final double maxVerticalScrollExtent; - if (_rowMetrics.length <= delegate.pinnedRowCount) { - assert(_firstNonPinnedRow == null && _lastNonPinnedRow == null); - maxVerticalScrollExtent = 0.0; - } else { - final int lastRow = _rowMetrics.length - 1; - if (_firstNonPinnedRow != null) { - _lastNonPinnedRow ??= lastRow; - } - maxVerticalScrollExtent = math.max( - 0.0, - _rowMetrics[lastRow]!.trailingOffset - - viewportDimension.height + - startOfPinnedRow, - ); + assert(_rowMetrics.length >= delegate.pinnedRowCount); + } + + void _updateScrollBounds() { + final bool acceptedDimension = + _updateHorizontalScrollBounds() && _updateVerticalScrollBounds(); + if (!acceptedDimension) { + _updateFirstAndLastVisibleCell(); } + } + bool _updateHorizontalScrollBounds() { final double maxHorizontalScrollExtent; - if (_columnMetrics.length <= delegate.pinnedColumnCount) { + if (_columnsAreInfinite && _columnNullTerminatedIndex == null) { + maxHorizontalScrollExtent = double.infinity; + } else if (!_columnsAreInfinite && + _columnMetrics.length <= delegate.pinnedColumnCount) { assert(_firstNonPinnedColumn == null && _lastNonPinnedColumn == null); maxHorizontalScrollExtent = 0.0; } else { @@ -549,29 +680,61 @@ class RenderTableViewport extends RenderTwoDimensionalViewport { 0.0, _columnMetrics[lastColumn]!.trailingOffset - viewportDimension.width + - startOfPinnedColumn, + _pinnedColumnsExtent, ); } + return horizontalOffset.applyContentDimensions( + 0.0, + maxHorizontalScrollExtent, + ); + } - final bool acceptedDimension = horizontalOffset.applyContentDimensions( - 0.0, maxHorizontalScrollExtent) && - verticalOffset.applyContentDimensions(0.0, maxVerticalScrollExtent); - if (!acceptedDimension) { - _updateFirstAndLastVisibleCell(); + bool _updateVerticalScrollBounds() { + final double maxVerticalScrollExtent; + if (_rowsAreInfinite && _rowNullTerminatedIndex == null) { + maxVerticalScrollExtent = double.infinity; + } else if (!_rowsAreInfinite && + _rowMetrics.length <= delegate.pinnedRowCount) { + assert(_firstNonPinnedRow == null && _lastNonPinnedRow == null); + maxVerticalScrollExtent = 0.0; + } else { + final int lastRow = _rowMetrics.length - 1; + if (_firstNonPinnedRow != null) { + _lastNonPinnedRow ??= lastRow; + } + maxVerticalScrollExtent = math.max( + 0.0, + _rowMetrics[lastRow]!.trailingOffset - + viewportDimension.height + + _pinnedRowsExtent, + ); } + return verticalOffset.applyContentDimensions( + 0.0, + maxVerticalScrollExtent, + ); } - // Uses the cached metrics to update the currently visible cells - // - // TODO(Piinks): Add back infinite separately for easier review, https://github.com/flutter/flutter/issues/131226 - // Only relevant when the number of rows and columns is finite + // Uses the cached metrics to update the currently visible cells. If the + // number of rows or columns are infinite, the layout is computed lazily, so + // this will call for an update to the metrics if we have scrolled beyond the + // layout portion we know about. void _updateFirstAndLastVisibleCell() { + if (_columnMetrics.isNotEmpty) { + _Span lastKnownColumn = _columnMetrics[_columnMetrics.length - 1]!; + if (_columnsAreInfinite && + lastKnownColumn.trailingOffset < _targetColumnPixel) { + // This will add the column metrics we do not know about up to the + // _targetColumnPixel, while keeping the ones we already know about. + _updateColumnMetrics(appendColumns: true); + lastKnownColumn = _columnMetrics[_columnMetrics.length - 1]!; + assert(_columnMetrics.length == delegate.columnCount || + lastKnownColumn.trailingOffset >= _targetColumnPixel || + _columnNullTerminatedIndex != null); + } + } _firstNonPinnedColumn = null; _lastNonPinnedColumn = null; - final double targetColumnPixel = cacheExtent + - horizontalOffset.pixels + - viewportDimension.width - - _pinnedColumnsExtent; for (int column = 0; column < _columnMetrics.length; column++) { if (_columnMetrics[column]!.isPinned) { continue; @@ -581,7 +744,7 @@ class RenderTableViewport extends RenderTwoDimensionalViewport { _firstNonPinnedColumn == null) { _firstNonPinnedColumn = column; } - if (endOfColumn >= targetColumnPixel && _lastNonPinnedColumn == null) { + if (endOfColumn >= _targetColumnPixel && _lastNonPinnedColumn == null) { _lastNonPinnedColumn = column; break; } @@ -590,12 +753,20 @@ class RenderTableViewport extends RenderTwoDimensionalViewport { _lastNonPinnedColumn ??= _columnMetrics.length - 1; } + if (_rowMetrics.isNotEmpty) { + _Span lastKnownRow = _rowMetrics[_rowMetrics.length - 1]!; + if (_rowsAreInfinite && lastKnownRow.trailingOffset < _targetRowPixel) { + // This will add the row metrics we do not know about up to the + // _targetRowPixel, while keeping the ones we already know about. + _updateRowMetrics(appendRows: true); + lastKnownRow = _rowMetrics[_rowMetrics.length - 1]!; + assert(_rowMetrics.length == delegate.rowCount || + lastKnownRow.trailingOffset >= _targetRowPixel || + _rowNullTerminatedIndex != null); + } + } _firstNonPinnedRow = null; _lastNonPinnedRow = null; - final double targetRowPixel = cacheExtent + - verticalOffset.pixels + - viewportDimension.height - - _pinnedRowsExtent; for (int row = 0; row < _rowMetrics.length; row++) { if (_rowMetrics[row]!.isPinned) { continue; @@ -604,7 +775,7 @@ class RenderTableViewport extends RenderTwoDimensionalViewport { if (endOfRow >= verticalOffset.pixels && _firstNonPinnedRow == null) { _firstNonPinnedRow = row; } - if (endOfRow >= targetRowPixel && _lastNonPinnedRow == null) { + if (endOfRow >= _targetRowPixel && _lastNonPinnedRow == null) { _lastNonPinnedRow = row; break; } @@ -617,13 +788,27 @@ class RenderTableViewport extends RenderTwoDimensionalViewport { @override void layoutChildSequence() { // Reset for a new frame + // We always reset the null terminating indices in case rows or columns have + // been added or removed. + _rowNullTerminatedIndex = null; + _columnNullTerminatedIndex = null; _mergedVicinities.clear(); _mergedRows.clear(); _mergedColumns.clear(); if (needsDelegateRebuild || didResize) { // Recomputes the table metrics, invalidates any cached information. - _updateAllMetrics(); + for (final _Span span in _columnMetrics.values) { + span.dispose(); + } + _columnMetrics.clear(); + for (final _Span span in _rowMetrics.values) { + span.dispose(); + } + _rowMetrics.clear(); + _updateColumnMetrics(); + _updateRowMetrics(); + _updateScrollBounds(); } else { // Updates the visible cells based on cached table metrics. _updateFirstAndLastVisibleCell(); @@ -696,7 +881,7 @@ class RenderTableViewport extends RenderTwoDimensionalViewport { required int currentSpan, required int spanMergeStart, required int spanMergeEnd, - required int spanCount, + required int? spanCount, required int pinnedSpanCount, required TableVicinity currentVicinity, }) { @@ -712,7 +897,7 @@ class RenderTableViewport extends RenderTwoDimensionalViewport { 'than the current $lowerSpanOrientation at $currentVicinity.', ); assert( - spanMergeEnd < spanCount, + spanCount == null || spanMergeEnd < spanCount, '$spanOrientation merge configuration exceeds number of ' '${lowerSpanOrientation}s in the table. $spanOrientation merge ' 'containing $currentVicinity starts at $spanMergeStart, and ends at ' @@ -740,6 +925,7 @@ class RenderTableViewport extends RenderTwoDimensionalViewport { double rowOffset = -offset.dy; for (int row = start.row; row <= end.row; row += 1) { double columnOffset = -offset.dx; + assert(row < _rowMetrics.length); rowSpan = _rowMetrics[row]!; final double standardRowHeight = rowSpan.extent; double? mergedRowHeight; @@ -747,6 +933,7 @@ class RenderTableViewport extends RenderTwoDimensionalViewport { rowOffset += rowSpan.configuration.padding.leading; for (int column = start.column; column <= end.column; column += 1) { + assert(column < _columnMetrics.length); colSpan = _columnMetrics[column]!; final double standardColumnWidth = colSpan.extent; double? mergedColumnWidth; @@ -819,6 +1006,19 @@ class RenderTableViewport extends RenderTwoDimensionalViewport { mergedRowOffset = baseRowOffset + _rowMetrics[firstRow]!.leadingOffset + _rowMetrics[firstRow]!.configuration.padding.leading; + if (_rowsAreInfinite && _rowMetrics[lastRow] == null) { + // The number of rows is infinte, and we have not calculated + // the metrics to the full extent of the merged cell. Update the + // metrics so we have all the information for the merged area. + _updateRowMetrics(appendRows: true, toRowIndex: lastRow); + } + assert( + _rowMetrics[lastRow] != null, + 'The merged cell containing $vicinity is missing TableSpan ' + 'information necessary for layout. The rowBuilder returned ' + 'null, signifying the end, at row $_rowNullTerminatedIndex but the ' + 'merged cell is configured to end with row $lastRow.', + ); mergedRowHeight = _rowMetrics[lastRow]!.trailingOffset - _rowMetrics[firstRow]!.leadingOffset - _rowMetrics[lastRow]!.configuration.padding.trailing - @@ -840,6 +1040,23 @@ class RenderTableViewport extends RenderTwoDimensionalViewport { mergedColumnOffset = baseColumnOffset + _columnMetrics[firstColumn]!.leadingOffset + _columnMetrics[firstColumn]!.configuration.padding.leading; + + if (_columnsAreInfinite && _columnMetrics[lastColumn] == null) { + // The number of columns is infinte, and we have not calculated + // the metrics to the full extent of the merged cell. Update the + // metrics so we have all the information for the merged area. + _updateColumnMetrics( + appendColumns: true, + toColumnIndex: lastColumn, + ); + } + assert( + _columnMetrics[lastColumn] != null, + 'The merged cell containing $vicinity is missing TableSpan ' + 'information necessary for layout. The columnBuilder returned ' + 'null, signifying the end, at column $_columnNullTerminatedIndex but ' + 'the merged cell is configured to end with column $lastColumn.', + ); mergedColumnWidth = _columnMetrics[lastColumn]!.trailingOffset - _columnMetrics[firstColumn]!.leadingOffset - _columnMetrics[lastColumn]!.configuration.padding.trailing - @@ -1040,7 +1257,10 @@ class RenderTableViewport extends RenderTwoDimensionalViewport { // A merged cell spans multiple vicinities, but only lays out one child for // the full area. Returns the child that has been laid out to span the given // vicinity. - assert(_mergedVicinities.keys.contains(vicinity)); + assert( + _mergedVicinities.keys.contains(vicinity), + 'The vicinity $vicinity is not accounted for as covered by a merged cell.', + ); final TableVicinity mergedVicinity = _mergedVicinities[vicinity]!; // This vicinity must resolve to a child, unless something has gone wrong! return getChildFor( @@ -1468,6 +1688,12 @@ class RenderTableViewport extends RenderTwoDimensionalViewport { _clipPinnedRowsHandle.layer = null; _clipPinnedColumnsHandle.layer = null; _clipCellsHandle.layer = null; + for (final _Span span in _rowMetrics.values) { + span.dispose(); + } + for (final _Span span in _columnMetrics.values) { + span.dispose(); + } super.dispose(); } } diff --git a/packages/two_dimensional_scrollables/lib/src/table_view/table_delegate.dart b/packages/two_dimensional_scrollables/lib/src/table_view/table_delegate.dart index 39a8d676a549..11ea2459ea08 100644 --- a/packages/two_dimensional_scrollables/lib/src/table_view/table_delegate.dart +++ b/packages/two_dimensional_scrollables/lib/src/table_view/table_delegate.dart @@ -14,7 +14,10 @@ import 'table_span.dart'; /// Used by the [TableCellDelegateMixin.buildColumn] and /// [TableCellDelegateMixin.buildRow] to configure rows and columns in the /// [TableView]. -typedef TableSpanBuilder = TableSpan Function(int index); +/// +/// Returning null from this builder signifies the end of rows or columns being +/// built if a row or column count has not been specified for the table. +typedef TableSpanBuilder = TableSpan? Function(int index); /// Signature for a function that creates a child [TableViewCell] for a given /// [TableVicinity] in a [TableView], but may return null. @@ -33,9 +36,8 @@ mixin TableCellDelegateMixin on TwoDimensionalChildDelegate { /// /// The [buildColumn] method will be called for indices smaller than the value /// provided here to learn more about the extent and visual appearance of a - /// particular column. - // TODO(Piinks): land infinite separately, https://github.com/flutter/flutter/issues/131226 - // If null, the table will have an infinite number of columns. + /// particular column. If null, the table will have an infinite number of + /// columns, unless [buildColumn] returns null to signify the end. /// /// The value returned by this getter may be an estimate of the total /// available columns, but [buildColumn] method must provide a valid @@ -46,15 +48,18 @@ mixin TableCellDelegateMixin on TwoDimensionalChildDelegate { /// /// If the value returned by this getter changes throughout the lifetime of /// the delegate object, [notifyListeners] must be called. - int get columnCount; + /// + /// When null, the number of columns will be infinite in number, unless null + /// is returned from [TableCellBuilderDelegate.columnBuilder]. The + /// [TableCellListDelegate] does not support an infinite number of columns. + int? get columnCount; /// The number of rows that the table has content for. /// /// The [buildRow] method will be called for indices smaller than the value /// provided here to learn more about the extent and visual appearance of a - /// particular row. - // TODO(Piinks): land infinite separately, https://github.com/flutter/flutter/issues/131226 - // If null, the table will have an infinite number of rows. + /// particular row. If null, the table will have an infinite number of rows, + /// unless [buildRow] returns null to signify the end. /// /// The value returned by this getter may be an estimate of the total /// available rows, but [buildRow] method must provide a valid @@ -65,7 +70,11 @@ mixin TableCellDelegateMixin on TwoDimensionalChildDelegate { /// /// If the value returned by this getter changes throughout the lifetime of /// the delegate object, [notifyListeners] must be called. - int get rowCount; + /// + /// When null, the number of rows will be infinite in number, unless null + /// is returned from [TableCellBuilderDelegate.rowBuilder]. The + /// [TableCellListDelegate] does not support an infinite number of rows. + int? get rowCount; /// The number of columns that are permanently shown on the leading vertical /// edge of the viewport. @@ -104,14 +113,18 @@ mixin TableCellDelegateMixin on TwoDimensionalChildDelegate { /// Builds the [TableSpan] that describes the column at the provided index. /// /// The builder must return a valid [TableSpan] for all indices smaller than - /// [columnCount]. - TableSpan buildColumn(int index); + /// [columnCount]. If [columnCount] is null, the number of columns will be + /// infinite, unless this builder returns null to signal the end of the + /// columns. + TableSpan? buildColumn(int index); /// Builds the [TableSpan] that describe the row at the provided index. /// /// The builder must return a valid [TableSpan] for all indices smaller than - /// [rowCount]. - TableSpan buildRow(int index); + /// [rowCount]. If [rowCount] is null, the number of rows will be + /// infinite, unless this builder returns null to signal the end of the + /// columns. + TableSpan? buildRow(int index); } /// A delegate that supplies children for a [TableViewport] on demand using a @@ -120,12 +133,17 @@ mixin TableCellDelegateMixin on TwoDimensionalChildDelegate { /// Unlike the base [TwoDimensionalChildBuilderDelegate] this delegate does not /// automatically insert repaint boundaries. Instead, repaint boundaries are /// controlled by [TableViewCell.addRepaintBoundaries]. +/// +/// If the [rowCount] or [columnCount] is not provided, the number of rows +/// and/or columns will be infinite. Returning null from the [columnBuilder] +/// and/or [rowBuilder] in this case can terminate the number of rows and +/// columns at the given index. class TableCellBuilderDelegate extends TwoDimensionalChildBuilderDelegate with TableCellDelegateMixin { /// Creates a lazy building delegate to use with a [TableView]. TableCellBuilderDelegate({ - required int columnCount, - required int rowCount, + int? columnCount, + int? rowCount, int pinnedColumnCount = 0, int pinnedRowCount = 0, super.addAutomaticKeepAlives, @@ -134,10 +152,10 @@ class TableCellBuilderDelegate extends TwoDimensionalChildBuilderDelegate required TableSpanBuilder rowBuilder, }) : assert(pinnedColumnCount >= 0), assert(pinnedRowCount >= 0), - assert(rowCount >= 0), - assert(columnCount >= 0), - assert(pinnedColumnCount <= columnCount), - assert(pinnedRowCount <= rowCount), + assert(rowCount == null || rowCount >= 0), + assert(columnCount == null || columnCount >= 0), + assert(columnCount == null || pinnedColumnCount <= columnCount), + assert(rowCount == null || pinnedRowCount <= rowCount), _rowBuilder = rowBuilder, _columnBuilder = columnBuilder, _pinnedColumnCount = pinnedColumnCount, @@ -145,33 +163,36 @@ class TableCellBuilderDelegate extends TwoDimensionalChildBuilderDelegate super( builder: (BuildContext context, ChildVicinity vicinity) => cellBuilder(context, vicinity as TableVicinity), - maxXIndex: columnCount - 1, - maxYIndex: rowCount - 1, + maxXIndex: columnCount == null ? columnCount : columnCount - 1, + maxYIndex: rowCount == null ? rowCount : rowCount - 1, // repaintBoundaries handled by TableViewCell addRepaintBoundaries: false, ); @override - int get columnCount => maxXIndex! + 1; - set columnCount(int value) { - assert(pinnedColumnCount <= value); - maxXIndex = value - 1; + int? get columnCount => maxXIndex == null ? null : maxXIndex! + 1; + + set columnCount(int? value) { + assert(value == null || pinnedColumnCount <= value); + maxXIndex = value == null ? null : value - 1; } /// Builds the [TableSpan] that describes the column at the provided index. /// /// The builder must return a valid [TableSpan] for all indices smaller than - /// [columnCount]. + /// [columnCount]. If [columnCount] is null, the number of columns will be + /// infinite, unless this builder returns null to signal the end of the + /// columns. final TableSpanBuilder _columnBuilder; @override - TableSpan buildColumn(int index) => _columnBuilder(index); + TableSpan? buildColumn(int index) => _columnBuilder(index); @override int get pinnedColumnCount => _pinnedColumnCount; int _pinnedColumnCount; set pinnedColumnCount(int value) { assert(value >= 0); - assert(value <= columnCount); + assert(columnCount == null || value <= columnCount!); if (pinnedColumnCount == value) { return; } @@ -180,26 +201,29 @@ class TableCellBuilderDelegate extends TwoDimensionalChildBuilderDelegate } @override - int get rowCount => maxYIndex! + 1; - set rowCount(int value) { - assert(pinnedRowCount <= value); - maxYIndex = value - 1; + int? get rowCount => maxYIndex == null ? null : maxYIndex! + 1; + + set rowCount(int? value) { + assert(value == null || pinnedRowCount <= value); + maxYIndex = value == null ? null : value - 1; } /// Builds the [TableSpan] that describes the row at the provided index. /// /// The builder must return a valid [TableSpan] for all indices smaller than - /// [rowCount]. + /// [rowCount]. If [rowCount] is null, the number of rows will be + /// infinite, unless this builder returns null to signal the end of the + /// rows. final TableSpanBuilder _rowBuilder; @override - TableSpan buildRow(int index) => _rowBuilder(index); + TableSpan? buildRow(int index) => _rowBuilder(index); @override int get pinnedRowCount => _pinnedRowCount; int _pinnedRowCount; set pinnedRowCount(int value) { assert(value >= 0); - assert(value <= rowCount); + assert(rowCount == null || value <= rowCount!); if (pinnedRowCount == value) { return; } @@ -261,7 +285,13 @@ class TableCellListDelegate extends TwoDimensionalChildListDelegate /// [columnCount]. final TableSpanBuilder _columnBuilder; @override - TableSpan buildColumn(int index) => _columnBuilder(index); + TableSpan? buildColumn(int index) { + if (index >= columnCount) { + // The list delegate has a finite number of columns. + return null; + } + return _columnBuilder(index); + } @override int get pinnedColumnCount => _pinnedColumnCount; @@ -285,7 +315,13 @@ class TableCellListDelegate extends TwoDimensionalChildListDelegate /// [rowCount]. final TableSpanBuilder _rowBuilder; @override - TableSpan buildRow(int index) => _rowBuilder(index); + TableSpan? buildRow(int index) { + if (index >= rowCount) { + // The list deleagte has a finite number of rows. + return null; + } + return _rowBuilder(index); + } @override int get pinnedRowCount => _pinnedRowCount; diff --git a/packages/two_dimensional_scrollables/pubspec.yaml b/packages/two_dimensional_scrollables/pubspec.yaml index 45f1d77ea6e1..fb9dc192f702 100644 --- a/packages/two_dimensional_scrollables/pubspec.yaml +++ b/packages/two_dimensional_scrollables/pubspec.yaml @@ -1,6 +1,6 @@ name: two_dimensional_scrollables description: Widgets that scroll using the two dimensional scrolling foundation. -version: 0.1.2 +version: 0.2.0 repository: https://github.com/flutter/packages/tree/main/packages/two_dimensional_scrollables issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+two_dimensional_scrollables%22+ diff --git a/packages/two_dimensional_scrollables/test/table_view/table_test.dart b/packages/two_dimensional_scrollables/test/table_view/table_test.dart index de08873cfb2a..ce879b75b277 100644 --- a/packages/two_dimensional_scrollables/test/table_view/table_test.dart +++ b/packages/two_dimensional_scrollables/test/table_view/table_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; @@ -181,6 +182,1861 @@ void main() { ); expect(tableView, isNull); }); + + group('Infinite spans - ', () { + late ScrollController verticalController; + late ScrollController horizontalController; + const TableSpan largeSpan = TableSpan(extent: FixedTableSpanExtent(200)); + + setUp(() { + verticalController = ScrollController(); + horizontalController = ScrollController(); + }); + + tearDown(() { + verticalController.dispose(); + horizontalController.dispose(); + }); + + TableView getTableView({ + int? columnCount, + int? rowCount, + TableSpanBuilder? columnBuilder, + TableSpanBuilder? rowBuilder, + TableViewCellBuilder? cellBuilder, + int pinnedColumnCount = 0, + int pinnedRowCount = 0, + }) { + return TableView.builder( + verticalDetails: ScrollableDetails.vertical( + controller: verticalController, + ), + horizontalDetails: ScrollableDetails.horizontal( + controller: horizontalController, + ), + columnCount: columnCount, + pinnedColumnCount: pinnedColumnCount, + columnBuilder: columnBuilder ?? (_) => largeSpan, + rowCount: rowCount, + pinnedRowCount: pinnedRowCount, + rowBuilder: rowBuilder ?? (_) => largeSpan, + cellBuilder: cellBuilder ?? + (_, TableVicinity vicinity) { + return TableViewCell( + child: Text('R${vicinity.row}:C${vicinity.column}'), + ); + }, + ); + } + + testWidgets('infinite rows, columns are finite', + (WidgetTester tester) async { + // Nothing pinned --- + await tester.pumpWidget(MaterialApp( + home: getTableView(columnCount: 10), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C4'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C4')), + const Rect.fromLTRB(800.0, 800.0, 1000.0, 1000.0), + ); + // No rows laid out beyond row 4. + expect(find.text('R5:C0'), findsNothing); + // Change the vertical scroll offset, validate more rows were populated. + verticalController.jumpTo(1000.0); + await tester.pump(); + expect(verticalController.position.pixels, 1000.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R5:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R5:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R9:C4'), findsOneWidget); + expect( + tester.getRect(find.text('R9:C4')), + const Rect.fromLTRB(800.0, 800.0, 1000.0, 1000.0), + ); + // No rows laid out before row 5, or after row 9. + expect(find.text('R0:C0'), findsNothing); + expect(find.text('R10:C0'), findsNothing); + + await tester.pumpWidget(Container()); + + // Pinned columns --- + await tester.pumpWidget(MaterialApp( + home: getTableView( + columnCount: 10, + pinnedColumnCount: 1, + ), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C4'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C4')), + const Rect.fromLTRB(800.0, 800.0, 1000.0, 1000.0), + ); + // No rows laid out beyond row 4. + expect(find.text('R5:C0'), findsNothing); + // Change the vertical scroll offset, validate more rows were populated. + verticalController.jumpTo(1000.0); + await tester.pump(); + expect(verticalController.position.pixels, 1000.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R5:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R5:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R9:C4'), findsOneWidget); + expect( + tester.getRect(find.text('R9:C4')), + const Rect.fromLTRB(800.0, 800.0, 1000.0, 1000.0), + ); + // No rows laid out before row 5, or after row 9. + expect(find.text('R0:C0'), findsNothing); + expect(find.text('R10:C0'), findsNothing); + + await tester.pumpWidget(Container()); + + // Pinned rows --- + await tester.pumpWidget(MaterialApp( + home: getTableView( + columnCount: 10, + pinnedRowCount: 1, + ), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C4'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C4')), + const Rect.fromLTRB(800.0, 800.0, 1000.0, 1000.0), + ); + // No rows laid out beyond row 4. + expect(find.text('R5:C0'), findsNothing); + // Change the vertical scroll offset, validate more rows were populated. + verticalController.jumpTo(1000.0); + await tester.pump(); + expect(verticalController.position.pixels, 1000.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R5:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R5:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R9:C4'), findsOneWidget); + expect( + tester.getRect(find.text('R9:C4')), + const Rect.fromLTRB(800.0, 800.0, 1000.0, 1000.0), + ); + // No rows laid out before row 5, or after row 9, except for pinned row + // 0. + expect(find.text('R0:C0'), findsOneWidget); + expect(find.text('R1:C0'), findsNothing); + expect(find.text('R10:C0'), findsNothing); + + await tester.pumpWidget(Container()); + + // Pinned columns and rows --- + await tester.pumpWidget(MaterialApp( + home: getTableView( + columnCount: 10, + pinnedColumnCount: 1, + pinnedRowCount: 1, + ), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C4'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C4')), + const Rect.fromLTRB(800.0, 800.0, 1000.0, 1000.0), + ); + // No rows laid out beyond row 4. + expect(find.text('R5:C0'), findsNothing); + // Change the vertical scroll offset, validate more rows were populated. + verticalController.jumpTo(1000.0); + await tester.pump(); + expect(verticalController.position.pixels, 1000.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R5:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R5:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R9:C4'), findsOneWidget); + expect( + tester.getRect(find.text('R9:C4')), + const Rect.fromLTRB(800.0, 800.0, 1000.0, 1000.0), + ); + // No rows laid out before row 5, or after row 9, except for pinned row + // 0. + expect(find.text('R0:C0'), findsOneWidget); + expect(find.text('R1:C0'), findsNothing); + expect(find.text('R10:C0'), findsNothing); + }); + + testWidgets('infinite columns, rows are finite', + (WidgetTester tester) async { + // Nothing pinned --- + await tester.pumpWidget(MaterialApp( + home: getTableView(rowCount: 10), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, 1400.0); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C5'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C5')), + const Rect.fromLTRB(1000.0, 800.0, 1200.0, 1000.0), + ); + // No columns laid out beyond column 5. + expect(find.text('R0:C6'), findsNothing); + // Change the horizontal scroll offset, validate more columns were + // populated. + horizontalController.jumpTo(1200.0); + await tester.pump(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 1200.0); + expect(verticalController.position.maxScrollExtent, 1400.0); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R0:C6'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C6')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C11'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C11')), + const Rect.fromLTRB(1000.0, 800.0, 1200.0, 1000.0), + ); + // No columns laid out before column 5, or after column 12. + expect(find.text('R0:C4'), findsNothing); + expect(find.text('R0:C12'), findsNothing); + + await tester.pumpWidget(Container()); + + // Pinned columns --- + await tester.pumpWidget(MaterialApp( + home: getTableView(rowCount: 10, pinnedColumnCount: 1), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, 1400.0); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C5'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C5')), + const Rect.fromLTRB(1000.0, 800.0, 1200.0, 1000.0), + ); + // No columns laid out beyond column 5. + expect(find.text('R0:C6'), findsNothing); + // Change the horizontal scroll offset, validate more columns were + // populated. + horizontalController.jumpTo(1200.0); + await tester.pump(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 1200.0); + expect(verticalController.position.maxScrollExtent, 1400.0); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R0:C6'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C6')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C11'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C11')), + const Rect.fromLTRB(1000.0, 800.0, 1200.0, 1000.0), + ); + // No columns laid out before column 5, or after column 12, except for + // pinned column. + expect(find.text('R0:C0'), findsOneWidget); + expect(find.text('R0:C4'), findsNothing); + expect(find.text('R0:C12'), findsNothing); + + await tester.pumpWidget(Container()); + + // Pinned rows --- + await tester.pumpWidget(MaterialApp( + home: getTableView(rowCount: 10, pinnedRowCount: 1), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, 1400.0); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C5'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C5')), + const Rect.fromLTRB(1000.0, 800.0, 1200.0, 1000.0), + ); + // No columns laid out beyond column 5. + expect(find.text('R0:C6'), findsNothing); + // Change the horizontal scroll offset, validate more columns were + // populated. + horizontalController.jumpTo(1200.0); + await tester.pump(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 1200.0); + expect(verticalController.position.maxScrollExtent, 1400.0); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R0:C6'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C6')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C11'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C11')), + const Rect.fromLTRB(1000.0, 800.0, 1200.0, 1000.0), + ); + // No columns laid out before column 5, or after column 12. + expect(find.text('R0:C4'), findsNothing); + expect(find.text('R0:C12'), findsNothing); + + await tester.pumpWidget(Container()); + + // Pinned columns and rows --- + await tester.pumpWidget(MaterialApp( + home: getTableView( + rowCount: 10, + pinnedRowCount: 1, + pinnedColumnCount: 1, + ), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, 1400.0); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C5'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C5')), + const Rect.fromLTRB(1000.0, 800.0, 1200.0, 1000.0), + ); + // No columns laid out beyond column 5. + expect(find.text('R0:C6'), findsNothing); + // Change the horizontal scroll offset, validate more columns were + // populated. + horizontalController.jumpTo(1200.0); + await tester.pump(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 1200.0); + expect(verticalController.position.maxScrollExtent, 1400.0); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R0:C6'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C6')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C11'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C11')), + const Rect.fromLTRB(1000.0, 800.0, 1200.0, 1000.0), + ); + // No columns laid out before column 5, or after column 12, except for + // pinned column. + expect(find.text('R0:C0'), findsOneWidget); + expect(find.text('R0:C4'), findsNothing); + expect(find.text('R0:C12'), findsNothing); + }); + + testWidgets('infinite rows & columns', (WidgetTester tester) async { + // No pinned --- + await tester.pumpWidget(MaterialApp( + home: getTableView(), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C5'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C5')), + const Rect.fromLTRB(1000.0, 800.0, 1200.0, 1000.0), + ); + // No columns laid out beyond column 5, no rows beyond row 4. + expect(find.text('R0:C6'), findsNothing); + expect(find.text('R5:C0'), findsNothing); + // Change both scroll offsets, validate more columns and rows were + // populated. + horizontalController.jumpTo(1200.0); + verticalController.jumpTo(1000.0); + await tester.pump(); + expect(verticalController.position.pixels, 1000.0); + expect(horizontalController.position.pixels, 1200.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R5:C6'), findsOneWidget); + expect( + tester.getRect(find.text('R5:C6')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R9:C11'), findsOneWidget); + expect( + tester.getRect(find.text('R9:C11')), + const Rect.fromLTRB(1000.0, 800.0, 1200.0, 1000.0), + ); + // No columns laid out before column 5, or after column 12. + expect(find.text('R5:C4'), findsNothing); + expect(find.text('R5:C12'), findsNothing); + // No rows laid out before row 4, or after row 9. + expect(find.text('R3:C6'), findsNothing); + expect(find.text('R10:C6'), findsNothing); + + await tester.pumpWidget(Container()); + + // Pinned columns --- + await tester.pumpWidget(MaterialApp( + home: getTableView(pinnedColumnCount: 1), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C5'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C5')), + const Rect.fromLTRB(1000.0, 800.0, 1200.0, 1000.0), + ); + // No columns laid out beyond column 5, no rows beyond row 4. + expect(find.text('R0:C6'), findsNothing); + expect(find.text('R5:C0'), findsNothing); + // Change both scroll offsets, validate more columns and rows were + // populated. + horizontalController.jumpTo(1200.0); + verticalController.jumpTo(1000.0); + await tester.pump(); + expect(verticalController.position.pixels, 1000.0); + expect(horizontalController.position.pixels, 1200.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R5:C6'), findsOneWidget); + expect( + tester.getRect(find.text('R5:C6')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R9:C11'), findsOneWidget); + expect( + tester.getRect(find.text('R9:C11')), + const Rect.fromLTRB(1000.0, 800.0, 1200.0, 1000.0), + ); + // No columns laid out before column 5, or after column 12, except for + // pinned first column. + expect(find.text('R5:C0'), findsOneWidget); + expect(find.text('R5:C4'), findsNothing); + expect(find.text('R5:C12'), findsNothing); + // No rows laid out before row 4, or after row 9. + expect(find.text('R3:C6'), findsNothing); + expect(find.text('R10:C6'), findsNothing); + + await tester.pumpWidget(Container()); + + // Pinned Rows --- + await tester.pumpWidget(MaterialApp( + home: getTableView(pinnedRowCount: 1), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C5'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C5')), + const Rect.fromLTRB(1000.0, 800.0, 1200.0, 1000.0), + ); + // No columns laid out beyond column 5, no rows beyond row 4. + expect(find.text('R0:C6'), findsNothing); + expect(find.text('R5:C0'), findsNothing); + // Change both scroll offsets, validate more columns and rows were + // populated. + horizontalController.jumpTo(1200.0); + verticalController.jumpTo(1000.0); + await tester.pump(); + expect(verticalController.position.pixels, 1000.0); + expect(horizontalController.position.pixels, 1200.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R5:C6'), findsOneWidget); + expect( + tester.getRect(find.text('R5:C6')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R9:C11'), findsOneWidget); + expect( + tester.getRect(find.text('R9:C11')), + const Rect.fromLTRB(1000.0, 800.0, 1200.0, 1000.0), + ); + // No columns laid out before column 5, or after column 12. + expect(find.text('R5:C4'), findsNothing); + expect(find.text('R5:C12'), findsNothing); + // No rows laid out before row 4, or after row 9, except for pinned + // first row. + expect(find.text('R0:C6'), findsOneWidget); + expect(find.text('R3:C6'), findsNothing); + expect(find.text('R10:C6'), findsNothing); + + await tester.pumpWidget(Container()); + + // Pinned columns and rows --- + await tester.pumpWidget(MaterialApp( + home: getTableView( + pinnedRowCount: 1, + pinnedColumnCount: 1, + ), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C5'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C5')), + const Rect.fromLTRB(1000.0, 800.0, 1200.0, 1000.0), + ); + // No columns laid out beyond column 5, no rows beyond row 4. + expect(find.text('R0:C6'), findsNothing); + expect(find.text('R5:C0'), findsNothing); + // Change both scroll offsets, validate more columns and rows were + // populated. + horizontalController.jumpTo(1200.0); + verticalController.jumpTo(1000.0); + await tester.pump(); + expect(verticalController.position.pixels, 1000.0); + expect(horizontalController.position.pixels, 1200.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R5:C6'), findsOneWidget); + expect( + tester.getRect(find.text('R5:C6')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R9:C11'), findsOneWidget); + expect( + tester.getRect(find.text('R9:C11')), + const Rect.fromLTRB(1000.0, 800.0, 1200.0, 1000.0), + ); + // No columns laid out before column 5, or after column 12, except for + // pinned first column. + expect(find.text('R5:C0'), findsOneWidget); + expect(find.text('R5:C4'), findsNothing); + expect(find.text('R5:C12'), findsNothing); + // No rows laid out before row 4, or after row 9, except for pinned + // first row. + expect(find.text('R0:C6'), findsOneWidget); + expect(find.text('R3:C6'), findsNothing); + expect(find.text('R10:C6'), findsNothing); + }); + + testWidgets('infinite rows can null terminate', + (WidgetTester tester) async { + // Nothing pinned --- + bool calledOutOfBounds = false; + await tester.pumpWidget(MaterialApp( + home: getTableView( + columnCount: 10, + rowBuilder: (int index) { + // There will only be 8 rows. + if (index == 8) { + return null; + } + if (index > 8) { + calledOutOfBounds = true; + } + return largeSpan; + }, + ), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C4'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C4')), + const Rect.fromLTRB(800.0, 800.0, 1000.0, 1000.0), + ); + // No rows laid out beyond row 4. + expect(find.text('R5:C0'), findsNothing); + // Change the vertical scroll offset, validate more rows were populated. + // This exceeds the bounds of the scroll view once the rows have been + // null terminated. + verticalController.jumpTo(1200.0); + await tester.pumpAndSettle(); + // Position was corrected. + expect(verticalController.position.pixels, 1000.0); + expect(horizontalController.position.pixels, 0.0); + // After null terminating, the builder was not called further. + expect(calledOutOfBounds, isFalse); + // Max scroll extent was updated to reflect reaching the end of the rows + // after returning null. + expect(verticalController.position.maxScrollExtent, 1000.0); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R5:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R5:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R7:C4'), findsOneWidget); + expect( + tester.getRect(find.text('R7:C4')), + const Rect.fromLTRB(800.0, 400.0, 1000.0, 600.0), + ); + // No rows laid out before row 5, or after row 7. + expect(find.text('R0:C0'), findsNothing); + expect(find.text('R8:C0'), findsNothing); + + await tester.pumpWidget(Container()); + + // Pinned columns --- + await tester.pumpWidget(MaterialApp( + home: getTableView( + columnCount: 10, + pinnedColumnCount: 1, + rowBuilder: (int index) { + // There will only be 8 rows. + if (index == 8) { + return null; + } + return largeSpan; + }, + ), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C4'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C4')), + const Rect.fromLTRB(800.0, 800.0, 1000.0, 1000.0), + ); + // No rows laid out beyond row 4. + expect(find.text('R5:C0'), findsNothing); + // Change the vertical scroll offset, validate more rows were populated. + // This exceeds the bounds of the scroll view once the rows have been + // null terminated. + verticalController.jumpTo(1200.0); + await tester.pumpAndSettle(); + // Position was corrected. + expect(verticalController.position.pixels, 1000.0); + expect(horizontalController.position.pixels, 0.0); + // Max scroll extent was updated to reflect reaching the end of the rows + // after returning null. + expect(verticalController.position.maxScrollExtent, 1000.0); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R5:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R5:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R7:C4'), findsOneWidget); + expect( + tester.getRect(find.text('R7:C4')), + const Rect.fromLTRB(800.0, 400.0, 1000.0, 600.0), + ); + // No rows laid out before row 5, or after row 7. + expect(find.text('R0:C0'), findsNothing); + expect(find.text('R8:C0'), findsNothing); + + await tester.pumpWidget(Container()); + + // Pinned rows --- + await tester.pumpWidget(MaterialApp( + home: getTableView( + columnCount: 10, + pinnedRowCount: 1, + rowBuilder: (int index) { + // There will only be 8 rows. + if (index == 8) { + return null; + } + return largeSpan; + }, + ), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C4'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C4')), + const Rect.fromLTRB(800.0, 800.0, 1000.0, 1000.0), + ); + // No rows laid out beyond row 4. + expect(find.text('R5:C0'), findsNothing); + // Change the vertical scroll offset, validate more rows were populated. + // This exceeds the bounds of the scroll view once the rows have been + // null terminated. + verticalController.jumpTo(1200.0); + await tester.pumpAndSettle(); + // Position was corrected. + expect(verticalController.position.pixels, 1000.0); + expect(horizontalController.position.pixels, 0.0); + // Max scroll extent was updated to reflect reaching the end of the rows + // after returning null. + expect(verticalController.position.maxScrollExtent, 1000.0); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R5:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R5:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R7:C4'), findsOneWidget); + expect( + tester.getRect(find.text('R7:C4')), + const Rect.fromLTRB(800.0, 400.0, 1000.0, 600.0), + ); + // No rows laid out before row 5, or after row 7, except for the first + // pinned row. + expect(find.text('R0:C0'), findsOneWidget); + expect(find.text('R1:C0'), findsNothing); + expect(find.text('R8:C0'), findsNothing); + + await tester.pumpWidget(Container()); + + // Pinned columns and rows --- + await tester.pumpWidget(MaterialApp( + home: getTableView( + columnCount: 10, + pinnedColumnCount: 1, + pinnedRowCount: 1, + rowBuilder: (int index) { + // There will only be 8 rows. + if (index == 8) { + return null; + } + return largeSpan; + }, + ), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C4'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C4')), + const Rect.fromLTRB(800.0, 800.0, 1000.0, 1000.0), + ); + // No rows laid out beyond row 4. + expect(find.text('R5:C0'), findsNothing); + // Change the vertical scroll offset, validate more rows were populated. + // This exceeds the bounds of the scroll view once the rows have been + // null terminated. + verticalController.jumpTo(1200.0); + await tester.pumpAndSettle(); + // Position was corrected. + expect(verticalController.position.pixels, 1000.0); + expect(horizontalController.position.pixels, 0.0); + // Max scroll extent was updated to reflect reaching the end of the rows + // after returning null. + expect(verticalController.position.maxScrollExtent, 1000.0); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R5:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R5:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R7:C4'), findsOneWidget); + expect( + tester.getRect(find.text('R7:C4')), + const Rect.fromLTRB(800.0, 400.0, 1000.0, 600.0), + ); + // No rows laid out before row 5, or after row 7, except for the first + // pinned row. + expect(find.text('R0:C0'), findsOneWidget); + expect(find.text('R1:C0'), findsNothing); + expect(find.text('R8:C0'), findsNothing); + }); + + testWidgets('Null terminated rows will update', + (WidgetTester tester) async { + await tester.pumpWidget(MaterialApp( + home: getTableView( + columnCount: 10, + rowBuilder: (int index) { + // There will only be 8 rows. + if (index == 8) { + return null; + } + return largeSpan; + }, + ), + )); + await tester.pumpAndSettle(); + // Change the vertical scroll offset, validate more rows were populated. + // This exceeds the bounds of the scroll view once the rows have been + // null terminated. + verticalController.jumpTo(1200.0); + await tester.pumpAndSettle(); + // Position was corrected. + expect(verticalController.position.pixels, 1000.0); + expect(horizontalController.position.pixels, 0.0); + // Max scroll extent was updated to reflect reaching the end of the rows + // after returning null. + expect(verticalController.position.maxScrollExtent, 1000.0); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R5:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R5:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R7:C4'), findsOneWidget); + expect( + tester.getRect(find.text('R7:C4')), + const Rect.fromLTRB(800.0, 400.0, 1000.0, 600.0), + ); + // No rows laid out before row 5, or after row 7. + expect(find.text('R0:C0'), findsNothing); + expect(find.text('R8:C0'), findsNothing); + + // Increase the number of rows + await tester.pumpWidget(MaterialApp( + home: getTableView( + columnCount: 10, + rowBuilder: (int index) { + // There will only be 16 rows. + if (index == 16) { + return null; + } + return largeSpan; + }, + ), + )); + await tester.pumpAndSettle(); + + // The position should not have changed. + expect(verticalController.position.pixels, 1000.0); + expect(horizontalController.position.pixels, 0.0); + // Max scroll extent was updated to reflect we no longer know where the + // end is, until the rowBuilder returns null again. + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, 1200.0); + // The layout should not have changed. + expect(find.text('R5:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R5:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R7:C4'), findsOneWidget); + expect( + tester.getRect(find.text('R7:C4')), + const Rect.fromLTRB(800.0, 400.0, 1000.0, 600.0), + ); + // No rows laid out before row 5, but more rows were laid out into the + // newly updated cacheExtent (row 8). + expect(find.text('R0:C0'), findsNothing); + expect(find.text('R8:C0'), findsOneWidget); + // This exceeds the new bounds. + verticalController.jumpTo(3200.0); + await tester.pumpAndSettle(); + // Position was corrected. + expect(verticalController.position.pixels, 2600.0); + expect(horizontalController.position.pixels, 0.0); + // Max scroll extent was updated to reflect reaching the end of the rows + // after returning null again at the new index. + expect(verticalController.position.maxScrollExtent, 2600.0); + expect(horizontalController.position.maxScrollExtent, 1200.0); + + // Decrease the number of rows + await tester.pumpWidget(MaterialApp( + home: getTableView( + columnCount: 10, + rowBuilder: (int index) { + // There will only be 5 rows. + if (index == 5) { + return null; + } + return largeSpan; + }, + ), + )); + await tester.pumpAndSettle(); + + // The position should have changed. + expect(verticalController.position.pixels, 400.0); + expect(horizontalController.position.pixels, 0.0); + // Max scroll extent was updated to the new end we have corrected to. + expect(verticalController.position.maxScrollExtent, 400.0); + expect(horizontalController.position.maxScrollExtent, 1200.0); + // The layout updated. + expect(find.text('R2:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R2:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C4'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C4')), + const Rect.fromLTRB(800.0, 400.0, 1000.0, 600.0), + ); + // No rows laid out after row 5. + expect(find.text('R5:C0'), findsNothing); + }); + + testWidgets('Null terminated columns will update', + (WidgetTester tester) async { + await tester.pumpWidget(MaterialApp( + home: getTableView( + rowCount: 10, + columnBuilder: (int index) { + // There will only be 8 columns. + if (index == 8) { + return null; + } + return largeSpan; + }, + ), + )); + await tester.pumpAndSettle(); + // Change the horizontal scroll offset, validate more columns were + // populated. This exceeds the bounds of the scroll view once the + // columns have been null terminated. + horizontalController.jumpTo(1400.0); + await tester.pumpAndSettle(); + // Position was corrected. + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 800.0); + // Max scroll extent was updated to reflect reaching the end of the + // columns after returning null. + expect(verticalController.position.maxScrollExtent, 1400.0); + expect(horizontalController.position.maxScrollExtent, 800.0); + expect(find.text('R0:C5'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C5')), + const Rect.fromLTRB(200.0, 0.0, 400.0, 200.0), + ); + expect(find.text('R4:C7'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C7')), + const Rect.fromLTRB(600.0, 800.0, 800.0, 1000.0), + ); + // No columns laid out before column 3, or after column 7. + expect(find.text('R0:C2'), findsNothing); + expect(find.text('R0:C8'), findsNothing); + + // Increase the number of rows + await tester.pumpWidget(MaterialApp( + home: getTableView( + rowCount: 10, + columnBuilder: (int index) { + // There will only be 16 column. + if (index == 16) { + return null; + } + return largeSpan; + }, + ), + )); + await tester.pumpAndSettle(); + + // The position should not have changed. + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 800.0); + // Max scroll extent was updated to reflect we no longer know where the + // end is, until the rowBuilder returns null again. + expect(verticalController.position.maxScrollExtent, 1400.0); + expect(horizontalController.position.maxScrollExtent, double.infinity); + // The layout should not have changed. + expect(find.text('R0:C5'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C5')), + const Rect.fromLTRB(200.0, 0.0, 400.0, 200.0), + ); + expect(find.text('R4:C7'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C7')), + const Rect.fromLTRB(600.0, 800.0, 800.0, 1000.0), + ); + // No columns laid out before column 3, but after column 7 we have added + // new columns. + expect(find.text('R0:C2'), findsNothing); + expect(find.text('R0:C8'), findsOneWidget); + // This exceeds the new bounds. + horizontalController.jumpTo(3200.0); + await tester.pumpAndSettle(); + // Position was corrected. + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 2400.0); + // Max scroll extent was updated to reflect reaching the end of the + // columns after returning null again at the new index. + expect(verticalController.position.maxScrollExtent, 1400.0); + expect(horizontalController.position.maxScrollExtent, 2400.0); + + // Decrease the number of columns + await tester.pumpWidget(MaterialApp( + home: getTableView( + rowCount: 10, + columnBuilder: (int index) { + // There will only be 5 columns. + if (index == 5) { + return null; + } + return largeSpan; + }, + ), + )); + await tester.pumpAndSettle(); + + // The position should have changed. + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 200.0); + // Max scroll extent was updated to the new end we have corrected to. + expect(verticalController.position.maxScrollExtent, 1400.0); + expect(horizontalController.position.maxScrollExtent, 200.0); + // The layout updated. + expect(find.text('R0:C2'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C2')), + const Rect.fromLTRB(200.0, 0.0, 400.0, 200.0), + ); + expect(find.text('R4:C4'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C4')), + const Rect.fromLTRB(600.0, 800.0, 800.0, 1000.0), + ); + // No columns laid out after column 5. + expect(find.text('R0:C5'), findsNothing); + }); + + testWidgets('infinite columns can null terminate', + (WidgetTester tester) async { + // Nothing pinned --- + bool calledOutOfBounds = false; + await tester.pumpWidget(MaterialApp( + home: getTableView( + rowCount: 10, + columnBuilder: (int index) { + // There will only be 10 columns. + if (index == 10) { + return null; + } + if (index > 10) { + calledOutOfBounds = true; + } + return largeSpan; + }, + ), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, 1400.00); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C5'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C5')), + const Rect.fromLTRB(1000.0, 800.0, 1200.0, 1000.0), + ); + // No columns laid out beyond column 5. + expect(find.text('R0:C6'), findsNothing); + // Change the horizontal scroll offset, validate more columns were + // populated. This exceeds the bounds of the scroll view once the + // columns have been null terminated. + horizontalController.jumpTo(1400.0); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + // Position was corrected. + expect(horizontalController.position.pixels, 1200.0); + // After null terminating, the builder was not called further. + expect(calledOutOfBounds, isFalse); + // Max scroll extent was updated to reflect reaching the end of the + // columns after returning null. + expect(verticalController.position.maxScrollExtent, 1400.0); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R0:C6'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C6')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C9'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C9')), + const Rect.fromLTRB(600.0, 800.0, 800.0, 1000.0), + ); + // No columns laid out before column 5, or after column 9. + expect(find.text('R0:C4'), findsNothing); + expect(find.text('R0:C10'), findsNothing); + + await tester.pumpWidget(Container()); + + // Pinned columns --- + await tester.pumpWidget(MaterialApp( + home: getTableView( + rowCount: 10, + pinnedColumnCount: 1, + columnBuilder: (int index) { + // There will only be 10 columns. + if (index == 10) { + return null; + } + return largeSpan; + }, + ), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, 1400.00); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C5'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C5')), + const Rect.fromLTRB(1000.0, 800.0, 1200.0, 1000.0), + ); + // No columns laid out beyond column 5. + expect(find.text('R0:C6'), findsNothing); + // Change the horizontal scroll offset, validate more columns were + // populated. This exceeds the bounds of the scroll view once the + // columns have been null terminated. + horizontalController.jumpTo(1400.0); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + // Position was corrected. + expect(horizontalController.position.pixels, 1200.0); + // Max scroll extent was updated to reflect reaching the end of the + // columns after returning null. + expect(verticalController.position.maxScrollExtent, 1400.0); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R0:C6'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C6')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C9'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C9')), + const Rect.fromLTRB(600.0, 800.0, 800.0, 1000.0), + ); + // No columns laid out before column 5, or after column 9, except for + // the pinned first column. + expect(find.text('R0:C0'), findsOneWidget); + expect(find.text('R0:C4'), findsNothing); + expect(find.text('R0:C10'), findsNothing); + + await tester.pumpWidget(Container()); + + // Pinned rows --- + await tester.pumpWidget(MaterialApp( + home: getTableView( + rowCount: 10, + pinnedRowCount: 1, + columnBuilder: (int index) { + // There will only be 10 columns. + if (index == 10) { + return null; + } + return largeSpan; + }, + ), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, 1400.00); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C5'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C5')), + const Rect.fromLTRB(1000.0, 800.0, 1200.0, 1000.0), + ); + // No columns laid out beyond column 5. + expect(find.text('R0:C6'), findsNothing); + // Change the horizontal scroll offset, validate more columns were + // populated. This exceeds the bounds of the scroll view once the + // columns have been null terminated. + horizontalController.jumpTo(1400.0); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + // Position was corrected. + expect(horizontalController.position.pixels, 1200.0); + // Max scroll extent was updated to reflect reaching the end of the + // columns after returning null. + expect(verticalController.position.maxScrollExtent, 1400.0); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R0:C6'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C6')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C9'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C9')), + const Rect.fromLTRB(600.0, 800.0, 800.0, 1000.0), + ); + // No columns laid out before column 5, or after column 9. + expect(find.text('R0:C4'), findsNothing); + expect(find.text('R0:C10'), findsNothing); + + await tester.pumpWidget(Container()); + + // Pinned columns and rows --- + await tester.pumpWidget(MaterialApp( + home: getTableView( + rowCount: 10, + pinnedRowCount: 1, + pinnedColumnCount: 1, + columnBuilder: (int index) { + // There will only be 10 columns. + if (index == 10) { + return null; + } + return largeSpan; + }, + ), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, 1400.00); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C5'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C5')), + const Rect.fromLTRB(1000.0, 800.0, 1200.0, 1000.0), + ); + // No columns laid out beyond column 5. + expect(find.text('R0:C6'), findsNothing); + // Change the horizontal scroll offset, validate more columns were + // populated. This exceeds the bounds of the scroll view once the + // columns have been null terminated. + horizontalController.jumpTo(1400.0); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + // Position was corrected. + expect(horizontalController.position.pixels, 1200.0); + // Max scroll extent was updated to reflect reaching the end of the + // columns after returning null. + expect(verticalController.position.maxScrollExtent, 1400.0); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R0:C6'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C6')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C9'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C9')), + const Rect.fromLTRB(600.0, 800.0, 800.0, 1000.0), + ); + // No columns laid out before column 5, or after column 9, except for + // the pinned first column. + expect(find.text('R0:C0'), findsOneWidget); + expect(find.text('R0:C4'), findsNothing); + expect(find.text('R0:C10'), findsNothing); + }); + testWidgets('infinite rows & columns can null terminate', + (WidgetTester tester) async { + // Nothing pinned --- + bool calledRowOutOfBounds = false; + bool calledColumnOutOfBounds = false; + await tester.pumpWidget(MaterialApp( + home: getTableView( + rowBuilder: (int index) { + // There will only be 8 rows. + if (index == 8) { + return null; + } + if (index > 8) { + calledRowOutOfBounds = true; + } + return largeSpan; + }, + columnBuilder: (int index) { + // There will only be 10 columns. + if (index == 10) { + return null; + } + if (index > 10) { + calledColumnOutOfBounds = true; + } + return largeSpan; + }, + ), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C5'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C5')), + const Rect.fromLTRB(1000.0, 800.0, 1200.0, 1000.0), + ); + // No columns laid out beyond column 5. + expect(find.text('R0:C6'), findsNothing); + // No rows laid out beyond row 4. + expect(find.text('R5:C0'), findsNothing); + // Change the scroll offsets, validate more columns and rows were + // populated. + // These exceed the bounds of the scroll view once the column and row + // builders have null terminated. + horizontalController.jumpTo(1400.0); + verticalController.jumpTo(1200.0); + await tester.pumpAndSettle(); + // Position was corrected for both. + expect(verticalController.position.pixels, 1000.0); + expect(horizontalController.position.pixels, 1200.0); + // After null terminating, the builders were not called further. + expect(calledRowOutOfBounds, isFalse); + expect(calledColumnOutOfBounds, isFalse); + // Max scroll extents were updated to reflect reaching the ends after + // returning null. + expect(verticalController.position.maxScrollExtent, 1000.0); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R5:C6'), findsOneWidget); + expect( + tester.getRect(find.text('R5:C6')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R7:C9'), findsOneWidget); + expect( + tester.getRect(find.text('R7:C9')), + const Rect.fromLTRB(600.0, 400.0, 800.0, 600.0), + ); + // No columns laid out before column 5, or after column 9. + expect(find.text('R5:C4'), findsNothing); + expect(find.text('R5:C10'), findsNothing); + // No rows laid out before row 4, or after row 7. + expect(find.text('R3:C6'), findsNothing); + expect(find.text('R8:C6'), findsNothing); + + await tester.pumpWidget(Container()); + + // Pinned columns --- + await tester.pumpWidget(MaterialApp( + home: getTableView( + rowBuilder: (int index) { + // There will only be 8 rows. + if (index == 8) { + return null; + } + return largeSpan; + }, + pinnedColumnCount: 1, + columnBuilder: (int index) { + // There will only be 10 columns. + if (index == 10) { + return null; + } + return largeSpan; + }, + ), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C5'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C5')), + const Rect.fromLTRB(1000.0, 800.0, 1200.0, 1000.0), + ); + // No columns laid out beyond column 5. + expect(find.text('R0:C6'), findsNothing); + // No rows laid out beyond row 4. + expect(find.text('R5:C0'), findsNothing); + // Change the scroll offsets, validate more columns and rows were + // populated. + // These exceed the bounds of the scroll view once the column and row + // builders have null terminated. + horizontalController.jumpTo(1400.0); + verticalController.jumpTo(1200.0); + await tester.pumpAndSettle(); + // Position was corrected for both. + expect(verticalController.position.pixels, 1000.0); + expect(horizontalController.position.pixels, 1200.0); + // Max scroll extents were updated to reflect reaching the ends after + // returning null. + expect(verticalController.position.maxScrollExtent, 1000.0); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R5:C6'), findsOneWidget); + expect( + tester.getRect(find.text('R5:C6')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R7:C9'), findsOneWidget); + expect( + tester.getRect(find.text('R7:C9')), + const Rect.fromLTRB(600.0, 400.0, 800.0, 600.0), + ); + // No columns laid out before column 5, or after column 9, except for + // the pinned first column. + expect(find.text('R5:C0'), findsOneWidget); + expect(find.text('R5:C4'), findsNothing); + expect(find.text('R5:C10'), findsNothing); + // No rows laid out before row 4, or after row 7. + expect(find.text('R3:C6'), findsNothing); + expect(find.text('R8:C6'), findsNothing); + + await tester.pumpWidget(Container()); + + // Pinned rows --- + await tester.pumpWidget(MaterialApp( + home: getTableView( + rowBuilder: (int index) { + // There will only be 8 rows. + if (index == 8) { + return null; + } + return largeSpan; + }, + pinnedRowCount: 1, + columnBuilder: (int index) { + // There will only be 10 columns. + if (index == 10) { + return null; + } + return largeSpan; + }, + ), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C5'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C5')), + const Rect.fromLTRB(1000.0, 800.0, 1200.0, 1000.0), + ); + // No columns laid out beyond column 5. + expect(find.text('R0:C6'), findsNothing); + // No rows laid out beyond row 4. + expect(find.text('R5:C0'), findsNothing); + // Change the scroll offsets, validate more columns and rows were + // populated. + // These exceed the bounds of the scroll view once the column and row + // builders have null terminated. + horizontalController.jumpTo(1400.0); + verticalController.jumpTo(1200.0); + await tester.pumpAndSettle(); + // Position was corrected for both. + expect(verticalController.position.pixels, 1000.0); + expect(horizontalController.position.pixels, 1200.0); + // Max scroll extents were updated to reflect reaching the ends after + // returning null. + expect(verticalController.position.maxScrollExtent, 1000.0); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R5:C6'), findsOneWidget); + expect( + tester.getRect(find.text('R5:C6')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R7:C9'), findsOneWidget); + expect( + tester.getRect(find.text('R7:C9')), + const Rect.fromLTRB(600.0, 400.0, 800.0, 600.0), + ); + // No columns laid out before column 5, or after column 9. + expect(find.text('R5:C4'), findsNothing); + expect(find.text('R5:C10'), findsNothing); + // No rows laid out before row 4, or after row 7, except for first + // pinned row. + expect(find.text('R0:C6'), findsOneWidget); + expect(find.text('R3:C6'), findsNothing); + expect(find.text('R8:C6'), findsNothing); + + await tester.pumpWidget(Container()); + + // Pinned columns and rows --- + await tester.pumpWidget(MaterialApp( + home: getTableView( + rowBuilder: (int index) { + // There will only be 8 rows. + if (index == 8) { + return null; + } + return largeSpan; + }, + pinnedRowCount: 1, + pinnedColumnCount: 1, + columnBuilder: (int index) { + // There will only be 10 columns. + if (index == 10) { + return null; + } + return largeSpan; + }, + ), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R4:C5'), findsOneWidget); + expect( + tester.getRect(find.text('R4:C5')), + const Rect.fromLTRB(1000.0, 800.0, 1200.0, 1000.0), + ); + // No columns laid out beyond column 5. + expect(find.text('R0:C6'), findsNothing); + // No rows laid out beyond row 4. + expect(find.text('R5:C0'), findsNothing); + // Change the scroll offsets, validate more columns and rows were + // populated. + // These exceed the bounds of the scroll view once the column and row + // builders have null terminated. + horizontalController.jumpTo(1400.0); + verticalController.jumpTo(1200.0); + await tester.pumpAndSettle(); + // Position was corrected for both. + expect(verticalController.position.pixels, 1000.0); + expect(horizontalController.position.pixels, 1200.0); + // Max scroll extents were updated to reflect reaching the ends after + // returning null. + expect(verticalController.position.maxScrollExtent, 1000.0); + expect(horizontalController.position.maxScrollExtent, 1200.0); + expect(find.text('R5:C6'), findsOneWidget); + expect( + tester.getRect(find.text('R5:C6')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), + ); + expect(find.text('R7:C9'), findsOneWidget); + expect( + tester.getRect(find.text('R7:C9')), + const Rect.fromLTRB(600.0, 400.0, 800.0, 600.0), + ); + // No columns laid out before column 5, or after column 9, except for + //the first pinned column. + expect(find.text('R5:C0'), findsOneWidget); + expect(find.text('R5:C4'), findsNothing); + expect(find.text('R5:C10'), findsNothing); + // No rows laid out before row 4, or after row 7, except for first + // pinned row. + expect(find.text('R0:C6'), findsOneWidget); + expect(find.text('R3:C6'), findsNothing); + expect(find.text('R8:C6'), findsNothing); + }); + testWidgets('merged cells work with lazy layout computation', + (WidgetTester tester) async { + // When columns and rows are finite, the layout is eagerly computed and + // the children are lazily laid out. This makes computing merged cell + // layouts easy. In an infinite world, the layout is also lazily + // computed, so not all of the information may be available for a merged + // cell if it extends into an area we have not computed the layout for + // yet. + const ({int start, int span}) rowConfig = (start: 0, span: 10); + final List mergedRows = List.generate( + 10, + (int index) => index, + ); + const ({int start, int span}) columnConfig = (start: 1, span: 10); + final List mergedColumns = List.generate( + 10, + (int index) => index + 1, + ); + await tester.pumpWidget(MaterialApp( + home: getTableView( + cellBuilder: (_, TableVicinity vicinity) { + // Merged row + if (mergedRows.contains(vicinity.row) && vicinity.column == 0) { + return TableViewCell( + rowMergeStart: rowConfig.start, + rowMergeSpan: rowConfig.span, + child: const Text('R0:C0'), + ); + } + // Merged column + if (mergedColumns.contains(vicinity.column) && + vicinity.row == 0) { + return TableViewCell( + columnMergeStart: columnConfig.start, + columnMergeSpan: columnConfig.span, + child: const Text('R0:C1'), + ); + } + return TableViewCell( + child: Text('R${vicinity.row}:C${vicinity.column}'), + ); + }, + ), + )); + await tester.pumpAndSettle(); + expect(verticalController.position.pixels, 0.0); + expect(horizontalController.position.pixels, 0.0); + expect(verticalController.position.maxScrollExtent, double.infinity); + expect(horizontalController.position.maxScrollExtent, double.infinity); + expect(find.text('R0:C0'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C0')), + const Rect.fromLTRB(0.0, 0.0, 200.0, 2000.0), + ); + expect(find.text('R0:C1'), findsOneWidget); + expect( + tester.getRect(find.text('R0:C1')), + const Rect.fromLTRB(200.0, 0.0, 2200.0, 200.0), + ); + expect(find.text('R1:C1'), findsOneWidget); + expect( + tester.getRect(find.text('R1:C1')), + const Rect.fromLTRB(200.0, 200.0, 400.0, 400.0), + ); + }); + + testWidgets('merged column that exceeds metrics will assert', + (WidgetTester tester) async { + final List exceptions = []; + final FlutterExceptionHandler? oldHandler = FlutterError.onError; + FlutterError.onError = (FlutterErrorDetails details) { + exceptions.add(details.exception); + }; + const ({int start, int span}) columnConfig = (start: 1, span: 10); + final List mergedColumns = List.generate( + 10, + (int index) => index + 1, + ); + await tester.pumpWidget(MaterialApp( + home: getTableView( + columnBuilder: (int index) { + // There will only be 8 columns, but the merge is set up for 10. + if (index == 8) { + return null; + } + return largeSpan; + }, + cellBuilder: (_, TableVicinity vicinity) { + // Merged column + if (mergedColumns.contains(vicinity.column) && + vicinity.row == 0) { + return TableViewCell( + columnMergeStart: columnConfig.start, + columnMergeSpan: columnConfig.span, + child: const Text('R0:C1'), + ); + } + return TableViewCell( + child: Text('R${vicinity.row}:C${vicinity.column}'), + ); + }, + ), + )); + await tester.pumpWidget(Container()); + FlutterError.onError = oldHandler; + expect(exceptions.length, 3); + expect( + exceptions.first.toString(), + contains( + 'The merged cell containing (row: 0, column: 1) is ' + 'missing TableSpan information necessary for layout. The ' + 'columnBuilder returned null, signifying the end, at column 8 but ' + 'the merged cell is configured to end with column 10.', + ), + ); + }); + + testWidgets('merged row that exceeds metrics will assert', + (WidgetTester tester) async { + final List exceptions = []; + final FlutterExceptionHandler? oldHandler = FlutterError.onError; + FlutterError.onError = (FlutterErrorDetails details) { + exceptions.add(details.exception); + }; + const ({int start, int span}) rowConfig = (start: 0, span: 10); + final List mergedRows = List.generate( + 10, + (int index) => index, + ); + await tester.pumpWidget(MaterialApp( + home: getTableView( + rowBuilder: (int index) { + // There will only be 8 rows, but the merge is set up for 9. + if (index == 8) { + return null; + } + return largeSpan; + }, + cellBuilder: (_, TableVicinity vicinity) { + // Merged column + if (mergedRows.contains(vicinity.row) && vicinity.column == 0) { + return TableViewCell( + rowMergeStart: rowConfig.start, + rowMergeSpan: rowConfig.span, + child: const Text('R0:C0'), + ); + } + return TableViewCell( + child: Text('R${vicinity.row}:C${vicinity.column}'), + ); + }, + ), + )); + await tester.pumpWidget(Container()); + FlutterError.onError = oldHandler; + expect(exceptions.length, 3); + expect( + exceptions.first.toString(), + contains( + 'The merged cell containing (row: 0, column: 0) is ' + 'missing TableSpan information necessary for layout. The ' + 'rowBuilder returned null, signifying the end, at row 8 but ' + 'the merged cell is configured to end with row 9.', + ), + ); + }); + }); }); group('TableView.list', () { @@ -580,7 +2436,7 @@ void main() { await tester.pumpAndSettle(); // Even columns and rows are set up for taps, mainAxis is vertical by - // default, mening row major order. Rows should take precedence where they + // default, meaning row major order. Rows should take precedence where they // intersect at even indices. expect(columnTapCounter, 0); expect(rowTapCounter, 0); From 67d90257cdce9ab483acec9b7bae9fcb835d7669 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 29 Mar 2024 13:17:22 -0400 Subject: [PATCH 124/126] Roll Flutter from 89ea49204b37 to 85288818b59e (11 revisions) (#6436) https://github.com/flutter/flutter/compare/89ea49204b37...85288818b59e 2024-03-29 34871572+gmackall@users.noreply.github.com Remove trailing commas in android dependency version checking gradle plugin (flutter/flutter#145718) 2024-03-29 polinach@google.com Upgrade leak_tracker. (flutter/flutter#145940) 2024-03-29 engine-flutter-autoroll@skia.org Roll Flutter Engine from 8b893cb3b607 to 68aa9ba386e1 (2 revisions) (flutter/flutter#145946) 2024-03-29 103135467+sealesj@users.noreply.github.com Refactor flutter_plugins (flutter/flutter#145870) 2024-03-29 34871572+gmackall@users.noreply.github.com Point kotlin message in `gradle_errors.dart` towards new place where templates define the kotlin version (flutter/flutter#145936) 2024-03-29 engine-flutter-autoroll@skia.org Roll Flutter Engine from b917b24836fd to 8b893cb3b607 (1 revision) (flutter/flutter#145941) 2024-03-28 engine-flutter-autoroll@skia.org Roll Flutter Engine from c176d114d01e to b917b24836fd (4 revisions) (flutter/flutter#145938) 2024-03-28 31859944+LongCatIsLooong@users.noreply.github.com `RenderFlex` baseline intrinsics (flutter/flutter#145483) 2024-03-28 godofredoc@google.com Use reporter extended consistently. (flutter/flutter#145617) 2024-03-28 36861262+QuncCccccc@users.noreply.github.com Update tokens to v2.3.5 (flutter/flutter#145356) 2024-03-28 engine-flutter-autoroll@skia.org Roll Flutter Engine from a396dc1a03a9 to c176d114d01e (2 revisions) (flutter/flutter#145934) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC dit@google.com,rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index bdb8491481f6..fe718452d8b6 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -89ea49204b37523a16daec53b5e6fae70995929d +85288818b59e1c9c3cc7dc6aa9694bdeab414a40 From d5aff1980bc8d13c714864e49d2ba295bf6a9e9a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 30 Mar 2024 14:58:23 -0400 Subject: [PATCH 125/126] Roll Flutter from 85288818b59e to d12ba5c270d8 (21 revisions) (#6440) https://github.com/flutter/flutter/compare/85288818b59e...d12ba5c270d8 2024-03-30 engine-flutter-autoroll@skia.org Roll Flutter Engine from 08eb5ad6e498 to 3588d31a98f6 (1 revision) (flutter/flutter#146031) 2024-03-30 engine-flutter-autoroll@skia.org Roll Flutter Engine from df581c316485 to 08eb5ad6e498 (2 revisions) (flutter/flutter#146028) 2024-03-30 engine-flutter-autoroll@skia.org Roll Flutter Engine from 5df1042cd927 to df581c316485 (1 revision) (flutter/flutter#146025) 2024-03-30 engine-flutter-autoroll@skia.org Roll Flutter Engine from dce6ce366c74 to 5df1042cd927 (2 revisions) (flutter/flutter#146024) 2024-03-30 engine-flutter-autoroll@skia.org Roll Flutter Engine from 221b49ae4a82 to dce6ce366c74 (1 revision) (flutter/flutter#146023) 2024-03-30 engine-flutter-autoroll@skia.org Roll Flutter Engine from 0a751669e722 to 221b49ae4a82 (1 revision) (flutter/flutter#146022) 2024-03-30 engine-flutter-autoroll@skia.org Roll Flutter Engine from 8ec35b6d63ba to 0a751669e722 (2 revisions) (flutter/flutter#146020) 2024-03-29 engine-flutter-autoroll@skia.org Roll Flutter Engine from 4b6836f8ef00 to 8ec35b6d63ba (3 revisions) (flutter/flutter#146014) 2024-03-29 engine-flutter-autoroll@skia.org Roll Flutter Engine from 4c079a6ff502 to 4b6836f8ef00 (3 revisions) (flutter/flutter#146010) 2024-03-29 nate.w5687@gmail.com Implementing switch expressions in `flutter_tools/` (flutter/flutter#145632) 2024-03-29 godofredoc@google.com Generate test metrics consistently. (flutter/flutter#145943) 2024-03-29 engine-flutter-autoroll@skia.org Roll Flutter Engine from 32c9dab552f0 to 4c079a6ff502 (3 revisions) (flutter/flutter#146007) 2024-03-29 ian@hixie.ch Add flutter_goldens README (flutter/flutter#145278) 2024-03-29 ian@hixie.ch Remove state shared across tests (flutter/flutter#145281) 2024-03-29 engine-flutter-autoroll@skia.org Roll Flutter Engine from 8480145cc39d to 32c9dab552f0 (1 revision) (flutter/flutter#146005) 2024-03-29 leroux_bruno@yahoo.fr InputDecorator M3 tests migration - Step7 - container (flutter/flutter#145583) 2024-03-29 engine-flutter-autoroll@skia.org Roll Flutter Engine from 7176173ea303 to 8480145cc39d (3 revisions) (flutter/flutter#146001) 2024-03-29 engine-flutter-autoroll@skia.org Roll Flutter Engine from b16c0f136cdd to 7176173ea303 (1 revision) (flutter/flutter#145994) 2024-03-29 polinach@google.com Upgrade leak_tracker. (flutter/flutter#145997) 2024-03-29 engine-flutter-autoroll@skia.org Roll Flutter Engine from 68aa9ba386e1 to b16c0f136cdd (10 revisions) (flutter/flutter#145990) 2024-03-29 engine-flutter-autoroll@skia.org Roll Packages from 924c7e6e899d to 51faaa156b61 (8 revisions) (flutter/flutter#145986) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC dit@google.com,rmistry@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- .ci/flutter_master.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index fe718452d8b6..ac3ccc411fdc 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -85288818b59e1c9c3cc7dc6aa9694bdeab414a40 +d12ba5c270d83c63cd3c1e89c1cd1f279bbb5696 From 8fcffdd797d5930a4a659ac83b681d6e02a2c860 Mon Sep 17 00:00:00 2001 From: Reid Baker Date: Mon, 1 Apr 2024 09:46:22 -0400 Subject: [PATCH 126/126] Move integration_test dependency from regular dependencies to test dependencies --- packages/camera/camera_android_camerax/CHANGELOG.md | 4 ++++ packages/camera/camera_android_camerax/pubspec.yaml | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index 68fdea1e7554..83ad40f8f5b5 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.2 + +* Move integration_test dependency to test. + ## 0.6.1 * Modifies resolution selection logic to use an `AspectRatioStrategy` for all aspect ratios supported by CameraX. diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index 0f4756be1bb7..cb53c9d6c636 100644 --- a/packages/camera/camera_android_camerax/pubspec.yaml +++ b/packages/camera/camera_android_camerax/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android_camerax description: Android implementation of the camera plugin using the CameraX library. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android_camerax issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.6.1 +version: 0.6.2 environment: sdk: ^3.1.0 @@ -22,8 +22,6 @@ dependencies: camera_platform_interface: ^2.3.2 flutter: sdk: flutter - integration_test: - sdk: flutter meta: ^1.7.0 stream_transform: ^2.1.0 @@ -31,6 +29,8 @@ dev_dependencies: build_runner: ^2.2.0 flutter_test: sdk: flutter + integration_test: + sdk: flutter mockito: 5.4.4 pigeon: ^9.1.0